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) {
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)
4527 if (is_edit && this.is_header_editing) {
4528 return; // already editing..
4532 this.headerEditEl.dom.value = this.title;
4533 this.headerEditEl.removeClass('d-none');
4534 this.headerEditEl.dom.focus();
4535 this.titleEl.addClass('d-none');
4537 this.is_header_editing = true;
4540 // flip back to not editing.
4541 this.title = this.headerEditEl.dom.value;
4542 this.headerEditEl.addClass('d-none');
4543 this.titleEl.removeClass('d-none');
4544 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4545 this.is_header_editing = false;
4546 this.fireEvent('titlechanged', this, this.title);
4555 Roo.apply(Roo.bootstrap.Modal, {
4557 * Button config that displays a single OK button
4566 * Button config that displays Yes and No buttons
4582 * Button config that displays OK and Cancel buttons
4597 * Button config that displays Yes, No and Cancel buttons
4622 * messagebox - can be used as a replace
4626 * @class Roo.MessageBox
4627 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4631 Roo.Msg.alert('Status', 'Changes saved successfully.');
4633 // Prompt for user data:
4634 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4636 // process text value...
4640 // Show a dialog using config options:
4642 title:'Save Changes?',
4643 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4644 buttons: Roo.Msg.YESNOCANCEL,
4651 Roo.bootstrap.MessageBox = function(){
4652 var dlg, opt, mask, waitTimer;
4653 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4654 var buttons, activeTextEl, bwidth;
4658 var handleButton = function(button){
4660 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4664 var handleHide = function(){
4666 dlg.el.removeClass(opt.cls);
4669 // Roo.TaskMgr.stop(waitTimer);
4670 // waitTimer = null;
4675 var updateButtons = function(b){
4678 buttons["ok"].hide();
4679 buttons["cancel"].hide();
4680 buttons["yes"].hide();
4681 buttons["no"].hide();
4682 dlg.footerEl.hide();
4686 dlg.footerEl.show();
4687 for(var k in buttons){
4688 if(typeof buttons[k] != "function"){
4691 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4692 width += buttons[k].el.getWidth()+15;
4702 var handleEsc = function(d, k, e){
4703 if(opt && opt.closable !== false){
4713 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4714 * @return {Roo.BasicDialog} The BasicDialog element
4716 getDialog : function(){
4718 dlg = new Roo.bootstrap.Modal( {
4721 //constraintoviewport:false,
4723 //collapsible : false,
4728 //buttonAlign:"center",
4729 closeClick : function(){
4730 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4733 handleButton("cancel");
4738 dlg.on("hide", handleHide);
4740 //dlg.addKeyListener(27, handleEsc);
4742 this.buttons = buttons;
4743 var bt = this.buttonText;
4744 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4745 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4746 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4747 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4749 bodyEl = dlg.bodyEl.createChild({
4751 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4752 '<textarea class="roo-mb-textarea"></textarea>' +
4753 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4755 msgEl = bodyEl.dom.firstChild;
4756 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4757 textboxEl.enableDisplayMode();
4758 textboxEl.addKeyListener([10,13], function(){
4759 if(dlg.isVisible() && opt && opt.buttons){
4762 }else if(opt.buttons.yes){
4763 handleButton("yes");
4767 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4768 textareaEl.enableDisplayMode();
4769 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4770 progressEl.enableDisplayMode();
4772 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4773 var pf = progressEl.dom.firstChild;
4775 pp = Roo.get(pf.firstChild);
4776 pp.setHeight(pf.offsetHeight);
4784 * Updates the message box body text
4785 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4786 * the XHTML-compliant non-breaking space character '&#160;')
4787 * @return {Roo.MessageBox} This message box
4789 updateText : function(text)
4791 if(!dlg.isVisible() && !opt.width){
4792 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4793 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4795 msgEl.innerHTML = text || ' ';
4797 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4798 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4800 Math.min(opt.width || cw , this.maxWidth),
4801 Math.max(opt.minWidth || this.minWidth, bwidth)
4804 activeTextEl.setWidth(w);
4806 if(dlg.isVisible()){
4807 dlg.fixedcenter = false;
4809 // to big, make it scroll. = But as usual stupid IE does not support
4812 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4813 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4814 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4816 bodyEl.dom.style.height = '';
4817 bodyEl.dom.style.overflowY = '';
4820 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4822 bodyEl.dom.style.overflowX = '';
4825 dlg.setContentSize(w, bodyEl.getHeight());
4826 if(dlg.isVisible()){
4827 dlg.fixedcenter = true;
4833 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4834 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4835 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4836 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4837 * @return {Roo.MessageBox} This message box
4839 updateProgress : function(value, text){
4841 this.updateText(text);
4844 if (pp) { // weird bug on my firefox - for some reason this is not defined
4845 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4846 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4852 * Returns true if the message box is currently displayed
4853 * @return {Boolean} True if the message box is visible, else false
4855 isVisible : function(){
4856 return dlg && dlg.isVisible();
4860 * Hides the message box if it is displayed
4863 if(this.isVisible()){
4869 * Displays a new message box, or reinitializes an existing message box, based on the config options
4870 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4871 * The following config object properties are supported:
4873 Property Type Description
4874 ---------- --------------- ------------------------------------------------------------------------------------
4875 animEl String/Element An id or Element from which the message box should animate as it opens and
4876 closes (defaults to undefined)
4877 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4878 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4879 closable Boolean False to hide the top-right close button (defaults to true). Note that
4880 progress and wait dialogs will ignore this property and always hide the
4881 close button as they can only be closed programmatically.
4882 cls String A custom CSS class to apply to the message box element
4883 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4884 displayed (defaults to 75)
4885 fn Function A callback function to execute after closing the dialog. The arguments to the
4886 function will be btn (the name of the button that was clicked, if applicable,
4887 e.g. "ok"), and text (the value of the active text field, if applicable).
4888 Progress and wait dialogs will ignore this option since they do not respond to
4889 user actions and can only be closed programmatically, so any required function
4890 should be called by the same code after it closes the dialog.
4891 icon String A CSS class that provides a background image to be used as an icon for
4892 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4893 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4894 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4895 modal Boolean False to allow user interaction with the page while the message box is
4896 displayed (defaults to true)
4897 msg String A string that will replace the existing message box body text (defaults
4898 to the XHTML-compliant non-breaking space character ' ')
4899 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4900 progress Boolean True to display a progress bar (defaults to false)
4901 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4902 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4903 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4904 title String The title text
4905 value String The string value to set into the active textbox element if displayed
4906 wait Boolean True to display a progress bar (defaults to false)
4907 width Number The width of the dialog in pixels
4914 msg: 'Please enter your address:',
4916 buttons: Roo.MessageBox.OKCANCEL,
4919 animEl: 'addAddressBtn'
4922 * @param {Object} config Configuration options
4923 * @return {Roo.MessageBox} This message box
4925 show : function(options)
4928 // this causes nightmares if you show one dialog after another
4929 // especially on callbacks..
4931 if(this.isVisible()){
4934 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4935 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4936 Roo.log("New Dialog Message:" + options.msg )
4937 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4938 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4941 var d = this.getDialog();
4943 d.setTitle(opt.title || " ");
4944 d.closeEl.setDisplayed(opt.closable !== false);
4945 activeTextEl = textboxEl;
4946 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4951 textareaEl.setHeight(typeof opt.multiline == "number" ?
4952 opt.multiline : this.defaultTextHeight);
4953 activeTextEl = textareaEl;
4962 progressEl.setDisplayed(opt.progress === true);
4964 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4966 this.updateProgress(0);
4967 activeTextEl.dom.value = opt.value || "";
4969 dlg.setDefaultButton(activeTextEl);
4971 var bs = opt.buttons;
4975 }else if(bs && bs.yes){
4976 db = buttons["yes"];
4978 dlg.setDefaultButton(db);
4980 bwidth = updateButtons(opt.buttons);
4981 this.updateText(opt.msg);
4983 d.el.addClass(opt.cls);
4985 d.proxyDrag = opt.proxyDrag === true;
4986 d.modal = opt.modal !== false;
4987 d.mask = opt.modal !== false ? mask : false;
4989 // force it to the end of the z-index stack so it gets a cursor in FF
4990 document.body.appendChild(dlg.el.dom);
4991 d.animateTarget = null;
4992 d.show(options.animEl);
4998 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
4999 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5000 * and closing the message box when the process is complete.
5001 * @param {String} title The title bar text
5002 * @param {String} msg The message box body text
5003 * @return {Roo.MessageBox} This message box
5005 progress : function(title, msg){
5012 minWidth: this.minProgressWidth,
5019 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5020 * If a callback function is passed it will be called after the user clicks the button, and the
5021 * id of the button that was clicked will be passed as the only parameter to the callback
5022 * (could also be the top-right close button).
5023 * @param {String} title The title bar text
5024 * @param {String} msg The message box body text
5025 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5026 * @param {Object} scope (optional) The scope of the callback function
5027 * @return {Roo.MessageBox} This message box
5029 alert : function(title, msg, fn, scope)
5044 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5045 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5046 * You are responsible for closing the message box when the process is complete.
5047 * @param {String} msg The message box body text
5048 * @param {String} title (optional) The title bar text
5049 * @return {Roo.MessageBox} This message box
5051 wait : function(msg, title){
5062 waitTimer = Roo.TaskMgr.start({
5064 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5072 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5073 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5074 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5075 * @param {String} title The title bar text
5076 * @param {String} msg The message box body text
5077 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5078 * @param {Object} scope (optional) The scope of the callback function
5079 * @return {Roo.MessageBox} This message box
5081 confirm : function(title, msg, fn, scope){
5085 buttons: this.YESNO,
5094 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5095 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5096 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5097 * (could also be the top-right close button) and the text that was entered will be passed as the two
5098 * parameters to the callback.
5099 * @param {String} title The title bar text
5100 * @param {String} msg The message box body text
5101 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5102 * @param {Object} scope (optional) The scope of the callback function
5103 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5104 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5105 * @return {Roo.MessageBox} This message box
5107 prompt : function(title, msg, fn, scope, multiline){
5111 buttons: this.OKCANCEL,
5116 multiline: multiline,
5123 * Button config that displays a single OK button
5128 * Button config that displays Yes and No buttons
5131 YESNO : {yes:true, no:true},
5133 * Button config that displays OK and Cancel buttons
5136 OKCANCEL : {ok:true, cancel:true},
5138 * Button config that displays Yes, No and Cancel buttons
5141 YESNOCANCEL : {yes:true, no:true, cancel:true},
5144 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5147 defaultTextHeight : 75,
5149 * The maximum width in pixels of the message box (defaults to 600)
5154 * The minimum width in pixels of the message box (defaults to 100)
5159 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5160 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5163 minProgressWidth : 250,
5165 * An object containing the default button text strings that can be overriden for localized language support.
5166 * Supported properties are: ok, cancel, yes and no.
5167 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5180 * Shorthand for {@link Roo.MessageBox}
5182 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5183 Roo.Msg = Roo.Msg || Roo.MessageBox;
5192 * @class Roo.bootstrap.Navbar
5193 * @extends Roo.bootstrap.Component
5194 * Bootstrap Navbar class
5197 * Create a new Navbar
5198 * @param {Object} config The config object
5202 Roo.bootstrap.Navbar = function(config){
5203 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5207 * @event beforetoggle
5208 * Fire before toggle the menu
5209 * @param {Roo.EventObject} e
5211 "beforetoggle" : true
5215 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5224 getAutoCreate : function(){
5227 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5231 initEvents :function ()
5233 //Roo.log(this.el.select('.navbar-toggle',true));
5234 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5241 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5243 var size = this.el.getSize();
5244 this.maskEl.setSize(size.width, size.height);
5245 this.maskEl.enableDisplayMode("block");
5254 getChildContainer : function()
5256 if (this.el && this.el.select('.collapse').getCount()) {
5257 return this.el.select('.collapse',true).first();
5272 onToggle : function()
5275 if(this.fireEvent('beforetoggle', this) === false){
5278 var ce = this.el.select('.navbar-collapse',true).first();
5280 if (!ce.hasClass('show')) {
5290 * Expand the navbar pulldown
5292 expand : function ()
5295 var ce = this.el.select('.navbar-collapse',true).first();
5296 if (ce.hasClass('collapsing')) {
5299 ce.dom.style.height = '';
5301 ce.addClass('in'); // old...
5302 ce.removeClass('collapse');
5303 ce.addClass('show');
5304 var h = ce.getHeight();
5306 ce.removeClass('show');
5307 // at this point we should be able to see it..
5308 ce.addClass('collapsing');
5310 ce.setHeight(0); // resize it ...
5311 ce.on('transitionend', function() {
5312 //Roo.log('done transition');
5313 ce.removeClass('collapsing');
5314 ce.addClass('show');
5315 ce.removeClass('collapse');
5317 ce.dom.style.height = '';
5318 }, this, { single: true} );
5320 ce.dom.scrollTop = 0;
5323 * Collapse the navbar pulldown
5325 collapse : function()
5327 var ce = this.el.select('.navbar-collapse',true).first();
5329 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5330 // it's collapsed or collapsing..
5333 ce.removeClass('in'); // old...
5334 ce.setHeight(ce.getHeight());
5335 ce.removeClass('show');
5336 ce.addClass('collapsing');
5338 ce.on('transitionend', function() {
5339 ce.dom.style.height = '';
5340 ce.removeClass('collapsing');
5341 ce.addClass('collapse');
5342 }, this, { single: true} );
5362 * @class Roo.bootstrap.NavSimplebar
5363 * @extends Roo.bootstrap.Navbar
5364 * Bootstrap Sidebar class
5366 * @cfg {Boolean} inverse is inverted color
5368 * @cfg {String} type (nav | pills | tabs)
5369 * @cfg {Boolean} arrangement stacked | justified
5370 * @cfg {String} align (left | right) alignment
5372 * @cfg {Boolean} main (true|false) main nav bar? default false
5373 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5375 * @cfg {String} tag (header|footer|nav|div) default is nav
5377 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5381 * Create a new Sidebar
5382 * @param {Object} config The config object
5386 Roo.bootstrap.NavSimplebar = function(config){
5387 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5390 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5406 getAutoCreate : function(){
5410 tag : this.tag || 'div',
5411 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5413 if (['light','white'].indexOf(this.weight) > -1) {
5414 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5416 cfg.cls += ' bg-' + this.weight;
5419 cfg.cls += ' navbar-inverse';
5423 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5425 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5434 cls: 'nav nav-' + this.xtype,
5440 this.type = this.type || 'nav';
5441 if (['tabs','pills'].indexOf(this.type) != -1) {
5442 cfg.cn[0].cls += ' nav-' + this.type
5446 if (this.type!=='nav') {
5447 Roo.log('nav type must be nav/tabs/pills')
5449 cfg.cn[0].cls += ' navbar-nav'
5455 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5456 cfg.cn[0].cls += ' nav-' + this.arrangement;
5460 if (this.align === 'right') {
5461 cfg.cn[0].cls += ' navbar-right';
5486 * navbar-expand-md fixed-top
5490 * @class Roo.bootstrap.NavHeaderbar
5491 * @extends Roo.bootstrap.NavSimplebar
5492 * Bootstrap Sidebar class
5494 * @cfg {String} brand what is brand
5495 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5496 * @cfg {String} brand_href href of the brand
5497 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5498 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5499 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5500 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5503 * Create a new Sidebar
5504 * @param {Object} config The config object
5508 Roo.bootstrap.NavHeaderbar = function(config){
5509 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5513 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5520 desktopCenter : false,
5523 getAutoCreate : function(){
5526 tag: this.nav || 'nav',
5527 cls: 'navbar navbar-expand-md',
5533 if (this.desktopCenter) {
5534 cn.push({cls : 'container', cn : []});
5542 cls: 'navbar-toggle navbar-toggler',
5543 'data-toggle': 'collapse',
5548 html: 'Toggle navigation'
5552 cls: 'icon-bar navbar-toggler-icon'
5565 cn.push( Roo.bootstrap.version == 4 ? btn : {
5567 cls: 'navbar-header',
5576 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5580 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5582 if (['light','white'].indexOf(this.weight) > -1) {
5583 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5585 cfg.cls += ' bg-' + this.weight;
5588 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5589 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5591 // tag can override this..
5593 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5596 if (this.brand !== '') {
5597 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5598 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5600 href: this.brand_href ? this.brand_href : '#',
5601 cls: 'navbar-brand',
5609 cfg.cls += ' main-nav';
5617 getHeaderChildContainer : function()
5619 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5620 return this.el.select('.navbar-header',true).first();
5623 return this.getChildContainer();
5626 getChildContainer : function()
5629 return this.el.select('.roo-navbar-collapse',true).first();
5634 initEvents : function()
5636 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5638 if (this.autohide) {
5643 Roo.get(document).on('scroll',function(e) {
5644 var ns = Roo.get(document).getScroll().top;
5645 var os = prevScroll;
5649 ft.removeClass('slideDown');
5650 ft.addClass('slideUp');
5653 ft.removeClass('slideUp');
5654 ft.addClass('slideDown');
5675 * @class Roo.bootstrap.NavSidebar
5676 * @extends Roo.bootstrap.Navbar
5677 * Bootstrap Sidebar class
5680 * Create a new Sidebar
5681 * @param {Object} config The config object
5685 Roo.bootstrap.NavSidebar = function(config){
5686 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5689 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5691 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5693 getAutoCreate : function(){
5698 cls: 'sidebar sidebar-nav'
5720 * @class Roo.bootstrap.NavGroup
5721 * @extends Roo.bootstrap.Component
5722 * Bootstrap NavGroup class
5723 * @cfg {String} align (left|right)
5724 * @cfg {Boolean} inverse
5725 * @cfg {String} type (nav|pills|tab) default nav
5726 * @cfg {String} navId - reference Id for navbar.
5730 * Create a new nav group
5731 * @param {Object} config The config object
5734 Roo.bootstrap.NavGroup = function(config){
5735 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5738 Roo.bootstrap.NavGroup.register(this);
5742 * Fires when the active item changes
5743 * @param {Roo.bootstrap.NavGroup} this
5744 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5745 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5752 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5763 getAutoCreate : function()
5765 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5771 if (Roo.bootstrap.version == 4) {
5772 if (['tabs','pills'].indexOf(this.type) != -1) {
5773 cfg.cls += ' nav-' + this.type;
5775 // trying to remove so header bar can right align top?
5776 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5777 // do not use on header bar...
5778 cfg.cls += ' navbar-nav';
5783 if (['tabs','pills'].indexOf(this.type) != -1) {
5784 cfg.cls += ' nav-' + this.type
5786 if (this.type !== 'nav') {
5787 Roo.log('nav type must be nav/tabs/pills')
5789 cfg.cls += ' navbar-nav'
5793 if (this.parent() && this.parent().sidebar) {
5796 cls: 'dashboard-menu sidebar-menu'
5802 if (this.form === true) {
5805 cls: 'navbar-form form-inline'
5807 //nav navbar-right ml-md-auto
5808 if (this.align === 'right') {
5809 cfg.cls += ' navbar-right ml-md-auto';
5811 cfg.cls += ' navbar-left';
5815 if (this.align === 'right') {
5816 cfg.cls += ' navbar-right ml-md-auto';
5818 cfg.cls += ' mr-auto';
5822 cfg.cls += ' navbar-inverse';
5830 * sets the active Navigation item
5831 * @param {Roo.bootstrap.NavItem} the new current navitem
5833 setActiveItem : function(item)
5836 Roo.each(this.navItems, function(v){
5841 v.setActive(false, true);
5848 item.setActive(true, true);
5849 this.fireEvent('changed', this, item, prev);
5854 * gets the active Navigation item
5855 * @return {Roo.bootstrap.NavItem} the current navitem
5857 getActive : function()
5861 Roo.each(this.navItems, function(v){
5872 indexOfNav : function()
5876 Roo.each(this.navItems, function(v,i){
5887 * adds a Navigation item
5888 * @param {Roo.bootstrap.NavItem} the navitem to add
5890 addItem : function(cfg)
5892 if (this.form && Roo.bootstrap.version == 4) {
5895 var cn = new Roo.bootstrap.NavItem(cfg);
5897 cn.parentId = this.id;
5898 cn.onRender(this.el, null);
5902 * register a Navigation item
5903 * @param {Roo.bootstrap.NavItem} the navitem to add
5905 register : function(item)
5907 this.navItems.push( item);
5908 item.navId = this.navId;
5913 * clear all the Navigation item
5916 clearAll : function()
5919 this.el.dom.innerHTML = '';
5922 getNavItem: function(tabId)
5925 Roo.each(this.navItems, function(e) {
5926 if (e.tabId == tabId) {
5936 setActiveNext : function()
5938 var i = this.indexOfNav(this.getActive());
5939 if (i > this.navItems.length) {
5942 this.setActiveItem(this.navItems[i+1]);
5944 setActivePrev : function()
5946 var i = this.indexOfNav(this.getActive());
5950 this.setActiveItem(this.navItems[i-1]);
5952 clearWasActive : function(except) {
5953 Roo.each(this.navItems, function(e) {
5954 if (e.tabId != except.tabId && e.was_active) {
5955 e.was_active = false;
5962 getWasActive : function ()
5965 Roo.each(this.navItems, function(e) {
5980 Roo.apply(Roo.bootstrap.NavGroup, {
5984 * register a Navigation Group
5985 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5987 register : function(navgrp)
5989 this.groups[navgrp.navId] = navgrp;
5993 * fetch a Navigation Group based on the navigation ID
5994 * @param {string} the navgroup to add
5995 * @returns {Roo.bootstrap.NavGroup} the navgroup
5997 get: function(navId) {
5998 if (typeof(this.groups[navId]) == 'undefined') {
6000 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6002 return this.groups[navId] ;
6017 * @class Roo.bootstrap.NavItem
6018 * @extends Roo.bootstrap.Component
6019 * Bootstrap Navbar.NavItem class
6020 * @cfg {String} href link to
6021 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6023 * @cfg {String} html content of button
6024 * @cfg {String} badge text inside badge
6025 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6026 * @cfg {String} glyphicon DEPRICATED - use fa
6027 * @cfg {String} icon DEPRICATED - use fa
6028 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6029 * @cfg {Boolean} active Is item active
6030 * @cfg {Boolean} disabled Is item disabled
6032 * @cfg {Boolean} preventDefault (true | false) default false
6033 * @cfg {String} tabId the tab that this item activates.
6034 * @cfg {String} tagtype (a|span) render as a href or span?
6035 * @cfg {Boolean} animateRef (true|false) link to element default false
6038 * Create a new Navbar Item
6039 * @param {Object} config The config object
6041 Roo.bootstrap.NavItem = function(config){
6042 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6047 * The raw click event for the entire grid.
6048 * @param {Roo.EventObject} e
6053 * Fires when the active item active state changes
6054 * @param {Roo.bootstrap.NavItem} this
6055 * @param {boolean} state the new state
6061 * Fires when scroll to element
6062 * @param {Roo.bootstrap.NavItem} this
6063 * @param {Object} options
6064 * @param {Roo.EventObject} e
6072 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6081 preventDefault : false,
6089 button_outline : false,
6093 getAutoCreate : function(){
6101 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6103 if (this.disabled) {
6104 cfg.cls += ' disabled';
6108 if (this.button_weight.length) {
6109 cfg.tag = this.href ? 'a' : 'button';
6110 cfg.html = this.html || '';
6111 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6113 cfg.href = this.href;
6116 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6119 // menu .. should add dropdown-menu class - so no need for carat..
6121 if (this.badge !== '') {
6123 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6128 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6132 href : this.href || "#",
6133 html: this.html || ''
6136 if (this.tagtype == 'a') {
6137 cfg.cn[0].cls = 'nav-link';
6140 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6143 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6145 if(this.glyphicon) {
6146 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6151 cfg.cn[0].html += " <span class='caret'></span>";
6155 if (this.badge !== '') {
6157 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6165 onRender : function(ct, position)
6167 // Roo.log("Call onRender: " + this.xtype);
6168 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6172 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6173 this.navLink = this.el.select('.nav-link',true).first();
6178 initEvents: function()
6180 if (typeof (this.menu) != 'undefined') {
6181 this.menu.parentType = this.xtype;
6182 this.menu.triggerEl = this.el;
6183 this.menu = this.addxtype(Roo.apply({}, this.menu));
6186 this.el.select('a',true).on('click', this.onClick, this);
6188 if(this.tagtype == 'span'){
6189 this.el.select('span',true).on('click', this.onClick, this);
6192 // at this point parent should be available..
6193 this.parent().register(this);
6196 onClick : function(e)
6198 if (e.getTarget('.dropdown-menu-item')) {
6199 // did you click on a menu itemm.... - then don't trigger onclick..
6204 this.preventDefault ||
6207 Roo.log("NavItem - prevent Default?");
6211 if (this.disabled) {
6215 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6216 if (tg && tg.transition) {
6217 Roo.log("waiting for the transitionend");
6223 //Roo.log("fire event clicked");
6224 if(this.fireEvent('click', this, e) === false){
6228 if(this.tagtype == 'span'){
6232 //Roo.log(this.href);
6233 var ael = this.el.select('a',true).first();
6236 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6237 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6238 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6239 return; // ignore... - it's a 'hash' to another page.
6241 Roo.log("NavItem - prevent Default?");
6243 this.scrollToElement(e);
6247 var p = this.parent();
6249 if (['tabs','pills'].indexOf(p.type)!==-1) {
6250 if (typeof(p.setActiveItem) !== 'undefined') {
6251 p.setActiveItem(this);
6255 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6256 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6257 // remove the collapsed menu expand...
6258 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6262 isActive: function () {
6265 setActive : function(state, fire, is_was_active)
6267 if (this.active && !state && this.navId) {
6268 this.was_active = true;
6269 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6271 nv.clearWasActive(this);
6275 this.active = state;
6278 this.el.removeClass('active');
6279 this.navLink ? this.navLink.removeClass('active') : false;
6280 } else if (!this.el.hasClass('active')) {
6282 this.el.addClass('active');
6283 if (Roo.bootstrap.version == 4 && this.navLink ) {
6284 this.navLink.addClass('active');
6289 this.fireEvent('changed', this, state);
6292 // show a panel if it's registered and related..
6294 if (!this.navId || !this.tabId || !state || is_was_active) {
6298 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6302 var pan = tg.getPanelByName(this.tabId);
6306 // if we can not flip to new panel - go back to old nav highlight..
6307 if (false == tg.showPanel(pan)) {
6308 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6310 var onav = nv.getWasActive();
6312 onav.setActive(true, false, true);
6321 // this should not be here...
6322 setDisabled : function(state)
6324 this.disabled = state;
6326 this.el.removeClass('disabled');
6327 } else if (!this.el.hasClass('disabled')) {
6328 this.el.addClass('disabled');
6334 * Fetch the element to display the tooltip on.
6335 * @return {Roo.Element} defaults to this.el
6337 tooltipEl : function()
6339 return this.el.select('' + this.tagtype + '', true).first();
6342 scrollToElement : function(e)
6344 var c = document.body;
6347 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6349 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6350 c = document.documentElement;
6353 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6359 var o = target.calcOffsetsTo(c);
6366 this.fireEvent('scrollto', this, options, e);
6368 Roo.get(c).scrollTo('top', options.value, true);
6381 * <span> icon </span>
6382 * <span> text </span>
6383 * <span>badge </span>
6387 * @class Roo.bootstrap.NavSidebarItem
6388 * @extends Roo.bootstrap.NavItem
6389 * Bootstrap Navbar.NavSidebarItem class
6390 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6391 * {Boolean} open is the menu open
6392 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6393 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6394 * {String} buttonSize (sm|md|lg)the extra classes for the button
6395 * {Boolean} showArrow show arrow next to the text (default true)
6397 * Create a new Navbar Button
6398 * @param {Object} config The config object
6400 Roo.bootstrap.NavSidebarItem = function(config){
6401 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6406 * The raw click event for the entire grid.
6407 * @param {Roo.EventObject} e
6412 * Fires when the active item active state changes
6413 * @param {Roo.bootstrap.NavSidebarItem} this
6414 * @param {boolean} state the new state
6422 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6424 badgeWeight : 'default',
6430 buttonWeight : 'default',
6436 getAutoCreate : function(){
6441 href : this.href || '#',
6447 if(this.buttonView){
6450 href : this.href || '#',
6451 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6464 cfg.cls += ' active';
6467 if (this.disabled) {
6468 cfg.cls += ' disabled';
6471 cfg.cls += ' open x-open';
6474 if (this.glyphicon || this.icon) {
6475 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6476 a.cn.push({ tag : 'i', cls : c }) ;
6479 if(!this.buttonView){
6482 html : this.html || ''
6489 if (this.badge !== '') {
6490 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6496 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6499 a.cls += ' dropdown-toggle treeview' ;
6505 initEvents : function()
6507 if (typeof (this.menu) != 'undefined') {
6508 this.menu.parentType = this.xtype;
6509 this.menu.triggerEl = this.el;
6510 this.menu = this.addxtype(Roo.apply({}, this.menu));
6513 this.el.on('click', this.onClick, this);
6515 if(this.badge !== ''){
6516 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6521 onClick : function(e)
6528 if(this.preventDefault){
6532 this.fireEvent('click', this, e);
6535 disable : function()
6537 this.setDisabled(true);
6542 this.setDisabled(false);
6545 setDisabled : function(state)
6547 if(this.disabled == state){
6551 this.disabled = state;
6554 this.el.addClass('disabled');
6558 this.el.removeClass('disabled');
6563 setActive : function(state)
6565 if(this.active == state){
6569 this.active = state;
6572 this.el.addClass('active');
6576 this.el.removeClass('active');
6581 isActive: function ()
6586 setBadge : function(str)
6592 this.badgeEl.dom.innerHTML = str;
6609 * @class Roo.bootstrap.Row
6610 * @extends Roo.bootstrap.Component
6611 * Bootstrap Row class (contains columns...)
6615 * @param {Object} config The config object
6618 Roo.bootstrap.Row = function(config){
6619 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6622 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6624 getAutoCreate : function(){
6643 * @class Roo.bootstrap.Pagination
6644 * @extends Roo.bootstrap.Component
6645 * Bootstrap Pagination class
6646 * @cfg {String} size xs | sm | md | lg
6647 * @cfg {Boolean} inverse false | true
6650 * Create a new Pagination
6651 * @param {Object} config The config object
6654 Roo.bootstrap.Pagination = function(config){
6655 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6658 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6664 getAutoCreate : function(){
6670 cfg.cls += ' inverse';
6676 cfg.cls += " " + this.cls;
6694 * @class Roo.bootstrap.PaginationItem
6695 * @extends Roo.bootstrap.Component
6696 * Bootstrap PaginationItem class
6697 * @cfg {String} html text
6698 * @cfg {String} href the link
6699 * @cfg {Boolean} preventDefault (true | false) default true
6700 * @cfg {Boolean} active (true | false) default false
6701 * @cfg {Boolean} disabled default false
6705 * Create a new PaginationItem
6706 * @param {Object} config The config object
6710 Roo.bootstrap.PaginationItem = function(config){
6711 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6716 * The raw click event for the entire grid.
6717 * @param {Roo.EventObject} e
6723 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6727 preventDefault: true,
6732 getAutoCreate : function(){
6738 href : this.href ? this.href : '#',
6739 html : this.html ? this.html : ''
6749 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6753 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6759 initEvents: function() {
6761 this.el.on('click', this.onClick, this);
6764 onClick : function(e)
6766 Roo.log('PaginationItem on click ');
6767 if(this.preventDefault){
6775 this.fireEvent('click', this, e);
6791 * @class Roo.bootstrap.Slider
6792 * @extends Roo.bootstrap.Component
6793 * Bootstrap Slider class
6796 * Create a new Slider
6797 * @param {Object} config The config object
6800 Roo.bootstrap.Slider = function(config){
6801 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6804 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6806 getAutoCreate : function(){
6810 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6814 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6826 * Ext JS Library 1.1.1
6827 * Copyright(c) 2006-2007, Ext JS, LLC.
6829 * Originally Released Under LGPL - original licence link has changed is not relivant.
6832 * <script type="text/javascript">
6837 * @class Roo.grid.ColumnModel
6838 * @extends Roo.util.Observable
6839 * This is the default implementation of a ColumnModel used by the Grid. It defines
6840 * the columns in the grid.
6843 var colModel = new Roo.grid.ColumnModel([
6844 {header: "Ticker", width: 60, sortable: true, locked: true},
6845 {header: "Company Name", width: 150, sortable: true},
6846 {header: "Market Cap.", width: 100, sortable: true},
6847 {header: "$ Sales", width: 100, sortable: true, renderer: money},
6848 {header: "Employees", width: 100, sortable: true, resizable: false}
6853 * The config options listed for this class are options which may appear in each
6854 * individual column definition.
6855 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6857 * @param {Object} config An Array of column config objects. See this class's
6858 * config objects for details.
6860 Roo.grid.ColumnModel = function(config){
6862 * The config passed into the constructor
6864 this.config = config;
6867 // if no id, create one
6868 // if the column does not have a dataIndex mapping,
6869 // map it to the order it is in the config
6870 for(var i = 0, len = config.length; i < len; i++){
6872 if(typeof c.dataIndex == "undefined"){
6875 if(typeof c.renderer == "string"){
6876 c.renderer = Roo.util.Format[c.renderer];
6878 if(typeof c.id == "undefined"){
6881 if(c.editor && c.editor.xtype){
6882 c.editor = Roo.factory(c.editor, Roo.grid);
6884 if(c.editor && c.editor.isFormField){
6885 c.editor = new Roo.grid.GridEditor(c.editor);
6887 this.lookup[c.id] = c;
6891 * The width of columns which have no width specified (defaults to 100)
6894 this.defaultWidth = 100;
6897 * Default sortable of columns which have no sortable specified (defaults to false)
6900 this.defaultSortable = false;
6904 * @event widthchange
6905 * Fires when the width of a column changes.
6906 * @param {ColumnModel} this
6907 * @param {Number} columnIndex The column index
6908 * @param {Number} newWidth The new width
6910 "widthchange": true,
6912 * @event headerchange
6913 * Fires when the text of a header changes.
6914 * @param {ColumnModel} this
6915 * @param {Number} columnIndex The column index
6916 * @param {Number} newText The new header text
6918 "headerchange": true,
6920 * @event hiddenchange
6921 * Fires when a column is hidden or "unhidden".
6922 * @param {ColumnModel} this
6923 * @param {Number} columnIndex The column index
6924 * @param {Boolean} hidden true if hidden, false otherwise
6926 "hiddenchange": true,
6928 * @event columnmoved
6929 * Fires when a column is moved.
6930 * @param {ColumnModel} this
6931 * @param {Number} oldIndex
6932 * @param {Number} newIndex
6934 "columnmoved" : true,
6936 * @event columlockchange
6937 * Fires when a column's locked state is changed
6938 * @param {ColumnModel} this
6939 * @param {Number} colIndex
6940 * @param {Boolean} locked true if locked
6942 "columnlockchange" : true
6944 Roo.grid.ColumnModel.superclass.constructor.call(this);
6946 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6948 * @cfg {String} header The header text to display in the Grid view.
6951 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6952 * {@link Roo.data.Record} definition from which to draw the column's value. If not
6953 * specified, the column's index is used as an index into the Record's data Array.
6956 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6957 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6960 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6961 * Defaults to the value of the {@link #defaultSortable} property.
6962 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6965 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
6968 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
6971 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6974 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6977 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6978 * given the cell's data value. See {@link #setRenderer}. If not specified, the
6979 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6980 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6983 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
6986 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
6989 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
6992 * @cfg {String} cursor (Optional)
6995 * @cfg {String} tooltip (Optional)
6998 * @cfg {Number} xs (Optional)
7001 * @cfg {Number} sm (Optional)
7004 * @cfg {Number} md (Optional)
7007 * @cfg {Number} lg (Optional)
7010 * Returns the id of the column at the specified index.
7011 * @param {Number} index The column index
7012 * @return {String} the id
7014 getColumnId : function(index){
7015 return this.config[index].id;
7019 * Returns the column for a specified id.
7020 * @param {String} id The column id
7021 * @return {Object} the column
7023 getColumnById : function(id){
7024 return this.lookup[id];
7029 * Returns the column for a specified dataIndex.
7030 * @param {String} dataIndex The column dataIndex
7031 * @return {Object|Boolean} the column or false if not found
7033 getColumnByDataIndex: function(dataIndex){
7034 var index = this.findColumnIndex(dataIndex);
7035 return index > -1 ? this.config[index] : false;
7039 * Returns the index for a specified column id.
7040 * @param {String} id The column id
7041 * @return {Number} the index, or -1 if not found
7043 getIndexById : function(id){
7044 for(var i = 0, len = this.config.length; i < len; i++){
7045 if(this.config[i].id == id){
7053 * Returns the index for a specified column dataIndex.
7054 * @param {String} dataIndex The column dataIndex
7055 * @return {Number} the index, or -1 if not found
7058 findColumnIndex : function(dataIndex){
7059 for(var i = 0, len = this.config.length; i < len; i++){
7060 if(this.config[i].dataIndex == dataIndex){
7068 moveColumn : function(oldIndex, newIndex){
7069 var c = this.config[oldIndex];
7070 this.config.splice(oldIndex, 1);
7071 this.config.splice(newIndex, 0, c);
7072 this.dataMap = null;
7073 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7076 isLocked : function(colIndex){
7077 return this.config[colIndex].locked === true;
7080 setLocked : function(colIndex, value, suppressEvent){
7081 if(this.isLocked(colIndex) == value){
7084 this.config[colIndex].locked = value;
7086 this.fireEvent("columnlockchange", this, colIndex, value);
7090 getTotalLockedWidth : function(){
7092 for(var i = 0; i < this.config.length; i++){
7093 if(this.isLocked(i) && !this.isHidden(i)){
7094 this.totalWidth += this.getColumnWidth(i);
7100 getLockedCount : function(){
7101 for(var i = 0, len = this.config.length; i < len; i++){
7102 if(!this.isLocked(i)){
7107 return this.config.length;
7111 * Returns the number of columns.
7114 getColumnCount : function(visibleOnly){
7115 if(visibleOnly === true){
7117 for(var i = 0, len = this.config.length; i < len; i++){
7118 if(!this.isHidden(i)){
7124 return this.config.length;
7128 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7129 * @param {Function} fn
7130 * @param {Object} scope (optional)
7131 * @return {Array} result
7133 getColumnsBy : function(fn, scope){
7135 for(var i = 0, len = this.config.length; i < len; i++){
7136 var c = this.config[i];
7137 if(fn.call(scope||this, c, i) === true){
7145 * Returns true if the specified column is sortable.
7146 * @param {Number} col The column index
7149 isSortable : function(col){
7150 if(typeof this.config[col].sortable == "undefined"){
7151 return this.defaultSortable;
7153 return this.config[col].sortable;
7157 * Returns the rendering (formatting) function defined for the column.
7158 * @param {Number} col The column index.
7159 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7161 getRenderer : function(col){
7162 if(!this.config[col].renderer){
7163 return Roo.grid.ColumnModel.defaultRenderer;
7165 return this.config[col].renderer;
7169 * Sets the rendering (formatting) function for a column.
7170 * @param {Number} col The column index
7171 * @param {Function} fn The function to use to process the cell's raw data
7172 * to return HTML markup for the grid view. The render function is called with
7173 * the following parameters:<ul>
7174 * <li>Data value.</li>
7175 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7176 * <li>css A CSS style string to apply to the table cell.</li>
7177 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7178 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7179 * <li>Row index</li>
7180 * <li>Column index</li>
7181 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7183 setRenderer : function(col, fn){
7184 this.config[col].renderer = fn;
7188 * Returns the width for the specified column.
7189 * @param {Number} col The column index
7192 getColumnWidth : function(col){
7193 return this.config[col].width * 1 || this.defaultWidth;
7197 * Sets the width for a column.
7198 * @param {Number} col The column index
7199 * @param {Number} width The new width
7201 setColumnWidth : function(col, width, suppressEvent){
7202 this.config[col].width = width;
7203 this.totalWidth = null;
7205 this.fireEvent("widthchange", this, col, width);
7210 * Returns the total width of all columns.
7211 * @param {Boolean} includeHidden True to include hidden column widths
7214 getTotalWidth : function(includeHidden){
7215 if(!this.totalWidth){
7216 this.totalWidth = 0;
7217 for(var i = 0, len = this.config.length; i < len; i++){
7218 if(includeHidden || !this.isHidden(i)){
7219 this.totalWidth += this.getColumnWidth(i);
7223 return this.totalWidth;
7227 * Returns the header for the specified column.
7228 * @param {Number} col The column index
7231 getColumnHeader : function(col){
7232 return this.config[col].header;
7236 * Sets the header for a column.
7237 * @param {Number} col The column index
7238 * @param {String} header The new header
7240 setColumnHeader : function(col, header){
7241 this.config[col].header = header;
7242 this.fireEvent("headerchange", this, col, header);
7246 * Returns the tooltip for the specified column.
7247 * @param {Number} col The column index
7250 getColumnTooltip : function(col){
7251 return this.config[col].tooltip;
7254 * Sets the tooltip for a column.
7255 * @param {Number} col The column index
7256 * @param {String} tooltip The new tooltip
7258 setColumnTooltip : function(col, tooltip){
7259 this.config[col].tooltip = tooltip;
7263 * Returns the dataIndex for the specified column.
7264 * @param {Number} col The column index
7267 getDataIndex : function(col){
7268 return this.config[col].dataIndex;
7272 * Sets the dataIndex for a column.
7273 * @param {Number} col The column index
7274 * @param {Number} dataIndex The new dataIndex
7276 setDataIndex : function(col, dataIndex){
7277 this.config[col].dataIndex = dataIndex;
7283 * Returns true if the cell is editable.
7284 * @param {Number} colIndex The column index
7285 * @param {Number} rowIndex The row index - this is nto actually used..?
7288 isCellEditable : function(colIndex, rowIndex){
7289 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7293 * Returns the editor defined for the cell/column.
7294 * return false or null to disable editing.
7295 * @param {Number} colIndex The column index
7296 * @param {Number} rowIndex The row index
7299 getCellEditor : function(colIndex, rowIndex){
7300 return this.config[colIndex].editor;
7304 * Sets if a column is editable.
7305 * @param {Number} col The column index
7306 * @param {Boolean} editable True if the column is editable
7308 setEditable : function(col, editable){
7309 this.config[col].editable = editable;
7314 * Returns true if the column is hidden.
7315 * @param {Number} colIndex The column index
7318 isHidden : function(colIndex){
7319 return this.config[colIndex].hidden;
7324 * Returns true if the column width cannot be changed
7326 isFixed : function(colIndex){
7327 return this.config[colIndex].fixed;
7331 * Returns true if the column can be resized
7334 isResizable : function(colIndex){
7335 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7338 * Sets if a column is hidden.
7339 * @param {Number} colIndex The column index
7340 * @param {Boolean} hidden True if the column is hidden
7342 setHidden : function(colIndex, hidden){
7343 this.config[colIndex].hidden = hidden;
7344 this.totalWidth = null;
7345 this.fireEvent("hiddenchange", this, colIndex, hidden);
7349 * Sets the editor for a column.
7350 * @param {Number} col The column index
7351 * @param {Object} editor The editor object
7353 setEditor : function(col, editor){
7354 this.config[col].editor = editor;
7358 Roo.grid.ColumnModel.defaultRenderer = function(value)
7360 if(typeof value == "object") {
7363 if(typeof value == "string" && value.length < 1){
7367 return String.format("{0}", value);
7370 // Alias for backwards compatibility
7371 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7374 * Ext JS Library 1.1.1
7375 * Copyright(c) 2006-2007, Ext JS, LLC.
7377 * Originally Released Under LGPL - original licence link has changed is not relivant.
7380 * <script type="text/javascript">
7384 * @class Roo.LoadMask
7385 * A simple utility class for generically masking elements while loading data. If the element being masked has
7386 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7387 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7388 * element's UpdateManager load indicator and will be destroyed after the initial load.
7390 * Create a new LoadMask
7391 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7392 * @param {Object} config The config object
7394 Roo.LoadMask = function(el, config){
7395 this.el = Roo.get(el);
7396 Roo.apply(this, config);
7398 this.store.on('beforeload', this.onBeforeLoad, this);
7399 this.store.on('load', this.onLoad, this);
7400 this.store.on('loadexception', this.onLoadException, this);
7401 this.removeMask = false;
7403 var um = this.el.getUpdateManager();
7404 um.showLoadIndicator = false; // disable the default indicator
7405 um.on('beforeupdate', this.onBeforeLoad, this);
7406 um.on('update', this.onLoad, this);
7407 um.on('failure', this.onLoad, this);
7408 this.removeMask = true;
7412 Roo.LoadMask.prototype = {
7414 * @cfg {Boolean} removeMask
7415 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7416 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7420 * The text to display in a centered loading message box (defaults to 'Loading...')
7424 * @cfg {String} msgCls
7425 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7427 msgCls : 'x-mask-loading',
7430 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7436 * Disables the mask to prevent it from being displayed
7438 disable : function(){
7439 this.disabled = true;
7443 * Enables the mask so that it can be displayed
7445 enable : function(){
7446 this.disabled = false;
7449 onLoadException : function()
7453 if (typeof(arguments[3]) != 'undefined') {
7454 Roo.MessageBox.alert("Error loading",arguments[3]);
7458 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7459 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7466 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7471 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7475 onBeforeLoad : function(){
7477 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7482 destroy : function(){
7484 this.store.un('beforeload', this.onBeforeLoad, this);
7485 this.store.un('load', this.onLoad, this);
7486 this.store.un('loadexception', this.onLoadException, this);
7488 var um = this.el.getUpdateManager();
7489 um.un('beforeupdate', this.onBeforeLoad, this);
7490 um.un('update', this.onLoad, this);
7491 um.un('failure', this.onLoad, this);
7502 * @class Roo.bootstrap.Table
7503 * @extends Roo.bootstrap.Component
7504 * Bootstrap Table class
7505 * @cfg {String} cls table class
7506 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7507 * @cfg {String} bgcolor Specifies the background color for a table
7508 * @cfg {Number} border Specifies whether the table cells should have borders or not
7509 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7510 * @cfg {Number} cellspacing Specifies the space between cells
7511 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7512 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7513 * @cfg {String} sortable Specifies that the table should be sortable
7514 * @cfg {String} summary Specifies a summary of the content of a table
7515 * @cfg {Number} width Specifies the width of a table
7516 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7518 * @cfg {boolean} striped Should the rows be alternative striped
7519 * @cfg {boolean} bordered Add borders to the table
7520 * @cfg {boolean} hover Add hover highlighting
7521 * @cfg {boolean} condensed Format condensed
7522 * @cfg {boolean} responsive Format condensed
7523 * @cfg {Boolean} loadMask (true|false) default false
7524 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7525 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7526 * @cfg {Boolean} rowSelection (true|false) default false
7527 * @cfg {Boolean} cellSelection (true|false) default false
7528 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7529 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7530 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7531 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7535 * Create a new Table
7536 * @param {Object} config The config object
7539 Roo.bootstrap.Table = function(config){
7540 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7545 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7546 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7547 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7548 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7550 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7552 this.sm.grid = this;
7553 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7554 this.sm = this.selModel;
7555 this.sm.xmodule = this.xmodule || false;
7558 if (this.cm && typeof(this.cm.config) == 'undefined') {
7559 this.colModel = new Roo.grid.ColumnModel(this.cm);
7560 this.cm = this.colModel;
7561 this.cm.xmodule = this.xmodule || false;
7564 this.store= Roo.factory(this.store, Roo.data);
7565 this.ds = this.store;
7566 this.ds.xmodule = this.xmodule || false;
7569 if (this.footer && this.store) {
7570 this.footer.dataSource = this.ds;
7571 this.footer = Roo.factory(this.footer);
7578 * Fires when a cell is clicked
7579 * @param {Roo.bootstrap.Table} this
7580 * @param {Roo.Element} el
7581 * @param {Number} rowIndex
7582 * @param {Number} columnIndex
7583 * @param {Roo.EventObject} e
7587 * @event celldblclick
7588 * Fires when a cell is double clicked
7589 * @param {Roo.bootstrap.Table} this
7590 * @param {Roo.Element} el
7591 * @param {Number} rowIndex
7592 * @param {Number} columnIndex
7593 * @param {Roo.EventObject} e
7595 "celldblclick" : true,
7598 * Fires when a row is clicked
7599 * @param {Roo.bootstrap.Table} this
7600 * @param {Roo.Element} el
7601 * @param {Number} rowIndex
7602 * @param {Roo.EventObject} e
7606 * @event rowdblclick
7607 * Fires when a row is double clicked
7608 * @param {Roo.bootstrap.Table} this
7609 * @param {Roo.Element} el
7610 * @param {Number} rowIndex
7611 * @param {Roo.EventObject} e
7613 "rowdblclick" : true,
7616 * Fires when a mouseover occur
7617 * @param {Roo.bootstrap.Table} this
7618 * @param {Roo.Element} el
7619 * @param {Number} rowIndex
7620 * @param {Number} columnIndex
7621 * @param {Roo.EventObject} e
7626 * Fires when a mouseout occur
7627 * @param {Roo.bootstrap.Table} this
7628 * @param {Roo.Element} el
7629 * @param {Number} rowIndex
7630 * @param {Number} columnIndex
7631 * @param {Roo.EventObject} e
7636 * Fires when a row is rendered, so you can change add a style to it.
7637 * @param {Roo.bootstrap.Table} this
7638 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7642 * @event rowsrendered
7643 * Fires when all the rows have been rendered
7644 * @param {Roo.bootstrap.Table} this
7646 'rowsrendered' : true,
7648 * @event contextmenu
7649 * The raw contextmenu event for the entire grid.
7650 * @param {Roo.EventObject} e
7652 "contextmenu" : true,
7654 * @event rowcontextmenu
7655 * Fires when a row is right clicked
7656 * @param {Roo.bootstrap.Table} this
7657 * @param {Number} rowIndex
7658 * @param {Roo.EventObject} e
7660 "rowcontextmenu" : true,
7662 * @event cellcontextmenu
7663 * Fires when a cell is right clicked
7664 * @param {Roo.bootstrap.Table} this
7665 * @param {Number} rowIndex
7666 * @param {Number} cellIndex
7667 * @param {Roo.EventObject} e
7669 "cellcontextmenu" : true,
7671 * @event headercontextmenu
7672 * Fires when a header is right clicked
7673 * @param {Roo.bootstrap.Table} this
7674 * @param {Number} columnIndex
7675 * @param {Roo.EventObject} e
7677 "headercontextmenu" : true
7681 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7707 rowSelection : false,
7708 cellSelection : false,
7711 // Roo.Element - the tbody
7713 // Roo.Element - thead element
7716 container: false, // used by gridpanel...
7722 auto_hide_footer : false,
7724 getAutoCreate : function()
7726 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7733 if (this.scrollBody) {
7734 cfg.cls += ' table-body-fixed';
7737 cfg.cls += ' table-striped';
7741 cfg.cls += ' table-hover';
7743 if (this.bordered) {
7744 cfg.cls += ' table-bordered';
7746 if (this.condensed) {
7747 cfg.cls += ' table-condensed';
7749 if (this.responsive) {
7750 cfg.cls += ' table-responsive';
7754 cfg.cls+= ' ' +this.cls;
7757 // this lot should be simplifed...
7770 ].forEach(function(k) {
7778 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7781 if(this.store || this.cm){
7782 if(this.headerShow){
7783 cfg.cn.push(this.renderHeader());
7786 cfg.cn.push(this.renderBody());
7788 if(this.footerShow){
7789 cfg.cn.push(this.renderFooter());
7791 // where does this come from?
7792 //cfg.cls+= ' TableGrid';
7795 return { cn : [ cfg ] };
7798 initEvents : function()
7800 if(!this.store || !this.cm){
7803 if (this.selModel) {
7804 this.selModel.initEvents();
7808 //Roo.log('initEvents with ds!!!!');
7810 this.mainBody = this.el.select('tbody', true).first();
7811 this.mainHead = this.el.select('thead', true).first();
7812 this.mainFoot = this.el.select('tfoot', true).first();
7818 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7819 e.on('click', _this.sort, _this);
7822 this.mainBody.on("click", this.onClick, this);
7823 this.mainBody.on("dblclick", this.onDblClick, this);
7825 // why is this done????? = it breaks dialogs??
7826 //this.parent().el.setStyle('position', 'relative');
7830 this.footer.parentId = this.id;
7831 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7834 this.el.select('tfoot tr td').first().addClass('hide');
7839 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7842 this.store.on('load', this.onLoad, this);
7843 this.store.on('beforeload', this.onBeforeLoad, this);
7844 this.store.on('update', this.onUpdate, this);
7845 this.store.on('add', this.onAdd, this);
7846 this.store.on("clear", this.clear, this);
7848 this.el.on("contextmenu", this.onContextMenu, this);
7850 this.mainBody.on('scroll', this.onBodyScroll, this);
7852 this.cm.on("headerchange", this.onHeaderChange, this);
7854 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7858 onContextMenu : function(e, t)
7860 this.processEvent("contextmenu", e);
7863 processEvent : function(name, e)
7865 if (name != 'touchstart' ) {
7866 this.fireEvent(name, e);
7869 var t = e.getTarget();
7871 var cell = Roo.get(t);
7877 if(cell.findParent('tfoot', false, true)){
7881 if(cell.findParent('thead', false, true)){
7883 if(e.getTarget().nodeName.toLowerCase() != 'th'){
7884 cell = Roo.get(t).findParent('th', false, true);
7886 Roo.log("failed to find th in thead?");
7887 Roo.log(e.getTarget());
7892 var cellIndex = cell.dom.cellIndex;
7894 var ename = name == 'touchstart' ? 'click' : name;
7895 this.fireEvent("header" + ename, this, cellIndex, e);
7900 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7901 cell = Roo.get(t).findParent('td', false, true);
7903 Roo.log("failed to find th in tbody?");
7904 Roo.log(e.getTarget());
7909 var row = cell.findParent('tr', false, true);
7910 var cellIndex = cell.dom.cellIndex;
7911 var rowIndex = row.dom.rowIndex - 1;
7915 this.fireEvent("row" + name, this, rowIndex, e);
7919 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7925 onMouseover : function(e, el)
7927 var cell = Roo.get(el);
7933 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7934 cell = cell.findParent('td', false, true);
7937 var row = cell.findParent('tr', false, true);
7938 var cellIndex = cell.dom.cellIndex;
7939 var rowIndex = row.dom.rowIndex - 1; // start from 0
7941 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7945 onMouseout : function(e, el)
7947 var cell = Roo.get(el);
7953 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7954 cell = cell.findParent('td', false, true);
7957 var row = cell.findParent('tr', false, true);
7958 var cellIndex = cell.dom.cellIndex;
7959 var rowIndex = row.dom.rowIndex - 1; // start from 0
7961 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7965 onClick : function(e, el)
7967 var cell = Roo.get(el);
7969 if(!cell || (!this.cellSelection && !this.rowSelection)){
7973 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7974 cell = cell.findParent('td', false, true);
7977 if(!cell || typeof(cell) == 'undefined'){
7981 var row = cell.findParent('tr', false, true);
7983 if(!row || typeof(row) == 'undefined'){
7987 var cellIndex = cell.dom.cellIndex;
7988 var rowIndex = this.getRowIndex(row);
7990 // why??? - should these not be based on SelectionModel?
7991 if(this.cellSelection){
7992 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7995 if(this.rowSelection){
7996 this.fireEvent('rowclick', this, row, rowIndex, e);
8002 onDblClick : function(e,el)
8004 var cell = Roo.get(el);
8006 if(!cell || (!this.cellSelection && !this.rowSelection)){
8010 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8011 cell = cell.findParent('td', false, true);
8014 if(!cell || typeof(cell) == 'undefined'){
8018 var row = cell.findParent('tr', false, true);
8020 if(!row || typeof(row) == 'undefined'){
8024 var cellIndex = cell.dom.cellIndex;
8025 var rowIndex = this.getRowIndex(row);
8027 if(this.cellSelection){
8028 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8031 if(this.rowSelection){
8032 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8036 sort : function(e,el)
8038 var col = Roo.get(el);
8040 if(!col.hasClass('sortable')){
8044 var sort = col.attr('sort');
8047 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8051 this.store.sortInfo = {field : sort, direction : dir};
8054 Roo.log("calling footer first");
8055 this.footer.onClick('first');
8058 this.store.load({ params : { start : 0 } });
8062 renderHeader : function()
8070 this.totalWidth = 0;
8072 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8074 var config = cm.config[i];
8078 cls : 'x-hcol-' + i,
8080 html: cm.getColumnHeader(i)
8085 if(typeof(config.sortable) != 'undefined' && config.sortable){
8087 c.html = '<i class="glyphicon"></i>' + c.html;
8090 // could use BS4 hidden-..-down
8092 if(typeof(config.lgHeader) != 'undefined'){
8093 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8096 if(typeof(config.mdHeader) != 'undefined'){
8097 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8100 if(typeof(config.smHeader) != 'undefined'){
8101 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8104 if(typeof(config.xsHeader) != 'undefined'){
8105 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8112 if(typeof(config.tooltip) != 'undefined'){
8113 c.tooltip = config.tooltip;
8116 if(typeof(config.colspan) != 'undefined'){
8117 c.colspan = config.colspan;
8120 if(typeof(config.hidden) != 'undefined' && config.hidden){
8121 c.style += ' display:none;';
8124 if(typeof(config.dataIndex) != 'undefined'){
8125 c.sort = config.dataIndex;
8130 if(typeof(config.align) != 'undefined' && config.align.length){
8131 c.style += ' text-align:' + config.align + ';';
8134 if(typeof(config.width) != 'undefined'){
8135 c.style += ' width:' + config.width + 'px;';
8136 this.totalWidth += config.width;
8138 this.totalWidth += 100; // assume minimum of 100 per column?
8141 if(typeof(config.cls) != 'undefined'){
8142 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8145 ['xs','sm','md','lg'].map(function(size){
8147 if(typeof(config[size]) == 'undefined'){
8151 if (!config[size]) { // 0 = hidden
8152 // BS 4 '0' is treated as hide that column and below.
8153 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8157 c.cls += ' col-' + size + '-' + config[size] + (
8158 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8170 renderBody : function()
8180 colspan : this.cm.getColumnCount()
8190 renderFooter : function()
8200 colspan : this.cm.getColumnCount()
8214 // Roo.log('ds onload');
8219 var ds = this.store;
8221 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8222 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8223 if (_this.store.sortInfo) {
8225 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8226 e.select('i', true).addClass(['glyphicon-arrow-up']);
8229 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8230 e.select('i', true).addClass(['glyphicon-arrow-down']);
8235 var tbody = this.mainBody;
8237 if(ds.getCount() > 0){
8238 ds.data.each(function(d,rowIndex){
8239 var row = this.renderRow(cm, ds, rowIndex);
8241 tbody.createChild(row);
8245 if(row.cellObjects.length){
8246 Roo.each(row.cellObjects, function(r){
8247 _this.renderCellObject(r);
8254 var tfoot = this.el.select('tfoot', true).first();
8256 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8258 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8260 var total = this.ds.getTotalCount();
8262 if(this.footer.pageSize < total){
8263 this.mainFoot.show();
8267 Roo.each(this.el.select('tbody td', true).elements, function(e){
8268 e.on('mouseover', _this.onMouseover, _this);
8271 Roo.each(this.el.select('tbody td', true).elements, function(e){
8272 e.on('mouseout', _this.onMouseout, _this);
8274 this.fireEvent('rowsrendered', this);
8280 onUpdate : function(ds,record)
8282 this.refreshRow(record);
8286 onRemove : function(ds, record, index, isUpdate){
8287 if(isUpdate !== true){
8288 this.fireEvent("beforerowremoved", this, index, record);
8290 var bt = this.mainBody.dom;
8292 var rows = this.el.select('tbody > tr', true).elements;
8294 if(typeof(rows[index]) != 'undefined'){
8295 bt.removeChild(rows[index].dom);
8298 // if(bt.rows[index]){
8299 // bt.removeChild(bt.rows[index]);
8302 if(isUpdate !== true){
8303 //this.stripeRows(index);
8304 //this.syncRowHeights(index, index);
8306 this.fireEvent("rowremoved", this, index, record);
8310 onAdd : function(ds, records, rowIndex)
8312 //Roo.log('on Add called');
8313 // - note this does not handle multiple adding very well..
8314 var bt = this.mainBody.dom;
8315 for (var i =0 ; i < records.length;i++) {
8316 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8317 //Roo.log(records[i]);
8318 //Roo.log(this.store.getAt(rowIndex+i));
8319 this.insertRow(this.store, rowIndex + i, false);
8326 refreshRow : function(record){
8327 var ds = this.store, index;
8328 if(typeof record == 'number'){
8330 record = ds.getAt(index);
8332 index = ds.indexOf(record);
8334 return; // should not happen - but seems to
8337 this.insertRow(ds, index, true);
8339 this.onRemove(ds, record, index+1, true);
8341 //this.syncRowHeights(index, index);
8343 this.fireEvent("rowupdated", this, index, record);
8346 insertRow : function(dm, rowIndex, isUpdate){
8349 this.fireEvent("beforerowsinserted", this, rowIndex);
8351 //var s = this.getScrollState();
8352 var row = this.renderRow(this.cm, this.store, rowIndex);
8353 // insert before rowIndex..
8354 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8358 if(row.cellObjects.length){
8359 Roo.each(row.cellObjects, function(r){
8360 _this.renderCellObject(r);
8365 this.fireEvent("rowsinserted", this, rowIndex);
8366 //this.syncRowHeights(firstRow, lastRow);
8367 //this.stripeRows(firstRow);
8374 getRowDom : function(rowIndex)
8376 var rows = this.el.select('tbody > tr', true).elements;
8378 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8381 // returns the object tree for a tr..
8384 renderRow : function(cm, ds, rowIndex)
8386 var d = ds.getAt(rowIndex);
8390 cls : 'x-row-' + rowIndex,
8394 var cellObjects = [];
8396 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8397 var config = cm.config[i];
8399 var renderer = cm.getRenderer(i);
8403 if(typeof(renderer) !== 'undefined'){
8404 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8406 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8407 // and are rendered into the cells after the row is rendered - using the id for the element.
8409 if(typeof(value) === 'object'){
8419 rowIndex : rowIndex,
8424 this.fireEvent('rowclass', this, rowcfg);
8428 cls : rowcfg.rowClass + ' x-col-' + i,
8430 html: (typeof(value) === 'object') ? '' : value
8437 if(typeof(config.colspan) != 'undefined'){
8438 td.colspan = config.colspan;
8441 if(typeof(config.hidden) != 'undefined' && config.hidden){
8442 td.style += ' display:none;';
8445 if(typeof(config.align) != 'undefined' && config.align.length){
8446 td.style += ' text-align:' + config.align + ';';
8448 if(typeof(config.valign) != 'undefined' && config.valign.length){
8449 td.style += ' vertical-align:' + config.valign + ';';
8452 if(typeof(config.width) != 'undefined'){
8453 td.style += ' width:' + config.width + 'px;';
8456 if(typeof(config.cursor) != 'undefined'){
8457 td.style += ' cursor:' + config.cursor + ';';
8460 if(typeof(config.cls) != 'undefined'){
8461 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8464 ['xs','sm','md','lg'].map(function(size){
8466 if(typeof(config[size]) == 'undefined'){
8472 if (!config[size]) { // 0 = hidden
8473 // BS 4 '0' is treated as hide that column and below.
8474 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8478 td.cls += ' col-' + size + '-' + config[size] + (
8479 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8489 row.cellObjects = cellObjects;
8497 onBeforeLoad : function()
8506 this.el.select('tbody', true).first().dom.innerHTML = '';
8509 * Show or hide a row.
8510 * @param {Number} rowIndex to show or hide
8511 * @param {Boolean} state hide
8513 setRowVisibility : function(rowIndex, state)
8515 var bt = this.mainBody.dom;
8517 var rows = this.el.select('tbody > tr', true).elements;
8519 if(typeof(rows[rowIndex]) == 'undefined'){
8522 rows[rowIndex].dom.style.display = state ? '' : 'none';
8526 getSelectionModel : function(){
8528 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8530 return this.selModel;
8533 * Render the Roo.bootstrap object from renderder
8535 renderCellObject : function(r)
8539 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8541 var t = r.cfg.render(r.container);
8544 Roo.each(r.cfg.cn, function(c){
8546 container: t.getChildContainer(),
8549 _this.renderCellObject(child);
8554 getRowIndex : function(row)
8558 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8569 * Returns the grid's underlying element = used by panel.Grid
8570 * @return {Element} The element
8572 getGridEl : function(){
8576 * Forces a resize - used by panel.Grid
8577 * @return {Element} The element
8579 autoSize : function()
8581 //var ctr = Roo.get(this.container.dom.parentElement);
8582 var ctr = Roo.get(this.el.dom);
8584 var thd = this.getGridEl().select('thead',true).first();
8585 var tbd = this.getGridEl().select('tbody', true).first();
8586 var tfd = this.getGridEl().select('tfoot', true).first();
8588 var cw = ctr.getWidth();
8592 tbd.setWidth(ctr.getWidth());
8593 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8594 // this needs fixing for various usage - currently only hydra job advers I think..
8596 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8598 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8601 cw = Math.max(cw, this.totalWidth);
8602 this.getGridEl().select('tr',true).setWidth(cw);
8603 // resize 'expandable coloumn?
8605 return; // we doe not have a view in this design..
8608 onBodyScroll: function()
8610 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8612 this.mainHead.setStyle({
8613 'position' : 'relative',
8614 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8620 var scrollHeight = this.mainBody.dom.scrollHeight;
8622 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8624 var height = this.mainBody.getHeight();
8626 if(scrollHeight - height == scrollTop) {
8628 var total = this.ds.getTotalCount();
8630 if(this.footer.cursor + this.footer.pageSize < total){
8632 this.footer.ds.load({
8634 start : this.footer.cursor + this.footer.pageSize,
8635 limit : this.footer.pageSize
8645 onHeaderChange : function()
8647 var header = this.renderHeader();
8648 var table = this.el.select('table', true).first();
8650 this.mainHead.remove();
8651 this.mainHead = table.createChild(header, this.mainBody, false);
8654 onHiddenChange : function(colModel, colIndex, hidden)
8656 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8657 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8659 this.CSS.updateRule(thSelector, "display", "");
8660 this.CSS.updateRule(tdSelector, "display", "");
8663 this.CSS.updateRule(thSelector, "display", "none");
8664 this.CSS.updateRule(tdSelector, "display", "none");
8667 this.onHeaderChange();
8671 setColumnWidth: function(col_index, width)
8673 // width = "md-2 xs-2..."
8674 if(!this.colModel.config[col_index]) {
8678 var w = width.split(" ");
8680 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8682 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8685 for(var j = 0; j < w.length; j++) {
8691 var size_cls = w[j].split("-");
8693 if(!Number.isInteger(size_cls[1] * 1)) {
8697 if(!this.colModel.config[col_index][size_cls[0]]) {
8701 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8705 h_row[0].classList.replace(
8706 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8707 "col-"+size_cls[0]+"-"+size_cls[1]
8710 for(var i = 0; i < rows.length; i++) {
8712 var size_cls = w[j].split("-");
8714 if(!Number.isInteger(size_cls[1] * 1)) {
8718 if(!this.colModel.config[col_index][size_cls[0]]) {
8722 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8726 rows[i].classList.replace(
8727 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8728 "col-"+size_cls[0]+"-"+size_cls[1]
8732 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8747 * @class Roo.bootstrap.TableCell
8748 * @extends Roo.bootstrap.Component
8749 * Bootstrap TableCell class
8750 * @cfg {String} html cell contain text
8751 * @cfg {String} cls cell class
8752 * @cfg {String} tag cell tag (td|th) default td
8753 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8754 * @cfg {String} align Aligns the content in a cell
8755 * @cfg {String} axis Categorizes cells
8756 * @cfg {String} bgcolor Specifies the background color of a cell
8757 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8758 * @cfg {Number} colspan Specifies the number of columns a cell should span
8759 * @cfg {String} headers Specifies one or more header cells a cell is related to
8760 * @cfg {Number} height Sets the height of a cell
8761 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8762 * @cfg {Number} rowspan Sets the number of rows a cell should span
8763 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8764 * @cfg {String} valign Vertical aligns the content in a cell
8765 * @cfg {Number} width Specifies the width of a cell
8768 * Create a new TableCell
8769 * @param {Object} config The config object
8772 Roo.bootstrap.TableCell = function(config){
8773 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8776 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8796 getAutoCreate : function(){
8797 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8817 cfg.align=this.align
8823 cfg.bgcolor=this.bgcolor
8826 cfg.charoff=this.charoff
8829 cfg.colspan=this.colspan
8832 cfg.headers=this.headers
8835 cfg.height=this.height
8838 cfg.nowrap=this.nowrap
8841 cfg.rowspan=this.rowspan
8844 cfg.scope=this.scope
8847 cfg.valign=this.valign
8850 cfg.width=this.width
8869 * @class Roo.bootstrap.TableRow
8870 * @extends Roo.bootstrap.Component
8871 * Bootstrap TableRow class
8872 * @cfg {String} cls row class
8873 * @cfg {String} align Aligns the content in a table row
8874 * @cfg {String} bgcolor Specifies a background color for a table row
8875 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8876 * @cfg {String} valign Vertical aligns the content in a table row
8879 * Create a new TableRow
8880 * @param {Object} config The config object
8883 Roo.bootstrap.TableRow = function(config){
8884 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8887 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
8895 getAutoCreate : function(){
8896 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8906 cfg.align = this.align;
8909 cfg.bgcolor = this.bgcolor;
8912 cfg.charoff = this.charoff;
8915 cfg.valign = this.valign;
8933 * @class Roo.bootstrap.TableBody
8934 * @extends Roo.bootstrap.Component
8935 * Bootstrap TableBody class
8936 * @cfg {String} cls element class
8937 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8938 * @cfg {String} align Aligns the content inside the element
8939 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8940 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8943 * Create a new TableBody
8944 * @param {Object} config The config object
8947 Roo.bootstrap.TableBody = function(config){
8948 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8951 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
8959 getAutoCreate : function(){
8960 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8974 cfg.align = this.align;
8977 cfg.charoff = this.charoff;
8980 cfg.valign = this.valign;
8987 // initEvents : function()
8994 // this.store = Roo.factory(this.store, Roo.data);
8995 // this.store.on('load', this.onLoad, this);
8997 // this.store.load();
9001 // onLoad: function ()
9003 // this.fireEvent('load', this);
9013 * Ext JS Library 1.1.1
9014 * Copyright(c) 2006-2007, Ext JS, LLC.
9016 * Originally Released Under LGPL - original licence link has changed is not relivant.
9019 * <script type="text/javascript">
9022 // as we use this in bootstrap.
9023 Roo.namespace('Roo.form');
9025 * @class Roo.form.Action
9026 * Internal Class used to handle form actions
9028 * @param {Roo.form.BasicForm} el The form element or its id
9029 * @param {Object} config Configuration options
9034 // define the action interface
9035 Roo.form.Action = function(form, options){
9037 this.options = options || {};
9040 * Client Validation Failed
9043 Roo.form.Action.CLIENT_INVALID = 'client';
9045 * Server Validation Failed
9048 Roo.form.Action.SERVER_INVALID = 'server';
9050 * Connect to Server Failed
9053 Roo.form.Action.CONNECT_FAILURE = 'connect';
9055 * Reading Data from Server Failed
9058 Roo.form.Action.LOAD_FAILURE = 'load';
9060 Roo.form.Action.prototype = {
9062 failureType : undefined,
9063 response : undefined,
9067 run : function(options){
9072 success : function(response){
9077 handleResponse : function(response){
9081 // default connection failure
9082 failure : function(response){
9084 this.response = response;
9085 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9086 this.form.afterAction(this, false);
9089 processResponse : function(response){
9090 this.response = response;
9091 if(!response.responseText){
9094 this.result = this.handleResponse(response);
9098 // utility functions used internally
9099 getUrl : function(appendParams){
9100 var url = this.options.url || this.form.url || this.form.el.dom.action;
9102 var p = this.getParams();
9104 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9110 getMethod : function(){
9111 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9114 getParams : function(){
9115 var bp = this.form.baseParams;
9116 var p = this.options.params;
9118 if(typeof p == "object"){
9119 p = Roo.urlEncode(Roo.applyIf(p, bp));
9120 }else if(typeof p == 'string' && bp){
9121 p += '&' + Roo.urlEncode(bp);
9124 p = Roo.urlEncode(bp);
9129 createCallback : function(){
9131 success: this.success,
9132 failure: this.failure,
9134 timeout: (this.form.timeout*1000),
9135 upload: this.form.fileUpload ? this.success : undefined
9140 Roo.form.Action.Submit = function(form, options){
9141 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9144 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9147 haveProgress : false,
9148 uploadComplete : false,
9150 // uploadProgress indicator.
9151 uploadProgress : function()
9153 if (!this.form.progressUrl) {
9157 if (!this.haveProgress) {
9158 Roo.MessageBox.progress("Uploading", "Uploading");
9160 if (this.uploadComplete) {
9161 Roo.MessageBox.hide();
9165 this.haveProgress = true;
9167 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9169 var c = new Roo.data.Connection();
9171 url : this.form.progressUrl,
9176 success : function(req){
9177 //console.log(data);
9181 rdata = Roo.decode(req.responseText)
9183 Roo.log("Invalid data from server..");
9187 if (!rdata || !rdata.success) {
9189 Roo.MessageBox.alert(Roo.encode(rdata));
9192 var data = rdata.data;
9194 if (this.uploadComplete) {
9195 Roo.MessageBox.hide();
9200 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9201 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9204 this.uploadProgress.defer(2000,this);
9207 failure: function(data) {
9208 Roo.log('progress url failed ');
9219 // run get Values on the form, so it syncs any secondary forms.
9220 this.form.getValues();
9222 var o = this.options;
9223 var method = this.getMethod();
9224 var isPost = method == 'POST';
9225 if(o.clientValidation === false || this.form.isValid()){
9227 if (this.form.progressUrl) {
9228 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9229 (new Date() * 1) + '' + Math.random());
9234 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9235 form:this.form.el.dom,
9236 url:this.getUrl(!isPost),
9238 params:isPost ? this.getParams() : null,
9239 isUpload: this.form.fileUpload,
9240 formData : this.form.formData
9243 this.uploadProgress();
9245 }else if (o.clientValidation !== false){ // client validation failed
9246 this.failureType = Roo.form.Action.CLIENT_INVALID;
9247 this.form.afterAction(this, false);
9251 success : function(response)
9253 this.uploadComplete= true;
9254 if (this.haveProgress) {
9255 Roo.MessageBox.hide();
9259 var result = this.processResponse(response);
9260 if(result === true || result.success){
9261 this.form.afterAction(this, true);
9265 this.form.markInvalid(result.errors);
9266 this.failureType = Roo.form.Action.SERVER_INVALID;
9268 this.form.afterAction(this, false);
9270 failure : function(response)
9272 this.uploadComplete= true;
9273 if (this.haveProgress) {
9274 Roo.MessageBox.hide();
9277 this.response = response;
9278 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9279 this.form.afterAction(this, false);
9282 handleResponse : function(response){
9283 if(this.form.errorReader){
9284 var rs = this.form.errorReader.read(response);
9287 for(var i = 0, len = rs.records.length; i < len; i++) {
9288 var r = rs.records[i];
9292 if(errors.length < 1){
9296 success : rs.success,
9302 ret = Roo.decode(response.responseText);
9306 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9316 Roo.form.Action.Load = function(form, options){
9317 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9318 this.reader = this.form.reader;
9321 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9326 Roo.Ajax.request(Roo.apply(
9327 this.createCallback(), {
9328 method:this.getMethod(),
9329 url:this.getUrl(false),
9330 params:this.getParams()
9334 success : function(response){
9336 var result = this.processResponse(response);
9337 if(result === true || !result.success || !result.data){
9338 this.failureType = Roo.form.Action.LOAD_FAILURE;
9339 this.form.afterAction(this, false);
9342 this.form.clearInvalid();
9343 this.form.setValues(result.data);
9344 this.form.afterAction(this, true);
9347 handleResponse : function(response){
9348 if(this.form.reader){
9349 var rs = this.form.reader.read(response);
9350 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9352 success : rs.success,
9356 return Roo.decode(response.responseText);
9360 Roo.form.Action.ACTION_TYPES = {
9361 'load' : Roo.form.Action.Load,
9362 'submit' : Roo.form.Action.Submit
9371 * @class Roo.bootstrap.Form
9372 * @extends Roo.bootstrap.Component
9373 * Bootstrap Form class
9374 * @cfg {String} method GET | POST (default POST)
9375 * @cfg {String} labelAlign top | left (default top)
9376 * @cfg {String} align left | right - for navbars
9377 * @cfg {Boolean} loadMask load mask when submit (default true)
9382 * @param {Object} config The config object
9386 Roo.bootstrap.Form = function(config){
9388 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9390 Roo.bootstrap.Form.popover.apply();
9394 * @event clientvalidation
9395 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9396 * @param {Form} this
9397 * @param {Boolean} valid true if the form has passed client-side validation
9399 clientvalidation: true,
9401 * @event beforeaction
9402 * Fires before any action is performed. Return false to cancel the action.
9403 * @param {Form} this
9404 * @param {Action} action The action to be performed
9408 * @event actionfailed
9409 * Fires when an action fails.
9410 * @param {Form} this
9411 * @param {Action} action The action that failed
9413 actionfailed : true,
9415 * @event actioncomplete
9416 * Fires when an action is completed.
9417 * @param {Form} this
9418 * @param {Action} action The action that completed
9420 actioncomplete : true
9424 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9427 * @cfg {String} method
9428 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9433 * The URL to use for form actions if one isn't supplied in the action options.
9436 * @cfg {Boolean} fileUpload
9437 * Set to true if this form is a file upload.
9441 * @cfg {Object} baseParams
9442 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9446 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9450 * @cfg {Sting} align (left|right) for navbar forms
9455 activeAction : null,
9458 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9459 * element by passing it or its id or mask the form itself by passing in true.
9462 waitMsgTarget : false,
9467 * @cfg {Boolean} errorMask (true|false) default false
9472 * @cfg {Number} maskOffset Default 100
9477 * @cfg {Boolean} maskBody
9481 getAutoCreate : function(){
9485 method : this.method || 'POST',
9486 id : this.id || Roo.id(),
9489 if (this.parent().xtype.match(/^Nav/)) {
9490 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9494 if (this.labelAlign == 'left' ) {
9495 cfg.cls += ' form-horizontal';
9501 initEvents : function()
9503 this.el.on('submit', this.onSubmit, this);
9504 // this was added as random key presses on the form where triggering form submit.
9505 this.el.on('keypress', function(e) {
9506 if (e.getCharCode() != 13) {
9509 // we might need to allow it for textareas.. and some other items.
9510 // check e.getTarget().
9512 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9516 Roo.log("keypress blocked");
9524 onSubmit : function(e){
9529 * Returns true if client-side validation on the form is successful.
9532 isValid : function(){
9533 var items = this.getItems();
9537 items.each(function(f){
9543 Roo.log('invalid field: ' + f.name);
9547 if(!target && f.el.isVisible(true)){
9553 if(this.errorMask && !valid){
9554 Roo.bootstrap.Form.popover.mask(this, target);
9561 * Returns true if any fields in this form have changed since their original load.
9564 isDirty : function(){
9566 var items = this.getItems();
9567 items.each(function(f){
9577 * Performs a predefined action (submit or load) or custom actions you define on this form.
9578 * @param {String} actionName The name of the action type
9579 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9580 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9581 * accept other config options):
9583 Property Type Description
9584 ---------------- --------------- ----------------------------------------------------------------------------------
9585 url String The url for the action (defaults to the form's url)
9586 method String The form method to use (defaults to the form's method, or POST if not defined)
9587 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9588 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9589 validate the form on the client (defaults to false)
9591 * @return {BasicForm} this
9593 doAction : function(action, options){
9594 if(typeof action == 'string'){
9595 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9597 if(this.fireEvent('beforeaction', this, action) !== false){
9598 this.beforeAction(action);
9599 action.run.defer(100, action);
9605 beforeAction : function(action){
9606 var o = action.options;
9611 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9613 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9616 // not really supported yet.. ??
9618 //if(this.waitMsgTarget === true){
9619 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9620 //}else if(this.waitMsgTarget){
9621 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9622 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9624 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9630 afterAction : function(action, success){
9631 this.activeAction = null;
9632 var o = action.options;
9637 Roo.get(document.body).unmask();
9643 //if(this.waitMsgTarget === true){
9644 // this.el.unmask();
9645 //}else if(this.waitMsgTarget){
9646 // this.waitMsgTarget.unmask();
9648 // Roo.MessageBox.updateProgress(1);
9649 // Roo.MessageBox.hide();
9656 Roo.callback(o.success, o.scope, [this, action]);
9657 this.fireEvent('actioncomplete', this, action);
9661 // failure condition..
9662 // we have a scenario where updates need confirming.
9663 // eg. if a locking scenario exists..
9664 // we look for { errors : { needs_confirm : true }} in the response.
9666 (typeof(action.result) != 'undefined') &&
9667 (typeof(action.result.errors) != 'undefined') &&
9668 (typeof(action.result.errors.needs_confirm) != 'undefined')
9671 Roo.log("not supported yet");
9674 Roo.MessageBox.confirm(
9675 "Change requires confirmation",
9676 action.result.errorMsg,
9681 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9691 Roo.callback(o.failure, o.scope, [this, action]);
9692 // show an error message if no failed handler is set..
9693 if (!this.hasListener('actionfailed')) {
9694 Roo.log("need to add dialog support");
9696 Roo.MessageBox.alert("Error",
9697 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9698 action.result.errorMsg :
9699 "Saving Failed, please check your entries or try again"
9704 this.fireEvent('actionfailed', this, action);
9709 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9710 * @param {String} id The value to search for
9713 findField : function(id){
9714 var items = this.getItems();
9715 var field = items.get(id);
9717 items.each(function(f){
9718 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9725 return field || null;
9728 * Mark fields in this form invalid in bulk.
9729 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9730 * @return {BasicForm} this
9732 markInvalid : function(errors){
9733 if(errors instanceof Array){
9734 for(var i = 0, len = errors.length; i < len; i++){
9735 var fieldError = errors[i];
9736 var f = this.findField(fieldError.id);
9738 f.markInvalid(fieldError.msg);
9744 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9745 field.markInvalid(errors[id]);
9749 //Roo.each(this.childForms || [], function (f) {
9750 // f.markInvalid(errors);
9757 * Set values for fields in this form in bulk.
9758 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9759 * @return {BasicForm} this
9761 setValues : function(values){
9762 if(values instanceof Array){ // array of objects
9763 for(var i = 0, len = values.length; i < len; i++){
9765 var f = this.findField(v.id);
9767 f.setValue(v.value);
9768 if(this.trackResetOnLoad){
9769 f.originalValue = f.getValue();
9773 }else{ // object hash
9776 if(typeof values[id] != 'function' && (field = this.findField(id))){
9778 if (field.setFromData &&
9780 field.displayField &&
9781 // combos' with local stores can
9782 // be queried via setValue()
9783 // to set their value..
9784 (field.store && !field.store.isLocal)
9788 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9789 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9790 field.setFromData(sd);
9792 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9794 field.setFromData(values);
9797 field.setValue(values[id]);
9801 if(this.trackResetOnLoad){
9802 field.originalValue = field.getValue();
9808 //Roo.each(this.childForms || [], function (f) {
9809 // f.setValues(values);
9816 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9817 * they are returned as an array.
9818 * @param {Boolean} asString
9821 getValues : function(asString){
9822 //if (this.childForms) {
9823 // copy values from the child forms
9824 // Roo.each(this.childForms, function (f) {
9825 // this.setValues(f.getValues());
9831 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9832 if(asString === true){
9835 return Roo.urlDecode(fs);
9839 * Returns the fields in this form as an object with key/value pairs.
9840 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9843 getFieldValues : function(with_hidden)
9845 var items = this.getItems();
9847 items.each(function(f){
9853 var v = f.getValue();
9855 if (f.inputType =='radio') {
9856 if (typeof(ret[f.getName()]) == 'undefined') {
9857 ret[f.getName()] = ''; // empty..
9860 if (!f.el.dom.checked) {
9868 if(f.xtype == 'MoneyField'){
9869 ret[f.currencyName] = f.getCurrency();
9872 // not sure if this supported any more..
9873 if ((typeof(v) == 'object') && f.getRawValue) {
9874 v = f.getRawValue() ; // dates..
9876 // combo boxes where name != hiddenName...
9877 if (f.name !== false && f.name != '' && f.name != f.getName()) {
9878 ret[f.name] = f.getRawValue();
9880 ret[f.getName()] = v;
9887 * Clears all invalid messages in this form.
9888 * @return {BasicForm} this
9890 clearInvalid : function(){
9891 var items = this.getItems();
9893 items.each(function(f){
9902 * @return {BasicForm} this
9905 var items = this.getItems();
9906 items.each(function(f){
9910 Roo.each(this.childForms || [], function (f) {
9918 getItems : function()
9920 var r=new Roo.util.MixedCollection(false, function(o){
9921 return o.id || (o.id = Roo.id());
9923 var iter = function(el) {
9930 Roo.each(el.items,function(e) {
9939 hideFields : function(items)
9941 Roo.each(items, function(i){
9943 var f = this.findField(i);
9954 showFields : function(items)
9956 Roo.each(items, function(i){
9958 var f = this.findField(i);
9971 Roo.apply(Roo.bootstrap.Form, {
9998 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9999 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10000 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10001 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10004 this.maskEl.top.enableDisplayMode("block");
10005 this.maskEl.left.enableDisplayMode("block");
10006 this.maskEl.bottom.enableDisplayMode("block");
10007 this.maskEl.right.enableDisplayMode("block");
10009 this.toolTip = new Roo.bootstrap.Tooltip({
10010 cls : 'roo-form-error-popover',
10012 'left' : ['r-l', [-2,0], 'right'],
10013 'right' : ['l-r', [2,0], 'left'],
10014 'bottom' : ['tl-bl', [0,2], 'top'],
10015 'top' : [ 'bl-tl', [0,-2], 'bottom']
10019 this.toolTip.render(Roo.get(document.body));
10021 this.toolTip.el.enableDisplayMode("block");
10023 Roo.get(document.body).on('click', function(){
10027 Roo.get(document.body).on('touchstart', function(){
10031 this.isApplied = true
10034 mask : function(form, target)
10038 this.target = target;
10040 if(!this.form.errorMask || !target.el){
10044 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10046 Roo.log(scrollable);
10048 var ot = this.target.el.calcOffsetsTo(scrollable);
10050 var scrollTo = ot[1] - this.form.maskOffset;
10052 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10054 scrollable.scrollTo('top', scrollTo);
10056 var box = this.target.el.getBox();
10058 var zIndex = Roo.bootstrap.Modal.zIndex++;
10061 this.maskEl.top.setStyle('position', 'absolute');
10062 this.maskEl.top.setStyle('z-index', zIndex);
10063 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10064 this.maskEl.top.setLeft(0);
10065 this.maskEl.top.setTop(0);
10066 this.maskEl.top.show();
10068 this.maskEl.left.setStyle('position', 'absolute');
10069 this.maskEl.left.setStyle('z-index', zIndex);
10070 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10071 this.maskEl.left.setLeft(0);
10072 this.maskEl.left.setTop(box.y - this.padding);
10073 this.maskEl.left.show();
10075 this.maskEl.bottom.setStyle('position', 'absolute');
10076 this.maskEl.bottom.setStyle('z-index', zIndex);
10077 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10078 this.maskEl.bottom.setLeft(0);
10079 this.maskEl.bottom.setTop(box.bottom + this.padding);
10080 this.maskEl.bottom.show();
10082 this.maskEl.right.setStyle('position', 'absolute');
10083 this.maskEl.right.setStyle('z-index', zIndex);
10084 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10085 this.maskEl.right.setLeft(box.right + this.padding);
10086 this.maskEl.right.setTop(box.y - this.padding);
10087 this.maskEl.right.show();
10089 this.toolTip.bindEl = this.target.el;
10091 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10093 var tip = this.target.blankText;
10095 if(this.target.getValue() !== '' ) {
10097 if (this.target.invalidText.length) {
10098 tip = this.target.invalidText;
10099 } else if (this.target.regexText.length){
10100 tip = this.target.regexText;
10104 this.toolTip.show(tip);
10106 this.intervalID = window.setInterval(function() {
10107 Roo.bootstrap.Form.popover.unmask();
10110 window.onwheel = function(){ return false;};
10112 (function(){ this.isMasked = true; }).defer(500, this);
10116 unmask : function()
10118 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10122 this.maskEl.top.setStyle('position', 'absolute');
10123 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10124 this.maskEl.top.hide();
10126 this.maskEl.left.setStyle('position', 'absolute');
10127 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10128 this.maskEl.left.hide();
10130 this.maskEl.bottom.setStyle('position', 'absolute');
10131 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10132 this.maskEl.bottom.hide();
10134 this.maskEl.right.setStyle('position', 'absolute');
10135 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10136 this.maskEl.right.hide();
10138 this.toolTip.hide();
10140 this.toolTip.el.hide();
10142 window.onwheel = function(){ return true;};
10144 if(this.intervalID){
10145 window.clearInterval(this.intervalID);
10146 this.intervalID = false;
10149 this.isMasked = false;
10159 * Ext JS Library 1.1.1
10160 * Copyright(c) 2006-2007, Ext JS, LLC.
10162 * Originally Released Under LGPL - original licence link has changed is not relivant.
10165 * <script type="text/javascript">
10168 * @class Roo.form.VTypes
10169 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10172 Roo.form.VTypes = function(){
10173 // closure these in so they are only created once.
10174 var alpha = /^[a-zA-Z_]+$/;
10175 var alphanum = /^[a-zA-Z0-9_]+$/;
10176 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10177 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10179 // All these messages and functions are configurable
10182 * The function used to validate email addresses
10183 * @param {String} value The email address
10185 'email' : function(v){
10186 return email.test(v);
10189 * The error text to display when the email validation function returns false
10192 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10194 * The keystroke filter mask to be applied on email input
10197 'emailMask' : /[a-z0-9_\.\-@]/i,
10200 * The function used to validate URLs
10201 * @param {String} value The URL
10203 'url' : function(v){
10204 return url.test(v);
10207 * The error text to display when the url validation function returns false
10210 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10213 * The function used to validate alpha values
10214 * @param {String} value The value
10216 'alpha' : function(v){
10217 return alpha.test(v);
10220 * The error text to display when the alpha validation function returns false
10223 'alphaText' : 'This field should only contain letters and _',
10225 * The keystroke filter mask to be applied on alpha input
10228 'alphaMask' : /[a-z_]/i,
10231 * The function used to validate alphanumeric values
10232 * @param {String} value The value
10234 'alphanum' : function(v){
10235 return alphanum.test(v);
10238 * The error text to display when the alphanumeric validation function returns false
10241 'alphanumText' : 'This field should only contain letters, numbers and _',
10243 * The keystroke filter mask to be applied on alphanumeric input
10246 'alphanumMask' : /[a-z0-9_]/i
10256 * @class Roo.bootstrap.Input
10257 * @extends Roo.bootstrap.Component
10258 * Bootstrap Input class
10259 * @cfg {Boolean} disabled is it disabled
10260 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10261 * @cfg {String} name name of the input
10262 * @cfg {string} fieldLabel - the label associated
10263 * @cfg {string} placeholder - placeholder to put in text.
10264 * @cfg {string} before - input group add on before
10265 * @cfg {string} after - input group add on after
10266 * @cfg {string} size - (lg|sm) or leave empty..
10267 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10268 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10269 * @cfg {Number} md colspan out of 12 for computer-sized screens
10270 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10271 * @cfg {string} value default value of the input
10272 * @cfg {Number} labelWidth set the width of label
10273 * @cfg {Number} labellg set the width of label (1-12)
10274 * @cfg {Number} labelmd set the width of label (1-12)
10275 * @cfg {Number} labelsm set the width of label (1-12)
10276 * @cfg {Number} labelxs set the width of label (1-12)
10277 * @cfg {String} labelAlign (top|left)
10278 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10279 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10280 * @cfg {String} indicatorpos (left|right) default left
10281 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10282 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10283 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10285 * @cfg {String} align (left|center|right) Default left
10286 * @cfg {Boolean} forceFeedback (true|false) Default false
10289 * Create a new Input
10290 * @param {Object} config The config object
10293 Roo.bootstrap.Input = function(config){
10295 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10300 * Fires when this field receives input focus.
10301 * @param {Roo.form.Field} this
10306 * Fires when this field loses input focus.
10307 * @param {Roo.form.Field} this
10311 * @event specialkey
10312 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10313 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10314 * @param {Roo.form.Field} this
10315 * @param {Roo.EventObject} e The event object
10320 * Fires just before the field blurs if the field value has changed.
10321 * @param {Roo.form.Field} this
10322 * @param {Mixed} newValue The new value
10323 * @param {Mixed} oldValue The original value
10328 * Fires after the field has been marked as invalid.
10329 * @param {Roo.form.Field} this
10330 * @param {String} msg The validation message
10335 * Fires after the field has been validated with no errors.
10336 * @param {Roo.form.Field} this
10341 * Fires after the key up
10342 * @param {Roo.form.Field} this
10343 * @param {Roo.EventObject} e The event Object
10349 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10351 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10352 automatic validation (defaults to "keyup").
10354 validationEvent : "keyup",
10356 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10358 validateOnBlur : true,
10360 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10362 validationDelay : 250,
10364 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10366 focusClass : "x-form-focus", // not needed???
10370 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10372 invalidClass : "has-warning",
10375 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10377 validClass : "has-success",
10380 * @cfg {Boolean} hasFeedback (true|false) default true
10382 hasFeedback : true,
10385 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10387 invalidFeedbackClass : "glyphicon-warning-sign",
10390 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10392 validFeedbackClass : "glyphicon-ok",
10395 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10397 selectOnFocus : false,
10400 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10404 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10409 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10411 disableKeyFilter : false,
10414 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10418 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10422 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10424 blankText : "Please complete this mandatory field",
10427 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10431 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10433 maxLength : Number.MAX_VALUE,
10435 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10437 minLengthText : "The minimum length for this field is {0}",
10439 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10441 maxLengthText : "The maximum length for this field is {0}",
10445 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10446 * If available, this function will be called only after the basic validators all return true, and will be passed the
10447 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10451 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10452 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10453 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10457 * @cfg {String} regexText -- Depricated - use Invalid Text
10462 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10468 autocomplete: false,
10472 inputType : 'text',
10475 placeholder: false,
10480 preventMark: false,
10481 isFormField : true,
10484 labelAlign : false,
10487 formatedValue : false,
10488 forceFeedback : false,
10490 indicatorpos : 'left',
10500 parentLabelAlign : function()
10503 while (parent.parent()) {
10504 parent = parent.parent();
10505 if (typeof(parent.labelAlign) !='undefined') {
10506 return parent.labelAlign;
10513 getAutoCreate : function()
10515 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10521 if(this.inputType != 'hidden'){
10522 cfg.cls = 'form-group' //input-group
10528 type : this.inputType,
10529 value : this.value,
10530 cls : 'form-control',
10531 placeholder : this.placeholder || '',
10532 autocomplete : this.autocomplete || 'new-password'
10534 if (this.inputType == 'file') {
10535 input.style = 'overflow:hidden'; // why not in CSS?
10538 if(this.capture.length){
10539 input.capture = this.capture;
10542 if(this.accept.length){
10543 input.accept = this.accept + "/*";
10547 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10550 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10551 input.maxLength = this.maxLength;
10554 if (this.disabled) {
10555 input.disabled=true;
10558 if (this.readOnly) {
10559 input.readonly=true;
10563 input.name = this.name;
10567 input.cls += ' input-' + this.size;
10571 ['xs','sm','md','lg'].map(function(size){
10572 if (settings[size]) {
10573 cfg.cls += ' col-' + size + '-' + settings[size];
10577 var inputblock = input;
10581 cls: 'glyphicon form-control-feedback'
10584 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10587 cls : 'has-feedback',
10595 if (this.before || this.after) {
10598 cls : 'input-group',
10602 if (this.before && typeof(this.before) == 'string') {
10604 inputblock.cn.push({
10606 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10610 if (this.before && typeof(this.before) == 'object') {
10611 this.before = Roo.factory(this.before);
10613 inputblock.cn.push({
10615 cls : 'roo-input-before input-group-prepend input-group-' +
10616 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10620 inputblock.cn.push(input);
10622 if (this.after && typeof(this.after) == 'string') {
10623 inputblock.cn.push({
10625 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10629 if (this.after && typeof(this.after) == 'object') {
10630 this.after = Roo.factory(this.after);
10632 inputblock.cn.push({
10634 cls : 'roo-input-after input-group-append input-group-' +
10635 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10639 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10640 inputblock.cls += ' has-feedback';
10641 inputblock.cn.push(feedback);
10646 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10647 tooltip : 'This field is required'
10649 if (this.allowBlank ) {
10650 indicator.style = this.allowBlank ? ' display:none' : '';
10652 if (align ==='left' && this.fieldLabel.length) {
10654 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10661 cls : 'control-label col-form-label',
10662 html : this.fieldLabel
10673 var labelCfg = cfg.cn[1];
10674 var contentCfg = cfg.cn[2];
10676 if(this.indicatorpos == 'right'){
10681 cls : 'control-label col-form-label',
10685 html : this.fieldLabel
10699 labelCfg = cfg.cn[0];
10700 contentCfg = cfg.cn[1];
10704 if(this.labelWidth > 12){
10705 labelCfg.style = "width: " + this.labelWidth + 'px';
10708 if(this.labelWidth < 13 && this.labelmd == 0){
10709 this.labelmd = this.labelWidth;
10712 if(this.labellg > 0){
10713 labelCfg.cls += ' col-lg-' + this.labellg;
10714 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10717 if(this.labelmd > 0){
10718 labelCfg.cls += ' col-md-' + this.labelmd;
10719 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10722 if(this.labelsm > 0){
10723 labelCfg.cls += ' col-sm-' + this.labelsm;
10724 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10727 if(this.labelxs > 0){
10728 labelCfg.cls += ' col-xs-' + this.labelxs;
10729 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10733 } else if ( this.fieldLabel.length) {
10740 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10741 tooltip : 'This field is required',
10742 style : this.allowBlank ? ' display:none' : ''
10746 //cls : 'input-group-addon',
10747 html : this.fieldLabel
10755 if(this.indicatorpos == 'right'){
10760 //cls : 'input-group-addon',
10761 html : this.fieldLabel
10766 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10767 tooltip : 'This field is required',
10768 style : this.allowBlank ? ' display:none' : ''
10788 if (this.parentType === 'Navbar' && this.parent().bar) {
10789 cfg.cls += ' navbar-form';
10792 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10793 // on BS4 we do this only if not form
10794 cfg.cls += ' navbar-form';
10802 * return the real input element.
10804 inputEl: function ()
10806 return this.el.select('input.form-control',true).first();
10809 tooltipEl : function()
10811 return this.inputEl();
10814 indicatorEl : function()
10816 if (Roo.bootstrap.version == 4) {
10817 return false; // not enabled in v4 yet.
10820 var indicator = this.el.select('i.roo-required-indicator',true).first();
10830 setDisabled : function(v)
10832 var i = this.inputEl().dom;
10834 i.removeAttribute('disabled');
10838 i.setAttribute('disabled','true');
10840 initEvents : function()
10843 this.inputEl().on("keydown" , this.fireKey, this);
10844 this.inputEl().on("focus", this.onFocus, this);
10845 this.inputEl().on("blur", this.onBlur, this);
10847 this.inputEl().relayEvent('keyup', this);
10849 this.indicator = this.indicatorEl();
10851 if(this.indicator){
10852 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
10855 // reference to original value for reset
10856 this.originalValue = this.getValue();
10857 //Roo.form.TextField.superclass.initEvents.call(this);
10858 if(this.validationEvent == 'keyup'){
10859 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10860 this.inputEl().on('keyup', this.filterValidation, this);
10862 else if(this.validationEvent !== false){
10863 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10866 if(this.selectOnFocus){
10867 this.on("focus", this.preFocus, this);
10870 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10871 this.inputEl().on("keypress", this.filterKeys, this);
10873 this.inputEl().relayEvent('keypress', this);
10876 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
10877 this.el.on("click", this.autoSize, this);
10880 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10881 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10884 if (typeof(this.before) == 'object') {
10885 this.before.render(this.el.select('.roo-input-before',true).first());
10887 if (typeof(this.after) == 'object') {
10888 this.after.render(this.el.select('.roo-input-after',true).first());
10891 this.inputEl().on('change', this.onChange, this);
10894 filterValidation : function(e){
10895 if(!e.isNavKeyPress()){
10896 this.validationTask.delay(this.validationDelay);
10900 * Validates the field value
10901 * @return {Boolean} True if the value is valid, else false
10903 validate : function(){
10904 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10905 if(this.disabled || this.validateValue(this.getRawValue())){
10910 this.markInvalid();
10916 * Validates a value according to the field's validation rules and marks the field as invalid
10917 * if the validation fails
10918 * @param {Mixed} value The value to validate
10919 * @return {Boolean} True if the value is valid, else false
10921 validateValue : function(value)
10923 if(this.getVisibilityEl().hasClass('hidden')){
10927 if(value.length < 1) { // if it's blank
10928 if(this.allowBlank){
10934 if(value.length < this.minLength){
10937 if(value.length > this.maxLength){
10941 var vt = Roo.form.VTypes;
10942 if(!vt[this.vtype](value, this)){
10946 if(typeof this.validator == "function"){
10947 var msg = this.validator(value);
10951 if (typeof(msg) == 'string') {
10952 this.invalidText = msg;
10956 if(this.regex && !this.regex.test(value)){
10964 fireKey : function(e){
10965 //Roo.log('field ' + e.getKey());
10966 if(e.isNavKeyPress()){
10967 this.fireEvent("specialkey", this, e);
10970 focus : function (selectText){
10972 this.inputEl().focus();
10973 if(selectText === true){
10974 this.inputEl().dom.select();
10980 onFocus : function(){
10981 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10982 // this.el.addClass(this.focusClass);
10984 if(!this.hasFocus){
10985 this.hasFocus = true;
10986 this.startValue = this.getValue();
10987 this.fireEvent("focus", this);
10991 beforeBlur : Roo.emptyFn,
10995 onBlur : function(){
10997 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10998 //this.el.removeClass(this.focusClass);
11000 this.hasFocus = false;
11001 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11004 var v = this.getValue();
11005 if(String(v) !== String(this.startValue)){
11006 this.fireEvent('change', this, v, this.startValue);
11008 this.fireEvent("blur", this);
11011 onChange : function(e)
11013 var v = this.getValue();
11014 if(String(v) !== String(this.startValue)){
11015 this.fireEvent('change', this, v, this.startValue);
11021 * Resets the current field value to the originally loaded value and clears any validation messages
11023 reset : function(){
11024 this.setValue(this.originalValue);
11028 * Returns the name of the field
11029 * @return {Mixed} name The name field
11031 getName: function(){
11035 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11036 * @return {Mixed} value The field value
11038 getValue : function(){
11040 var v = this.inputEl().getValue();
11045 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11046 * @return {Mixed} value The field value
11048 getRawValue : function(){
11049 var v = this.inputEl().getValue();
11055 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11056 * @param {Mixed} value The value to set
11058 setRawValue : function(v){
11059 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11062 selectText : function(start, end){
11063 var v = this.getRawValue();
11065 start = start === undefined ? 0 : start;
11066 end = end === undefined ? v.length : end;
11067 var d = this.inputEl().dom;
11068 if(d.setSelectionRange){
11069 d.setSelectionRange(start, end);
11070 }else if(d.createTextRange){
11071 var range = d.createTextRange();
11072 range.moveStart("character", start);
11073 range.moveEnd("character", v.length-end);
11080 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11081 * @param {Mixed} value The value to set
11083 setValue : function(v){
11086 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11092 processValue : function(value){
11093 if(this.stripCharsRe){
11094 var newValue = value.replace(this.stripCharsRe, '');
11095 if(newValue !== value){
11096 this.setRawValue(newValue);
11103 preFocus : function(){
11105 if(this.selectOnFocus){
11106 this.inputEl().dom.select();
11109 filterKeys : function(e){
11110 var k = e.getKey();
11111 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11114 var c = e.getCharCode(), cc = String.fromCharCode(c);
11115 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11118 if(!this.maskRe.test(cc)){
11123 * Clear any invalid styles/messages for this field
11125 clearInvalid : function(){
11127 if(!this.el || this.preventMark){ // not rendered
11132 this.el.removeClass([this.invalidClass, 'is-invalid']);
11134 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11136 var feedback = this.el.select('.form-control-feedback', true).first();
11139 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11144 if(this.indicator){
11145 this.indicator.removeClass('visible');
11146 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11149 this.fireEvent('valid', this);
11153 * Mark this field as valid
11155 markValid : function()
11157 if(!this.el || this.preventMark){ // not rendered...
11161 this.el.removeClass([this.invalidClass, this.validClass]);
11162 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11164 var feedback = this.el.select('.form-control-feedback', true).first();
11167 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11170 if(this.indicator){
11171 this.indicator.removeClass('visible');
11172 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11180 if(this.allowBlank && !this.getRawValue().length){
11183 if (Roo.bootstrap.version == 3) {
11184 this.el.addClass(this.validClass);
11186 this.inputEl().addClass('is-valid');
11189 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11191 var feedback = this.el.select('.form-control-feedback', true).first();
11194 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11195 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11200 this.fireEvent('valid', this);
11204 * Mark this field as invalid
11205 * @param {String} msg The validation message
11207 markInvalid : function(msg)
11209 if(!this.el || this.preventMark){ // not rendered
11213 this.el.removeClass([this.invalidClass, this.validClass]);
11214 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11216 var feedback = this.el.select('.form-control-feedback', true).first();
11219 this.el.select('.form-control-feedback', true).first().removeClass(
11220 [this.invalidFeedbackClass, this.validFeedbackClass]);
11227 if(this.allowBlank && !this.getRawValue().length){
11231 if(this.indicator){
11232 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11233 this.indicator.addClass('visible');
11235 if (Roo.bootstrap.version == 3) {
11236 this.el.addClass(this.invalidClass);
11238 this.inputEl().addClass('is-invalid');
11243 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11245 var feedback = this.el.select('.form-control-feedback', true).first();
11248 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11250 if(this.getValue().length || this.forceFeedback){
11251 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11258 this.fireEvent('invalid', this, msg);
11261 SafariOnKeyDown : function(event)
11263 // this is a workaround for a password hang bug on chrome/ webkit.
11264 if (this.inputEl().dom.type != 'password') {
11268 var isSelectAll = false;
11270 if(this.inputEl().dom.selectionEnd > 0){
11271 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11273 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11274 event.preventDefault();
11279 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11281 event.preventDefault();
11282 // this is very hacky as keydown always get's upper case.
11284 var cc = String.fromCharCode(event.getCharCode());
11285 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11289 adjustWidth : function(tag, w){
11290 tag = tag.toLowerCase();
11291 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11292 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11293 if(tag == 'input'){
11296 if(tag == 'textarea'){
11299 }else if(Roo.isOpera){
11300 if(tag == 'input'){
11303 if(tag == 'textarea'){
11311 setFieldLabel : function(v)
11313 if(!this.rendered){
11317 if(this.indicatorEl()){
11318 var ar = this.el.select('label > span',true);
11320 if (ar.elements.length) {
11321 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11322 this.fieldLabel = v;
11326 var br = this.el.select('label',true);
11328 if(br.elements.length) {
11329 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11330 this.fieldLabel = v;
11334 Roo.log('Cannot Found any of label > span || label in input');
11338 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11339 this.fieldLabel = v;
11354 * @class Roo.bootstrap.TextArea
11355 * @extends Roo.bootstrap.Input
11356 * Bootstrap TextArea class
11357 * @cfg {Number} cols Specifies the visible width of a text area
11358 * @cfg {Number} rows Specifies the visible number of lines in a text area
11359 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11360 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11361 * @cfg {string} html text
11364 * Create a new TextArea
11365 * @param {Object} config The config object
11368 Roo.bootstrap.TextArea = function(config){
11369 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11373 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11383 getAutoCreate : function(){
11385 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11391 if(this.inputType != 'hidden'){
11392 cfg.cls = 'form-group' //input-group
11400 value : this.value || '',
11401 html: this.html || '',
11402 cls : 'form-control',
11403 placeholder : this.placeholder || ''
11407 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11408 input.maxLength = this.maxLength;
11412 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11416 input.cols = this.cols;
11419 if (this.readOnly) {
11420 input.readonly = true;
11424 input.name = this.name;
11428 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11432 ['xs','sm','md','lg'].map(function(size){
11433 if (settings[size]) {
11434 cfg.cls += ' col-' + size + '-' + settings[size];
11438 var inputblock = input;
11440 if(this.hasFeedback && !this.allowBlank){
11444 cls: 'glyphicon form-control-feedback'
11448 cls : 'has-feedback',
11457 if (this.before || this.after) {
11460 cls : 'input-group',
11464 inputblock.cn.push({
11466 cls : 'input-group-addon',
11471 inputblock.cn.push(input);
11473 if(this.hasFeedback && !this.allowBlank){
11474 inputblock.cls += ' has-feedback';
11475 inputblock.cn.push(feedback);
11479 inputblock.cn.push({
11481 cls : 'input-group-addon',
11488 if (align ==='left' && this.fieldLabel.length) {
11493 cls : 'control-label',
11494 html : this.fieldLabel
11505 if(this.labelWidth > 12){
11506 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11509 if(this.labelWidth < 13 && this.labelmd == 0){
11510 this.labelmd = this.labelWidth;
11513 if(this.labellg > 0){
11514 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11515 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11518 if(this.labelmd > 0){
11519 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11520 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11523 if(this.labelsm > 0){
11524 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11525 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11528 if(this.labelxs > 0){
11529 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11530 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11533 } else if ( this.fieldLabel.length) {
11538 //cls : 'input-group-addon',
11539 html : this.fieldLabel
11557 if (this.disabled) {
11558 input.disabled=true;
11565 * return the real textarea element.
11567 inputEl: function ()
11569 return this.el.select('textarea.form-control',true).first();
11573 * Clear any invalid styles/messages for this field
11575 clearInvalid : function()
11578 if(!this.el || this.preventMark){ // not rendered
11582 var label = this.el.select('label', true).first();
11583 var icon = this.el.select('i.fa-star', true).first();
11588 this.el.removeClass( this.validClass);
11589 this.inputEl().removeClass('is-invalid');
11591 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11593 var feedback = this.el.select('.form-control-feedback', true).first();
11596 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11601 this.fireEvent('valid', this);
11605 * Mark this field as valid
11607 markValid : function()
11609 if(!this.el || this.preventMark){ // not rendered
11613 this.el.removeClass([this.invalidClass, this.validClass]);
11614 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11616 var feedback = this.el.select('.form-control-feedback', true).first();
11619 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11622 if(this.disabled || this.allowBlank){
11626 var label = this.el.select('label', true).first();
11627 var icon = this.el.select('i.fa-star', true).first();
11632 if (Roo.bootstrap.version == 3) {
11633 this.el.addClass(this.validClass);
11635 this.inputEl().addClass('is-valid');
11639 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11641 var feedback = this.el.select('.form-control-feedback', true).first();
11644 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11645 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11650 this.fireEvent('valid', this);
11654 * Mark this field as invalid
11655 * @param {String} msg The validation message
11657 markInvalid : function(msg)
11659 if(!this.el || this.preventMark){ // not rendered
11663 this.el.removeClass([this.invalidClass, this.validClass]);
11664 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11666 var feedback = this.el.select('.form-control-feedback', true).first();
11669 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11672 if(this.disabled || this.allowBlank){
11676 var label = this.el.select('label', true).first();
11677 var icon = this.el.select('i.fa-star', true).first();
11679 if(!this.getValue().length && label && !icon){
11680 this.el.createChild({
11682 cls : 'text-danger fa fa-lg fa-star',
11683 tooltip : 'This field is required',
11684 style : 'margin-right:5px;'
11688 if (Roo.bootstrap.version == 3) {
11689 this.el.addClass(this.invalidClass);
11691 this.inputEl().addClass('is-invalid');
11694 // fixme ... this may be depricated need to test..
11695 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11697 var feedback = this.el.select('.form-control-feedback', true).first();
11700 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11702 if(this.getValue().length || this.forceFeedback){
11703 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11710 this.fireEvent('invalid', this, msg);
11718 * trigger field - base class for combo..
11723 * @class Roo.bootstrap.TriggerField
11724 * @extends Roo.bootstrap.Input
11725 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11726 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11727 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11728 * for which you can provide a custom implementation. For example:
11730 var trigger = new Roo.bootstrap.TriggerField();
11731 trigger.onTriggerClick = myTriggerFn;
11732 trigger.applyTo('my-field');
11735 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11736 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11737 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11738 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11739 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11742 * Create a new TriggerField.
11743 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11744 * to the base TextField)
11746 Roo.bootstrap.TriggerField = function(config){
11747 this.mimicing = false;
11748 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11751 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11753 * @cfg {String} triggerClass A CSS class to apply to the trigger
11756 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11761 * @cfg {Boolean} removable (true|false) special filter default false
11765 /** @cfg {Boolean} grow @hide */
11766 /** @cfg {Number} growMin @hide */
11767 /** @cfg {Number} growMax @hide */
11773 autoSize: Roo.emptyFn,
11777 deferHeight : true,
11780 actionMode : 'wrap',
11785 getAutoCreate : function(){
11787 var align = this.labelAlign || this.parentLabelAlign();
11792 cls: 'form-group' //input-group
11799 type : this.inputType,
11800 cls : 'form-control',
11801 autocomplete: 'new-password',
11802 placeholder : this.placeholder || ''
11806 input.name = this.name;
11809 input.cls += ' input-' + this.size;
11812 if (this.disabled) {
11813 input.disabled=true;
11816 var inputblock = input;
11818 if(this.hasFeedback && !this.allowBlank){
11822 cls: 'glyphicon form-control-feedback'
11825 if(this.removable && !this.editable ){
11827 cls : 'has-feedback',
11833 cls : 'roo-combo-removable-btn close'
11840 cls : 'has-feedback',
11849 if(this.removable && !this.editable ){
11851 cls : 'roo-removable',
11857 cls : 'roo-combo-removable-btn close'
11864 if (this.before || this.after) {
11867 cls : 'input-group',
11871 inputblock.cn.push({
11873 cls : 'input-group-addon input-group-prepend input-group-text',
11878 inputblock.cn.push(input);
11880 if(this.hasFeedback && !this.allowBlank){
11881 inputblock.cls += ' has-feedback';
11882 inputblock.cn.push(feedback);
11886 inputblock.cn.push({
11888 cls : 'input-group-addon input-group-append input-group-text',
11897 var ibwrap = inputblock;
11902 cls: 'roo-select2-choices',
11906 cls: 'roo-select2-search-field',
11918 cls: 'roo-select2-container input-group',
11923 cls: 'form-hidden-field'
11929 if(!this.multiple && this.showToggleBtn){
11935 if (this.caret != false) {
11938 cls: 'fa fa-' + this.caret
11945 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11947 Roo.bootstrap.version == 3 ? caret : '',
11950 cls: 'combobox-clear',
11964 combobox.cls += ' roo-select2-container-multi';
11968 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11969 tooltip : 'This field is required'
11971 if (Roo.bootstrap.version == 4) {
11974 style : 'display:none'
11979 if (align ==='left' && this.fieldLabel.length) {
11981 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11988 cls : 'control-label',
11989 html : this.fieldLabel
12001 var labelCfg = cfg.cn[1];
12002 var contentCfg = cfg.cn[2];
12004 if(this.indicatorpos == 'right'){
12009 cls : 'control-label',
12013 html : this.fieldLabel
12027 labelCfg = cfg.cn[0];
12028 contentCfg = cfg.cn[1];
12031 if(this.labelWidth > 12){
12032 labelCfg.style = "width: " + this.labelWidth + 'px';
12035 if(this.labelWidth < 13 && this.labelmd == 0){
12036 this.labelmd = this.labelWidth;
12039 if(this.labellg > 0){
12040 labelCfg.cls += ' col-lg-' + this.labellg;
12041 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12044 if(this.labelmd > 0){
12045 labelCfg.cls += ' col-md-' + this.labelmd;
12046 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12049 if(this.labelsm > 0){
12050 labelCfg.cls += ' col-sm-' + this.labelsm;
12051 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12054 if(this.labelxs > 0){
12055 labelCfg.cls += ' col-xs-' + this.labelxs;
12056 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12059 } else if ( this.fieldLabel.length) {
12060 // Roo.log(" label");
12065 //cls : 'input-group-addon',
12066 html : this.fieldLabel
12074 if(this.indicatorpos == 'right'){
12082 html : this.fieldLabel
12096 // Roo.log(" no label && no align");
12103 ['xs','sm','md','lg'].map(function(size){
12104 if (settings[size]) {
12105 cfg.cls += ' col-' + size + '-' + settings[size];
12116 onResize : function(w, h){
12117 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12118 // if(typeof w == 'number'){
12119 // var x = w - this.trigger.getWidth();
12120 // this.inputEl().setWidth(this.adjustWidth('input', x));
12121 // this.trigger.setStyle('left', x+'px');
12126 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12129 getResizeEl : function(){
12130 return this.inputEl();
12134 getPositionEl : function(){
12135 return this.inputEl();
12139 alignErrorIcon : function(){
12140 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12144 initEvents : function(){
12148 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12149 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12150 if(!this.multiple && this.showToggleBtn){
12151 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12152 if(this.hideTrigger){
12153 this.trigger.setDisplayed(false);
12155 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12159 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12162 if(this.removable && !this.editable && !this.tickable){
12163 var close = this.closeTriggerEl();
12166 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12167 close.on('click', this.removeBtnClick, this, close);
12171 //this.trigger.addClassOnOver('x-form-trigger-over');
12172 //this.trigger.addClassOnClick('x-form-trigger-click');
12175 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12179 closeTriggerEl : function()
12181 var close = this.el.select('.roo-combo-removable-btn', true).first();
12182 return close ? close : false;
12185 removeBtnClick : function(e, h, el)
12187 e.preventDefault();
12189 if(this.fireEvent("remove", this) !== false){
12191 this.fireEvent("afterremove", this)
12195 createList : function()
12197 this.list = Roo.get(document.body).createChild({
12198 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12199 cls: 'typeahead typeahead-long dropdown-menu',
12200 style: 'display:none'
12203 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12208 initTrigger : function(){
12213 onDestroy : function(){
12215 this.trigger.removeAllListeners();
12216 // this.trigger.remove();
12219 // this.wrap.remove();
12221 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12225 onFocus : function(){
12226 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12228 if(!this.mimicing){
12229 this.wrap.addClass('x-trigger-wrap-focus');
12230 this.mimicing = true;
12231 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12232 if(this.monitorTab){
12233 this.el.on("keydown", this.checkTab, this);
12240 checkTab : function(e){
12241 if(e.getKey() == e.TAB){
12242 this.triggerBlur();
12247 onBlur : function(){
12252 mimicBlur : function(e, t){
12254 if(!this.wrap.contains(t) && this.validateBlur()){
12255 this.triggerBlur();
12261 triggerBlur : function(){
12262 this.mimicing = false;
12263 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12264 if(this.monitorTab){
12265 this.el.un("keydown", this.checkTab, this);
12267 //this.wrap.removeClass('x-trigger-wrap-focus');
12268 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12272 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12273 validateBlur : function(e, t){
12278 onDisable : function(){
12279 this.inputEl().dom.disabled = true;
12280 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12282 // this.wrap.addClass('x-item-disabled');
12287 onEnable : function(){
12288 this.inputEl().dom.disabled = false;
12289 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12291 // this.el.removeClass('x-item-disabled');
12296 onShow : function(){
12297 var ae = this.getActionEl();
12300 ae.dom.style.display = '';
12301 ae.dom.style.visibility = 'visible';
12307 onHide : function(){
12308 var ae = this.getActionEl();
12309 ae.dom.style.display = 'none';
12313 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12314 * by an implementing function.
12316 * @param {EventObject} e
12318 onTriggerClick : Roo.emptyFn
12326 * @class Roo.bootstrap.CardUploader
12327 * @extends Roo.bootstrap.Button
12328 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12329 * @cfg {Number} errorTimeout default 3000
12330 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12331 * @cfg {Array} html The button text.
12335 * Create a new CardUploader
12336 * @param {Object} config The config object
12339 Roo.bootstrap.CardUploader = function(config){
12343 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12346 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12353 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12356 errorTimeout : 3000,
12360 fileCollection : false,
12363 getAutoCreate : function()
12367 cls :'form-group' ,
12372 //cls : 'input-group-addon',
12373 html : this.fieldLabel
12380 value : this.value,
12381 cls : 'd-none form-control'
12386 multiple : 'multiple',
12388 cls : 'd-none roo-card-upload-selector'
12392 cls : 'roo-card-uploader-button-container w-100 mb-2'
12395 cls : 'card-columns roo-card-uploader-container'
12405 getChildContainer : function() /// what children are added to.
12407 return this.containerEl;
12410 getButtonContainer : function() /// what children are added to.
12412 return this.el.select(".roo-card-uploader-button-container").first();
12415 initEvents : function()
12418 Roo.bootstrap.Input.prototype.initEvents.call(this);
12422 xns: Roo.bootstrap,
12425 container_method : 'getButtonContainer' ,
12426 html : this.html, // fix changable?
12429 'click' : function(btn, e) {
12438 this.urlAPI = (window.createObjectURL && window) ||
12439 (window.URL && URL.revokeObjectURL && URL) ||
12440 (window.webkitURL && webkitURL);
12445 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12447 this.selectorEl.on('change', this.onFileSelected, this);
12450 this.images.forEach(function(img) {
12453 this.images = false;
12455 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12461 onClick : function(e)
12463 e.preventDefault();
12465 this.selectorEl.dom.click();
12469 onFileSelected : function(e)
12471 e.preventDefault();
12473 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12477 Roo.each(this.selectorEl.dom.files, function(file){
12478 this.addFile(file);
12487 addFile : function(file)
12490 if(typeof(file) === 'string'){
12491 throw "Add file by name?"; // should not happen
12495 if(!file || !this.urlAPI){
12505 var url = _this.urlAPI.createObjectURL( file);
12508 id : Roo.bootstrap.CardUploader.ID--,
12509 is_uploaded : false,
12512 mimetype : file.type,
12519 addCard : function (data)
12521 // hidden input element?
12522 // if the file is not an image...
12523 //then we need to use something other that and header_image
12528 xns : Roo.bootstrap,
12529 xtype : 'CardFooter',
12532 xns : Roo.bootstrap,
12538 xns : Roo.bootstrap,
12540 html : String.format("<small>{0}</small>", data.title),
12541 cls : 'col-11 text-left',
12546 click : function() {
12547 this.downloadCard(data.id)
12553 xns : Roo.bootstrap,
12561 click : function() {
12562 t.removeCard(data.id)
12574 var cn = this.addxtype(
12577 xns : Roo.bootstrap,
12580 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12581 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12582 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12587 initEvents : function() {
12588 Roo.bootstrap.Card.prototype.initEvents.call(this);
12589 this.imgEl = this.el.select('.card-img-top').first();
12591 this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12592 this.imgEl.set({ 'pointer' : 'cursor' });
12601 // dont' really need ot update items.
12602 // this.items.push(cn);
12603 this.fileCollection.add(cn);
12604 this.updateInput();
12607 removeCard : function(id)
12610 var card = this.fileCollection.get(id);
12611 card.data.is_deleted = 1;
12612 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12613 this.fileCollection.remove(card);
12614 //this.items = this.items.filter(function(e) { return e != card });
12615 // dont' really need ot update items.
12616 card.el.dom.parentNode.removeChild(card.el.dom);
12621 this.fileCollection.each(function(card) {
12622 card.el.dom.parentNode.removeChild(card.el.dom);
12624 this.fileCollection.clear();
12625 this.updateInput();
12628 updateInput : function()
12631 this.fileCollection.each(function(e) {
12635 this.inputEl().dom.value = JSON.stringify(data);
12642 Roo.bootstrap.CardUploader.ID = -1;/*
12644 * Ext JS Library 1.1.1
12645 * Copyright(c) 2006-2007, Ext JS, LLC.
12647 * Originally Released Under LGPL - original licence link has changed is not relivant.
12650 * <script type="text/javascript">
12655 * @class Roo.data.SortTypes
12657 * Defines the default sorting (casting?) comparison functions used when sorting data.
12659 Roo.data.SortTypes = {
12661 * Default sort that does nothing
12662 * @param {Mixed} s The value being converted
12663 * @return {Mixed} The comparison value
12665 none : function(s){
12670 * The regular expression used to strip tags
12674 stripTagsRE : /<\/?[^>]+>/gi,
12677 * Strips all HTML tags to sort on text only
12678 * @param {Mixed} s The value being converted
12679 * @return {String} The comparison value
12681 asText : function(s){
12682 return String(s).replace(this.stripTagsRE, "");
12686 * Strips all HTML tags to sort on text only - Case insensitive
12687 * @param {Mixed} s The value being converted
12688 * @return {String} The comparison value
12690 asUCText : function(s){
12691 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12695 * Case insensitive string
12696 * @param {Mixed} s The value being converted
12697 * @return {String} The comparison value
12699 asUCString : function(s) {
12700 return String(s).toUpperCase();
12705 * @param {Mixed} s The value being converted
12706 * @return {Number} The comparison value
12708 asDate : function(s) {
12712 if(s instanceof Date){
12713 return s.getTime();
12715 return Date.parse(String(s));
12720 * @param {Mixed} s The value being converted
12721 * @return {Float} The comparison value
12723 asFloat : function(s) {
12724 var val = parseFloat(String(s).replace(/,/g, ""));
12733 * @param {Mixed} s The value being converted
12734 * @return {Number} The comparison value
12736 asInt : function(s) {
12737 var val = parseInt(String(s).replace(/,/g, ""));
12745 * Ext JS Library 1.1.1
12746 * Copyright(c) 2006-2007, Ext JS, LLC.
12748 * Originally Released Under LGPL - original licence link has changed is not relivant.
12751 * <script type="text/javascript">
12755 * @class Roo.data.Record
12756 * Instances of this class encapsulate both record <em>definition</em> information, and record
12757 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12758 * to access Records cached in an {@link Roo.data.Store} object.<br>
12760 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12761 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12764 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12766 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12767 * {@link #create}. The parameters are the same.
12768 * @param {Array} data An associative Array of data values keyed by the field name.
12769 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12770 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12771 * not specified an integer id is generated.
12773 Roo.data.Record = function(data, id){
12774 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12779 * Generate a constructor for a specific record layout.
12780 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12781 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12782 * Each field definition object may contain the following properties: <ul>
12783 * <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,
12784 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12785 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12786 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12787 * is being used, then this is a string containing the javascript expression to reference the data relative to
12788 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12789 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12790 * this may be omitted.</p></li>
12791 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12792 * <ul><li>auto (Default, implies no conversion)</li>
12797 * <li>date</li></ul></p></li>
12798 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12799 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12800 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12801 * by the Reader into an object that will be stored in the Record. It is passed the
12802 * following parameters:<ul>
12803 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12805 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12807 * <br>usage:<br><pre><code>
12808 var TopicRecord = Roo.data.Record.create(
12809 {name: 'title', mapping: 'topic_title'},
12810 {name: 'author', mapping: 'username'},
12811 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12812 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12813 {name: 'lastPoster', mapping: 'user2'},
12814 {name: 'excerpt', mapping: 'post_text'}
12817 var myNewRecord = new TopicRecord({
12818 title: 'Do my job please',
12821 lastPost: new Date(),
12822 lastPoster: 'Animal',
12823 excerpt: 'No way dude!'
12825 myStore.add(myNewRecord);
12830 Roo.data.Record.create = function(o){
12831 var f = function(){
12832 f.superclass.constructor.apply(this, arguments);
12834 Roo.extend(f, Roo.data.Record);
12835 var p = f.prototype;
12836 p.fields = new Roo.util.MixedCollection(false, function(field){
12839 for(var i = 0, len = o.length; i < len; i++){
12840 p.fields.add(new Roo.data.Field(o[i]));
12842 f.getField = function(name){
12843 return p.fields.get(name);
12848 Roo.data.Record.AUTO_ID = 1000;
12849 Roo.data.Record.EDIT = 'edit';
12850 Roo.data.Record.REJECT = 'reject';
12851 Roo.data.Record.COMMIT = 'commit';
12853 Roo.data.Record.prototype = {
12855 * Readonly flag - true if this record has been modified.
12864 join : function(store){
12865 this.store = store;
12869 * Set the named field to the specified value.
12870 * @param {String} name The name of the field to set.
12871 * @param {Object} value The value to set the field to.
12873 set : function(name, value){
12874 if(this.data[name] == value){
12878 if(!this.modified){
12879 this.modified = {};
12881 if(typeof this.modified[name] == 'undefined'){
12882 this.modified[name] = this.data[name];
12884 this.data[name] = value;
12885 if(!this.editing && this.store){
12886 this.store.afterEdit(this);
12891 * Get the value of the named field.
12892 * @param {String} name The name of the field to get the value of.
12893 * @return {Object} The value of the field.
12895 get : function(name){
12896 return this.data[name];
12900 beginEdit : function(){
12901 this.editing = true;
12902 this.modified = {};
12906 cancelEdit : function(){
12907 this.editing = false;
12908 delete this.modified;
12912 endEdit : function(){
12913 this.editing = false;
12914 if(this.dirty && this.store){
12915 this.store.afterEdit(this);
12920 * Usually called by the {@link Roo.data.Store} which owns the Record.
12921 * Rejects all changes made to the Record since either creation, or the last commit operation.
12922 * Modified fields are reverted to their original values.
12924 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12925 * of reject operations.
12927 reject : function(){
12928 var m = this.modified;
12930 if(typeof m[n] != "function"){
12931 this.data[n] = m[n];
12934 this.dirty = false;
12935 delete this.modified;
12936 this.editing = false;
12938 this.store.afterReject(this);
12943 * Usually called by the {@link Roo.data.Store} which owns the Record.
12944 * Commits all changes made to the Record since either creation, or the last commit operation.
12946 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12947 * of commit operations.
12949 commit : function(){
12950 this.dirty = false;
12951 delete this.modified;
12952 this.editing = false;
12954 this.store.afterCommit(this);
12959 hasError : function(){
12960 return this.error != null;
12964 clearError : function(){
12969 * Creates a copy of this record.
12970 * @param {String} id (optional) A new record id if you don't want to use this record's id
12973 copy : function(newId) {
12974 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12978 * Ext JS Library 1.1.1
12979 * Copyright(c) 2006-2007, Ext JS, LLC.
12981 * Originally Released Under LGPL - original licence link has changed is not relivant.
12984 * <script type="text/javascript">
12990 * @class Roo.data.Store
12991 * @extends Roo.util.Observable
12992 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12993 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12995 * 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
12996 * has no knowledge of the format of the data returned by the Proxy.<br>
12998 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12999 * instances from the data object. These records are cached and made available through accessor functions.
13001 * Creates a new Store.
13002 * @param {Object} config A config object containing the objects needed for the Store to access data,
13003 * and read the data into Records.
13005 Roo.data.Store = function(config){
13006 this.data = new Roo.util.MixedCollection(false);
13007 this.data.getKey = function(o){
13010 this.baseParams = {};
13012 this.paramNames = {
13017 "multisort" : "_multisort"
13020 if(config && config.data){
13021 this.inlineData = config.data;
13022 delete config.data;
13025 Roo.apply(this, config);
13027 if(this.reader){ // reader passed
13028 this.reader = Roo.factory(this.reader, Roo.data);
13029 this.reader.xmodule = this.xmodule || false;
13030 if(!this.recordType){
13031 this.recordType = this.reader.recordType;
13033 if(this.reader.onMetaChange){
13034 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13038 if(this.recordType){
13039 this.fields = this.recordType.prototype.fields;
13041 this.modified = [];
13045 * @event datachanged
13046 * Fires when the data cache has changed, and a widget which is using this Store
13047 * as a Record cache should refresh its view.
13048 * @param {Store} this
13050 datachanged : true,
13052 * @event metachange
13053 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13054 * @param {Store} this
13055 * @param {Object} meta The JSON metadata
13060 * Fires when Records have been added to the Store
13061 * @param {Store} this
13062 * @param {Roo.data.Record[]} records The array of Records added
13063 * @param {Number} index The index at which the record(s) were added
13068 * Fires when a Record has been removed from the Store
13069 * @param {Store} this
13070 * @param {Roo.data.Record} record The Record that was removed
13071 * @param {Number} index The index at which the record was removed
13076 * Fires when a Record has been updated
13077 * @param {Store} this
13078 * @param {Roo.data.Record} record The Record that was updated
13079 * @param {String} operation The update operation being performed. Value may be one of:
13081 Roo.data.Record.EDIT
13082 Roo.data.Record.REJECT
13083 Roo.data.Record.COMMIT
13089 * Fires when the data cache has been cleared.
13090 * @param {Store} this
13094 * @event beforeload
13095 * Fires before a request is made for a new data object. If the beforeload handler returns false
13096 * the load action will be canceled.
13097 * @param {Store} this
13098 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13102 * @event beforeloadadd
13103 * Fires after a new set of Records has been loaded.
13104 * @param {Store} this
13105 * @param {Roo.data.Record[]} records The Records that were loaded
13106 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13108 beforeloadadd : true,
13111 * Fires after a new set of Records has been loaded, before they are added to the store.
13112 * @param {Store} this
13113 * @param {Roo.data.Record[]} records The Records that were loaded
13114 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13115 * @params {Object} return from reader
13119 * @event loadexception
13120 * Fires if an exception occurs in the Proxy during loading.
13121 * Called with the signature of the Proxy's "loadexception" event.
13122 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13125 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13126 * @param {Object} load options
13127 * @param {Object} jsonData from your request (normally this contains the Exception)
13129 loadexception : true
13133 this.proxy = Roo.factory(this.proxy, Roo.data);
13134 this.proxy.xmodule = this.xmodule || false;
13135 this.relayEvents(this.proxy, ["loadexception"]);
13137 this.sortToggle = {};
13138 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13140 Roo.data.Store.superclass.constructor.call(this);
13142 if(this.inlineData){
13143 this.loadData(this.inlineData);
13144 delete this.inlineData;
13148 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13150 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13151 * without a remote query - used by combo/forms at present.
13155 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13158 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13161 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13162 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13165 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13166 * on any HTTP request
13169 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13172 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13176 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13177 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13179 remoteSort : false,
13182 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13183 * loaded or when a record is removed. (defaults to false).
13185 pruneModifiedRecords : false,
13188 lastOptions : null,
13191 * Add Records to the Store and fires the add event.
13192 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13194 add : function(records){
13195 records = [].concat(records);
13196 for(var i = 0, len = records.length; i < len; i++){
13197 records[i].join(this);
13199 var index = this.data.length;
13200 this.data.addAll(records);
13201 this.fireEvent("add", this, records, index);
13205 * Remove a Record from the Store and fires the remove event.
13206 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13208 remove : function(record){
13209 var index = this.data.indexOf(record);
13210 this.data.removeAt(index);
13212 if(this.pruneModifiedRecords){
13213 this.modified.remove(record);
13215 this.fireEvent("remove", this, record, index);
13219 * Remove all Records from the Store and fires the clear event.
13221 removeAll : function(){
13223 if(this.pruneModifiedRecords){
13224 this.modified = [];
13226 this.fireEvent("clear", this);
13230 * Inserts Records to the Store at the given index and fires the add event.
13231 * @param {Number} index The start index at which to insert the passed Records.
13232 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13234 insert : function(index, records){
13235 records = [].concat(records);
13236 for(var i = 0, len = records.length; i < len; i++){
13237 this.data.insert(index, records[i]);
13238 records[i].join(this);
13240 this.fireEvent("add", this, records, index);
13244 * Get the index within the cache of the passed Record.
13245 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13246 * @return {Number} The index of the passed Record. Returns -1 if not found.
13248 indexOf : function(record){
13249 return this.data.indexOf(record);
13253 * Get the index within the cache of the Record with the passed id.
13254 * @param {String} id The id of the Record to find.
13255 * @return {Number} The index of the Record. Returns -1 if not found.
13257 indexOfId : function(id){
13258 return this.data.indexOfKey(id);
13262 * Get the Record with the specified id.
13263 * @param {String} id The id of the Record to find.
13264 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13266 getById : function(id){
13267 return this.data.key(id);
13271 * Get the Record at the specified index.
13272 * @param {Number} index The index of the Record to find.
13273 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13275 getAt : function(index){
13276 return this.data.itemAt(index);
13280 * Returns a range of Records between specified indices.
13281 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13282 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13283 * @return {Roo.data.Record[]} An array of Records
13285 getRange : function(start, end){
13286 return this.data.getRange(start, end);
13290 storeOptions : function(o){
13291 o = Roo.apply({}, o);
13294 this.lastOptions = o;
13298 * Loads the Record cache from the configured Proxy using the configured Reader.
13300 * If using remote paging, then the first load call must specify the <em>start</em>
13301 * and <em>limit</em> properties in the options.params property to establish the initial
13302 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13304 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13305 * and this call will return before the new data has been loaded. Perform any post-processing
13306 * in a callback function, or in a "load" event handler.</strong>
13308 * @param {Object} options An object containing properties which control loading options:<ul>
13309 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13310 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13311 * passed the following arguments:<ul>
13312 * <li>r : Roo.data.Record[]</li>
13313 * <li>options: Options object from the load call</li>
13314 * <li>success: Boolean success indicator</li></ul></li>
13315 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13316 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13319 load : function(options){
13320 options = options || {};
13321 if(this.fireEvent("beforeload", this, options) !== false){
13322 this.storeOptions(options);
13323 var p = Roo.apply(options.params || {}, this.baseParams);
13324 // if meta was not loaded from remote source.. try requesting it.
13325 if (!this.reader.metaFromRemote) {
13326 p._requestMeta = 1;
13328 if(this.sortInfo && this.remoteSort){
13329 var pn = this.paramNames;
13330 p[pn["sort"]] = this.sortInfo.field;
13331 p[pn["dir"]] = this.sortInfo.direction;
13333 if (this.multiSort) {
13334 var pn = this.paramNames;
13335 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13338 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13343 * Reloads the Record cache from the configured Proxy using the configured Reader and
13344 * the options from the last load operation performed.
13345 * @param {Object} options (optional) An object containing properties which may override the options
13346 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13347 * the most recently used options are reused).
13349 reload : function(options){
13350 this.load(Roo.applyIf(options||{}, this.lastOptions));
13354 // Called as a callback by the Reader during a load operation.
13355 loadRecords : function(o, options, success){
13356 if(!o || success === false){
13357 if(success !== false){
13358 this.fireEvent("load", this, [], options, o);
13360 if(options.callback){
13361 options.callback.call(options.scope || this, [], options, false);
13365 // if data returned failure - throw an exception.
13366 if (o.success === false) {
13367 // show a message if no listener is registered.
13368 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13369 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13371 // loadmask wil be hooked into this..
13372 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13375 var r = o.records, t = o.totalRecords || r.length;
13377 this.fireEvent("beforeloadadd", this, r, options, o);
13379 if(!options || options.add !== true){
13380 if(this.pruneModifiedRecords){
13381 this.modified = [];
13383 for(var i = 0, len = r.length; i < len; i++){
13387 this.data = this.snapshot;
13388 delete this.snapshot;
13391 this.data.addAll(r);
13392 this.totalLength = t;
13394 this.fireEvent("datachanged", this);
13396 this.totalLength = Math.max(t, this.data.length+r.length);
13400 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13402 var e = new Roo.data.Record({});
13404 e.set(this.parent.displayField, this.parent.emptyTitle);
13405 e.set(this.parent.valueField, '');
13410 this.fireEvent("load", this, r, options, o);
13411 if(options.callback){
13412 options.callback.call(options.scope || this, r, options, true);
13418 * Loads data from a passed data block. A Reader which understands the format of the data
13419 * must have been configured in the constructor.
13420 * @param {Object} data The data block from which to read the Records. The format of the data expected
13421 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13422 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13424 loadData : function(o, append){
13425 var r = this.reader.readRecords(o);
13426 this.loadRecords(r, {add: append}, true);
13430 * using 'cn' the nested child reader read the child array into it's child stores.
13431 * @param {Object} rec The record with a 'children array
13433 loadDataFromChildren : function(rec)
13435 this.loadData(this.reader.toLoadData(rec));
13440 * Gets the number of cached records.
13442 * <em>If using paging, this may not be the total size of the dataset. If the data object
13443 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13444 * the data set size</em>
13446 getCount : function(){
13447 return this.data.length || 0;
13451 * Gets the total number of records in the dataset as returned by the server.
13453 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13454 * the dataset size</em>
13456 getTotalCount : function(){
13457 return this.totalLength || 0;
13461 * Returns the sort state of the Store as an object with two properties:
13463 field {String} The name of the field by which the Records are sorted
13464 direction {String} The sort order, "ASC" or "DESC"
13467 getSortState : function(){
13468 return this.sortInfo;
13472 applySort : function(){
13473 if(this.sortInfo && !this.remoteSort){
13474 var s = this.sortInfo, f = s.field;
13475 var st = this.fields.get(f).sortType;
13476 var fn = function(r1, r2){
13477 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13478 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13480 this.data.sort(s.direction, fn);
13481 if(this.snapshot && this.snapshot != this.data){
13482 this.snapshot.sort(s.direction, fn);
13488 * Sets the default sort column and order to be used by the next load operation.
13489 * @param {String} fieldName The name of the field to sort by.
13490 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13492 setDefaultSort : function(field, dir){
13493 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13497 * Sort the Records.
13498 * If remote sorting is used, the sort is performed on the server, and the cache is
13499 * reloaded. If local sorting is used, the cache is sorted internally.
13500 * @param {String} fieldName The name of the field to sort by.
13501 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13503 sort : function(fieldName, dir){
13504 var f = this.fields.get(fieldName);
13506 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13508 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13509 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13514 this.sortToggle[f.name] = dir;
13515 this.sortInfo = {field: f.name, direction: dir};
13516 if(!this.remoteSort){
13518 this.fireEvent("datachanged", this);
13520 this.load(this.lastOptions);
13525 * Calls the specified function for each of the Records in the cache.
13526 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13527 * Returning <em>false</em> aborts and exits the iteration.
13528 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13530 each : function(fn, scope){
13531 this.data.each(fn, scope);
13535 * Gets all records modified since the last commit. Modified records are persisted across load operations
13536 * (e.g., during paging).
13537 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13539 getModifiedRecords : function(){
13540 return this.modified;
13544 createFilterFn : function(property, value, anyMatch){
13545 if(!value.exec){ // not a regex
13546 value = String(value);
13547 if(value.length == 0){
13550 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13552 return function(r){
13553 return value.test(r.data[property]);
13558 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13559 * @param {String} property A field on your records
13560 * @param {Number} start The record index to start at (defaults to 0)
13561 * @param {Number} end The last record index to include (defaults to length - 1)
13562 * @return {Number} The sum
13564 sum : function(property, start, end){
13565 var rs = this.data.items, v = 0;
13566 start = start || 0;
13567 end = (end || end === 0) ? end : rs.length-1;
13569 for(var i = start; i <= end; i++){
13570 v += (rs[i].data[property] || 0);
13576 * Filter the records by a specified property.
13577 * @param {String} field A field on your records
13578 * @param {String/RegExp} value Either a string that the field
13579 * should start with or a RegExp to test against the field
13580 * @param {Boolean} anyMatch True to match any part not just the beginning
13582 filter : function(property, value, anyMatch){
13583 var fn = this.createFilterFn(property, value, anyMatch);
13584 return fn ? this.filterBy(fn) : this.clearFilter();
13588 * Filter by a function. The specified function will be called with each
13589 * record in this data source. If the function returns true the record is included,
13590 * otherwise it is filtered.
13591 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13592 * @param {Object} scope (optional) The scope of the function (defaults to this)
13594 filterBy : function(fn, scope){
13595 this.snapshot = this.snapshot || this.data;
13596 this.data = this.queryBy(fn, scope||this);
13597 this.fireEvent("datachanged", this);
13601 * Query the records by a specified property.
13602 * @param {String} field A field on your records
13603 * @param {String/RegExp} value Either a string that the field
13604 * should start with or a RegExp to test against the field
13605 * @param {Boolean} anyMatch True to match any part not just the beginning
13606 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13608 query : function(property, value, anyMatch){
13609 var fn = this.createFilterFn(property, value, anyMatch);
13610 return fn ? this.queryBy(fn) : this.data.clone();
13614 * Query by a function. The specified function will be called with each
13615 * record in this data source. If the function returns true the record is included
13617 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13618 * @param {Object} scope (optional) The scope of the function (defaults to this)
13619 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13621 queryBy : function(fn, scope){
13622 var data = this.snapshot || this.data;
13623 return data.filterBy(fn, scope||this);
13627 * Collects unique values for a particular dataIndex from this store.
13628 * @param {String} dataIndex The property to collect
13629 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13630 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13631 * @return {Array} An array of the unique values
13633 collect : function(dataIndex, allowNull, bypassFilter){
13634 var d = (bypassFilter === true && this.snapshot) ?
13635 this.snapshot.items : this.data.items;
13636 var v, sv, r = [], l = {};
13637 for(var i = 0, len = d.length; i < len; i++){
13638 v = d[i].data[dataIndex];
13640 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13649 * Revert to a view of the Record cache with no filtering applied.
13650 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13652 clearFilter : function(suppressEvent){
13653 if(this.snapshot && this.snapshot != this.data){
13654 this.data = this.snapshot;
13655 delete this.snapshot;
13656 if(suppressEvent !== true){
13657 this.fireEvent("datachanged", this);
13663 afterEdit : function(record){
13664 if(this.modified.indexOf(record) == -1){
13665 this.modified.push(record);
13667 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13671 afterReject : function(record){
13672 this.modified.remove(record);
13673 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13677 afterCommit : function(record){
13678 this.modified.remove(record);
13679 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13683 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13684 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13686 commitChanges : function(){
13687 var m = this.modified.slice(0);
13688 this.modified = [];
13689 for(var i = 0, len = m.length; i < len; i++){
13695 * Cancel outstanding changes on all changed records.
13697 rejectChanges : function(){
13698 var m = this.modified.slice(0);
13699 this.modified = [];
13700 for(var i = 0, len = m.length; i < len; i++){
13705 onMetaChange : function(meta, rtype, o){
13706 this.recordType = rtype;
13707 this.fields = rtype.prototype.fields;
13708 delete this.snapshot;
13709 this.sortInfo = meta.sortInfo || this.sortInfo;
13710 this.modified = [];
13711 this.fireEvent('metachange', this, this.reader.meta);
13714 moveIndex : function(data, type)
13716 var index = this.indexOf(data);
13718 var newIndex = index + type;
13722 this.insert(newIndex, data);
13727 * Ext JS Library 1.1.1
13728 * Copyright(c) 2006-2007, Ext JS, LLC.
13730 * Originally Released Under LGPL - original licence link has changed is not relivant.
13733 * <script type="text/javascript">
13737 * @class Roo.data.SimpleStore
13738 * @extends Roo.data.Store
13739 * Small helper class to make creating Stores from Array data easier.
13740 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13741 * @cfg {Array} fields An array of field definition objects, or field name strings.
13742 * @cfg {Object} an existing reader (eg. copied from another store)
13743 * @cfg {Array} data The multi-dimensional array of data
13745 * @param {Object} config
13747 Roo.data.SimpleStore = function(config)
13749 Roo.data.SimpleStore.superclass.constructor.call(this, {
13751 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13754 Roo.data.Record.create(config.fields)
13756 proxy : new Roo.data.MemoryProxy(config.data)
13760 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13762 * Ext JS Library 1.1.1
13763 * Copyright(c) 2006-2007, Ext JS, LLC.
13765 * Originally Released Under LGPL - original licence link has changed is not relivant.
13768 * <script type="text/javascript">
13773 * @extends Roo.data.Store
13774 * @class Roo.data.JsonStore
13775 * Small helper class to make creating Stores for JSON data easier. <br/>
13777 var store = new Roo.data.JsonStore({
13778 url: 'get-images.php',
13780 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13783 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13784 * JsonReader and HttpProxy (unless inline data is provided).</b>
13785 * @cfg {Array} fields An array of field definition objects, or field name strings.
13787 * @param {Object} config
13789 Roo.data.JsonStore = function(c){
13790 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13791 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13792 reader: new Roo.data.JsonReader(c, c.fields)
13795 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13797 * Ext JS Library 1.1.1
13798 * Copyright(c) 2006-2007, Ext JS, LLC.
13800 * Originally Released Under LGPL - original licence link has changed is not relivant.
13803 * <script type="text/javascript">
13807 Roo.data.Field = function(config){
13808 if(typeof config == "string"){
13809 config = {name: config};
13811 Roo.apply(this, config);
13814 this.type = "auto";
13817 var st = Roo.data.SortTypes;
13818 // named sortTypes are supported, here we look them up
13819 if(typeof this.sortType == "string"){
13820 this.sortType = st[this.sortType];
13823 // set default sortType for strings and dates
13824 if(!this.sortType){
13827 this.sortType = st.asUCString;
13830 this.sortType = st.asDate;
13833 this.sortType = st.none;
13838 var stripRe = /[\$,%]/g;
13840 // prebuilt conversion function for this field, instead of
13841 // switching every time we're reading a value
13843 var cv, dateFormat = this.dateFormat;
13848 cv = function(v){ return v; };
13851 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13855 return v !== undefined && v !== null && v !== '' ?
13856 parseInt(String(v).replace(stripRe, ""), 10) : '';
13861 return v !== undefined && v !== null && v !== '' ?
13862 parseFloat(String(v).replace(stripRe, ""), 10) : '';
13867 cv = function(v){ return v === true || v === "true" || v == 1; };
13874 if(v instanceof Date){
13878 if(dateFormat == "timestamp"){
13879 return new Date(v*1000);
13881 return Date.parseDate(v, dateFormat);
13883 var parsed = Date.parse(v);
13884 return parsed ? new Date(parsed) : null;
13893 Roo.data.Field.prototype = {
13901 * Ext JS Library 1.1.1
13902 * Copyright(c) 2006-2007, Ext JS, LLC.
13904 * Originally Released Under LGPL - original licence link has changed is not relivant.
13907 * <script type="text/javascript">
13910 // Base class for reading structured data from a data source. This class is intended to be
13911 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13914 * @class Roo.data.DataReader
13915 * Base class for reading structured data from a data source. This class is intended to be
13916 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13919 Roo.data.DataReader = function(meta, recordType){
13923 this.recordType = recordType instanceof Array ?
13924 Roo.data.Record.create(recordType) : recordType;
13927 Roo.data.DataReader.prototype = {
13930 readerType : 'Data',
13932 * Create an empty record
13933 * @param {Object} data (optional) - overlay some values
13934 * @return {Roo.data.Record} record created.
13936 newRow : function(d) {
13938 this.recordType.prototype.fields.each(function(c) {
13940 case 'int' : da[c.name] = 0; break;
13941 case 'date' : da[c.name] = new Date(); break;
13942 case 'float' : da[c.name] = 0.0; break;
13943 case 'boolean' : da[c.name] = false; break;
13944 default : da[c.name] = ""; break;
13948 return new this.recordType(Roo.apply(da, d));
13954 * Ext JS Library 1.1.1
13955 * Copyright(c) 2006-2007, Ext JS, LLC.
13957 * Originally Released Under LGPL - original licence link has changed is not relivant.
13960 * <script type="text/javascript">
13964 * @class Roo.data.DataProxy
13965 * @extends Roo.data.Observable
13966 * This class is an abstract base class for implementations which provide retrieval of
13967 * unformatted data objects.<br>
13969 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13970 * (of the appropriate type which knows how to parse the data object) to provide a block of
13971 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13973 * Custom implementations must implement the load method as described in
13974 * {@link Roo.data.HttpProxy#load}.
13976 Roo.data.DataProxy = function(){
13979 * @event beforeload
13980 * Fires before a network request is made to retrieve a data object.
13981 * @param {Object} This DataProxy object.
13982 * @param {Object} params The params parameter to the load function.
13987 * Fires before the load method's callback is called.
13988 * @param {Object} This DataProxy object.
13989 * @param {Object} o The data object.
13990 * @param {Object} arg The callback argument object passed to the load function.
13994 * @event loadexception
13995 * Fires if an Exception occurs during data retrieval.
13996 * @param {Object} This DataProxy object.
13997 * @param {Object} o The data object.
13998 * @param {Object} arg The callback argument object passed to the load function.
13999 * @param {Object} e The Exception.
14001 loadexception : true
14003 Roo.data.DataProxy.superclass.constructor.call(this);
14006 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14009 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14013 * Ext JS Library 1.1.1
14014 * Copyright(c) 2006-2007, Ext JS, LLC.
14016 * Originally Released Under LGPL - original licence link has changed is not relivant.
14019 * <script type="text/javascript">
14022 * @class Roo.data.MemoryProxy
14023 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14024 * to the Reader when its load method is called.
14026 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14028 Roo.data.MemoryProxy = function(data){
14032 Roo.data.MemoryProxy.superclass.constructor.call(this);
14036 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14039 * Load data from the requested source (in this case an in-memory
14040 * data object passed to the constructor), read the data object into
14041 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14042 * process that block using the passed callback.
14043 * @param {Object} params This parameter is not used by the MemoryProxy class.
14044 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14045 * object into a block of Roo.data.Records.
14046 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14047 * The function must be passed <ul>
14048 * <li>The Record block object</li>
14049 * <li>The "arg" argument from the load function</li>
14050 * <li>A boolean success indicator</li>
14052 * @param {Object} scope The scope in which to call the callback
14053 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14055 load : function(params, reader, callback, scope, arg){
14056 params = params || {};
14059 result = reader.readRecords(params.data ? params.data :this.data);
14061 this.fireEvent("loadexception", this, arg, null, e);
14062 callback.call(scope, null, arg, false);
14065 callback.call(scope, result, arg, true);
14069 update : function(params, records){
14074 * Ext JS Library 1.1.1
14075 * Copyright(c) 2006-2007, Ext JS, LLC.
14077 * Originally Released Under LGPL - original licence link has changed is not relivant.
14080 * <script type="text/javascript">
14083 * @class Roo.data.HttpProxy
14084 * @extends Roo.data.DataProxy
14085 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14086 * configured to reference a certain URL.<br><br>
14088 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14089 * from which the running page was served.<br><br>
14091 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14093 * Be aware that to enable the browser to parse an XML document, the server must set
14094 * the Content-Type header in the HTTP response to "text/xml".
14096 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14097 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14098 * will be used to make the request.
14100 Roo.data.HttpProxy = function(conn){
14101 Roo.data.HttpProxy.superclass.constructor.call(this);
14102 // is conn a conn config or a real conn?
14104 this.useAjax = !conn || !conn.events;
14108 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14109 // thse are take from connection...
14112 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14115 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14116 * extra parameters to each request made by this object. (defaults to undefined)
14119 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14120 * to each request made by this object. (defaults to undefined)
14123 * @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)
14126 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14129 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14135 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14139 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14140 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14141 * a finer-grained basis than the DataProxy events.
14143 getConnection : function(){
14144 return this.useAjax ? Roo.Ajax : this.conn;
14148 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14149 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14150 * process that block using the passed callback.
14151 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14152 * for the request to the remote server.
14153 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14154 * object into a block of Roo.data.Records.
14155 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14156 * The function must be passed <ul>
14157 * <li>The Record block object</li>
14158 * <li>The "arg" argument from the load function</li>
14159 * <li>A boolean success indicator</li>
14161 * @param {Object} scope The scope in which to call the callback
14162 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14164 load : function(params, reader, callback, scope, arg){
14165 if(this.fireEvent("beforeload", this, params) !== false){
14167 params : params || {},
14169 callback : callback,
14174 callback : this.loadResponse,
14178 Roo.applyIf(o, this.conn);
14179 if(this.activeRequest){
14180 Roo.Ajax.abort(this.activeRequest);
14182 this.activeRequest = Roo.Ajax.request(o);
14184 this.conn.request(o);
14187 callback.call(scope||this, null, arg, false);
14192 loadResponse : function(o, success, response){
14193 delete this.activeRequest;
14195 this.fireEvent("loadexception", this, o, response);
14196 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14201 result = o.reader.read(response);
14203 this.fireEvent("loadexception", this, o, response, e);
14204 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14208 this.fireEvent("load", this, o, o.request.arg);
14209 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14213 update : function(dataSet){
14218 updateResponse : function(dataSet){
14223 * Ext JS Library 1.1.1
14224 * Copyright(c) 2006-2007, Ext JS, LLC.
14226 * Originally Released Under LGPL - original licence link has changed is not relivant.
14229 * <script type="text/javascript">
14233 * @class Roo.data.ScriptTagProxy
14234 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14235 * other than the originating domain of the running page.<br><br>
14237 * <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
14238 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14240 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14241 * source code that is used as the source inside a <script> tag.<br><br>
14243 * In order for the browser to process the returned data, the server must wrap the data object
14244 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14245 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14246 * depending on whether the callback name was passed:
14249 boolean scriptTag = false;
14250 String cb = request.getParameter("callback");
14253 response.setContentType("text/javascript");
14255 response.setContentType("application/x-json");
14257 Writer out = response.getWriter();
14259 out.write(cb + "(");
14261 out.print(dataBlock.toJsonString());
14268 * @param {Object} config A configuration object.
14270 Roo.data.ScriptTagProxy = function(config){
14271 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14272 Roo.apply(this, config);
14273 this.head = document.getElementsByTagName("head")[0];
14276 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14278 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14280 * @cfg {String} url The URL from which to request the data object.
14283 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14287 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14288 * the server the name of the callback function set up by the load call to process the returned data object.
14289 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14290 * javascript output which calls this named function passing the data object as its only parameter.
14292 callbackParam : "callback",
14294 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14295 * name to the request.
14300 * Load data from the configured URL, read the data object into
14301 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14302 * process that block using the passed callback.
14303 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14304 * for the request to the remote server.
14305 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14306 * object into a block of Roo.data.Records.
14307 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14308 * The function must be passed <ul>
14309 * <li>The Record block object</li>
14310 * <li>The "arg" argument from the load function</li>
14311 * <li>A boolean success indicator</li>
14313 * @param {Object} scope The scope in which to call the callback
14314 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14316 load : function(params, reader, callback, scope, arg){
14317 if(this.fireEvent("beforeload", this, params) !== false){
14319 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14321 var url = this.url;
14322 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14324 url += "&_dc=" + (new Date().getTime());
14326 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14329 cb : "stcCallback"+transId,
14330 scriptId : "stcScript"+transId,
14334 callback : callback,
14340 window[trans.cb] = function(o){
14341 conn.handleResponse(o, trans);
14344 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14346 if(this.autoAbort !== false){
14350 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14352 var script = document.createElement("script");
14353 script.setAttribute("src", url);
14354 script.setAttribute("type", "text/javascript");
14355 script.setAttribute("id", trans.scriptId);
14356 this.head.appendChild(script);
14358 this.trans = trans;
14360 callback.call(scope||this, null, arg, false);
14365 isLoading : function(){
14366 return this.trans ? true : false;
14370 * Abort the current server request.
14372 abort : function(){
14373 if(this.isLoading()){
14374 this.destroyTrans(this.trans);
14379 destroyTrans : function(trans, isLoaded){
14380 this.head.removeChild(document.getElementById(trans.scriptId));
14381 clearTimeout(trans.timeoutId);
14383 window[trans.cb] = undefined;
14385 delete window[trans.cb];
14388 // if hasn't been loaded, wait for load to remove it to prevent script error
14389 window[trans.cb] = function(){
14390 window[trans.cb] = undefined;
14392 delete window[trans.cb];
14399 handleResponse : function(o, trans){
14400 this.trans = false;
14401 this.destroyTrans(trans, true);
14404 result = trans.reader.readRecords(o);
14406 this.fireEvent("loadexception", this, o, trans.arg, e);
14407 trans.callback.call(trans.scope||window, null, trans.arg, false);
14410 this.fireEvent("load", this, o, trans.arg);
14411 trans.callback.call(trans.scope||window, result, trans.arg, true);
14415 handleFailure : function(trans){
14416 this.trans = false;
14417 this.destroyTrans(trans, false);
14418 this.fireEvent("loadexception", this, null, trans.arg);
14419 trans.callback.call(trans.scope||window, null, trans.arg, false);
14423 * Ext JS Library 1.1.1
14424 * Copyright(c) 2006-2007, Ext JS, LLC.
14426 * Originally Released Under LGPL - original licence link has changed is not relivant.
14429 * <script type="text/javascript">
14433 * @class Roo.data.JsonReader
14434 * @extends Roo.data.DataReader
14435 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14436 * based on mappings in a provided Roo.data.Record constructor.
14438 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14439 * in the reply previously.
14444 var RecordDef = Roo.data.Record.create([
14445 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14446 {name: 'occupation'} // This field will use "occupation" as the mapping.
14448 var myReader = new Roo.data.JsonReader({
14449 totalProperty: "results", // The property which contains the total dataset size (optional)
14450 root: "rows", // The property which contains an Array of row objects
14451 id: "id" // The property within each row object that provides an ID for the record (optional)
14455 * This would consume a JSON file like this:
14457 { 'results': 2, 'rows': [
14458 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14459 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14462 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14463 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14464 * paged from the remote server.
14465 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14466 * @cfg {String} root name of the property which contains the Array of row objects.
14467 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14468 * @cfg {Array} fields Array of field definition objects
14470 * Create a new JsonReader
14471 * @param {Object} meta Metadata configuration options
14472 * @param {Object} recordType Either an Array of field definition objects,
14473 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14475 Roo.data.JsonReader = function(meta, recordType){
14478 // set some defaults:
14479 Roo.applyIf(meta, {
14480 totalProperty: 'total',
14481 successProperty : 'success',
14486 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14488 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14490 readerType : 'Json',
14493 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14494 * Used by Store query builder to append _requestMeta to params.
14497 metaFromRemote : false,
14499 * This method is only used by a DataProxy which has retrieved data from a remote server.
14500 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14501 * @return {Object} data A data block which is used by an Roo.data.Store object as
14502 * a cache of Roo.data.Records.
14504 read : function(response){
14505 var json = response.responseText;
14507 var o = /* eval:var:o */ eval("("+json+")");
14509 throw {message: "JsonReader.read: Json object not found"};
14515 this.metaFromRemote = true;
14516 this.meta = o.metaData;
14517 this.recordType = Roo.data.Record.create(o.metaData.fields);
14518 this.onMetaChange(this.meta, this.recordType, o);
14520 return this.readRecords(o);
14523 // private function a store will implement
14524 onMetaChange : function(meta, recordType, o){
14531 simpleAccess: function(obj, subsc) {
14538 getJsonAccessor: function(){
14540 return function(expr) {
14542 return(re.test(expr))
14543 ? new Function("obj", "return obj." + expr)
14548 return Roo.emptyFn;
14553 * Create a data block containing Roo.data.Records from an XML document.
14554 * @param {Object} o An object which contains an Array of row objects in the property specified
14555 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14556 * which contains the total size of the dataset.
14557 * @return {Object} data A data block which is used by an Roo.data.Store object as
14558 * a cache of Roo.data.Records.
14560 readRecords : function(o){
14562 * After any data loads, the raw JSON data is available for further custom processing.
14566 var s = this.meta, Record = this.recordType,
14567 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14569 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14571 if(s.totalProperty) {
14572 this.getTotal = this.getJsonAccessor(s.totalProperty);
14574 if(s.successProperty) {
14575 this.getSuccess = this.getJsonAccessor(s.successProperty);
14577 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14579 var g = this.getJsonAccessor(s.id);
14580 this.getId = function(rec) {
14582 return (r === undefined || r === "") ? null : r;
14585 this.getId = function(){return null;};
14588 for(var jj = 0; jj < fl; jj++){
14590 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14591 this.ef[jj] = this.getJsonAccessor(map);
14595 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14596 if(s.totalProperty){
14597 var vt = parseInt(this.getTotal(o), 10);
14602 if(s.successProperty){
14603 var vs = this.getSuccess(o);
14604 if(vs === false || vs === 'false'){
14609 for(var i = 0; i < c; i++){
14612 var id = this.getId(n);
14613 for(var j = 0; j < fl; j++){
14615 var v = this.ef[j](n);
14617 Roo.log('missing convert for ' + f.name);
14621 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14623 var record = new Record(values, id);
14625 records[i] = record;
14631 totalRecords : totalRecords
14634 // used when loading children.. @see loadDataFromChildren
14635 toLoadData: function(rec)
14637 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14638 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14639 return { data : data, total : data.length };
14644 * Ext JS Library 1.1.1
14645 * Copyright(c) 2006-2007, Ext JS, LLC.
14647 * Originally Released Under LGPL - original licence link has changed is not relivant.
14650 * <script type="text/javascript">
14654 * @class Roo.data.ArrayReader
14655 * @extends Roo.data.DataReader
14656 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14657 * Each element of that Array represents a row of data fields. The
14658 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14659 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14663 var RecordDef = Roo.data.Record.create([
14664 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14665 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14667 var myReader = new Roo.data.ArrayReader({
14668 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14672 * This would consume an Array like this:
14674 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14678 * Create a new JsonReader
14679 * @param {Object} meta Metadata configuration options.
14680 * @param {Object|Array} recordType Either an Array of field definition objects
14682 * @cfg {Array} fields Array of field definition objects
14683 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14684 * as specified to {@link Roo.data.Record#create},
14685 * or an {@link Roo.data.Record} object
14688 * created using {@link Roo.data.Record#create}.
14690 Roo.data.ArrayReader = function(meta, recordType)
14692 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14695 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14698 * Create a data block containing Roo.data.Records from an XML document.
14699 * @param {Object} o An Array of row objects which represents the dataset.
14700 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14701 * a cache of Roo.data.Records.
14703 readRecords : function(o)
14705 var sid = this.meta ? this.meta.id : null;
14706 var recordType = this.recordType, fields = recordType.prototype.fields;
14709 for(var i = 0; i < root.length; i++){
14712 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14713 for(var j = 0, jlen = fields.length; j < jlen; j++){
14714 var f = fields.items[j];
14715 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14716 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14718 values[f.name] = v;
14720 var record = new recordType(values, id);
14722 records[records.length] = record;
14726 totalRecords : records.length
14729 // used when loading children.. @see loadDataFromChildren
14730 toLoadData: function(rec)
14732 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14733 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14744 * @class Roo.bootstrap.ComboBox
14745 * @extends Roo.bootstrap.TriggerField
14746 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14747 * @cfg {Boolean} append (true|false) default false
14748 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14749 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14750 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14751 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14752 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14753 * @cfg {Boolean} animate default true
14754 * @cfg {Boolean} emptyResultText only for touch device
14755 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14756 * @cfg {String} emptyTitle default ''
14757 * @cfg {Number} width fixed with? experimental
14759 * Create a new ComboBox.
14760 * @param {Object} config Configuration options
14762 Roo.bootstrap.ComboBox = function(config){
14763 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14767 * Fires when the dropdown list is expanded
14768 * @param {Roo.bootstrap.ComboBox} combo This combo box
14773 * Fires when the dropdown list is collapsed
14774 * @param {Roo.bootstrap.ComboBox} combo This combo box
14778 * @event beforeselect
14779 * Fires before a list item is selected. Return false to cancel the selection.
14780 * @param {Roo.bootstrap.ComboBox} combo This combo box
14781 * @param {Roo.data.Record} record The data record returned from the underlying store
14782 * @param {Number} index The index of the selected item in the dropdown list
14784 'beforeselect' : true,
14787 * Fires when a list item is selected
14788 * @param {Roo.bootstrap.ComboBox} combo This combo box
14789 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14790 * @param {Number} index The index of the selected item in the dropdown list
14794 * @event beforequery
14795 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14796 * The event object passed has these properties:
14797 * @param {Roo.bootstrap.ComboBox} combo This combo box
14798 * @param {String} query The query
14799 * @param {Boolean} forceAll true to force "all" query
14800 * @param {Boolean} cancel true to cancel the query
14801 * @param {Object} e The query event object
14803 'beforequery': true,
14806 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14807 * @param {Roo.bootstrap.ComboBox} combo This combo box
14812 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14813 * @param {Roo.bootstrap.ComboBox} combo This combo box
14814 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14819 * Fires when the remove value from the combobox array
14820 * @param {Roo.bootstrap.ComboBox} combo This combo box
14824 * @event afterremove
14825 * Fires when the remove value from the combobox array
14826 * @param {Roo.bootstrap.ComboBox} combo This combo box
14828 'afterremove' : true,
14830 * @event specialfilter
14831 * Fires when specialfilter
14832 * @param {Roo.bootstrap.ComboBox} combo This combo box
14834 'specialfilter' : true,
14837 * Fires when tick the element
14838 * @param {Roo.bootstrap.ComboBox} combo This combo box
14842 * @event touchviewdisplay
14843 * Fires when touch view require special display (default is using displayField)
14844 * @param {Roo.bootstrap.ComboBox} combo This combo box
14845 * @param {Object} cfg set html .
14847 'touchviewdisplay' : true
14852 this.tickItems = [];
14854 this.selectedIndex = -1;
14855 if(this.mode == 'local'){
14856 if(config.queryDelay === undefined){
14857 this.queryDelay = 10;
14859 if(config.minChars === undefined){
14865 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14868 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14869 * rendering into an Roo.Editor, defaults to false)
14872 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14873 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14876 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14879 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14880 * the dropdown list (defaults to undefined, with no header element)
14884 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
14888 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14890 listWidth: undefined,
14892 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14893 * mode = 'remote' or 'text' if mode = 'local')
14895 displayField: undefined,
14898 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14899 * mode = 'remote' or 'value' if mode = 'local').
14900 * Note: use of a valueField requires the user make a selection
14901 * in order for a value to be mapped.
14903 valueField: undefined,
14905 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14910 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14911 * field's data value (defaults to the underlying DOM element's name)
14913 hiddenName: undefined,
14915 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14919 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14921 selectedClass: 'active',
14924 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14928 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14929 * anchor positions (defaults to 'tl-bl')
14931 listAlign: 'tl-bl?',
14933 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14937 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
14938 * query specified by the allQuery config option (defaults to 'query')
14940 triggerAction: 'query',
14942 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14943 * (defaults to 4, does not apply if editable = false)
14947 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14948 * delay (typeAheadDelay) if it matches a known value (defaults to false)
14952 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14953 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14957 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14958 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
14962 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
14963 * when editable = true (defaults to false)
14965 selectOnFocus:false,
14967 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14969 queryParam: 'query',
14971 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
14972 * when mode = 'remote' (defaults to 'Loading...')
14974 loadingText: 'Loading...',
14976 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14980 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14984 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14985 * traditional select (defaults to true)
14989 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14993 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14997 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14998 * listWidth has a higher value)
15002 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15003 * allow the user to set arbitrary text into the field (defaults to false)
15005 forceSelection:false,
15007 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15008 * if typeAhead = true (defaults to 250)
15010 typeAheadDelay : 250,
15012 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15013 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15015 valueNotFoundText : undefined,
15017 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15019 blockFocus : false,
15022 * @cfg {Boolean} disableClear Disable showing of clear button.
15024 disableClear : false,
15026 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15028 alwaysQuery : false,
15031 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15036 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15038 invalidClass : "has-warning",
15041 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15043 validClass : "has-success",
15046 * @cfg {Boolean} specialFilter (true|false) special filter default false
15048 specialFilter : false,
15051 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15053 mobileTouchView : true,
15056 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15058 useNativeIOS : false,
15061 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15063 mobile_restrict_height : false,
15065 ios_options : false,
15077 btnPosition : 'right',
15078 triggerList : true,
15079 showToggleBtn : true,
15081 emptyResultText: 'Empty',
15082 triggerText : 'Select',
15086 // element that contains real text value.. (when hidden is used..)
15088 getAutoCreate : function()
15093 * Render classic select for iso
15096 if(Roo.isIOS && this.useNativeIOS){
15097 cfg = this.getAutoCreateNativeIOS();
15105 if(Roo.isTouch && this.mobileTouchView){
15106 cfg = this.getAutoCreateTouchView();
15113 if(!this.tickable){
15114 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15119 * ComboBox with tickable selections
15122 var align = this.labelAlign || this.parentLabelAlign();
15125 cls : 'form-group roo-combobox-tickable' //input-group
15128 var btn_text_select = '';
15129 var btn_text_done = '';
15130 var btn_text_cancel = '';
15132 if (this.btn_text_show) {
15133 btn_text_select = 'Select';
15134 btn_text_done = 'Done';
15135 btn_text_cancel = 'Cancel';
15140 cls : 'tickable-buttons',
15145 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15146 //html : this.triggerText
15147 html: btn_text_select
15153 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15155 html: btn_text_done
15161 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15163 html: btn_text_cancel
15169 buttons.cn.unshift({
15171 cls: 'roo-select2-search-field-input'
15177 Roo.each(buttons.cn, function(c){
15179 c.cls += ' btn-' + _this.size;
15182 if (_this.disabled) {
15189 style : 'display: contents',
15194 cls: 'form-hidden-field'
15198 cls: 'roo-select2-choices',
15202 cls: 'roo-select2-search-field',
15213 cls: 'roo-select2-container input-group roo-select2-container-multi',
15219 // cls: 'typeahead typeahead-long dropdown-menu',
15220 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15225 if(this.hasFeedback && !this.allowBlank){
15229 cls: 'glyphicon form-control-feedback'
15232 combobox.cn.push(feedback);
15239 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15240 tooltip : 'This field is required'
15242 if (Roo.bootstrap.version == 4) {
15245 style : 'display:none'
15248 if (align ==='left' && this.fieldLabel.length) {
15250 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15257 cls : 'control-label col-form-label',
15258 html : this.fieldLabel
15270 var labelCfg = cfg.cn[1];
15271 var contentCfg = cfg.cn[2];
15274 if(this.indicatorpos == 'right'){
15280 cls : 'control-label col-form-label',
15284 html : this.fieldLabel
15300 labelCfg = cfg.cn[0];
15301 contentCfg = cfg.cn[1];
15305 if(this.labelWidth > 12){
15306 labelCfg.style = "width: " + this.labelWidth + 'px';
15308 if(this.width * 1 > 0){
15309 contentCfg.style = "width: " + this.width + 'px';
15311 if(this.labelWidth < 13 && this.labelmd == 0){
15312 this.labelmd = this.labelWidth;
15315 if(this.labellg > 0){
15316 labelCfg.cls += ' col-lg-' + this.labellg;
15317 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15320 if(this.labelmd > 0){
15321 labelCfg.cls += ' col-md-' + this.labelmd;
15322 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15325 if(this.labelsm > 0){
15326 labelCfg.cls += ' col-sm-' + this.labelsm;
15327 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15330 if(this.labelxs > 0){
15331 labelCfg.cls += ' col-xs-' + this.labelxs;
15332 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15336 } else if ( this.fieldLabel.length) {
15337 // Roo.log(" label");
15342 //cls : 'input-group-addon',
15343 html : this.fieldLabel
15348 if(this.indicatorpos == 'right'){
15352 //cls : 'input-group-addon',
15353 html : this.fieldLabel
15363 // Roo.log(" no label && no align");
15370 ['xs','sm','md','lg'].map(function(size){
15371 if (settings[size]) {
15372 cfg.cls += ' col-' + size + '-' + settings[size];
15380 _initEventsCalled : false,
15383 initEvents: function()
15385 if (this._initEventsCalled) { // as we call render... prevent looping...
15388 this._initEventsCalled = true;
15391 throw "can not find store for combo";
15394 this.indicator = this.indicatorEl();
15396 this.store = Roo.factory(this.store, Roo.data);
15397 this.store.parent = this;
15399 // if we are building from html. then this element is so complex, that we can not really
15400 // use the rendered HTML.
15401 // so we have to trash and replace the previous code.
15402 if (Roo.XComponent.build_from_html) {
15403 // remove this element....
15404 var e = this.el.dom, k=0;
15405 while (e ) { e = e.previousSibling; ++k;}
15410 this.rendered = false;
15412 this.render(this.parent().getChildContainer(true), k);
15415 if(Roo.isIOS && this.useNativeIOS){
15416 this.initIOSView();
15424 if(Roo.isTouch && this.mobileTouchView){
15425 this.initTouchView();
15430 this.initTickableEvents();
15434 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15436 if(this.hiddenName){
15438 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15440 this.hiddenField.dom.value =
15441 this.hiddenValue !== undefined ? this.hiddenValue :
15442 this.value !== undefined ? this.value : '';
15444 // prevent input submission
15445 this.el.dom.removeAttribute('name');
15446 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15451 // this.el.dom.setAttribute('autocomplete', 'off');
15454 var cls = 'x-combo-list';
15456 //this.list = new Roo.Layer({
15457 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15463 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15464 _this.list.setWidth(lw);
15467 this.list.on('mouseover', this.onViewOver, this);
15468 this.list.on('mousemove', this.onViewMove, this);
15469 this.list.on('scroll', this.onViewScroll, this);
15472 this.list.swallowEvent('mousewheel');
15473 this.assetHeight = 0;
15476 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15477 this.assetHeight += this.header.getHeight();
15480 this.innerList = this.list.createChild({cls:cls+'-inner'});
15481 this.innerList.on('mouseover', this.onViewOver, this);
15482 this.innerList.on('mousemove', this.onViewMove, this);
15483 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15485 if(this.allowBlank && !this.pageSize && !this.disableClear){
15486 this.footer = this.list.createChild({cls:cls+'-ft'});
15487 this.pageTb = new Roo.Toolbar(this.footer);
15491 this.footer = this.list.createChild({cls:cls+'-ft'});
15492 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15493 {pageSize: this.pageSize});
15497 if (this.pageTb && this.allowBlank && !this.disableClear) {
15499 this.pageTb.add(new Roo.Toolbar.Fill(), {
15500 cls: 'x-btn-icon x-btn-clear',
15502 handler: function()
15505 _this.clearValue();
15506 _this.onSelect(false, -1);
15511 this.assetHeight += this.footer.getHeight();
15516 this.tpl = Roo.bootstrap.version == 4 ?
15517 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15518 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15521 this.view = new Roo.View(this.list, this.tpl, {
15522 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15524 //this.view.wrapEl.setDisplayed(false);
15525 this.view.on('click', this.onViewClick, this);
15528 this.store.on('beforeload', this.onBeforeLoad, this);
15529 this.store.on('load', this.onLoad, this);
15530 this.store.on('loadexception', this.onLoadException, this);
15532 if(this.resizable){
15533 this.resizer = new Roo.Resizable(this.list, {
15534 pinned:true, handles:'se'
15536 this.resizer.on('resize', function(r, w, h){
15537 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15538 this.listWidth = w;
15539 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15540 this.restrictHeight();
15542 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15545 if(!this.editable){
15546 this.editable = true;
15547 this.setEditable(false);
15552 if (typeof(this.events.add.listeners) != 'undefined') {
15554 this.addicon = this.wrap.createChild(
15555 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15557 this.addicon.on('click', function(e) {
15558 this.fireEvent('add', this);
15561 if (typeof(this.events.edit.listeners) != 'undefined') {
15563 this.editicon = this.wrap.createChild(
15564 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15565 if (this.addicon) {
15566 this.editicon.setStyle('margin-left', '40px');
15568 this.editicon.on('click', function(e) {
15570 // we fire even if inothing is selected..
15571 this.fireEvent('edit', this, this.lastData );
15577 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15578 "up" : function(e){
15579 this.inKeyMode = true;
15583 "down" : function(e){
15584 if(!this.isExpanded()){
15585 this.onTriggerClick();
15587 this.inKeyMode = true;
15592 "enter" : function(e){
15593 // this.onViewClick();
15597 if(this.fireEvent("specialkey", this, e)){
15598 this.onViewClick(false);
15604 "esc" : function(e){
15608 "tab" : function(e){
15611 if(this.fireEvent("specialkey", this, e)){
15612 this.onViewClick(false);
15620 doRelay : function(foo, bar, hname){
15621 if(hname == 'down' || this.scope.isExpanded()){
15622 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15631 this.queryDelay = Math.max(this.queryDelay || 10,
15632 this.mode == 'local' ? 10 : 250);
15635 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15637 if(this.typeAhead){
15638 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15640 if(this.editable !== false){
15641 this.inputEl().on("keyup", this.onKeyUp, this);
15643 if(this.forceSelection){
15644 this.inputEl().on('blur', this.doForce, this);
15648 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15649 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15653 initTickableEvents: function()
15657 if(this.hiddenName){
15659 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15661 this.hiddenField.dom.value =
15662 this.hiddenValue !== undefined ? this.hiddenValue :
15663 this.value !== undefined ? this.value : '';
15665 // prevent input submission
15666 this.el.dom.removeAttribute('name');
15667 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15672 // this.list = this.el.select('ul.dropdown-menu',true).first();
15674 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15675 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15676 if(this.triggerList){
15677 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15680 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15681 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15683 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15684 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15686 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15687 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15689 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15690 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15691 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15694 this.cancelBtn.hide();
15699 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15700 _this.list.setWidth(lw);
15703 this.list.on('mouseover', this.onViewOver, this);
15704 this.list.on('mousemove', this.onViewMove, this);
15706 this.list.on('scroll', this.onViewScroll, this);
15709 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15710 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15713 this.view = new Roo.View(this.list, this.tpl, {
15718 selectedClass: this.selectedClass
15721 //this.view.wrapEl.setDisplayed(false);
15722 this.view.on('click', this.onViewClick, this);
15726 this.store.on('beforeload', this.onBeforeLoad, this);
15727 this.store.on('load', this.onLoad, this);
15728 this.store.on('loadexception', this.onLoadException, this);
15731 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15732 "up" : function(e){
15733 this.inKeyMode = true;
15737 "down" : function(e){
15738 this.inKeyMode = true;
15742 "enter" : function(e){
15743 if(this.fireEvent("specialkey", this, e)){
15744 this.onViewClick(false);
15750 "esc" : function(e){
15751 this.onTickableFooterButtonClick(e, false, false);
15754 "tab" : function(e){
15755 this.fireEvent("specialkey", this, e);
15757 this.onTickableFooterButtonClick(e, false, false);
15764 doRelay : function(e, fn, key){
15765 if(this.scope.isExpanded()){
15766 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15775 this.queryDelay = Math.max(this.queryDelay || 10,
15776 this.mode == 'local' ? 10 : 250);
15779 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15781 if(this.typeAhead){
15782 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15785 if(this.editable !== false){
15786 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15789 this.indicator = this.indicatorEl();
15791 if(this.indicator){
15792 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15793 this.indicator.hide();
15798 onDestroy : function(){
15800 this.view.setStore(null);
15801 this.view.el.removeAllListeners();
15802 this.view.el.remove();
15803 this.view.purgeListeners();
15806 this.list.dom.innerHTML = '';
15810 this.store.un('beforeload', this.onBeforeLoad, this);
15811 this.store.un('load', this.onLoad, this);
15812 this.store.un('loadexception', this.onLoadException, this);
15814 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15818 fireKey : function(e){
15819 if(e.isNavKeyPress() && !this.list.isVisible()){
15820 this.fireEvent("specialkey", this, e);
15825 onResize: function(w, h)
15829 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15831 // if(typeof w != 'number'){
15832 // // we do not handle it!?!?
15835 // var tw = this.trigger.getWidth();
15836 // // tw += this.addicon ? this.addicon.getWidth() : 0;
15837 // // tw += this.editicon ? this.editicon.getWidth() : 0;
15839 // this.inputEl().setWidth( this.adjustWidth('input', x));
15841 // //this.trigger.setStyle('left', x+'px');
15843 // if(this.list && this.listWidth === undefined){
15844 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15845 // this.list.setWidth(lw);
15846 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15854 * Allow or prevent the user from directly editing the field text. If false is passed,
15855 * the user will only be able to select from the items defined in the dropdown list. This method
15856 * is the runtime equivalent of setting the 'editable' config option at config time.
15857 * @param {Boolean} value True to allow the user to directly edit the field text
15859 setEditable : function(value){
15860 if(value == this.editable){
15863 this.editable = value;
15865 this.inputEl().dom.setAttribute('readOnly', true);
15866 this.inputEl().on('mousedown', this.onTriggerClick, this);
15867 this.inputEl().addClass('x-combo-noedit');
15869 this.inputEl().dom.setAttribute('readOnly', false);
15870 this.inputEl().un('mousedown', this.onTriggerClick, this);
15871 this.inputEl().removeClass('x-combo-noedit');
15877 onBeforeLoad : function(combo,opts){
15878 if(!this.hasFocus){
15882 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15884 this.restrictHeight();
15885 this.selectedIndex = -1;
15889 onLoad : function(){
15891 this.hasQuery = false;
15893 if(!this.hasFocus){
15897 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15898 this.loading.hide();
15901 if(this.store.getCount() > 0){
15904 this.restrictHeight();
15905 if(this.lastQuery == this.allQuery){
15906 if(this.editable && !this.tickable){
15907 this.inputEl().dom.select();
15911 !this.selectByValue(this.value, true) &&
15914 !this.store.lastOptions ||
15915 typeof(this.store.lastOptions.add) == 'undefined' ||
15916 this.store.lastOptions.add != true
15919 this.select(0, true);
15922 if(this.autoFocus){
15925 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15926 this.taTask.delay(this.typeAheadDelay);
15930 this.onEmptyResults();
15936 onLoadException : function()
15938 this.hasQuery = false;
15940 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15941 this.loading.hide();
15944 if(this.tickable && this.editable){
15949 // only causes errors at present
15950 //Roo.log(this.store.reader.jsonData);
15951 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15953 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15959 onTypeAhead : function(){
15960 if(this.store.getCount() > 0){
15961 var r = this.store.getAt(0);
15962 var newValue = r.data[this.displayField];
15963 var len = newValue.length;
15964 var selStart = this.getRawValue().length;
15966 if(selStart != len){
15967 this.setRawValue(newValue);
15968 this.selectText(selStart, newValue.length);
15974 onSelect : function(record, index){
15976 if(this.fireEvent('beforeselect', this, record, index) !== false){
15978 this.setFromData(index > -1 ? record.data : false);
15981 this.fireEvent('select', this, record, index);
15986 * Returns the currently selected field value or empty string if no value is set.
15987 * @return {String} value The selected value
15989 getValue : function()
15991 if(Roo.isIOS && this.useNativeIOS){
15992 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15996 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15999 if(this.valueField){
16000 return typeof this.value != 'undefined' ? this.value : '';
16002 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16006 getRawValue : function()
16008 if(Roo.isIOS && this.useNativeIOS){
16009 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16012 var v = this.inputEl().getValue();
16018 * Clears any text/value currently set in the field
16020 clearValue : function(){
16022 if(this.hiddenField){
16023 this.hiddenField.dom.value = '';
16026 this.setRawValue('');
16027 this.lastSelectionText = '';
16028 this.lastData = false;
16030 var close = this.closeTriggerEl();
16041 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16042 * will be displayed in the field. If the value does not match the data value of an existing item,
16043 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16044 * Otherwise the field will be blank (although the value will still be set).
16045 * @param {String} value The value to match
16047 setValue : function(v)
16049 if(Roo.isIOS && this.useNativeIOS){
16050 this.setIOSValue(v);
16060 if(this.valueField){
16061 var r = this.findRecord(this.valueField, v);
16063 text = r.data[this.displayField];
16064 }else if(this.valueNotFoundText !== undefined){
16065 text = this.valueNotFoundText;
16068 this.lastSelectionText = text;
16069 if(this.hiddenField){
16070 this.hiddenField.dom.value = v;
16072 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16075 var close = this.closeTriggerEl();
16078 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16084 * @property {Object} the last set data for the element
16089 * Sets the value of the field based on a object which is related to the record format for the store.
16090 * @param {Object} value the value to set as. or false on reset?
16092 setFromData : function(o){
16099 var dv = ''; // display value
16100 var vv = ''; // value value..
16102 if (this.displayField) {
16103 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16105 // this is an error condition!!!
16106 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16109 if(this.valueField){
16110 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16113 var close = this.closeTriggerEl();
16116 if(dv.length || vv * 1 > 0){
16118 this.blockFocus=true;
16124 if(this.hiddenField){
16125 this.hiddenField.dom.value = vv;
16127 this.lastSelectionText = dv;
16128 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16132 // no hidden field.. - we store the value in 'value', but still display
16133 // display field!!!!
16134 this.lastSelectionText = dv;
16135 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16142 reset : function(){
16143 // overridden so that last data is reset..
16150 this.setValue(this.originalValue);
16151 //this.clearInvalid();
16152 this.lastData = false;
16154 this.view.clearSelections();
16160 findRecord : function(prop, value){
16162 if(this.store.getCount() > 0){
16163 this.store.each(function(r){
16164 if(r.data[prop] == value){
16174 getName: function()
16176 // returns hidden if it's set..
16177 if (!this.rendered) {return ''};
16178 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16182 onViewMove : function(e, t){
16183 this.inKeyMode = false;
16187 onViewOver : function(e, t){
16188 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16191 var item = this.view.findItemFromChild(t);
16194 var index = this.view.indexOf(item);
16195 this.select(index, false);
16200 onViewClick : function(view, doFocus, el, e)
16202 var index = this.view.getSelectedIndexes()[0];
16204 var r = this.store.getAt(index);
16208 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16215 Roo.each(this.tickItems, function(v,k){
16217 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16219 _this.tickItems.splice(k, 1);
16221 if(typeof(e) == 'undefined' && view == false){
16222 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16234 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16235 this.tickItems.push(r.data);
16238 if(typeof(e) == 'undefined' && view == false){
16239 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16246 this.onSelect(r, index);
16248 if(doFocus !== false && !this.blockFocus){
16249 this.inputEl().focus();
16254 restrictHeight : function(){
16255 //this.innerList.dom.style.height = '';
16256 //var inner = this.innerList.dom;
16257 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16258 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16259 //this.list.beginUpdate();
16260 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16261 this.list.alignTo(this.inputEl(), this.listAlign);
16262 this.list.alignTo(this.inputEl(), this.listAlign);
16263 //this.list.endUpdate();
16267 onEmptyResults : function(){
16269 if(this.tickable && this.editable){
16270 this.hasFocus = false;
16271 this.restrictHeight();
16279 * Returns true if the dropdown list is expanded, else false.
16281 isExpanded : function(){
16282 return this.list.isVisible();
16286 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16287 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16288 * @param {String} value The data value of the item to select
16289 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16290 * selected item if it is not currently in view (defaults to true)
16291 * @return {Boolean} True if the value matched an item in the list, else false
16293 selectByValue : function(v, scrollIntoView){
16294 if(v !== undefined && v !== null){
16295 var r = this.findRecord(this.valueField || this.displayField, v);
16297 this.select(this.store.indexOf(r), scrollIntoView);
16305 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16306 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16307 * @param {Number} index The zero-based index of the list item to select
16308 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16309 * selected item if it is not currently in view (defaults to true)
16311 select : function(index, scrollIntoView){
16312 this.selectedIndex = index;
16313 this.view.select(index);
16314 if(scrollIntoView !== false){
16315 var el = this.view.getNode(index);
16317 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16320 this.list.scrollChildIntoView(el, false);
16326 selectNext : function(){
16327 var ct = this.store.getCount();
16329 if(this.selectedIndex == -1){
16331 }else if(this.selectedIndex < ct-1){
16332 this.select(this.selectedIndex+1);
16338 selectPrev : function(){
16339 var ct = this.store.getCount();
16341 if(this.selectedIndex == -1){
16343 }else if(this.selectedIndex != 0){
16344 this.select(this.selectedIndex-1);
16350 onKeyUp : function(e){
16351 if(this.editable !== false && !e.isSpecialKey()){
16352 this.lastKey = e.getKey();
16353 this.dqTask.delay(this.queryDelay);
16358 validateBlur : function(){
16359 return !this.list || !this.list.isVisible();
16363 initQuery : function(){
16365 var v = this.getRawValue();
16367 if(this.tickable && this.editable){
16368 v = this.tickableInputEl().getValue();
16375 doForce : function(){
16376 if(this.inputEl().dom.value.length > 0){
16377 this.inputEl().dom.value =
16378 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16384 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16385 * query allowing the query action to be canceled if needed.
16386 * @param {String} query The SQL query to execute
16387 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16388 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16389 * saved in the current store (defaults to false)
16391 doQuery : function(q, forceAll){
16393 if(q === undefined || q === null){
16398 forceAll: forceAll,
16402 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16407 forceAll = qe.forceAll;
16408 if(forceAll === true || (q.length >= this.minChars)){
16410 this.hasQuery = true;
16412 if(this.lastQuery != q || this.alwaysQuery){
16413 this.lastQuery = q;
16414 if(this.mode == 'local'){
16415 this.selectedIndex = -1;
16417 this.store.clearFilter();
16420 if(this.specialFilter){
16421 this.fireEvent('specialfilter', this);
16426 this.store.filter(this.displayField, q);
16429 this.store.fireEvent("datachanged", this.store);
16436 this.store.baseParams[this.queryParam] = q;
16438 var options = {params : this.getParams(q)};
16441 options.add = true;
16442 options.params.start = this.page * this.pageSize;
16445 this.store.load(options);
16448 * this code will make the page width larger, at the beginning, the list not align correctly,
16449 * we should expand the list on onLoad
16450 * so command out it
16455 this.selectedIndex = -1;
16460 this.loadNext = false;
16464 getParams : function(q){
16466 //p[this.queryParam] = q;
16470 p.limit = this.pageSize;
16476 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16478 collapse : function(){
16479 if(!this.isExpanded()){
16485 this.hasFocus = false;
16489 this.cancelBtn.hide();
16490 this.trigger.show();
16493 this.tickableInputEl().dom.value = '';
16494 this.tickableInputEl().blur();
16499 Roo.get(document).un('mousedown', this.collapseIf, this);
16500 Roo.get(document).un('mousewheel', this.collapseIf, this);
16501 if (!this.editable) {
16502 Roo.get(document).un('keydown', this.listKeyPress, this);
16504 this.fireEvent('collapse', this);
16510 collapseIf : function(e){
16511 var in_combo = e.within(this.el);
16512 var in_list = e.within(this.list);
16513 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16515 if (in_combo || in_list || is_list) {
16516 //e.stopPropagation();
16521 this.onTickableFooterButtonClick(e, false, false);
16529 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16531 expand : function(){
16533 if(this.isExpanded() || !this.hasFocus){
16537 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16538 this.list.setWidth(lw);
16544 this.restrictHeight();
16548 this.tickItems = Roo.apply([], this.item);
16551 this.cancelBtn.show();
16552 this.trigger.hide();
16555 this.tickableInputEl().focus();
16560 Roo.get(document).on('mousedown', this.collapseIf, this);
16561 Roo.get(document).on('mousewheel', this.collapseIf, this);
16562 if (!this.editable) {
16563 Roo.get(document).on('keydown', this.listKeyPress, this);
16566 this.fireEvent('expand', this);
16570 // Implements the default empty TriggerField.onTriggerClick function
16571 onTriggerClick : function(e)
16573 Roo.log('trigger click');
16575 if(this.disabled || !this.triggerList){
16580 this.loadNext = false;
16582 if(this.isExpanded()){
16584 if (!this.blockFocus) {
16585 this.inputEl().focus();
16589 this.hasFocus = true;
16590 if(this.triggerAction == 'all') {
16591 this.doQuery(this.allQuery, true);
16593 this.doQuery(this.getRawValue());
16595 if (!this.blockFocus) {
16596 this.inputEl().focus();
16601 onTickableTriggerClick : function(e)
16608 this.loadNext = false;
16609 this.hasFocus = true;
16611 if(this.triggerAction == 'all') {
16612 this.doQuery(this.allQuery, true);
16614 this.doQuery(this.getRawValue());
16618 onSearchFieldClick : function(e)
16620 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16621 this.onTickableFooterButtonClick(e, false, false);
16625 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16630 this.loadNext = false;
16631 this.hasFocus = true;
16633 if(this.triggerAction == 'all') {
16634 this.doQuery(this.allQuery, true);
16636 this.doQuery(this.getRawValue());
16640 listKeyPress : function(e)
16642 //Roo.log('listkeypress');
16643 // scroll to first matching element based on key pres..
16644 if (e.isSpecialKey()) {
16647 var k = String.fromCharCode(e.getKey()).toUpperCase();
16650 var csel = this.view.getSelectedNodes();
16651 var cselitem = false;
16653 var ix = this.view.indexOf(csel[0]);
16654 cselitem = this.store.getAt(ix);
16655 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16661 this.store.each(function(v) {
16663 // start at existing selection.
16664 if (cselitem.id == v.id) {
16670 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16671 match = this.store.indexOf(v);
16677 if (match === false) {
16678 return true; // no more action?
16681 this.view.select(match);
16682 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16683 sn.scrollIntoView(sn.dom.parentNode, false);
16686 onViewScroll : function(e, t){
16688 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){
16692 this.hasQuery = true;
16694 this.loading = this.list.select('.loading', true).first();
16696 if(this.loading === null){
16697 this.list.createChild({
16699 cls: 'loading roo-select2-more-results roo-select2-active',
16700 html: 'Loading more results...'
16703 this.loading = this.list.select('.loading', true).first();
16705 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16707 this.loading.hide();
16710 this.loading.show();
16715 this.loadNext = true;
16717 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16722 addItem : function(o)
16724 var dv = ''; // display value
16726 if (this.displayField) {
16727 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16729 // this is an error condition!!!
16730 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16737 var choice = this.choices.createChild({
16739 cls: 'roo-select2-search-choice',
16748 cls: 'roo-select2-search-choice-close fa fa-times',
16753 }, this.searchField);
16755 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16757 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16765 this.inputEl().dom.value = '';
16770 onRemoveItem : function(e, _self, o)
16772 e.preventDefault();
16774 this.lastItem = Roo.apply([], this.item);
16776 var index = this.item.indexOf(o.data) * 1;
16779 Roo.log('not this item?!');
16783 this.item.splice(index, 1);
16788 this.fireEvent('remove', this, e);
16794 syncValue : function()
16796 if(!this.item.length){
16803 Roo.each(this.item, function(i){
16804 if(_this.valueField){
16805 value.push(i[_this.valueField]);
16812 this.value = value.join(',');
16814 if(this.hiddenField){
16815 this.hiddenField.dom.value = this.value;
16818 this.store.fireEvent("datachanged", this.store);
16823 clearItem : function()
16825 if(!this.multiple){
16831 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16839 if(this.tickable && !Roo.isTouch){
16840 this.view.refresh();
16844 inputEl: function ()
16846 if(Roo.isIOS && this.useNativeIOS){
16847 return this.el.select('select.roo-ios-select', true).first();
16850 if(Roo.isTouch && this.mobileTouchView){
16851 return this.el.select('input.form-control',true).first();
16855 return this.searchField;
16858 return this.el.select('input.form-control',true).first();
16861 onTickableFooterButtonClick : function(e, btn, el)
16863 e.preventDefault();
16865 this.lastItem = Roo.apply([], this.item);
16867 if(btn && btn.name == 'cancel'){
16868 this.tickItems = Roo.apply([], this.item);
16877 Roo.each(this.tickItems, function(o){
16885 validate : function()
16887 if(this.getVisibilityEl().hasClass('hidden')){
16891 var v = this.getRawValue();
16894 v = this.getValue();
16897 if(this.disabled || this.allowBlank || v.length){
16902 this.markInvalid();
16906 tickableInputEl : function()
16908 if(!this.tickable || !this.editable){
16909 return this.inputEl();
16912 return this.inputEl().select('.roo-select2-search-field-input', true).first();
16916 getAutoCreateTouchView : function()
16921 cls: 'form-group' //input-group
16927 type : this.inputType,
16928 cls : 'form-control x-combo-noedit',
16929 autocomplete: 'new-password',
16930 placeholder : this.placeholder || '',
16935 input.name = this.name;
16939 input.cls += ' input-' + this.size;
16942 if (this.disabled) {
16943 input.disabled = true;
16947 cls : 'roo-combobox-wrap',
16954 inputblock.cls += ' input-group';
16956 inputblock.cn.unshift({
16958 cls : 'input-group-addon input-group-prepend input-group-text',
16963 if(this.removable && !this.multiple){
16964 inputblock.cls += ' roo-removable';
16966 inputblock.cn.push({
16969 cls : 'roo-combo-removable-btn close'
16973 if(this.hasFeedback && !this.allowBlank){
16975 inputblock.cls += ' has-feedback';
16977 inputblock.cn.push({
16979 cls: 'glyphicon form-control-feedback'
16986 inputblock.cls += (this.before) ? '' : ' input-group';
16988 inputblock.cn.push({
16990 cls : 'input-group-addon input-group-append input-group-text',
16996 var ibwrap = inputblock;
17001 cls: 'roo-select2-choices',
17005 cls: 'roo-select2-search-field',
17018 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17023 cls: 'form-hidden-field'
17029 if(!this.multiple && this.showToggleBtn){
17035 if (this.caret != false) {
17038 cls: 'fa fa-' + this.caret
17045 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17047 Roo.bootstrap.version == 3 ? caret : '',
17050 cls: 'combobox-clear',
17064 combobox.cls += ' roo-select2-container-multi';
17067 var align = this.labelAlign || this.parentLabelAlign();
17069 if (align ==='left' && this.fieldLabel.length) {
17074 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17075 tooltip : 'This field is required'
17079 cls : 'control-label col-form-label',
17080 html : this.fieldLabel
17084 cls : 'roo-combobox-wrap ',
17091 var labelCfg = cfg.cn[1];
17092 var contentCfg = cfg.cn[2];
17095 if(this.indicatorpos == 'right'){
17100 cls : 'control-label col-form-label',
17104 html : this.fieldLabel
17108 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17109 tooltip : 'This field is required'
17114 cls : "roo-combobox-wrap ",
17122 labelCfg = cfg.cn[0];
17123 contentCfg = cfg.cn[1];
17128 if(this.labelWidth > 12){
17129 labelCfg.style = "width: " + this.labelWidth + 'px';
17132 if(this.labelWidth < 13 && this.labelmd == 0){
17133 this.labelmd = this.labelWidth;
17136 if(this.labellg > 0){
17137 labelCfg.cls += ' col-lg-' + this.labellg;
17138 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17141 if(this.labelmd > 0){
17142 labelCfg.cls += ' col-md-' + this.labelmd;
17143 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17146 if(this.labelsm > 0){
17147 labelCfg.cls += ' col-sm-' + this.labelsm;
17148 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17151 if(this.labelxs > 0){
17152 labelCfg.cls += ' col-xs-' + this.labelxs;
17153 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17157 } else if ( this.fieldLabel.length) {
17161 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17162 tooltip : 'This field is required'
17166 cls : 'control-label',
17167 html : this.fieldLabel
17178 if(this.indicatorpos == 'right'){
17182 cls : 'control-label',
17183 html : this.fieldLabel,
17187 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17188 tooltip : 'This field is required'
17205 var settings = this;
17207 ['xs','sm','md','lg'].map(function(size){
17208 if (settings[size]) {
17209 cfg.cls += ' col-' + size + '-' + settings[size];
17216 initTouchView : function()
17218 this.renderTouchView();
17220 this.touchViewEl.on('scroll', function(){
17221 this.el.dom.scrollTop = 0;
17224 this.originalValue = this.getValue();
17226 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17228 this.inputEl().on("click", this.showTouchView, this);
17229 if (this.triggerEl) {
17230 this.triggerEl.on("click", this.showTouchView, this);
17234 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17235 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17237 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17239 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17240 this.store.on('load', this.onTouchViewLoad, this);
17241 this.store.on('loadexception', this.onTouchViewLoadException, this);
17243 if(this.hiddenName){
17245 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17247 this.hiddenField.dom.value =
17248 this.hiddenValue !== undefined ? this.hiddenValue :
17249 this.value !== undefined ? this.value : '';
17251 this.el.dom.removeAttribute('name');
17252 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17256 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17257 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17260 if(this.removable && !this.multiple){
17261 var close = this.closeTriggerEl();
17263 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17264 close.on('click', this.removeBtnClick, this, close);
17268 * fix the bug in Safari iOS8
17270 this.inputEl().on("focus", function(e){
17271 document.activeElement.blur();
17274 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17281 renderTouchView : function()
17283 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17284 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17286 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17287 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17289 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17290 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17291 this.touchViewBodyEl.setStyle('overflow', 'auto');
17293 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17294 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17296 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17297 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17301 showTouchView : function()
17307 this.touchViewHeaderEl.hide();
17309 if(this.modalTitle.length){
17310 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17311 this.touchViewHeaderEl.show();
17314 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17315 this.touchViewEl.show();
17317 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17319 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17320 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17322 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17324 if(this.modalTitle.length){
17325 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17328 this.touchViewBodyEl.setHeight(bodyHeight);
17332 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17334 this.touchViewEl.addClass(['in','show']);
17337 if(this._touchViewMask){
17338 Roo.get(document.body).addClass("x-body-masked");
17339 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17340 this._touchViewMask.setStyle('z-index', 10000);
17341 this._touchViewMask.addClass('show');
17344 this.doTouchViewQuery();
17348 hideTouchView : function()
17350 this.touchViewEl.removeClass(['in','show']);
17354 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17356 this.touchViewEl.setStyle('display', 'none');
17359 if(this._touchViewMask){
17360 this._touchViewMask.removeClass('show');
17361 Roo.get(document.body).removeClass("x-body-masked");
17365 setTouchViewValue : function()
17372 Roo.each(this.tickItems, function(o){
17377 this.hideTouchView();
17380 doTouchViewQuery : function()
17389 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17393 if(!this.alwaysQuery || this.mode == 'local'){
17394 this.onTouchViewLoad();
17401 onTouchViewBeforeLoad : function(combo,opts)
17407 onTouchViewLoad : function()
17409 if(this.store.getCount() < 1){
17410 this.onTouchViewEmptyResults();
17414 this.clearTouchView();
17416 var rawValue = this.getRawValue();
17418 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17420 this.tickItems = [];
17422 this.store.data.each(function(d, rowIndex){
17423 var row = this.touchViewListGroup.createChild(template);
17425 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17426 row.addClass(d.data.cls);
17429 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17432 html : d.data[this.displayField]
17435 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17436 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17439 row.removeClass('selected');
17440 if(!this.multiple && this.valueField &&
17441 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17444 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17445 row.addClass('selected');
17448 if(this.multiple && this.valueField &&
17449 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17453 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17454 this.tickItems.push(d.data);
17457 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17461 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17463 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17465 if(this.modalTitle.length){
17466 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17469 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17471 if(this.mobile_restrict_height && listHeight < bodyHeight){
17472 this.touchViewBodyEl.setHeight(listHeight);
17477 if(firstChecked && listHeight > bodyHeight){
17478 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17483 onTouchViewLoadException : function()
17485 this.hideTouchView();
17488 onTouchViewEmptyResults : function()
17490 this.clearTouchView();
17492 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17494 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17498 clearTouchView : function()
17500 this.touchViewListGroup.dom.innerHTML = '';
17503 onTouchViewClick : function(e, el, o)
17505 e.preventDefault();
17508 var rowIndex = o.rowIndex;
17510 var r = this.store.getAt(rowIndex);
17512 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17514 if(!this.multiple){
17515 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17516 c.dom.removeAttribute('checked');
17519 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17521 this.setFromData(r.data);
17523 var close = this.closeTriggerEl();
17529 this.hideTouchView();
17531 this.fireEvent('select', this, r, rowIndex);
17536 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17537 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17538 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17542 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17543 this.addItem(r.data);
17544 this.tickItems.push(r.data);
17548 getAutoCreateNativeIOS : function()
17551 cls: 'form-group' //input-group,
17556 cls : 'roo-ios-select'
17560 combobox.name = this.name;
17563 if (this.disabled) {
17564 combobox.disabled = true;
17567 var settings = this;
17569 ['xs','sm','md','lg'].map(function(size){
17570 if (settings[size]) {
17571 cfg.cls += ' col-' + size + '-' + settings[size];
17581 initIOSView : function()
17583 this.store.on('load', this.onIOSViewLoad, this);
17588 onIOSViewLoad : function()
17590 if(this.store.getCount() < 1){
17594 this.clearIOSView();
17596 if(this.allowBlank) {
17598 var default_text = '-- SELECT --';
17600 if(this.placeholder.length){
17601 default_text = this.placeholder;
17604 if(this.emptyTitle.length){
17605 default_text += ' - ' + this.emptyTitle + ' -';
17608 var opt = this.inputEl().createChild({
17611 html : default_text
17615 o[this.valueField] = 0;
17616 o[this.displayField] = default_text;
17618 this.ios_options.push({
17625 this.store.data.each(function(d, rowIndex){
17629 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17630 html = d.data[this.displayField];
17635 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17636 value = d.data[this.valueField];
17645 if(this.value == d.data[this.valueField]){
17646 option['selected'] = true;
17649 var opt = this.inputEl().createChild(option);
17651 this.ios_options.push({
17658 this.inputEl().on('change', function(){
17659 this.fireEvent('select', this);
17664 clearIOSView: function()
17666 this.inputEl().dom.innerHTML = '';
17668 this.ios_options = [];
17671 setIOSValue: function(v)
17675 if(!this.ios_options){
17679 Roo.each(this.ios_options, function(opts){
17681 opts.el.dom.removeAttribute('selected');
17683 if(opts.data[this.valueField] != v){
17687 opts.el.dom.setAttribute('selected', true);
17693 * @cfg {Boolean} grow
17697 * @cfg {Number} growMin
17701 * @cfg {Number} growMax
17710 Roo.apply(Roo.bootstrap.ComboBox, {
17714 cls: 'modal-header',
17736 cls: 'list-group-item',
17740 cls: 'roo-combobox-list-group-item-value'
17744 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17758 listItemCheckbox : {
17760 cls: 'list-group-item',
17764 cls: 'roo-combobox-list-group-item-value'
17768 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17784 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17789 cls: 'modal-footer',
17797 cls: 'col-xs-6 text-left',
17800 cls: 'btn btn-danger roo-touch-view-cancel',
17806 cls: 'col-xs-6 text-right',
17809 cls: 'btn btn-success roo-touch-view-ok',
17820 Roo.apply(Roo.bootstrap.ComboBox, {
17822 touchViewTemplate : {
17824 cls: 'modal fade roo-combobox-touch-view',
17828 cls: 'modal-dialog',
17829 style : 'position:fixed', // we have to fix position....
17833 cls: 'modal-content',
17835 Roo.bootstrap.ComboBox.header,
17836 Roo.bootstrap.ComboBox.body,
17837 Roo.bootstrap.ComboBox.footer
17846 * Ext JS Library 1.1.1
17847 * Copyright(c) 2006-2007, Ext JS, LLC.
17849 * Originally Released Under LGPL - original licence link has changed is not relivant.
17852 * <script type="text/javascript">
17857 * @extends Roo.util.Observable
17858 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
17859 * This class also supports single and multi selection modes. <br>
17860 * Create a data model bound view:
17862 var store = new Roo.data.Store(...);
17864 var view = new Roo.View({
17866 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
17868 singleSelect: true,
17869 selectedClass: "ydataview-selected",
17873 // listen for node click?
17874 view.on("click", function(vw, index, node, e){
17875 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17879 dataModel.load("foobar.xml");
17881 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17883 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17884 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17886 * Note: old style constructor is still suported (container, template, config)
17889 * Create a new View
17890 * @param {Object} config The config object
17893 Roo.View = function(config, depreciated_tpl, depreciated_config){
17895 this.parent = false;
17897 if (typeof(depreciated_tpl) == 'undefined') {
17898 // new way.. - universal constructor.
17899 Roo.apply(this, config);
17900 this.el = Roo.get(this.el);
17903 this.el = Roo.get(config);
17904 this.tpl = depreciated_tpl;
17905 Roo.apply(this, depreciated_config);
17907 this.wrapEl = this.el.wrap().wrap();
17908 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17911 if(typeof(this.tpl) == "string"){
17912 this.tpl = new Roo.Template(this.tpl);
17914 // support xtype ctors..
17915 this.tpl = new Roo.factory(this.tpl, Roo);
17919 this.tpl.compile();
17924 * @event beforeclick
17925 * Fires before a click is processed. Returns false to cancel the default action.
17926 * @param {Roo.View} this
17927 * @param {Number} index The index of the target node
17928 * @param {HTMLElement} node The target node
17929 * @param {Roo.EventObject} e The raw event object
17931 "beforeclick" : true,
17934 * Fires when a template node is clicked.
17935 * @param {Roo.View} this
17936 * @param {Number} index The index of the target node
17937 * @param {HTMLElement} node The target node
17938 * @param {Roo.EventObject} e The raw event object
17943 * Fires when a template node is double clicked.
17944 * @param {Roo.View} this
17945 * @param {Number} index The index of the target node
17946 * @param {HTMLElement} node The target node
17947 * @param {Roo.EventObject} e The raw event object
17951 * @event contextmenu
17952 * Fires when a template node is right clicked.
17953 * @param {Roo.View} this
17954 * @param {Number} index The index of the target node
17955 * @param {HTMLElement} node The target node
17956 * @param {Roo.EventObject} e The raw event object
17958 "contextmenu" : true,
17960 * @event selectionchange
17961 * Fires when the selected nodes change.
17962 * @param {Roo.View} this
17963 * @param {Array} selections Array of the selected nodes
17965 "selectionchange" : true,
17968 * @event beforeselect
17969 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17970 * @param {Roo.View} this
17971 * @param {HTMLElement} node The node to be selected
17972 * @param {Array} selections Array of currently selected nodes
17974 "beforeselect" : true,
17976 * @event preparedata
17977 * Fires on every row to render, to allow you to change the data.
17978 * @param {Roo.View} this
17979 * @param {Object} data to be rendered (change this)
17981 "preparedata" : true
17989 "click": this.onClick,
17990 "dblclick": this.onDblClick,
17991 "contextmenu": this.onContextMenu,
17995 this.selections = [];
17997 this.cmp = new Roo.CompositeElementLite([]);
17999 this.store = Roo.factory(this.store, Roo.data);
18000 this.setStore(this.store, true);
18003 if ( this.footer && this.footer.xtype) {
18005 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18007 this.footer.dataSource = this.store;
18008 this.footer.container = fctr;
18009 this.footer = Roo.factory(this.footer, Roo);
18010 fctr.insertFirst(this.el);
18012 // this is a bit insane - as the paging toolbar seems to detach the el..
18013 // dom.parentNode.parentNode.parentNode
18014 // they get detached?
18018 Roo.View.superclass.constructor.call(this);
18023 Roo.extend(Roo.View, Roo.util.Observable, {
18026 * @cfg {Roo.data.Store} store Data store to load data from.
18031 * @cfg {String|Roo.Element} el The container element.
18036 * @cfg {String|Roo.Template} tpl The template used by this View
18040 * @cfg {String} dataName the named area of the template to use as the data area
18041 * Works with domtemplates roo-name="name"
18045 * @cfg {String} selectedClass The css class to add to selected nodes
18047 selectedClass : "x-view-selected",
18049 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18054 * @cfg {String} text to display on mask (default Loading)
18058 * @cfg {Boolean} multiSelect Allow multiple selection
18060 multiSelect : false,
18062 * @cfg {Boolean} singleSelect Allow single selection
18064 singleSelect: false,
18067 * @cfg {Boolean} toggleSelect - selecting
18069 toggleSelect : false,
18072 * @cfg {Boolean} tickable - selecting
18077 * Returns the element this view is bound to.
18078 * @return {Roo.Element}
18080 getEl : function(){
18081 return this.wrapEl;
18087 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18089 refresh : function(){
18090 //Roo.log('refresh');
18093 // if we are using something like 'domtemplate', then
18094 // the what gets used is:
18095 // t.applySubtemplate(NAME, data, wrapping data..)
18096 // the outer template then get' applied with
18097 // the store 'extra data'
18098 // and the body get's added to the
18099 // roo-name="data" node?
18100 // <span class='roo-tpl-{name}'></span> ?????
18104 this.clearSelections();
18105 this.el.update("");
18107 var records = this.store.getRange();
18108 if(records.length < 1) {
18110 // is this valid?? = should it render a template??
18112 this.el.update(this.emptyText);
18116 if (this.dataName) {
18117 this.el.update(t.apply(this.store.meta)); //????
18118 el = this.el.child('.roo-tpl-' + this.dataName);
18121 for(var i = 0, len = records.length; i < len; i++){
18122 var data = this.prepareData(records[i].data, i, records[i]);
18123 this.fireEvent("preparedata", this, data, i, records[i]);
18125 var d = Roo.apply({}, data);
18128 Roo.apply(d, {'roo-id' : Roo.id()});
18132 Roo.each(this.parent.item, function(item){
18133 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18136 Roo.apply(d, {'roo-data-checked' : 'checked'});
18140 html[html.length] = Roo.util.Format.trim(
18142 t.applySubtemplate(this.dataName, d, this.store.meta) :
18149 el.update(html.join(""));
18150 this.nodes = el.dom.childNodes;
18151 this.updateIndexes(0);
18156 * Function to override to reformat the data that is sent to
18157 * the template for each node.
18158 * DEPRICATED - use the preparedata event handler.
18159 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18160 * a JSON object for an UpdateManager bound view).
18162 prepareData : function(data, index, record)
18164 this.fireEvent("preparedata", this, data, index, record);
18168 onUpdate : function(ds, record){
18169 // Roo.log('on update');
18170 this.clearSelections();
18171 var index = this.store.indexOf(record);
18172 var n = this.nodes[index];
18173 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18174 n.parentNode.removeChild(n);
18175 this.updateIndexes(index, index);
18181 onAdd : function(ds, records, index)
18183 //Roo.log(['on Add', ds, records, index] );
18184 this.clearSelections();
18185 if(this.nodes.length == 0){
18189 var n = this.nodes[index];
18190 for(var i = 0, len = records.length; i < len; i++){
18191 var d = this.prepareData(records[i].data, i, records[i]);
18193 this.tpl.insertBefore(n, d);
18196 this.tpl.append(this.el, d);
18199 this.updateIndexes(index);
18202 onRemove : function(ds, record, index){
18203 // Roo.log('onRemove');
18204 this.clearSelections();
18205 var el = this.dataName ?
18206 this.el.child('.roo-tpl-' + this.dataName) :
18209 el.dom.removeChild(this.nodes[index]);
18210 this.updateIndexes(index);
18214 * Refresh an individual node.
18215 * @param {Number} index
18217 refreshNode : function(index){
18218 this.onUpdate(this.store, this.store.getAt(index));
18221 updateIndexes : function(startIndex, endIndex){
18222 var ns = this.nodes;
18223 startIndex = startIndex || 0;
18224 endIndex = endIndex || ns.length - 1;
18225 for(var i = startIndex; i <= endIndex; i++){
18226 ns[i].nodeIndex = i;
18231 * Changes the data store this view uses and refresh the view.
18232 * @param {Store} store
18234 setStore : function(store, initial){
18235 if(!initial && this.store){
18236 this.store.un("datachanged", this.refresh);
18237 this.store.un("add", this.onAdd);
18238 this.store.un("remove", this.onRemove);
18239 this.store.un("update", this.onUpdate);
18240 this.store.un("clear", this.refresh);
18241 this.store.un("beforeload", this.onBeforeLoad);
18242 this.store.un("load", this.onLoad);
18243 this.store.un("loadexception", this.onLoad);
18247 store.on("datachanged", this.refresh, this);
18248 store.on("add", this.onAdd, this);
18249 store.on("remove", this.onRemove, this);
18250 store.on("update", this.onUpdate, this);
18251 store.on("clear", this.refresh, this);
18252 store.on("beforeload", this.onBeforeLoad, this);
18253 store.on("load", this.onLoad, this);
18254 store.on("loadexception", this.onLoad, this);
18262 * onbeforeLoad - masks the loading area.
18265 onBeforeLoad : function(store,opts)
18267 //Roo.log('onBeforeLoad');
18269 this.el.update("");
18271 this.el.mask(this.mask ? this.mask : "Loading" );
18273 onLoad : function ()
18280 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18281 * @param {HTMLElement} node
18282 * @return {HTMLElement} The template node
18284 findItemFromChild : function(node){
18285 var el = this.dataName ?
18286 this.el.child('.roo-tpl-' + this.dataName,true) :
18289 if(!node || node.parentNode == el){
18292 var p = node.parentNode;
18293 while(p && p != el){
18294 if(p.parentNode == el){
18303 onClick : function(e){
18304 var item = this.findItemFromChild(e.getTarget());
18306 var index = this.indexOf(item);
18307 if(this.onItemClick(item, index, e) !== false){
18308 this.fireEvent("click", this, index, item, e);
18311 this.clearSelections();
18316 onContextMenu : function(e){
18317 var item = this.findItemFromChild(e.getTarget());
18319 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18324 onDblClick : function(e){
18325 var item = this.findItemFromChild(e.getTarget());
18327 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18331 onItemClick : function(item, index, e)
18333 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18336 if (this.toggleSelect) {
18337 var m = this.isSelected(item) ? 'unselect' : 'select';
18340 _t[m](item, true, false);
18343 if(this.multiSelect || this.singleSelect){
18344 if(this.multiSelect && e.shiftKey && this.lastSelection){
18345 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18347 this.select(item, this.multiSelect && e.ctrlKey);
18348 this.lastSelection = item;
18351 if(!this.tickable){
18352 e.preventDefault();
18360 * Get the number of selected nodes.
18363 getSelectionCount : function(){
18364 return this.selections.length;
18368 * Get the currently selected nodes.
18369 * @return {Array} An array of HTMLElements
18371 getSelectedNodes : function(){
18372 return this.selections;
18376 * Get the indexes of the selected nodes.
18379 getSelectedIndexes : function(){
18380 var indexes = [], s = this.selections;
18381 for(var i = 0, len = s.length; i < len; i++){
18382 indexes.push(s[i].nodeIndex);
18388 * Clear all selections
18389 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18391 clearSelections : function(suppressEvent){
18392 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18393 this.cmp.elements = this.selections;
18394 this.cmp.removeClass(this.selectedClass);
18395 this.selections = [];
18396 if(!suppressEvent){
18397 this.fireEvent("selectionchange", this, this.selections);
18403 * Returns true if the passed node is selected
18404 * @param {HTMLElement/Number} node The node or node index
18405 * @return {Boolean}
18407 isSelected : function(node){
18408 var s = this.selections;
18412 node = this.getNode(node);
18413 return s.indexOf(node) !== -1;
18418 * @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
18419 * @param {Boolean} keepExisting (optional) true to keep existing selections
18420 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18422 select : function(nodeInfo, keepExisting, suppressEvent){
18423 if(nodeInfo instanceof Array){
18425 this.clearSelections(true);
18427 for(var i = 0, len = nodeInfo.length; i < len; i++){
18428 this.select(nodeInfo[i], true, true);
18432 var node = this.getNode(nodeInfo);
18433 if(!node || this.isSelected(node)){
18434 return; // already selected.
18437 this.clearSelections(true);
18440 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18441 Roo.fly(node).addClass(this.selectedClass);
18442 this.selections.push(node);
18443 if(!suppressEvent){
18444 this.fireEvent("selectionchange", this, this.selections);
18452 * @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
18453 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18454 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18456 unselect : function(nodeInfo, keepExisting, suppressEvent)
18458 if(nodeInfo instanceof Array){
18459 Roo.each(this.selections, function(s) {
18460 this.unselect(s, nodeInfo);
18464 var node = this.getNode(nodeInfo);
18465 if(!node || !this.isSelected(node)){
18466 //Roo.log("not selected");
18467 return; // not selected.
18471 Roo.each(this.selections, function(s) {
18473 Roo.fly(node).removeClass(this.selectedClass);
18480 this.selections= ns;
18481 this.fireEvent("selectionchange", this, this.selections);
18485 * Gets a template node.
18486 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18487 * @return {HTMLElement} The node or null if it wasn't found
18489 getNode : function(nodeInfo){
18490 if(typeof nodeInfo == "string"){
18491 return document.getElementById(nodeInfo);
18492 }else if(typeof nodeInfo == "number"){
18493 return this.nodes[nodeInfo];
18499 * Gets a range template nodes.
18500 * @param {Number} startIndex
18501 * @param {Number} endIndex
18502 * @return {Array} An array of nodes
18504 getNodes : function(start, end){
18505 var ns = this.nodes;
18506 start = start || 0;
18507 end = typeof end == "undefined" ? ns.length - 1 : end;
18510 for(var i = start; i <= end; i++){
18514 for(var i = start; i >= end; i--){
18522 * Finds the index of the passed node
18523 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18524 * @return {Number} The index of the node or -1
18526 indexOf : function(node){
18527 node = this.getNode(node);
18528 if(typeof node.nodeIndex == "number"){
18529 return node.nodeIndex;
18531 var ns = this.nodes;
18532 for(var i = 0, len = ns.length; i < len; i++){
18543 * based on jquery fullcalendar
18547 Roo.bootstrap = Roo.bootstrap || {};
18549 * @class Roo.bootstrap.Calendar
18550 * @extends Roo.bootstrap.Component
18551 * Bootstrap Calendar class
18552 * @cfg {Boolean} loadMask (true|false) default false
18553 * @cfg {Object} header generate the user specific header of the calendar, default false
18556 * Create a new Container
18557 * @param {Object} config The config object
18562 Roo.bootstrap.Calendar = function(config){
18563 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18567 * Fires when a date is selected
18568 * @param {DatePicker} this
18569 * @param {Date} date The selected date
18573 * @event monthchange
18574 * Fires when the displayed month changes
18575 * @param {DatePicker} this
18576 * @param {Date} date The selected month
18578 'monthchange': true,
18580 * @event evententer
18581 * Fires when mouse over an event
18582 * @param {Calendar} this
18583 * @param {event} Event
18585 'evententer': true,
18587 * @event eventleave
18588 * Fires when the mouse leaves an
18589 * @param {Calendar} this
18592 'eventleave': true,
18594 * @event eventclick
18595 * Fires when the mouse click an
18596 * @param {Calendar} this
18605 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18608 * @cfg {Number} startDay
18609 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18617 getAutoCreate : function(){
18620 var fc_button = function(name, corner, style, content ) {
18621 return Roo.apply({},{
18623 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18625 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18628 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18639 style : 'width:100%',
18646 cls : 'fc-header-left',
18648 fc_button('prev', 'left', 'arrow', '‹' ),
18649 fc_button('next', 'right', 'arrow', '›' ),
18650 { tag: 'span', cls: 'fc-header-space' },
18651 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18659 cls : 'fc-header-center',
18663 cls: 'fc-header-title',
18666 html : 'month / year'
18674 cls : 'fc-header-right',
18676 /* fc_button('month', 'left', '', 'month' ),
18677 fc_button('week', '', '', 'week' ),
18678 fc_button('day', 'right', '', 'day' )
18690 header = this.header;
18693 var cal_heads = function() {
18695 // fixme - handle this.
18697 for (var i =0; i < Date.dayNames.length; i++) {
18698 var d = Date.dayNames[i];
18701 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18702 html : d.substring(0,3)
18706 ret[0].cls += ' fc-first';
18707 ret[6].cls += ' fc-last';
18710 var cal_cell = function(n) {
18713 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18718 cls: 'fc-day-number',
18722 cls: 'fc-day-content',
18726 style: 'position: relative;' // height: 17px;
18738 var cal_rows = function() {
18741 for (var r = 0; r < 6; r++) {
18748 for (var i =0; i < Date.dayNames.length; i++) {
18749 var d = Date.dayNames[i];
18750 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18753 row.cn[0].cls+=' fc-first';
18754 row.cn[0].cn[0].style = 'min-height:90px';
18755 row.cn[6].cls+=' fc-last';
18759 ret[0].cls += ' fc-first';
18760 ret[4].cls += ' fc-prev-last';
18761 ret[5].cls += ' fc-last';
18768 cls: 'fc-border-separate',
18769 style : 'width:100%',
18777 cls : 'fc-first fc-last',
18795 cls : 'fc-content',
18796 style : "position: relative;",
18799 cls : 'fc-view fc-view-month fc-grid',
18800 style : 'position: relative',
18801 unselectable : 'on',
18804 cls : 'fc-event-container',
18805 style : 'position:absolute;z-index:8;top:0;left:0;'
18823 initEvents : function()
18826 throw "can not find store for calendar";
18832 style: "text-align:center",
18836 style: "background-color:white;width:50%;margin:250 auto",
18840 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
18851 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18853 var size = this.el.select('.fc-content', true).first().getSize();
18854 this.maskEl.setSize(size.width, size.height);
18855 this.maskEl.enableDisplayMode("block");
18856 if(!this.loadMask){
18857 this.maskEl.hide();
18860 this.store = Roo.factory(this.store, Roo.data);
18861 this.store.on('load', this.onLoad, this);
18862 this.store.on('beforeload', this.onBeforeLoad, this);
18866 this.cells = this.el.select('.fc-day',true);
18867 //Roo.log(this.cells);
18868 this.textNodes = this.el.query('.fc-day-number');
18869 this.cells.addClassOnOver('fc-state-hover');
18871 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18872 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18873 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18874 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18876 this.on('monthchange', this.onMonthChange, this);
18878 this.update(new Date().clearTime());
18881 resize : function() {
18882 var sz = this.el.getSize();
18884 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18885 this.el.select('.fc-day-content div',true).setHeight(34);
18890 showPrevMonth : function(e){
18891 this.update(this.activeDate.add("mo", -1));
18893 showToday : function(e){
18894 this.update(new Date().clearTime());
18897 showNextMonth : function(e){
18898 this.update(this.activeDate.add("mo", 1));
18902 showPrevYear : function(){
18903 this.update(this.activeDate.add("y", -1));
18907 showNextYear : function(){
18908 this.update(this.activeDate.add("y", 1));
18913 update : function(date)
18915 var vd = this.activeDate;
18916 this.activeDate = date;
18917 // if(vd && this.el){
18918 // var t = date.getTime();
18919 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18920 // Roo.log('using add remove');
18922 // this.fireEvent('monthchange', this, date);
18924 // this.cells.removeClass("fc-state-highlight");
18925 // this.cells.each(function(c){
18926 // if(c.dateValue == t){
18927 // c.addClass("fc-state-highlight");
18928 // setTimeout(function(){
18929 // try{c.dom.firstChild.focus();}catch(e){}
18939 var days = date.getDaysInMonth();
18941 var firstOfMonth = date.getFirstDateOfMonth();
18942 var startingPos = firstOfMonth.getDay()-this.startDay;
18944 if(startingPos < this.startDay){
18948 var pm = date.add(Date.MONTH, -1);
18949 var prevStart = pm.getDaysInMonth()-startingPos;
18951 this.cells = this.el.select('.fc-day',true);
18952 this.textNodes = this.el.query('.fc-day-number');
18953 this.cells.addClassOnOver('fc-state-hover');
18955 var cells = this.cells.elements;
18956 var textEls = this.textNodes;
18958 Roo.each(cells, function(cell){
18959 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18962 days += startingPos;
18964 // convert everything to numbers so it's fast
18965 var day = 86400000;
18966 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18969 //Roo.log(prevStart);
18971 var today = new Date().clearTime().getTime();
18972 var sel = date.clearTime().getTime();
18973 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18974 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18975 var ddMatch = this.disabledDatesRE;
18976 var ddText = this.disabledDatesText;
18977 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18978 var ddaysText = this.disabledDaysText;
18979 var format = this.format;
18981 var setCellClass = function(cal, cell){
18985 //Roo.log('set Cell Class');
18987 var t = d.getTime();
18991 cell.dateValue = t;
18993 cell.className += " fc-today";
18994 cell.className += " fc-state-highlight";
18995 cell.title = cal.todayText;
18998 // disable highlight in other month..
18999 //cell.className += " fc-state-highlight";
19004 cell.className = " fc-state-disabled";
19005 cell.title = cal.minText;
19009 cell.className = " fc-state-disabled";
19010 cell.title = cal.maxText;
19014 if(ddays.indexOf(d.getDay()) != -1){
19015 cell.title = ddaysText;
19016 cell.className = " fc-state-disabled";
19019 if(ddMatch && format){
19020 var fvalue = d.dateFormat(format);
19021 if(ddMatch.test(fvalue)){
19022 cell.title = ddText.replace("%0", fvalue);
19023 cell.className = " fc-state-disabled";
19027 if (!cell.initialClassName) {
19028 cell.initialClassName = cell.dom.className;
19031 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19036 for(; i < startingPos; i++) {
19037 textEls[i].innerHTML = (++prevStart);
19038 d.setDate(d.getDate()+1);
19040 cells[i].className = "fc-past fc-other-month";
19041 setCellClass(this, cells[i]);
19046 for(; i < days; i++){
19047 intDay = i - startingPos + 1;
19048 textEls[i].innerHTML = (intDay);
19049 d.setDate(d.getDate()+1);
19051 cells[i].className = ''; // "x-date-active";
19052 setCellClass(this, cells[i]);
19056 for(; i < 42; i++) {
19057 textEls[i].innerHTML = (++extraDays);
19058 d.setDate(d.getDate()+1);
19060 cells[i].className = "fc-future fc-other-month";
19061 setCellClass(this, cells[i]);
19064 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19066 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19068 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19069 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19071 if(totalRows != 6){
19072 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19073 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19076 this.fireEvent('monthchange', this, date);
19080 if(!this.internalRender){
19081 var main = this.el.dom.firstChild;
19082 var w = main.offsetWidth;
19083 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19084 Roo.fly(main).setWidth(w);
19085 this.internalRender = true;
19086 // opera does not respect the auto grow header center column
19087 // then, after it gets a width opera refuses to recalculate
19088 // without a second pass
19089 if(Roo.isOpera && !this.secondPass){
19090 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19091 this.secondPass = true;
19092 this.update.defer(10, this, [date]);
19099 findCell : function(dt) {
19100 dt = dt.clearTime().getTime();
19102 this.cells.each(function(c){
19103 //Roo.log("check " +c.dateValue + '?=' + dt);
19104 if(c.dateValue == dt){
19114 findCells : function(ev) {
19115 var s = ev.start.clone().clearTime().getTime();
19117 var e= ev.end.clone().clearTime().getTime();
19120 this.cells.each(function(c){
19121 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19123 if(c.dateValue > e){
19126 if(c.dateValue < s){
19135 // findBestRow: function(cells)
19139 // for (var i =0 ; i < cells.length;i++) {
19140 // ret = Math.max(cells[i].rows || 0,ret);
19147 addItem : function(ev)
19149 // look for vertical location slot in
19150 var cells = this.findCells(ev);
19152 // ev.row = this.findBestRow(cells);
19154 // work out the location.
19158 for(var i =0; i < cells.length; i++) {
19160 cells[i].row = cells[0].row;
19163 cells[i].row = cells[i].row + 1;
19173 if (crow.start.getY() == cells[i].getY()) {
19175 crow.end = cells[i];
19192 cells[0].events.push(ev);
19194 this.calevents.push(ev);
19197 clearEvents: function() {
19199 if(!this.calevents){
19203 Roo.each(this.cells.elements, function(c){
19209 Roo.each(this.calevents, function(e) {
19210 Roo.each(e.els, function(el) {
19211 el.un('mouseenter' ,this.onEventEnter, this);
19212 el.un('mouseleave' ,this.onEventLeave, this);
19217 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19223 renderEvents: function()
19227 this.cells.each(function(c) {
19236 if(c.row != c.events.length){
19237 r = 4 - (4 - (c.row - c.events.length));
19240 c.events = ev.slice(0, r);
19241 c.more = ev.slice(r);
19243 if(c.more.length && c.more.length == 1){
19244 c.events.push(c.more.pop());
19247 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19251 this.cells.each(function(c) {
19253 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19256 for (var e = 0; e < c.events.length; e++){
19257 var ev = c.events[e];
19258 var rows = ev.rows;
19260 for(var i = 0; i < rows.length; i++) {
19262 // how many rows should it span..
19265 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19266 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19268 unselectable : "on",
19271 cls: 'fc-event-inner',
19275 // cls: 'fc-event-time',
19276 // html : cells.length > 1 ? '' : ev.time
19280 cls: 'fc-event-title',
19281 html : String.format('{0}', ev.title)
19288 cls: 'ui-resizable-handle ui-resizable-e',
19289 html : '  '
19296 cfg.cls += ' fc-event-start';
19298 if ((i+1) == rows.length) {
19299 cfg.cls += ' fc-event-end';
19302 var ctr = _this.el.select('.fc-event-container',true).first();
19303 var cg = ctr.createChild(cfg);
19305 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19306 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19308 var r = (c.more.length) ? 1 : 0;
19309 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19310 cg.setWidth(ebox.right - sbox.x -2);
19312 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19313 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19314 cg.on('click', _this.onEventClick, _this, ev);
19325 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19326 style : 'position: absolute',
19327 unselectable : "on",
19330 cls: 'fc-event-inner',
19334 cls: 'fc-event-title',
19342 cls: 'ui-resizable-handle ui-resizable-e',
19343 html : '  '
19349 var ctr = _this.el.select('.fc-event-container',true).first();
19350 var cg = ctr.createChild(cfg);
19352 var sbox = c.select('.fc-day-content',true).first().getBox();
19353 var ebox = c.select('.fc-day-content',true).first().getBox();
19355 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19356 cg.setWidth(ebox.right - sbox.x -2);
19358 cg.on('click', _this.onMoreEventClick, _this, c.more);
19368 onEventEnter: function (e, el,event,d) {
19369 this.fireEvent('evententer', this, el, event);
19372 onEventLeave: function (e, el,event,d) {
19373 this.fireEvent('eventleave', this, el, event);
19376 onEventClick: function (e, el,event,d) {
19377 this.fireEvent('eventclick', this, el, event);
19380 onMonthChange: function () {
19384 onMoreEventClick: function(e, el, more)
19388 this.calpopover.placement = 'right';
19389 this.calpopover.setTitle('More');
19391 this.calpopover.setContent('');
19393 var ctr = this.calpopover.el.select('.popover-content', true).first();
19395 Roo.each(more, function(m){
19397 cls : 'fc-event-hori fc-event-draggable',
19400 var cg = ctr.createChild(cfg);
19402 cg.on('click', _this.onEventClick, _this, m);
19405 this.calpopover.show(el);
19410 onLoad: function ()
19412 this.calevents = [];
19415 if(this.store.getCount() > 0){
19416 this.store.data.each(function(d){
19419 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19420 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19421 time : d.data.start_time,
19422 title : d.data.title,
19423 description : d.data.description,
19424 venue : d.data.venue
19429 this.renderEvents();
19431 if(this.calevents.length && this.loadMask){
19432 this.maskEl.hide();
19436 onBeforeLoad: function()
19438 this.clearEvents();
19440 this.maskEl.show();
19454 * @class Roo.bootstrap.Popover
19455 * @extends Roo.bootstrap.Component
19456 * Bootstrap Popover class
19457 * @cfg {String} html contents of the popover (or false to use children..)
19458 * @cfg {String} title of popover (or false to hide)
19459 * @cfg {String} placement how it is placed
19460 * @cfg {String} trigger click || hover (or false to trigger manually)
19461 * @cfg {String} over what (parent or false to trigger manually.)
19462 * @cfg {Number} delay - delay before showing
19465 * Create a new Popover
19466 * @param {Object} config The config object
19469 Roo.bootstrap.Popover = function(config){
19470 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19476 * After the popover show
19478 * @param {Roo.bootstrap.Popover} this
19483 * After the popover hide
19485 * @param {Roo.bootstrap.Popover} this
19491 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19493 title: 'Fill in a title',
19496 placement : 'right',
19497 trigger : 'hover', // hover
19503 can_build_overlaid : false,
19505 getChildContainer : function()
19507 return this.el.select('.popover-content',true).first();
19510 getAutoCreate : function(){
19513 cls : 'popover roo-dynamic',
19514 style: 'display:block',
19520 cls : 'popover-inner',
19524 cls: 'popover-title popover-header',
19528 cls : 'popover-content popover-body',
19539 setTitle: function(str)
19542 this.el.select('.popover-title',true).first().dom.innerHTML = str;
19544 setContent: function(str)
19547 this.el.select('.popover-content',true).first().dom.innerHTML = str;
19549 // as it get's added to the bottom of the page.
19550 onRender : function(ct, position)
19552 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19554 var cfg = Roo.apply({}, this.getAutoCreate());
19558 cfg.cls += ' ' + this.cls;
19561 cfg.style = this.style;
19563 //Roo.log("adding to ");
19564 this.el = Roo.get(document.body).createChild(cfg, position);
19565 // Roo.log(this.el);
19570 initEvents : function()
19572 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19573 this.el.enableDisplayMode('block');
19575 if (this.over === false) {
19578 if (this.triggers === false) {
19581 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19582 var triggers = this.trigger ? this.trigger.split(' ') : [];
19583 Roo.each(triggers, function(trigger) {
19585 if (trigger == 'click') {
19586 on_el.on('click', this.toggle, this);
19587 } else if (trigger != 'manual') {
19588 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19589 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19591 on_el.on(eventIn ,this.enter, this);
19592 on_el.on(eventOut, this.leave, this);
19603 toggle : function () {
19604 this.hoverState == 'in' ? this.leave() : this.enter();
19607 enter : function () {
19609 clearTimeout(this.timeout);
19611 this.hoverState = 'in';
19613 if (!this.delay || !this.delay.show) {
19618 this.timeout = setTimeout(function () {
19619 if (_t.hoverState == 'in') {
19622 }, this.delay.show)
19625 leave : function() {
19626 clearTimeout(this.timeout);
19628 this.hoverState = 'out';
19630 if (!this.delay || !this.delay.hide) {
19635 this.timeout = setTimeout(function () {
19636 if (_t.hoverState == 'out') {
19639 }, this.delay.hide)
19642 show : function (on_el)
19645 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19649 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19650 if (this.html !== false) {
19651 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19653 this.el.removeClass([
19654 'fade','top','bottom', 'left', 'right','in',
19655 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19657 if (!this.title.length) {
19658 this.el.select('.popover-title',true).hide();
19661 var placement = typeof this.placement == 'function' ?
19662 this.placement.call(this, this.el, on_el) :
19665 var autoToken = /\s?auto?\s?/i;
19666 var autoPlace = autoToken.test(placement);
19668 placement = placement.replace(autoToken, '') || 'top';
19672 //this.el.setXY([0,0]);
19674 this.el.dom.style.display='block';
19675 this.el.addClass(placement);
19677 //this.el.appendTo(on_el);
19679 var p = this.getPosition();
19680 var box = this.el.getBox();
19685 var align = Roo.bootstrap.Popover.alignment[placement];
19688 this.el.alignTo(on_el, align[0],align[1]);
19689 //var arrow = this.el.select('.arrow',true).first();
19690 //arrow.set(align[2],
19692 this.el.addClass('in');
19695 if (this.el.hasClass('fade')) {
19699 this.hoverState = 'in';
19701 this.fireEvent('show', this);
19706 this.el.setXY([0,0]);
19707 this.el.removeClass('in');
19709 this.hoverState = null;
19711 this.fireEvent('hide', this);
19716 Roo.bootstrap.Popover.alignment = {
19717 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19718 'right' : ['l-r', [10,0], 'left bs-popover-left'],
19719 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19720 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19731 * @class Roo.bootstrap.Progress
19732 * @extends Roo.bootstrap.Component
19733 * Bootstrap Progress class
19734 * @cfg {Boolean} striped striped of the progress bar
19735 * @cfg {Boolean} active animated of the progress bar
19739 * Create a new Progress
19740 * @param {Object} config The config object
19743 Roo.bootstrap.Progress = function(config){
19744 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19747 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
19752 getAutoCreate : function(){
19760 cfg.cls += ' progress-striped';
19764 cfg.cls += ' active';
19783 * @class Roo.bootstrap.ProgressBar
19784 * @extends Roo.bootstrap.Component
19785 * Bootstrap ProgressBar class
19786 * @cfg {Number} aria_valuenow aria-value now
19787 * @cfg {Number} aria_valuemin aria-value min
19788 * @cfg {Number} aria_valuemax aria-value max
19789 * @cfg {String} label label for the progress bar
19790 * @cfg {String} panel (success | info | warning | danger )
19791 * @cfg {String} role role of the progress bar
19792 * @cfg {String} sr_only text
19796 * Create a new ProgressBar
19797 * @param {Object} config The config object
19800 Roo.bootstrap.ProgressBar = function(config){
19801 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19804 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
19808 aria_valuemax : 100,
19814 getAutoCreate : function()
19819 cls: 'progress-bar',
19820 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19832 cfg.role = this.role;
19835 if(this.aria_valuenow){
19836 cfg['aria-valuenow'] = this.aria_valuenow;
19839 if(this.aria_valuemin){
19840 cfg['aria-valuemin'] = this.aria_valuemin;
19843 if(this.aria_valuemax){
19844 cfg['aria-valuemax'] = this.aria_valuemax;
19847 if(this.label && !this.sr_only){
19848 cfg.html = this.label;
19852 cfg.cls += ' progress-bar-' + this.panel;
19858 update : function(aria_valuenow)
19860 this.aria_valuenow = aria_valuenow;
19862 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19877 * @class Roo.bootstrap.TabGroup
19878 * @extends Roo.bootstrap.Column
19879 * Bootstrap Column class
19880 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19881 * @cfg {Boolean} carousel true to make the group behave like a carousel
19882 * @cfg {Boolean} bullets show bullets for the panels
19883 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19884 * @cfg {Number} timer auto slide timer .. default 0 millisecond
19885 * @cfg {Boolean} showarrow (true|false) show arrow default true
19888 * Create a new TabGroup
19889 * @param {Object} config The config object
19892 Roo.bootstrap.TabGroup = function(config){
19893 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19895 this.navId = Roo.id();
19898 Roo.bootstrap.TabGroup.register(this);
19902 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
19905 transition : false,
19910 slideOnTouch : false,
19913 getAutoCreate : function()
19915 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19917 cfg.cls += ' tab-content';
19919 if (this.carousel) {
19920 cfg.cls += ' carousel slide';
19923 cls : 'carousel-inner',
19927 if(this.bullets && !Roo.isTouch){
19930 cls : 'carousel-bullets',
19934 if(this.bullets_cls){
19935 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19942 cfg.cn[0].cn.push(bullets);
19945 if(this.showarrow){
19946 cfg.cn[0].cn.push({
19948 class : 'carousel-arrow',
19952 class : 'carousel-prev',
19956 class : 'fa fa-chevron-left'
19962 class : 'carousel-next',
19966 class : 'fa fa-chevron-right'
19979 initEvents: function()
19981 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19982 // this.el.on("touchstart", this.onTouchStart, this);
19985 if(this.autoslide){
19988 this.slideFn = window.setInterval(function() {
19989 _this.showPanelNext();
19993 if(this.showarrow){
19994 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19995 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20001 // onTouchStart : function(e, el, o)
20003 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20007 // this.showPanelNext();
20011 getChildContainer : function()
20013 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20017 * register a Navigation item
20018 * @param {Roo.bootstrap.NavItem} the navitem to add
20020 register : function(item)
20022 this.tabs.push( item);
20023 item.navId = this.navId; // not really needed..
20028 getActivePanel : function()
20031 Roo.each(this.tabs, function(t) {
20041 getPanelByName : function(n)
20044 Roo.each(this.tabs, function(t) {
20045 if (t.tabId == n) {
20053 indexOfPanel : function(p)
20056 Roo.each(this.tabs, function(t,i) {
20057 if (t.tabId == p.tabId) {
20066 * show a specific panel
20067 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20068 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20070 showPanel : function (pan)
20072 if(this.transition || typeof(pan) == 'undefined'){
20073 Roo.log("waiting for the transitionend");
20077 if (typeof(pan) == 'number') {
20078 pan = this.tabs[pan];
20081 if (typeof(pan) == 'string') {
20082 pan = this.getPanelByName(pan);
20085 var cur = this.getActivePanel();
20088 Roo.log('pan or acitve pan is undefined');
20092 if (pan.tabId == this.getActivePanel().tabId) {
20096 if (false === cur.fireEvent('beforedeactivate')) {
20100 if(this.bullets > 0 && !Roo.isTouch){
20101 this.setActiveBullet(this.indexOfPanel(pan));
20104 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20106 //class="carousel-item carousel-item-next carousel-item-left"
20108 this.transition = true;
20109 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20110 var lr = dir == 'next' ? 'left' : 'right';
20111 pan.el.addClass(dir); // or prev
20112 pan.el.addClass('carousel-item-' + dir); // or prev
20113 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20114 cur.el.addClass(lr); // or right
20115 pan.el.addClass(lr);
20116 cur.el.addClass('carousel-item-' +lr); // or right
20117 pan.el.addClass('carousel-item-' +lr);
20121 cur.el.on('transitionend', function() {
20122 Roo.log("trans end?");
20124 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20125 pan.setActive(true);
20127 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20128 cur.setActive(false);
20130 _this.transition = false;
20132 }, this, { single: true } );
20137 cur.setActive(false);
20138 pan.setActive(true);
20143 showPanelNext : function()
20145 var i = this.indexOfPanel(this.getActivePanel());
20147 if (i >= this.tabs.length - 1 && !this.autoslide) {
20151 if (i >= this.tabs.length - 1 && this.autoslide) {
20155 this.showPanel(this.tabs[i+1]);
20158 showPanelPrev : function()
20160 var i = this.indexOfPanel(this.getActivePanel());
20162 if (i < 1 && !this.autoslide) {
20166 if (i < 1 && this.autoslide) {
20167 i = this.tabs.length;
20170 this.showPanel(this.tabs[i-1]);
20174 addBullet: function()
20176 if(!this.bullets || Roo.isTouch){
20179 var ctr = this.el.select('.carousel-bullets',true).first();
20180 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20181 var bullet = ctr.createChild({
20182 cls : 'bullet bullet-' + i
20183 },ctr.dom.lastChild);
20188 bullet.on('click', (function(e, el, o, ii, t){
20190 e.preventDefault();
20192 this.showPanel(ii);
20194 if(this.autoslide && this.slideFn){
20195 clearInterval(this.slideFn);
20196 this.slideFn = window.setInterval(function() {
20197 _this.showPanelNext();
20201 }).createDelegate(this, [i, bullet], true));
20206 setActiveBullet : function(i)
20212 Roo.each(this.el.select('.bullet', true).elements, function(el){
20213 el.removeClass('selected');
20216 var bullet = this.el.select('.bullet-' + i, true).first();
20222 bullet.addClass('selected');
20233 Roo.apply(Roo.bootstrap.TabGroup, {
20237 * register a Navigation Group
20238 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20240 register : function(navgrp)
20242 this.groups[navgrp.navId] = navgrp;
20246 * fetch a Navigation Group based on the navigation ID
20247 * if one does not exist , it will get created.
20248 * @param {string} the navgroup to add
20249 * @returns {Roo.bootstrap.NavGroup} the navgroup
20251 get: function(navId) {
20252 if (typeof(this.groups[navId]) == 'undefined') {
20253 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20255 return this.groups[navId] ;
20270 * @class Roo.bootstrap.TabPanel
20271 * @extends Roo.bootstrap.Component
20272 * Bootstrap TabPanel class
20273 * @cfg {Boolean} active panel active
20274 * @cfg {String} html panel content
20275 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20276 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20277 * @cfg {String} href click to link..
20278 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20282 * Create a new TabPanel
20283 * @param {Object} config The config object
20286 Roo.bootstrap.TabPanel = function(config){
20287 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20291 * Fires when the active status changes
20292 * @param {Roo.bootstrap.TabPanel} this
20293 * @param {Boolean} state the new state
20298 * @event beforedeactivate
20299 * Fires before a tab is de-activated - can be used to do validation on a form.
20300 * @param {Roo.bootstrap.TabPanel} this
20301 * @return {Boolean} false if there is an error
20304 'beforedeactivate': true
20307 this.tabId = this.tabId || Roo.id();
20311 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20318 touchSlide : false,
20319 getAutoCreate : function(){
20324 // item is needed for carousel - not sure if it has any effect otherwise
20325 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20326 html: this.html || ''
20330 cfg.cls += ' active';
20334 cfg.tabId = this.tabId;
20342 initEvents: function()
20344 var p = this.parent();
20346 this.navId = this.navId || p.navId;
20348 if (typeof(this.navId) != 'undefined') {
20349 // not really needed.. but just in case.. parent should be a NavGroup.
20350 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20354 var i = tg.tabs.length - 1;
20356 if(this.active && tg.bullets > 0 && i < tg.bullets){
20357 tg.setActiveBullet(i);
20361 this.el.on('click', this.onClick, this);
20363 if(Roo.isTouch && this.touchSlide){
20364 this.el.on("touchstart", this.onTouchStart, this);
20365 this.el.on("touchmove", this.onTouchMove, this);
20366 this.el.on("touchend", this.onTouchEnd, this);
20371 onRender : function(ct, position)
20373 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20376 setActive : function(state)
20378 Roo.log("panel - set active " + this.tabId + "=" + state);
20380 this.active = state;
20382 this.el.removeClass('active');
20384 } else if (!this.el.hasClass('active')) {
20385 this.el.addClass('active');
20388 this.fireEvent('changed', this, state);
20391 onClick : function(e)
20393 e.preventDefault();
20395 if(!this.href.length){
20399 window.location.href = this.href;
20408 onTouchStart : function(e)
20410 this.swiping = false;
20412 this.startX = e.browserEvent.touches[0].clientX;
20413 this.startY = e.browserEvent.touches[0].clientY;
20416 onTouchMove : function(e)
20418 this.swiping = true;
20420 this.endX = e.browserEvent.touches[0].clientX;
20421 this.endY = e.browserEvent.touches[0].clientY;
20424 onTouchEnd : function(e)
20431 var tabGroup = this.parent();
20433 if(this.endX > this.startX){ // swiping right
20434 tabGroup.showPanelPrev();
20438 if(this.startX > this.endX){ // swiping left
20439 tabGroup.showPanelNext();
20458 * @class Roo.bootstrap.DateField
20459 * @extends Roo.bootstrap.Input
20460 * Bootstrap DateField class
20461 * @cfg {Number} weekStart default 0
20462 * @cfg {String} viewMode default empty, (months|years)
20463 * @cfg {String} minViewMode default empty, (months|years)
20464 * @cfg {Number} startDate default -Infinity
20465 * @cfg {Number} endDate default Infinity
20466 * @cfg {Boolean} todayHighlight default false
20467 * @cfg {Boolean} todayBtn default false
20468 * @cfg {Boolean} calendarWeeks default false
20469 * @cfg {Object} daysOfWeekDisabled default empty
20470 * @cfg {Boolean} singleMode default false (true | false)
20472 * @cfg {Boolean} keyboardNavigation default true
20473 * @cfg {String} language default en
20476 * Create a new DateField
20477 * @param {Object} config The config object
20480 Roo.bootstrap.DateField = function(config){
20481 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20485 * Fires when this field show.
20486 * @param {Roo.bootstrap.DateField} this
20487 * @param {Mixed} date The date value
20492 * Fires when this field hide.
20493 * @param {Roo.bootstrap.DateField} this
20494 * @param {Mixed} date The date value
20499 * Fires when select a date.
20500 * @param {Roo.bootstrap.DateField} this
20501 * @param {Mixed} date The date value
20505 * @event beforeselect
20506 * Fires when before select a date.
20507 * @param {Roo.bootstrap.DateField} this
20508 * @param {Mixed} date The date value
20510 beforeselect : true
20514 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20517 * @cfg {String} format
20518 * The default date format string which can be overriden for localization support. The format must be
20519 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20523 * @cfg {String} altFormats
20524 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20525 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20527 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20535 todayHighlight : false,
20541 keyboardNavigation: true,
20543 calendarWeeks: false,
20545 startDate: -Infinity,
20549 daysOfWeekDisabled: [],
20553 singleMode : false,
20555 UTCDate: function()
20557 return new Date(Date.UTC.apply(Date, arguments));
20560 UTCToday: function()
20562 var today = new Date();
20563 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20566 getDate: function() {
20567 var d = this.getUTCDate();
20568 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20571 getUTCDate: function() {
20575 setDate: function(d) {
20576 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20579 setUTCDate: function(d) {
20581 this.setValue(this.formatDate(this.date));
20584 onRender: function(ct, position)
20587 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20589 this.language = this.language || 'en';
20590 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20591 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20593 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20594 this.format = this.format || 'm/d/y';
20595 this.isInline = false;
20596 this.isInput = true;
20597 this.component = this.el.select('.add-on', true).first() || false;
20598 this.component = (this.component && this.component.length === 0) ? false : this.component;
20599 this.hasInput = this.component && this.inputEl().length;
20601 if (typeof(this.minViewMode === 'string')) {
20602 switch (this.minViewMode) {
20604 this.minViewMode = 1;
20607 this.minViewMode = 2;
20610 this.minViewMode = 0;
20615 if (typeof(this.viewMode === 'string')) {
20616 switch (this.viewMode) {
20629 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20631 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20633 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20635 this.picker().on('mousedown', this.onMousedown, this);
20636 this.picker().on('click', this.onClick, this);
20638 this.picker().addClass('datepicker-dropdown');
20640 this.startViewMode = this.viewMode;
20642 if(this.singleMode){
20643 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20644 v.setVisibilityMode(Roo.Element.DISPLAY);
20648 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20649 v.setStyle('width', '189px');
20653 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20654 if(!this.calendarWeeks){
20659 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20660 v.attr('colspan', function(i, val){
20661 return parseInt(val) + 1;
20666 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20668 this.setStartDate(this.startDate);
20669 this.setEndDate(this.endDate);
20671 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20678 if(this.isInline) {
20683 picker : function()
20685 return this.pickerEl;
20686 // return this.el.select('.datepicker', true).first();
20689 fillDow: function()
20691 var dowCnt = this.weekStart;
20700 if(this.calendarWeeks){
20708 while (dowCnt < this.weekStart + 7) {
20712 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20716 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20719 fillMonths: function()
20722 var months = this.picker().select('>.datepicker-months td', true).first();
20724 months.dom.innerHTML = '';
20730 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20733 months.createChild(month);
20740 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;
20742 if (this.date < this.startDate) {
20743 this.viewDate = new Date(this.startDate);
20744 } else if (this.date > this.endDate) {
20745 this.viewDate = new Date(this.endDate);
20747 this.viewDate = new Date(this.date);
20755 var d = new Date(this.viewDate),
20756 year = d.getUTCFullYear(),
20757 month = d.getUTCMonth(),
20758 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20759 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20760 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20761 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20762 currentDate = this.date && this.date.valueOf(),
20763 today = this.UTCToday();
20765 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20767 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20769 // this.picker.select('>tfoot th.today').
20770 // .text(dates[this.language].today)
20771 // .toggle(this.todayBtn !== false);
20773 this.updateNavArrows();
20776 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20778 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20780 prevMonth.setUTCDate(day);
20782 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20784 var nextMonth = new Date(prevMonth);
20786 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20788 nextMonth = nextMonth.valueOf();
20790 var fillMonths = false;
20792 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20794 while(prevMonth.valueOf() <= nextMonth) {
20797 if (prevMonth.getUTCDay() === this.weekStart) {
20799 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20807 if(this.calendarWeeks){
20808 // ISO 8601: First week contains first thursday.
20809 // ISO also states week starts on Monday, but we can be more abstract here.
20811 // Start of current week: based on weekstart/current date
20812 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20813 // Thursday of this week
20814 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20815 // First Thursday of year, year from thursday
20816 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20817 // Calendar week: ms between thursdays, div ms per day, div 7 days
20818 calWeek = (th - yth) / 864e5 / 7 + 1;
20820 fillMonths.cn.push({
20828 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20830 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20833 if (this.todayHighlight &&
20834 prevMonth.getUTCFullYear() == today.getFullYear() &&
20835 prevMonth.getUTCMonth() == today.getMonth() &&
20836 prevMonth.getUTCDate() == today.getDate()) {
20837 clsName += ' today';
20840 if (currentDate && prevMonth.valueOf() === currentDate) {
20841 clsName += ' active';
20844 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20845 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20846 clsName += ' disabled';
20849 fillMonths.cn.push({
20851 cls: 'day ' + clsName,
20852 html: prevMonth.getDate()
20855 prevMonth.setDate(prevMonth.getDate()+1);
20858 var currentYear = this.date && this.date.getUTCFullYear();
20859 var currentMonth = this.date && this.date.getUTCMonth();
20861 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20863 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20864 v.removeClass('active');
20866 if(currentYear === year && k === currentMonth){
20867 v.addClass('active');
20870 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20871 v.addClass('disabled');
20877 year = parseInt(year/10, 10) * 10;
20879 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20881 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20884 for (var i = -1; i < 11; i++) {
20885 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20887 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20895 showMode: function(dir)
20898 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20901 Roo.each(this.picker().select('>div',true).elements, function(v){
20902 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20905 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20910 if(this.isInline) {
20914 this.picker().removeClass(['bottom', 'top']);
20916 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20918 * place to the top of element!
20922 this.picker().addClass('top');
20923 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20928 this.picker().addClass('bottom');
20930 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20933 parseDate : function(value)
20935 if(!value || value instanceof Date){
20938 var v = Date.parseDate(value, this.format);
20939 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20940 v = Date.parseDate(value, 'Y-m-d');
20942 if(!v && this.altFormats){
20943 if(!this.altFormatsArray){
20944 this.altFormatsArray = this.altFormats.split("|");
20946 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20947 v = Date.parseDate(value, this.altFormatsArray[i]);
20953 formatDate : function(date, fmt)
20955 return (!date || !(date instanceof Date)) ?
20956 date : date.dateFormat(fmt || this.format);
20959 onFocus : function()
20961 Roo.bootstrap.DateField.superclass.onFocus.call(this);
20965 onBlur : function()
20967 Roo.bootstrap.DateField.superclass.onBlur.call(this);
20969 var d = this.inputEl().getValue();
20976 showPopup : function()
20978 this.picker().show();
20982 this.fireEvent('showpopup', this, this.date);
20985 hidePopup : function()
20987 if(this.isInline) {
20990 this.picker().hide();
20991 this.viewMode = this.startViewMode;
20994 this.fireEvent('hidepopup', this, this.date);
20998 onMousedown: function(e)
21000 e.stopPropagation();
21001 e.preventDefault();
21006 Roo.bootstrap.DateField.superclass.keyup.call(this);
21010 setValue: function(v)
21012 if(this.fireEvent('beforeselect', this, v) !== false){
21013 var d = new Date(this.parseDate(v) ).clearTime();
21015 if(isNaN(d.getTime())){
21016 this.date = this.viewDate = '';
21017 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21021 v = this.formatDate(d);
21023 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21025 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21029 this.fireEvent('select', this, this.date);
21033 getValue: function()
21035 return this.formatDate(this.date);
21038 fireKey: function(e)
21040 if (!this.picker().isVisible()){
21041 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21047 var dateChanged = false,
21049 newDate, newViewDate;
21054 e.preventDefault();
21058 if (!this.keyboardNavigation) {
21061 dir = e.keyCode == 37 ? -1 : 1;
21064 newDate = this.moveYear(this.date, dir);
21065 newViewDate = this.moveYear(this.viewDate, dir);
21066 } else if (e.shiftKey){
21067 newDate = this.moveMonth(this.date, dir);
21068 newViewDate = this.moveMonth(this.viewDate, dir);
21070 newDate = new Date(this.date);
21071 newDate.setUTCDate(this.date.getUTCDate() + dir);
21072 newViewDate = new Date(this.viewDate);
21073 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21075 if (this.dateWithinRange(newDate)){
21076 this.date = newDate;
21077 this.viewDate = newViewDate;
21078 this.setValue(this.formatDate(this.date));
21080 e.preventDefault();
21081 dateChanged = true;
21086 if (!this.keyboardNavigation) {
21089 dir = e.keyCode == 38 ? -1 : 1;
21091 newDate = this.moveYear(this.date, dir);
21092 newViewDate = this.moveYear(this.viewDate, dir);
21093 } else if (e.shiftKey){
21094 newDate = this.moveMonth(this.date, dir);
21095 newViewDate = this.moveMonth(this.viewDate, dir);
21097 newDate = new Date(this.date);
21098 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21099 newViewDate = new Date(this.viewDate);
21100 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21102 if (this.dateWithinRange(newDate)){
21103 this.date = newDate;
21104 this.viewDate = newViewDate;
21105 this.setValue(this.formatDate(this.date));
21107 e.preventDefault();
21108 dateChanged = true;
21112 this.setValue(this.formatDate(this.date));
21114 e.preventDefault();
21117 this.setValue(this.formatDate(this.date));
21131 onClick: function(e)
21133 e.stopPropagation();
21134 e.preventDefault();
21136 var target = e.getTarget();
21138 if(target.nodeName.toLowerCase() === 'i'){
21139 target = Roo.get(target).dom.parentNode;
21142 var nodeName = target.nodeName;
21143 var className = target.className;
21144 var html = target.innerHTML;
21145 //Roo.log(nodeName);
21147 switch(nodeName.toLowerCase()) {
21149 switch(className) {
21155 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21156 switch(this.viewMode){
21158 this.viewDate = this.moveMonth(this.viewDate, dir);
21162 this.viewDate = this.moveYear(this.viewDate, dir);
21168 var date = new Date();
21169 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21171 this.setValue(this.formatDate(this.date));
21178 if (className.indexOf('disabled') < 0) {
21179 this.viewDate.setUTCDate(1);
21180 if (className.indexOf('month') > -1) {
21181 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21183 var year = parseInt(html, 10) || 0;
21184 this.viewDate.setUTCFullYear(year);
21188 if(this.singleMode){
21189 this.setValue(this.formatDate(this.viewDate));
21200 //Roo.log(className);
21201 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21202 var day = parseInt(html, 10) || 1;
21203 var year = this.viewDate.getUTCFullYear(),
21204 month = this.viewDate.getUTCMonth();
21206 if (className.indexOf('old') > -1) {
21213 } else if (className.indexOf('new') > -1) {
21221 //Roo.log([year,month,day]);
21222 this.date = this.UTCDate(year, month, day,0,0,0,0);
21223 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21225 //Roo.log(this.formatDate(this.date));
21226 this.setValue(this.formatDate(this.date));
21233 setStartDate: function(startDate)
21235 this.startDate = startDate || -Infinity;
21236 if (this.startDate !== -Infinity) {
21237 this.startDate = this.parseDate(this.startDate);
21240 this.updateNavArrows();
21243 setEndDate: function(endDate)
21245 this.endDate = endDate || Infinity;
21246 if (this.endDate !== Infinity) {
21247 this.endDate = this.parseDate(this.endDate);
21250 this.updateNavArrows();
21253 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21255 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21256 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21257 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21259 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21260 return parseInt(d, 10);
21263 this.updateNavArrows();
21266 updateNavArrows: function()
21268 if(this.singleMode){
21272 var d = new Date(this.viewDate),
21273 year = d.getUTCFullYear(),
21274 month = d.getUTCMonth();
21276 Roo.each(this.picker().select('.prev', true).elements, function(v){
21278 switch (this.viewMode) {
21281 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21287 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21294 Roo.each(this.picker().select('.next', true).elements, function(v){
21296 switch (this.viewMode) {
21299 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21305 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21313 moveMonth: function(date, dir)
21318 var new_date = new Date(date.valueOf()),
21319 day = new_date.getUTCDate(),
21320 month = new_date.getUTCMonth(),
21321 mag = Math.abs(dir),
21323 dir = dir > 0 ? 1 : -1;
21326 // If going back one month, make sure month is not current month
21327 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21329 return new_date.getUTCMonth() == month;
21331 // If going forward one month, make sure month is as expected
21332 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21334 return new_date.getUTCMonth() != new_month;
21336 new_month = month + dir;
21337 new_date.setUTCMonth(new_month);
21338 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21339 if (new_month < 0 || new_month > 11) {
21340 new_month = (new_month + 12) % 12;
21343 // For magnitudes >1, move one month at a time...
21344 for (var i=0; i<mag; i++) {
21345 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21346 new_date = this.moveMonth(new_date, dir);
21348 // ...then reset the day, keeping it in the new month
21349 new_month = new_date.getUTCMonth();
21350 new_date.setUTCDate(day);
21352 return new_month != new_date.getUTCMonth();
21355 // Common date-resetting loop -- if date is beyond end of month, make it
21358 new_date.setUTCDate(--day);
21359 new_date.setUTCMonth(new_month);
21364 moveYear: function(date, dir)
21366 return this.moveMonth(date, dir*12);
21369 dateWithinRange: function(date)
21371 return date >= this.startDate && date <= this.endDate;
21377 this.picker().remove();
21380 validateValue : function(value)
21382 if(this.getVisibilityEl().hasClass('hidden')){
21386 if(value.length < 1) {
21387 if(this.allowBlank){
21393 if(value.length < this.minLength){
21396 if(value.length > this.maxLength){
21400 var vt = Roo.form.VTypes;
21401 if(!vt[this.vtype](value, this)){
21405 if(typeof this.validator == "function"){
21406 var msg = this.validator(value);
21412 if(this.regex && !this.regex.test(value)){
21416 if(typeof(this.parseDate(value)) == 'undefined'){
21420 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21424 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21434 this.date = this.viewDate = '';
21436 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21441 Roo.apply(Roo.bootstrap.DateField, {
21452 html: '<i class="fa fa-arrow-left"/>'
21462 html: '<i class="fa fa-arrow-right"/>'
21504 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21505 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21506 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21507 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21508 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21521 navFnc: 'FullYear',
21526 navFnc: 'FullYear',
21531 Roo.apply(Roo.bootstrap.DateField, {
21535 cls: 'datepicker dropdown-menu roo-dynamic',
21539 cls: 'datepicker-days',
21543 cls: 'table-condensed',
21545 Roo.bootstrap.DateField.head,
21549 Roo.bootstrap.DateField.footer
21556 cls: 'datepicker-months',
21560 cls: 'table-condensed',
21562 Roo.bootstrap.DateField.head,
21563 Roo.bootstrap.DateField.content,
21564 Roo.bootstrap.DateField.footer
21571 cls: 'datepicker-years',
21575 cls: 'table-condensed',
21577 Roo.bootstrap.DateField.head,
21578 Roo.bootstrap.DateField.content,
21579 Roo.bootstrap.DateField.footer
21598 * @class Roo.bootstrap.TimeField
21599 * @extends Roo.bootstrap.Input
21600 * Bootstrap DateField class
21604 * Create a new TimeField
21605 * @param {Object} config The config object
21608 Roo.bootstrap.TimeField = function(config){
21609 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21613 * Fires when this field show.
21614 * @param {Roo.bootstrap.DateField} thisthis
21615 * @param {Mixed} date The date value
21620 * Fires when this field hide.
21621 * @param {Roo.bootstrap.DateField} this
21622 * @param {Mixed} date The date value
21627 * Fires when select a date.
21628 * @param {Roo.bootstrap.DateField} this
21629 * @param {Mixed} date The date value
21635 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
21638 * @cfg {String} format
21639 * The default time format string which can be overriden for localization support. The format must be
21640 * valid according to {@link Date#parseDate} (defaults to 'H:i').
21644 onRender: function(ct, position)
21647 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21649 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21651 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21653 this.pop = this.picker().select('>.datepicker-time',true).first();
21654 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21656 this.picker().on('mousedown', this.onMousedown, this);
21657 this.picker().on('click', this.onClick, this);
21659 this.picker().addClass('datepicker-dropdown');
21664 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21665 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21666 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21667 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21668 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21669 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21673 fireKey: function(e){
21674 if (!this.picker().isVisible()){
21675 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21681 e.preventDefault();
21689 this.onTogglePeriod();
21692 this.onIncrementMinutes();
21695 this.onDecrementMinutes();
21704 onClick: function(e) {
21705 e.stopPropagation();
21706 e.preventDefault();
21709 picker : function()
21711 return this.el.select('.datepicker', true).first();
21714 fillTime: function()
21716 var time = this.pop.select('tbody', true).first();
21718 time.dom.innerHTML = '';
21733 cls: 'hours-up glyphicon glyphicon-chevron-up'
21753 cls: 'minutes-up glyphicon glyphicon-chevron-up'
21774 cls: 'timepicker-hour',
21789 cls: 'timepicker-minute',
21804 cls: 'btn btn-primary period',
21826 cls: 'hours-down glyphicon glyphicon-chevron-down'
21846 cls: 'minutes-down glyphicon glyphicon-chevron-down'
21864 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21871 var hours = this.time.getHours();
21872 var minutes = this.time.getMinutes();
21885 hours = hours - 12;
21889 hours = '0' + hours;
21893 minutes = '0' + minutes;
21896 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21897 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21898 this.pop.select('button', true).first().dom.innerHTML = period;
21904 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21906 var cls = ['bottom'];
21908 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21915 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21920 this.picker().addClass(cls.join('-'));
21924 Roo.each(cls, function(c){
21926 _this.picker().setTop(_this.inputEl().getHeight());
21930 _this.picker().setTop(0 - _this.picker().getHeight());
21935 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21939 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21946 onFocus : function()
21948 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21952 onBlur : function()
21954 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21960 this.picker().show();
21965 this.fireEvent('show', this, this.date);
21970 this.picker().hide();
21973 this.fireEvent('hide', this, this.date);
21976 setTime : function()
21979 this.setValue(this.time.format(this.format));
21981 this.fireEvent('select', this, this.date);
21986 onMousedown: function(e){
21987 e.stopPropagation();
21988 e.preventDefault();
21991 onIncrementHours: function()
21993 Roo.log('onIncrementHours');
21994 this.time = this.time.add(Date.HOUR, 1);
21999 onDecrementHours: function()
22001 Roo.log('onDecrementHours');
22002 this.time = this.time.add(Date.HOUR, -1);
22006 onIncrementMinutes: function()
22008 Roo.log('onIncrementMinutes');
22009 this.time = this.time.add(Date.MINUTE, 1);
22013 onDecrementMinutes: function()
22015 Roo.log('onDecrementMinutes');
22016 this.time = this.time.add(Date.MINUTE, -1);
22020 onTogglePeriod: function()
22022 Roo.log('onTogglePeriod');
22023 this.time = this.time.add(Date.HOUR, 12);
22030 Roo.apply(Roo.bootstrap.TimeField, {
22060 cls: 'btn btn-info ok',
22072 Roo.apply(Roo.bootstrap.TimeField, {
22076 cls: 'datepicker dropdown-menu',
22080 cls: 'datepicker-time',
22084 cls: 'table-condensed',
22086 Roo.bootstrap.TimeField.content,
22087 Roo.bootstrap.TimeField.footer
22106 * @class Roo.bootstrap.MonthField
22107 * @extends Roo.bootstrap.Input
22108 * Bootstrap MonthField class
22110 * @cfg {String} language default en
22113 * Create a new MonthField
22114 * @param {Object} config The config object
22117 Roo.bootstrap.MonthField = function(config){
22118 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22123 * Fires when this field show.
22124 * @param {Roo.bootstrap.MonthField} this
22125 * @param {Mixed} date The date value
22130 * Fires when this field hide.
22131 * @param {Roo.bootstrap.MonthField} this
22132 * @param {Mixed} date The date value
22137 * Fires when select a date.
22138 * @param {Roo.bootstrap.MonthField} this
22139 * @param {String} oldvalue The old value
22140 * @param {String} newvalue The new value
22146 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22148 onRender: function(ct, position)
22151 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22153 this.language = this.language || 'en';
22154 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22155 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22157 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22158 this.isInline = false;
22159 this.isInput = true;
22160 this.component = this.el.select('.add-on', true).first() || false;
22161 this.component = (this.component && this.component.length === 0) ? false : this.component;
22162 this.hasInput = this.component && this.inputEL().length;
22164 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22166 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22168 this.picker().on('mousedown', this.onMousedown, this);
22169 this.picker().on('click', this.onClick, this);
22171 this.picker().addClass('datepicker-dropdown');
22173 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22174 v.setStyle('width', '189px');
22181 if(this.isInline) {
22187 setValue: function(v, suppressEvent)
22189 var o = this.getValue();
22191 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22195 if(suppressEvent !== true){
22196 this.fireEvent('select', this, o, v);
22201 getValue: function()
22206 onClick: function(e)
22208 e.stopPropagation();
22209 e.preventDefault();
22211 var target = e.getTarget();
22213 if(target.nodeName.toLowerCase() === 'i'){
22214 target = Roo.get(target).dom.parentNode;
22217 var nodeName = target.nodeName;
22218 var className = target.className;
22219 var html = target.innerHTML;
22221 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22225 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22227 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22233 picker : function()
22235 return this.pickerEl;
22238 fillMonths: function()
22241 var months = this.picker().select('>.datepicker-months td', true).first();
22243 months.dom.innerHTML = '';
22249 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22252 months.createChild(month);
22261 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22262 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22265 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22266 e.removeClass('active');
22268 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22269 e.addClass('active');
22276 if(this.isInline) {
22280 this.picker().removeClass(['bottom', 'top']);
22282 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22284 * place to the top of element!
22288 this.picker().addClass('top');
22289 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22294 this.picker().addClass('bottom');
22296 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22299 onFocus : function()
22301 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22305 onBlur : function()
22307 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22309 var d = this.inputEl().getValue();
22318 this.picker().show();
22319 this.picker().select('>.datepicker-months', true).first().show();
22323 this.fireEvent('show', this, this.date);
22328 if(this.isInline) {
22331 this.picker().hide();
22332 this.fireEvent('hide', this, this.date);
22336 onMousedown: function(e)
22338 e.stopPropagation();
22339 e.preventDefault();
22344 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22348 fireKey: function(e)
22350 if (!this.picker().isVisible()){
22351 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22362 e.preventDefault();
22366 dir = e.keyCode == 37 ? -1 : 1;
22368 this.vIndex = this.vIndex + dir;
22370 if(this.vIndex < 0){
22374 if(this.vIndex > 11){
22378 if(isNaN(this.vIndex)){
22382 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22388 dir = e.keyCode == 38 ? -1 : 1;
22390 this.vIndex = this.vIndex + dir * 4;
22392 if(this.vIndex < 0){
22396 if(this.vIndex > 11){
22400 if(isNaN(this.vIndex)){
22404 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22409 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22410 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22414 e.preventDefault();
22417 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22418 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22434 this.picker().remove();
22439 Roo.apply(Roo.bootstrap.MonthField, {
22458 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22459 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22464 Roo.apply(Roo.bootstrap.MonthField, {
22468 cls: 'datepicker dropdown-menu roo-dynamic',
22472 cls: 'datepicker-months',
22476 cls: 'table-condensed',
22478 Roo.bootstrap.DateField.content
22498 * @class Roo.bootstrap.CheckBox
22499 * @extends Roo.bootstrap.Input
22500 * Bootstrap CheckBox class
22502 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22503 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22504 * @cfg {String} boxLabel The text that appears beside the checkbox
22505 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22506 * @cfg {Boolean} checked initnal the element
22507 * @cfg {Boolean} inline inline the element (default false)
22508 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22509 * @cfg {String} tooltip label tooltip
22512 * Create a new CheckBox
22513 * @param {Object} config The config object
22516 Roo.bootstrap.CheckBox = function(config){
22517 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22522 * Fires when the element is checked or unchecked.
22523 * @param {Roo.bootstrap.CheckBox} this This input
22524 * @param {Boolean} checked The new checked value
22529 * Fires when the element is click.
22530 * @param {Roo.bootstrap.CheckBox} this This input
22537 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22539 inputType: 'checkbox',
22548 // checkbox success does not make any sense really..
22553 getAutoCreate : function()
22555 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22561 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22564 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22570 type : this.inputType,
22571 value : this.inputValue,
22572 cls : 'roo-' + this.inputType, //'form-box',
22573 placeholder : this.placeholder || ''
22577 if(this.inputType != 'radio'){
22581 cls : 'roo-hidden-value',
22582 value : this.checked ? this.inputValue : this.valueOff
22587 if (this.weight) { // Validity check?
22588 cfg.cls += " " + this.inputType + "-" + this.weight;
22591 if (this.disabled) {
22592 input.disabled=true;
22596 input.checked = this.checked;
22601 input.name = this.name;
22603 if(this.inputType != 'radio'){
22604 hidden.name = this.name;
22605 input.name = '_hidden_' + this.name;
22610 input.cls += ' input-' + this.size;
22615 ['xs','sm','md','lg'].map(function(size){
22616 if (settings[size]) {
22617 cfg.cls += ' col-' + size + '-' + settings[size];
22621 var inputblock = input;
22623 if (this.before || this.after) {
22626 cls : 'input-group',
22631 inputblock.cn.push({
22633 cls : 'input-group-addon',
22638 inputblock.cn.push(input);
22640 if(this.inputType != 'radio'){
22641 inputblock.cn.push(hidden);
22645 inputblock.cn.push({
22647 cls : 'input-group-addon',
22653 var boxLabelCfg = false;
22659 //'for': id, // box label is handled by onclick - so no for...
22661 html: this.boxLabel
22664 boxLabelCfg.tooltip = this.tooltip;
22670 if (align ==='left' && this.fieldLabel.length) {
22671 // Roo.log("left and has label");
22676 cls : 'control-label',
22677 html : this.fieldLabel
22688 cfg.cn[1].cn.push(boxLabelCfg);
22691 if(this.labelWidth > 12){
22692 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22695 if(this.labelWidth < 13 && this.labelmd == 0){
22696 this.labelmd = this.labelWidth;
22699 if(this.labellg > 0){
22700 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22701 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22704 if(this.labelmd > 0){
22705 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22706 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22709 if(this.labelsm > 0){
22710 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22711 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22714 if(this.labelxs > 0){
22715 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22716 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22719 } else if ( this.fieldLabel.length) {
22720 // Roo.log(" label");
22724 tag: this.boxLabel ? 'span' : 'label',
22726 cls: 'control-label box-input-label',
22727 //cls : 'input-group-addon',
22728 html : this.fieldLabel
22735 cfg.cn.push(boxLabelCfg);
22740 // Roo.log(" no label && no align");
22741 cfg.cn = [ inputblock ] ;
22743 cfg.cn.push(boxLabelCfg);
22751 if(this.inputType != 'radio'){
22752 cfg.cn.push(hidden);
22760 * return the real input element.
22762 inputEl: function ()
22764 return this.el.select('input.roo-' + this.inputType,true).first();
22766 hiddenEl: function ()
22768 return this.el.select('input.roo-hidden-value',true).first();
22771 labelEl: function()
22773 return this.el.select('label.control-label',true).first();
22775 /* depricated... */
22779 return this.labelEl();
22782 boxLabelEl: function()
22784 return this.el.select('label.box-label',true).first();
22787 initEvents : function()
22789 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22791 this.inputEl().on('click', this.onClick, this);
22793 if (this.boxLabel) {
22794 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
22797 this.startValue = this.getValue();
22800 Roo.bootstrap.CheckBox.register(this);
22804 onClick : function(e)
22806 if(this.fireEvent('click', this, e) !== false){
22807 this.setChecked(!this.checked);
22812 setChecked : function(state,suppressEvent)
22814 this.startValue = this.getValue();
22816 if(this.inputType == 'radio'){
22818 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22819 e.dom.checked = false;
22822 this.inputEl().dom.checked = true;
22824 this.inputEl().dom.value = this.inputValue;
22826 if(suppressEvent !== true){
22827 this.fireEvent('check', this, true);
22835 this.checked = state;
22837 this.inputEl().dom.checked = state;
22840 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22842 if(suppressEvent !== true){
22843 this.fireEvent('check', this, state);
22849 getValue : function()
22851 if(this.inputType == 'radio'){
22852 return this.getGroupValue();
22855 return this.hiddenEl().dom.value;
22859 getGroupValue : function()
22861 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22865 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22868 setValue : function(v,suppressEvent)
22870 if(this.inputType == 'radio'){
22871 this.setGroupValue(v, suppressEvent);
22875 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22880 setGroupValue : function(v, suppressEvent)
22882 this.startValue = this.getValue();
22884 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22885 e.dom.checked = false;
22887 if(e.dom.value == v){
22888 e.dom.checked = true;
22892 if(suppressEvent !== true){
22893 this.fireEvent('check', this, true);
22901 validate : function()
22903 if(this.getVisibilityEl().hasClass('hidden')){
22909 (this.inputType == 'radio' && this.validateRadio()) ||
22910 (this.inputType == 'checkbox' && this.validateCheckbox())
22916 this.markInvalid();
22920 validateRadio : function()
22922 if(this.getVisibilityEl().hasClass('hidden')){
22926 if(this.allowBlank){
22932 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22933 if(!e.dom.checked){
22945 validateCheckbox : function()
22948 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22949 //return (this.getValue() == this.inputValue) ? true : false;
22952 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22960 for(var i in group){
22961 if(group[i].el.isVisible(true)){
22969 for(var i in group){
22974 r = (group[i].getValue() == group[i].inputValue) ? true : false;
22981 * Mark this field as valid
22983 markValid : function()
22987 this.fireEvent('valid', this);
22989 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22992 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22999 if(this.inputType == 'radio'){
23000 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23001 var fg = e.findParent('.form-group', false, true);
23002 if (Roo.bootstrap.version == 3) {
23003 fg.removeClass([_this.invalidClass, _this.validClass]);
23004 fg.addClass(_this.validClass);
23006 fg.removeClass(['is-valid', 'is-invalid']);
23007 fg.addClass('is-valid');
23015 var fg = this.el.findParent('.form-group', false, true);
23016 if (Roo.bootstrap.version == 3) {
23017 fg.removeClass([this.invalidClass, this.validClass]);
23018 fg.addClass(this.validClass);
23020 fg.removeClass(['is-valid', 'is-invalid']);
23021 fg.addClass('is-valid');
23026 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23032 for(var i in group){
23033 var fg = group[i].el.findParent('.form-group', false, true);
23034 if (Roo.bootstrap.version == 3) {
23035 fg.removeClass([this.invalidClass, this.validClass]);
23036 fg.addClass(this.validClass);
23038 fg.removeClass(['is-valid', 'is-invalid']);
23039 fg.addClass('is-valid');
23045 * Mark this field as invalid
23046 * @param {String} msg The validation message
23048 markInvalid : function(msg)
23050 if(this.allowBlank){
23056 this.fireEvent('invalid', this, msg);
23058 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23061 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23065 label.markInvalid();
23068 if(this.inputType == 'radio'){
23070 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23071 var fg = e.findParent('.form-group', false, true);
23072 if (Roo.bootstrap.version == 3) {
23073 fg.removeClass([_this.invalidClass, _this.validClass]);
23074 fg.addClass(_this.invalidClass);
23076 fg.removeClass(['is-invalid', 'is-valid']);
23077 fg.addClass('is-invalid');
23085 var fg = this.el.findParent('.form-group', false, true);
23086 if (Roo.bootstrap.version == 3) {
23087 fg.removeClass([_this.invalidClass, _this.validClass]);
23088 fg.addClass(_this.invalidClass);
23090 fg.removeClass(['is-invalid', 'is-valid']);
23091 fg.addClass('is-invalid');
23096 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23102 for(var i in group){
23103 var fg = group[i].el.findParent('.form-group', false, true);
23104 if (Roo.bootstrap.version == 3) {
23105 fg.removeClass([_this.invalidClass, _this.validClass]);
23106 fg.addClass(_this.invalidClass);
23108 fg.removeClass(['is-invalid', 'is-valid']);
23109 fg.addClass('is-invalid');
23115 clearInvalid : function()
23117 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23119 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23121 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23123 if (label && label.iconEl) {
23124 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23125 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23129 disable : function()
23131 if(this.inputType != 'radio'){
23132 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23139 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23140 _this.getActionEl().addClass(this.disabledClass);
23141 e.dom.disabled = true;
23145 this.disabled = true;
23146 this.fireEvent("disable", this);
23150 enable : function()
23152 if(this.inputType != 'radio'){
23153 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23160 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23161 _this.getActionEl().removeClass(this.disabledClass);
23162 e.dom.disabled = false;
23166 this.disabled = false;
23167 this.fireEvent("enable", this);
23171 setBoxLabel : function(v)
23176 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23182 Roo.apply(Roo.bootstrap.CheckBox, {
23187 * register a CheckBox Group
23188 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23190 register : function(checkbox)
23192 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23193 this.groups[checkbox.groupId] = {};
23196 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23200 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23204 * fetch a CheckBox Group based on the group ID
23205 * @param {string} the group ID
23206 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23208 get: function(groupId) {
23209 if (typeof(this.groups[groupId]) == 'undefined') {
23213 return this.groups[groupId] ;
23226 * @class Roo.bootstrap.Radio
23227 * @extends Roo.bootstrap.Component
23228 * Bootstrap Radio class
23229 * @cfg {String} boxLabel - the label associated
23230 * @cfg {String} value - the value of radio
23233 * Create a new Radio
23234 * @param {Object} config The config object
23236 Roo.bootstrap.Radio = function(config){
23237 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23241 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23247 getAutoCreate : function()
23251 cls : 'form-group radio',
23256 html : this.boxLabel
23264 initEvents : function()
23266 this.parent().register(this);
23268 this.el.on('click', this.onClick, this);
23272 onClick : function(e)
23274 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23275 this.setChecked(true);
23279 setChecked : function(state, suppressEvent)
23281 this.parent().setValue(this.value, suppressEvent);
23285 setBoxLabel : function(v)
23290 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23305 * @class Roo.bootstrap.SecurePass
23306 * @extends Roo.bootstrap.Input
23307 * Bootstrap SecurePass class
23311 * Create a new SecurePass
23312 * @param {Object} config The config object
23315 Roo.bootstrap.SecurePass = function (config) {
23316 // these go here, so the translation tool can replace them..
23318 PwdEmpty: "Please type a password, and then retype it to confirm.",
23319 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23320 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23321 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23322 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23323 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23324 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23325 TooWeak: "Your password is Too Weak."
23327 this.meterLabel = "Password strength:";
23328 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23329 this.meterClass = [
23330 "roo-password-meter-tooweak",
23331 "roo-password-meter-weak",
23332 "roo-password-meter-medium",
23333 "roo-password-meter-strong",
23334 "roo-password-meter-grey"
23339 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23342 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23344 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23346 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23347 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23348 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23349 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23350 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23351 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23352 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23362 * @cfg {String/Object} Label for the strength meter (defaults to
23363 * 'Password strength:')
23368 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23369 * ['Weak', 'Medium', 'Strong'])
23372 pwdStrengths: false,
23385 initEvents: function ()
23387 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23389 if (this.el.is('input[type=password]') && Roo.isSafari) {
23390 this.el.on('keydown', this.SafariOnKeyDown, this);
23393 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23396 onRender: function (ct, position)
23398 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23399 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23400 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23402 this.trigger.createChild({
23407 cls: 'roo-password-meter-grey col-xs-12',
23410 //width: this.meterWidth + 'px'
23414 cls: 'roo-password-meter-text'
23420 if (this.hideTrigger) {
23421 this.trigger.setDisplayed(false);
23423 this.setSize(this.width || '', this.height || '');
23426 onDestroy: function ()
23428 if (this.trigger) {
23429 this.trigger.removeAllListeners();
23430 this.trigger.remove();
23433 this.wrap.remove();
23435 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23438 checkStrength: function ()
23440 var pwd = this.inputEl().getValue();
23441 if (pwd == this._lastPwd) {
23446 if (this.ClientSideStrongPassword(pwd)) {
23448 } else if (this.ClientSideMediumPassword(pwd)) {
23450 } else if (this.ClientSideWeakPassword(pwd)) {
23456 Roo.log('strength1: ' + strength);
23458 //var pm = this.trigger.child('div/div/div').dom;
23459 var pm = this.trigger.child('div/div');
23460 pm.removeClass(this.meterClass);
23461 pm.addClass(this.meterClass[strength]);
23464 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23466 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23468 this._lastPwd = pwd;
23472 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23474 this._lastPwd = '';
23476 var pm = this.trigger.child('div/div');
23477 pm.removeClass(this.meterClass);
23478 pm.addClass('roo-password-meter-grey');
23481 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23484 this.inputEl().dom.type='password';
23487 validateValue: function (value)
23489 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23492 if (value.length == 0) {
23493 if (this.allowBlank) {
23494 this.clearInvalid();
23498 this.markInvalid(this.errors.PwdEmpty);
23499 this.errorMsg = this.errors.PwdEmpty;
23507 if (!value.match(/[\x21-\x7e]*/)) {
23508 this.markInvalid(this.errors.PwdBadChar);
23509 this.errorMsg = this.errors.PwdBadChar;
23512 if (value.length < 6) {
23513 this.markInvalid(this.errors.PwdShort);
23514 this.errorMsg = this.errors.PwdShort;
23517 if (value.length > 16) {
23518 this.markInvalid(this.errors.PwdLong);
23519 this.errorMsg = this.errors.PwdLong;
23523 if (this.ClientSideStrongPassword(value)) {
23525 } else if (this.ClientSideMediumPassword(value)) {
23527 } else if (this.ClientSideWeakPassword(value)) {
23534 if (strength < 2) {
23535 //this.markInvalid(this.errors.TooWeak);
23536 this.errorMsg = this.errors.TooWeak;
23541 console.log('strength2: ' + strength);
23543 //var pm = this.trigger.child('div/div/div').dom;
23545 var pm = this.trigger.child('div/div');
23546 pm.removeClass(this.meterClass);
23547 pm.addClass(this.meterClass[strength]);
23549 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23551 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23553 this.errorMsg = '';
23557 CharacterSetChecks: function (type)
23560 this.fResult = false;
23563 isctype: function (character, type)
23566 case this.kCapitalLetter:
23567 if (character >= 'A' && character <= 'Z') {
23572 case this.kSmallLetter:
23573 if (character >= 'a' && character <= 'z') {
23579 if (character >= '0' && character <= '9') {
23584 case this.kPunctuation:
23585 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23596 IsLongEnough: function (pwd, size)
23598 return !(pwd == null || isNaN(size) || pwd.length < size);
23601 SpansEnoughCharacterSets: function (word, nb)
23603 if (!this.IsLongEnough(word, nb))
23608 var characterSetChecks = new Array(
23609 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23610 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23613 for (var index = 0; index < word.length; ++index) {
23614 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23615 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23616 characterSetChecks[nCharSet].fResult = true;
23623 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23624 if (characterSetChecks[nCharSet].fResult) {
23629 if (nCharSets < nb) {
23635 ClientSideStrongPassword: function (pwd)
23637 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23640 ClientSideMediumPassword: function (pwd)
23642 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23645 ClientSideWeakPassword: function (pwd)
23647 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23650 })//<script type="text/javascript">
23653 * Based Ext JS Library 1.1.1
23654 * Copyright(c) 2006-2007, Ext JS, LLC.
23660 * @class Roo.HtmlEditorCore
23661 * @extends Roo.Component
23662 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23664 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23667 Roo.HtmlEditorCore = function(config){
23670 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23675 * @event initialize
23676 * Fires when the editor is fully initialized (including the iframe)
23677 * @param {Roo.HtmlEditorCore} this
23682 * Fires when the editor is first receives the focus. Any insertion must wait
23683 * until after this event.
23684 * @param {Roo.HtmlEditorCore} this
23688 * @event beforesync
23689 * Fires before the textarea is updated with content from the editor iframe. Return false
23690 * to cancel the sync.
23691 * @param {Roo.HtmlEditorCore} this
23692 * @param {String} html
23696 * @event beforepush
23697 * Fires before the iframe editor is updated with content from the textarea. Return false
23698 * to cancel the push.
23699 * @param {Roo.HtmlEditorCore} this
23700 * @param {String} html
23705 * Fires when the textarea is updated with content from the editor iframe.
23706 * @param {Roo.HtmlEditorCore} this
23707 * @param {String} html
23712 * Fires when the iframe editor is updated with content from the textarea.
23713 * @param {Roo.HtmlEditorCore} this
23714 * @param {String} html
23719 * @event editorevent
23720 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23721 * @param {Roo.HtmlEditorCore} this
23727 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23729 // defaults : white / black...
23730 this.applyBlacklists();
23737 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
23741 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
23747 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23752 * @cfg {Number} height (in pixels)
23756 * @cfg {Number} width (in pixels)
23761 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23764 stylesheets: false,
23769 // private properties
23770 validationEvent : false,
23772 initialized : false,
23774 sourceEditMode : false,
23775 onFocus : Roo.emptyFn,
23777 hideMode:'offsets',
23781 // blacklist + whitelisted elements..
23788 * Protected method that will not generally be called directly. It
23789 * is called when the editor initializes the iframe with HTML contents. Override this method if you
23790 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23792 getDocMarkup : function(){
23796 // inherit styels from page...??
23797 if (this.stylesheets === false) {
23799 Roo.get(document.head).select('style').each(function(node) {
23800 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23803 Roo.get(document.head).select('link').each(function(node) {
23804 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23807 } else if (!this.stylesheets.length) {
23809 st = '<style type="text/css">' +
23810 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23813 for (var i in this.stylesheets) {
23814 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
23819 st += '<style type="text/css">' +
23820 'IMG { cursor: pointer } ' +
23823 var cls = 'roo-htmleditor-body';
23825 if(this.bodyCls.length){
23826 cls += ' ' + this.bodyCls;
23829 return '<html><head>' + st +
23830 //<style type="text/css">' +
23831 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23833 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
23837 onRender : function(ct, position)
23840 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23841 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23844 this.el.dom.style.border = '0 none';
23845 this.el.dom.setAttribute('tabIndex', -1);
23846 this.el.addClass('x-hidden hide');
23850 if(Roo.isIE){ // fix IE 1px bogus margin
23851 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23855 this.frameId = Roo.id();
23859 var iframe = this.owner.wrap.createChild({
23861 cls: 'form-control', // bootstrap..
23863 name: this.frameId,
23864 frameBorder : 'no',
23865 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
23870 this.iframe = iframe.dom;
23872 this.assignDocWin();
23874 this.doc.designMode = 'on';
23877 this.doc.write(this.getDocMarkup());
23881 var task = { // must defer to wait for browser to be ready
23883 //console.log("run task?" + this.doc.readyState);
23884 this.assignDocWin();
23885 if(this.doc.body || this.doc.readyState == 'complete'){
23887 this.doc.designMode="on";
23891 Roo.TaskMgr.stop(task);
23892 this.initEditor.defer(10, this);
23899 Roo.TaskMgr.start(task);
23904 onResize : function(w, h)
23906 Roo.log('resize: ' +w + ',' + h );
23907 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23911 if(typeof w == 'number'){
23913 this.iframe.style.width = w + 'px';
23915 if(typeof h == 'number'){
23917 this.iframe.style.height = h + 'px';
23919 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23926 * Toggles the editor between standard and source edit mode.
23927 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23929 toggleSourceEdit : function(sourceEditMode){
23931 this.sourceEditMode = sourceEditMode === true;
23933 if(this.sourceEditMode){
23935 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
23938 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23939 //this.iframe.className = '';
23942 //this.setSize(this.owner.wrap.getSize());
23943 //this.fireEvent('editmodechange', this, this.sourceEditMode);
23950 * Protected method that will not generally be called directly. If you need/want
23951 * custom HTML cleanup, this is the method you should override.
23952 * @param {String} html The HTML to be cleaned
23953 * return {String} The cleaned HTML
23955 cleanHtml : function(html){
23956 html = String(html);
23957 if(html.length > 5){
23958 if(Roo.isSafari){ // strip safari nonsense
23959 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23962 if(html == ' '){
23969 * HTML Editor -> Textarea
23970 * Protected method that will not generally be called directly. Syncs the contents
23971 * of the editor iframe with the textarea.
23973 syncValue : function(){
23974 if(this.initialized){
23975 var bd = (this.doc.body || this.doc.documentElement);
23976 //this.cleanUpPaste(); -- this is done else where and causes havoc..
23977 var html = bd.innerHTML;
23979 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23980 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23982 html = '<div style="'+m[0]+'">' + html + '</div>';
23985 html = this.cleanHtml(html);
23986 // fix up the special chars.. normaly like back quotes in word...
23987 // however we do not want to do this with chinese..
23988 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23990 var cc = match.charCodeAt();
23992 // Get the character value, handling surrogate pairs
23993 if (match.length == 2) {
23994 // It's a surrogate pair, calculate the Unicode code point
23995 var high = match.charCodeAt(0) - 0xD800;
23996 var low = match.charCodeAt(1) - 0xDC00;
23997 cc = (high * 0x400) + low + 0x10000;
23999 (cc >= 0x4E00 && cc < 0xA000 ) ||
24000 (cc >= 0x3400 && cc < 0x4E00 ) ||
24001 (cc >= 0xf900 && cc < 0xfb00 )
24006 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24007 return "&#" + cc + ";";
24014 if(this.owner.fireEvent('beforesync', this, html) !== false){
24015 this.el.dom.value = html;
24016 this.owner.fireEvent('sync', this, html);
24022 * Protected method that will not generally be called directly. Pushes the value of the textarea
24023 * into the iframe editor.
24025 pushValue : function(){
24026 if(this.initialized){
24027 var v = this.el.dom.value.trim();
24029 // if(v.length < 1){
24033 if(this.owner.fireEvent('beforepush', this, v) !== false){
24034 var d = (this.doc.body || this.doc.documentElement);
24036 this.cleanUpPaste();
24037 this.el.dom.value = d.innerHTML;
24038 this.owner.fireEvent('push', this, v);
24044 deferFocus : function(){
24045 this.focus.defer(10, this);
24049 focus : function(){
24050 if(this.win && !this.sourceEditMode){
24057 assignDocWin: function()
24059 var iframe = this.iframe;
24062 this.doc = iframe.contentWindow.document;
24063 this.win = iframe.contentWindow;
24065 // if (!Roo.get(this.frameId)) {
24068 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24069 // this.win = Roo.get(this.frameId).dom.contentWindow;
24071 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24075 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24076 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24081 initEditor : function(){
24082 //console.log("INIT EDITOR");
24083 this.assignDocWin();
24087 this.doc.designMode="on";
24089 this.doc.write(this.getDocMarkup());
24092 var dbody = (this.doc.body || this.doc.documentElement);
24093 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24094 // this copies styles from the containing element into thsi one..
24095 // not sure why we need all of this..
24096 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24098 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24099 //ss['background-attachment'] = 'fixed'; // w3c
24100 dbody.bgProperties = 'fixed'; // ie
24101 //Roo.DomHelper.applyStyles(dbody, ss);
24102 Roo.EventManager.on(this.doc, {
24103 //'mousedown': this.onEditorEvent,
24104 'mouseup': this.onEditorEvent,
24105 'dblclick': this.onEditorEvent,
24106 'click': this.onEditorEvent,
24107 'keyup': this.onEditorEvent,
24112 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24114 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24115 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24117 this.initialized = true;
24119 this.owner.fireEvent('initialize', this);
24124 onDestroy : function(){
24130 //for (var i =0; i < this.toolbars.length;i++) {
24131 // // fixme - ask toolbars for heights?
24132 // this.toolbars[i].onDestroy();
24135 //this.wrap.dom.innerHTML = '';
24136 //this.wrap.remove();
24141 onFirstFocus : function(){
24143 this.assignDocWin();
24146 this.activated = true;
24149 if(Roo.isGecko){ // prevent silly gecko errors
24151 var s = this.win.getSelection();
24152 if(!s.focusNode || s.focusNode.nodeType != 3){
24153 var r = s.getRangeAt(0);
24154 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24159 this.execCmd('useCSS', true);
24160 this.execCmd('styleWithCSS', false);
24163 this.owner.fireEvent('activate', this);
24167 adjustFont: function(btn){
24168 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24169 //if(Roo.isSafari){ // safari
24172 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24173 if(Roo.isSafari){ // safari
24174 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24175 v = (v < 10) ? 10 : v;
24176 v = (v > 48) ? 48 : v;
24177 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24182 v = Math.max(1, v+adjust);
24184 this.execCmd('FontSize', v );
24187 onEditorEvent : function(e)
24189 this.owner.fireEvent('editorevent', this, e);
24190 // this.updateToolbar();
24191 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24194 insertTag : function(tg)
24196 // could be a bit smarter... -> wrap the current selected tRoo..
24197 if (tg.toLowerCase() == 'span' ||
24198 tg.toLowerCase() == 'code' ||
24199 tg.toLowerCase() == 'sup' ||
24200 tg.toLowerCase() == 'sub'
24203 range = this.createRange(this.getSelection());
24204 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24205 wrappingNode.appendChild(range.extractContents());
24206 range.insertNode(wrappingNode);
24213 this.execCmd("formatblock", tg);
24217 insertText : function(txt)
24221 var range = this.createRange();
24222 range.deleteContents();
24223 //alert(Sender.getAttribute('label'));
24225 range.insertNode(this.doc.createTextNode(txt));
24231 * Executes a Midas editor command on the editor document and performs necessary focus and
24232 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24233 * @param {String} cmd The Midas command
24234 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24236 relayCmd : function(cmd, value){
24238 this.execCmd(cmd, value);
24239 this.owner.fireEvent('editorevent', this);
24240 //this.updateToolbar();
24241 this.owner.deferFocus();
24245 * Executes a Midas editor command directly on the editor document.
24246 * For visual commands, you should use {@link #relayCmd} instead.
24247 * <b>This should only be called after the editor is initialized.</b>
24248 * @param {String} cmd The Midas command
24249 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24251 execCmd : function(cmd, value){
24252 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24259 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24261 * @param {String} text | dom node..
24263 insertAtCursor : function(text)
24266 if(!this.activated){
24272 var r = this.doc.selection.createRange();
24283 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24287 // from jquery ui (MIT licenced)
24289 var win = this.win;
24291 if (win.getSelection && win.getSelection().getRangeAt) {
24292 range = win.getSelection().getRangeAt(0);
24293 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24294 range.insertNode(node);
24295 } else if (win.document.selection && win.document.selection.createRange) {
24296 // no firefox support
24297 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24298 win.document.selection.createRange().pasteHTML(txt);
24300 // no firefox support
24301 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24302 this.execCmd('InsertHTML', txt);
24311 mozKeyPress : function(e){
24313 var c = e.getCharCode(), cmd;
24316 c = String.fromCharCode(c).toLowerCase();
24330 this.cleanUpPaste.defer(100, this);
24338 e.preventDefault();
24346 fixKeys : function(){ // load time branching for fastest keydown performance
24348 return function(e){
24349 var k = e.getKey(), r;
24352 r = this.doc.selection.createRange();
24355 r.pasteHTML('    ');
24362 r = this.doc.selection.createRange();
24364 var target = r.parentElement();
24365 if(!target || target.tagName.toLowerCase() != 'li'){
24367 r.pasteHTML('<br />');
24373 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24374 this.cleanUpPaste.defer(100, this);
24380 }else if(Roo.isOpera){
24381 return function(e){
24382 var k = e.getKey();
24386 this.execCmd('InsertHTML','    ');
24389 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24390 this.cleanUpPaste.defer(100, this);
24395 }else if(Roo.isSafari){
24396 return function(e){
24397 var k = e.getKey();
24401 this.execCmd('InsertText','\t');
24405 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24406 this.cleanUpPaste.defer(100, this);
24414 getAllAncestors: function()
24416 var p = this.getSelectedNode();
24419 a.push(p); // push blank onto stack..
24420 p = this.getParentElement();
24424 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24428 a.push(this.doc.body);
24432 lastSelNode : false,
24435 getSelection : function()
24437 this.assignDocWin();
24438 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24441 getSelectedNode: function()
24443 // this may only work on Gecko!!!
24445 // should we cache this!!!!
24450 var range = this.createRange(this.getSelection()).cloneRange();
24453 var parent = range.parentElement();
24455 var testRange = range.duplicate();
24456 testRange.moveToElementText(parent);
24457 if (testRange.inRange(range)) {
24460 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24463 parent = parent.parentElement;
24468 // is ancestor a text element.
24469 var ac = range.commonAncestorContainer;
24470 if (ac.nodeType == 3) {
24471 ac = ac.parentNode;
24474 var ar = ac.childNodes;
24477 var other_nodes = [];
24478 var has_other_nodes = false;
24479 for (var i=0;i<ar.length;i++) {
24480 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24483 // fullly contained node.
24485 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24490 // probably selected..
24491 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24492 other_nodes.push(ar[i]);
24496 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24501 has_other_nodes = true;
24503 if (!nodes.length && other_nodes.length) {
24504 nodes= other_nodes;
24506 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24512 createRange: function(sel)
24514 // this has strange effects when using with
24515 // top toolbar - not sure if it's a great idea.
24516 //this.editor.contentWindow.focus();
24517 if (typeof sel != "undefined") {
24519 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24521 return this.doc.createRange();
24524 return this.doc.createRange();
24527 getParentElement: function()
24530 this.assignDocWin();
24531 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24533 var range = this.createRange(sel);
24536 var p = range.commonAncestorContainer;
24537 while (p.nodeType == 3) { // text node
24548 * Range intersection.. the hard stuff...
24552 * [ -- selected range --- ]
24556 * if end is before start or hits it. fail.
24557 * if start is after end or hits it fail.
24559 * if either hits (but other is outside. - then it's not
24565 // @see http://www.thismuchiknow.co.uk/?p=64.
24566 rangeIntersectsNode : function(range, node)
24568 var nodeRange = node.ownerDocument.createRange();
24570 nodeRange.selectNode(node);
24572 nodeRange.selectNodeContents(node);
24575 var rangeStartRange = range.cloneRange();
24576 rangeStartRange.collapse(true);
24578 var rangeEndRange = range.cloneRange();
24579 rangeEndRange.collapse(false);
24581 var nodeStartRange = nodeRange.cloneRange();
24582 nodeStartRange.collapse(true);
24584 var nodeEndRange = nodeRange.cloneRange();
24585 nodeEndRange.collapse(false);
24587 return rangeStartRange.compareBoundaryPoints(
24588 Range.START_TO_START, nodeEndRange) == -1 &&
24589 rangeEndRange.compareBoundaryPoints(
24590 Range.START_TO_START, nodeStartRange) == 1;
24594 rangeCompareNode : function(range, node)
24596 var nodeRange = node.ownerDocument.createRange();
24598 nodeRange.selectNode(node);
24600 nodeRange.selectNodeContents(node);
24604 range.collapse(true);
24606 nodeRange.collapse(true);
24608 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24609 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
24611 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24613 var nodeIsBefore = ss == 1;
24614 var nodeIsAfter = ee == -1;
24616 if (nodeIsBefore && nodeIsAfter) {
24619 if (!nodeIsBefore && nodeIsAfter) {
24620 return 1; //right trailed.
24623 if (nodeIsBefore && !nodeIsAfter) {
24624 return 2; // left trailed.
24630 // private? - in a new class?
24631 cleanUpPaste : function()
24633 // cleans up the whole document..
24634 Roo.log('cleanuppaste');
24636 this.cleanUpChildren(this.doc.body);
24637 var clean = this.cleanWordChars(this.doc.body.innerHTML);
24638 if (clean != this.doc.body.innerHTML) {
24639 this.doc.body.innerHTML = clean;
24644 cleanWordChars : function(input) {// change the chars to hex code
24645 var he = Roo.HtmlEditorCore;
24647 var output = input;
24648 Roo.each(he.swapCodes, function(sw) {
24649 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24651 output = output.replace(swapper, sw[1]);
24658 cleanUpChildren : function (n)
24660 if (!n.childNodes.length) {
24663 for (var i = n.childNodes.length-1; i > -1 ; i--) {
24664 this.cleanUpChild(n.childNodes[i]);
24671 cleanUpChild : function (node)
24674 //console.log(node);
24675 if (node.nodeName == "#text") {
24676 // clean up silly Windows -- stuff?
24679 if (node.nodeName == "#comment") {
24680 node.parentNode.removeChild(node);
24681 // clean up silly Windows -- stuff?
24684 var lcname = node.tagName.toLowerCase();
24685 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24686 // whitelist of tags..
24688 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24690 node.parentNode.removeChild(node);
24695 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24697 // spans with no attributes - just remove them..
24698 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
24699 remove_keep_children = true;
24702 // remove <a name=....> as rendering on yahoo mailer is borked with this.
24703 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24705 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24706 // remove_keep_children = true;
24709 if (remove_keep_children) {
24710 this.cleanUpChildren(node);
24711 // inserts everything just before this node...
24712 while (node.childNodes.length) {
24713 var cn = node.childNodes[0];
24714 node.removeChild(cn);
24715 node.parentNode.insertBefore(cn, node);
24717 node.parentNode.removeChild(node);
24721 if (!node.attributes || !node.attributes.length) {
24726 this.cleanUpChildren(node);
24730 function cleanAttr(n,v)
24733 if (v.match(/^\./) || v.match(/^\//)) {
24736 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24739 if (v.match(/^#/)) {
24742 if (v.match(/^\{/)) { // allow template editing.
24745 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24746 node.removeAttribute(n);
24750 var cwhite = this.cwhite;
24751 var cblack = this.cblack;
24753 function cleanStyle(n,v)
24755 if (v.match(/expression/)) { //XSS?? should we even bother..
24756 node.removeAttribute(n);
24760 var parts = v.split(/;/);
24763 Roo.each(parts, function(p) {
24764 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24768 var l = p.split(':').shift().replace(/\s+/g,'');
24769 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24771 if ( cwhite.length && cblack.indexOf(l) > -1) {
24772 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24773 //node.removeAttribute(n);
24777 // only allow 'c whitelisted system attributes'
24778 if ( cwhite.length && cwhite.indexOf(l) < 0) {
24779 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24780 //node.removeAttribute(n);
24790 if (clean.length) {
24791 node.setAttribute(n, clean.join(';'));
24793 node.removeAttribute(n);
24799 for (var i = node.attributes.length-1; i > -1 ; i--) {
24800 var a = node.attributes[i];
24803 if (a.name.toLowerCase().substr(0,2)=='on') {
24804 node.removeAttribute(a.name);
24807 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24808 node.removeAttribute(a.name);
24811 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24812 cleanAttr(a.name,a.value); // fixme..
24815 if (a.name == 'style') {
24816 cleanStyle(a.name,a.value);
24819 /// clean up MS crap..
24820 // tecnically this should be a list of valid class'es..
24823 if (a.name == 'class') {
24824 if (a.value.match(/^Mso/)) {
24825 node.removeAttribute('class');
24828 if (a.value.match(/^body$/)) {
24829 node.removeAttribute('class');
24840 this.cleanUpChildren(node);
24846 * Clean up MS wordisms...
24848 cleanWord : function(node)
24851 this.cleanWord(this.doc.body);
24856 node.nodeName == 'SPAN' &&
24857 !node.hasAttributes() &&
24858 node.childNodes.length == 1 &&
24859 node.firstChild.nodeName == "#text"
24861 var textNode = node.firstChild;
24862 node.removeChild(textNode);
24863 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24864 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24866 node.parentNode.insertBefore(textNode, node);
24867 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24868 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24870 node.parentNode.removeChild(node);
24873 if (node.nodeName == "#text") {
24874 // clean up silly Windows -- stuff?
24877 if (node.nodeName == "#comment") {
24878 node.parentNode.removeChild(node);
24879 // clean up silly Windows -- stuff?
24883 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24884 node.parentNode.removeChild(node);
24887 //Roo.log(node.tagName);
24888 // remove - but keep children..
24889 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24890 //Roo.log('-- removed');
24891 while (node.childNodes.length) {
24892 var cn = node.childNodes[0];
24893 node.removeChild(cn);
24894 node.parentNode.insertBefore(cn, node);
24895 // move node to parent - and clean it..
24896 this.cleanWord(cn);
24898 node.parentNode.removeChild(node);
24899 /// no need to iterate chidlren = it's got none..
24900 //this.iterateChildren(node, this.cleanWord);
24904 if (node.className.length) {
24906 var cn = node.className.split(/\W+/);
24908 Roo.each(cn, function(cls) {
24909 if (cls.match(/Mso[a-zA-Z]+/)) {
24914 node.className = cna.length ? cna.join(' ') : '';
24916 node.removeAttribute("class");
24920 if (node.hasAttribute("lang")) {
24921 node.removeAttribute("lang");
24924 if (node.hasAttribute("style")) {
24926 var styles = node.getAttribute("style").split(";");
24928 Roo.each(styles, function(s) {
24929 if (!s.match(/:/)) {
24932 var kv = s.split(":");
24933 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24936 // what ever is left... we allow.
24939 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24940 if (!nstyle.length) {
24941 node.removeAttribute('style');
24944 this.iterateChildren(node, this.cleanWord);
24950 * iterateChildren of a Node, calling fn each time, using this as the scole..
24951 * @param {DomNode} node node to iterate children of.
24952 * @param {Function} fn method of this class to call on each item.
24954 iterateChildren : function(node, fn)
24956 if (!node.childNodes.length) {
24959 for (var i = node.childNodes.length-1; i > -1 ; i--) {
24960 fn.call(this, node.childNodes[i])
24966 * cleanTableWidths.
24968 * Quite often pasting from word etc.. results in tables with column and widths.
24969 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24972 cleanTableWidths : function(node)
24977 this.cleanTableWidths(this.doc.body);
24982 if (node.nodeName == "#text" || node.nodeName == "#comment") {
24985 Roo.log(node.tagName);
24986 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24987 this.iterateChildren(node, this.cleanTableWidths);
24990 if (node.hasAttribute('width')) {
24991 node.removeAttribute('width');
24995 if (node.hasAttribute("style")) {
24998 var styles = node.getAttribute("style").split(";");
25000 Roo.each(styles, function(s) {
25001 if (!s.match(/:/)) {
25004 var kv = s.split(":");
25005 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25008 // what ever is left... we allow.
25011 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25012 if (!nstyle.length) {
25013 node.removeAttribute('style');
25017 this.iterateChildren(node, this.cleanTableWidths);
25025 domToHTML : function(currentElement, depth, nopadtext) {
25027 depth = depth || 0;
25028 nopadtext = nopadtext || false;
25030 if (!currentElement) {
25031 return this.domToHTML(this.doc.body);
25034 //Roo.log(currentElement);
25036 var allText = false;
25037 var nodeName = currentElement.nodeName;
25038 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25040 if (nodeName == '#text') {
25042 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25047 if (nodeName != 'BODY') {
25050 // Prints the node tagName, such as <A>, <IMG>, etc
25053 for(i = 0; i < currentElement.attributes.length;i++) {
25055 var aname = currentElement.attributes.item(i).name;
25056 if (!currentElement.attributes.item(i).value.length) {
25059 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25062 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25071 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25074 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25079 // Traverse the tree
25081 var currentElementChild = currentElement.childNodes.item(i);
25082 var allText = true;
25083 var innerHTML = '';
25085 while (currentElementChild) {
25086 // Formatting code (indent the tree so it looks nice on the screen)
25087 var nopad = nopadtext;
25088 if (lastnode == 'SPAN') {
25092 if (currentElementChild.nodeName == '#text') {
25093 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25094 toadd = nopadtext ? toadd : toadd.trim();
25095 if (!nopad && toadd.length > 80) {
25096 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25098 innerHTML += toadd;
25101 currentElementChild = currentElement.childNodes.item(i);
25107 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25109 // Recursively traverse the tree structure of the child node
25110 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25111 lastnode = currentElementChild.nodeName;
25113 currentElementChild=currentElement.childNodes.item(i);
25119 // The remaining code is mostly for formatting the tree
25120 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25125 ret+= "</"+tagName+">";
25131 applyBlacklists : function()
25133 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25134 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25138 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25139 if (b.indexOf(tag) > -1) {
25142 this.white.push(tag);
25146 Roo.each(w, function(tag) {
25147 if (b.indexOf(tag) > -1) {
25150 if (this.white.indexOf(tag) > -1) {
25153 this.white.push(tag);
25158 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25159 if (w.indexOf(tag) > -1) {
25162 this.black.push(tag);
25166 Roo.each(b, function(tag) {
25167 if (w.indexOf(tag) > -1) {
25170 if (this.black.indexOf(tag) > -1) {
25173 this.black.push(tag);
25178 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25179 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25183 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25184 if (b.indexOf(tag) > -1) {
25187 this.cwhite.push(tag);
25191 Roo.each(w, function(tag) {
25192 if (b.indexOf(tag) > -1) {
25195 if (this.cwhite.indexOf(tag) > -1) {
25198 this.cwhite.push(tag);
25203 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25204 if (w.indexOf(tag) > -1) {
25207 this.cblack.push(tag);
25211 Roo.each(b, function(tag) {
25212 if (w.indexOf(tag) > -1) {
25215 if (this.cblack.indexOf(tag) > -1) {
25218 this.cblack.push(tag);
25223 setStylesheets : function(stylesheets)
25225 if(typeof(stylesheets) == 'string'){
25226 Roo.get(this.iframe.contentDocument.head).createChild({
25228 rel : 'stylesheet',
25237 Roo.each(stylesheets, function(s) {
25242 Roo.get(_this.iframe.contentDocument.head).createChild({
25244 rel : 'stylesheet',
25253 removeStylesheets : function()
25257 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25262 setStyle : function(style)
25264 Roo.get(this.iframe.contentDocument.head).createChild({
25273 // hide stuff that is not compatible
25287 * @event specialkey
25291 * @cfg {String} fieldClass @hide
25294 * @cfg {String} focusClass @hide
25297 * @cfg {String} autoCreate @hide
25300 * @cfg {String} inputType @hide
25303 * @cfg {String} invalidClass @hide
25306 * @cfg {String} invalidText @hide
25309 * @cfg {String} msgFx @hide
25312 * @cfg {String} validateOnBlur @hide
25316 Roo.HtmlEditorCore.white = [
25317 'area', 'br', 'img', 'input', 'hr', 'wbr',
25319 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25320 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25321 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25322 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25323 'table', 'ul', 'xmp',
25325 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25328 'dir', 'menu', 'ol', 'ul', 'dl',
25334 Roo.HtmlEditorCore.black = [
25335 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25337 'base', 'basefont', 'bgsound', 'blink', 'body',
25338 'frame', 'frameset', 'head', 'html', 'ilayer',
25339 'iframe', 'layer', 'link', 'meta', 'object',
25340 'script', 'style' ,'title', 'xml' // clean later..
25342 Roo.HtmlEditorCore.clean = [
25343 'script', 'style', 'title', 'xml'
25345 Roo.HtmlEditorCore.remove = [
25350 Roo.HtmlEditorCore.ablack = [
25354 Roo.HtmlEditorCore.aclean = [
25355 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25359 Roo.HtmlEditorCore.pwhite= [
25360 'http', 'https', 'mailto'
25363 // white listed style attributes.
25364 Roo.HtmlEditorCore.cwhite= [
25365 // 'text-align', /// default is to allow most things..
25371 // black listed style attributes.
25372 Roo.HtmlEditorCore.cblack= [
25373 // 'font-size' -- this can be set by the project
25377 Roo.HtmlEditorCore.swapCodes =[
25396 * @class Roo.bootstrap.HtmlEditor
25397 * @extends Roo.bootstrap.TextArea
25398 * Bootstrap HtmlEditor class
25401 * Create a new HtmlEditor
25402 * @param {Object} config The config object
25405 Roo.bootstrap.HtmlEditor = function(config){
25406 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25407 if (!this.toolbars) {
25408 this.toolbars = [];
25411 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25414 * @event initialize
25415 * Fires when the editor is fully initialized (including the iframe)
25416 * @param {HtmlEditor} this
25421 * Fires when the editor is first receives the focus. Any insertion must wait
25422 * until after this event.
25423 * @param {HtmlEditor} this
25427 * @event beforesync
25428 * Fires before the textarea is updated with content from the editor iframe. Return false
25429 * to cancel the sync.
25430 * @param {HtmlEditor} this
25431 * @param {String} html
25435 * @event beforepush
25436 * Fires before the iframe editor is updated with content from the textarea. Return false
25437 * to cancel the push.
25438 * @param {HtmlEditor} this
25439 * @param {String} html
25444 * Fires when the textarea is updated with content from the editor iframe.
25445 * @param {HtmlEditor} this
25446 * @param {String} html
25451 * Fires when the iframe editor is updated with content from the textarea.
25452 * @param {HtmlEditor} this
25453 * @param {String} html
25457 * @event editmodechange
25458 * Fires when the editor switches edit modes
25459 * @param {HtmlEditor} this
25460 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25462 editmodechange: true,
25464 * @event editorevent
25465 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25466 * @param {HtmlEditor} this
25470 * @event firstfocus
25471 * Fires when on first focus - needed by toolbars..
25472 * @param {HtmlEditor} this
25477 * Auto save the htmlEditor value as a file into Events
25478 * @param {HtmlEditor} this
25482 * @event savedpreview
25483 * preview the saved version of htmlEditor
25484 * @param {HtmlEditor} this
25491 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25495 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25500 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25505 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25510 * @cfg {Number} height (in pixels)
25514 * @cfg {Number} width (in pixels)
25519 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25522 stylesheets: false,
25527 // private properties
25528 validationEvent : false,
25530 initialized : false,
25533 onFocus : Roo.emptyFn,
25535 hideMode:'offsets',
25537 tbContainer : false,
25541 toolbarContainer :function() {
25542 return this.wrap.select('.x-html-editor-tb',true).first();
25546 * Protected method that will not generally be called directly. It
25547 * is called when the editor creates its toolbar. Override this method if you need to
25548 * add custom toolbar buttons.
25549 * @param {HtmlEditor} editor
25551 createToolbar : function(){
25552 Roo.log('renewing');
25553 Roo.log("create toolbars");
25555 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25556 this.toolbars[0].render(this.toolbarContainer());
25560 // if (!editor.toolbars || !editor.toolbars.length) {
25561 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25564 // for (var i =0 ; i < editor.toolbars.length;i++) {
25565 // editor.toolbars[i] = Roo.factory(
25566 // typeof(editor.toolbars[i]) == 'string' ?
25567 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25568 // Roo.bootstrap.HtmlEditor);
25569 // editor.toolbars[i].init(editor);
25575 onRender : function(ct, position)
25577 // Roo.log("Call onRender: " + this.xtype);
25579 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25581 this.wrap = this.inputEl().wrap({
25582 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25585 this.editorcore.onRender(ct, position);
25587 if (this.resizable) {
25588 this.resizeEl = new Roo.Resizable(this.wrap, {
25592 minHeight : this.height,
25593 height: this.height,
25594 handles : this.resizable,
25597 resize : function(r, w, h) {
25598 _t.onResize(w,h); // -something
25604 this.createToolbar(this);
25607 if(!this.width && this.resizable){
25608 this.setSize(this.wrap.getSize());
25610 if (this.resizeEl) {
25611 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25612 // should trigger onReize..
25618 onResize : function(w, h)
25620 Roo.log('resize: ' +w + ',' + h );
25621 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25625 if(this.inputEl() ){
25626 if(typeof w == 'number'){
25627 var aw = w - this.wrap.getFrameWidth('lr');
25628 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25631 if(typeof h == 'number'){
25632 var tbh = -11; // fixme it needs to tool bar size!
25633 for (var i =0; i < this.toolbars.length;i++) {
25634 // fixme - ask toolbars for heights?
25635 tbh += this.toolbars[i].el.getHeight();
25636 //if (this.toolbars[i].footer) {
25637 // tbh += this.toolbars[i].footer.el.getHeight();
25645 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25646 ah -= 5; // knock a few pixes off for look..
25647 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25651 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25652 this.editorcore.onResize(ew,eh);
25657 * Toggles the editor between standard and source edit mode.
25658 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25660 toggleSourceEdit : function(sourceEditMode)
25662 this.editorcore.toggleSourceEdit(sourceEditMode);
25664 if(this.editorcore.sourceEditMode){
25665 Roo.log('editor - showing textarea');
25668 // Roo.log(this.syncValue());
25670 this.inputEl().removeClass(['hide', 'x-hidden']);
25671 this.inputEl().dom.removeAttribute('tabIndex');
25672 this.inputEl().focus();
25674 Roo.log('editor - hiding textarea');
25676 // Roo.log(this.pushValue());
25679 this.inputEl().addClass(['hide', 'x-hidden']);
25680 this.inputEl().dom.setAttribute('tabIndex', -1);
25681 //this.deferFocus();
25684 if(this.resizable){
25685 this.setSize(this.wrap.getSize());
25688 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25691 // private (for BoxComponent)
25692 adjustSize : Roo.BoxComponent.prototype.adjustSize,
25694 // private (for BoxComponent)
25695 getResizeEl : function(){
25699 // private (for BoxComponent)
25700 getPositionEl : function(){
25705 initEvents : function(){
25706 this.originalValue = this.getValue();
25710 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25713 // markInvalid : Roo.emptyFn,
25715 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25718 // clearInvalid : Roo.emptyFn,
25720 setValue : function(v){
25721 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25722 this.editorcore.pushValue();
25727 deferFocus : function(){
25728 this.focus.defer(10, this);
25732 focus : function(){
25733 this.editorcore.focus();
25739 onDestroy : function(){
25745 for (var i =0; i < this.toolbars.length;i++) {
25746 // fixme - ask toolbars for heights?
25747 this.toolbars[i].onDestroy();
25750 this.wrap.dom.innerHTML = '';
25751 this.wrap.remove();
25756 onFirstFocus : function(){
25757 //Roo.log("onFirstFocus");
25758 this.editorcore.onFirstFocus();
25759 for (var i =0; i < this.toolbars.length;i++) {
25760 this.toolbars[i].onFirstFocus();
25766 syncValue : function()
25768 this.editorcore.syncValue();
25771 pushValue : function()
25773 this.editorcore.pushValue();
25777 // hide stuff that is not compatible
25791 * @event specialkey
25795 * @cfg {String} fieldClass @hide
25798 * @cfg {String} focusClass @hide
25801 * @cfg {String} autoCreate @hide
25804 * @cfg {String} inputType @hide
25808 * @cfg {String} invalidText @hide
25811 * @cfg {String} msgFx @hide
25814 * @cfg {String} validateOnBlur @hide
25823 Roo.namespace('Roo.bootstrap.htmleditor');
25825 * @class Roo.bootstrap.HtmlEditorToolbar1
25831 new Roo.bootstrap.HtmlEditor({
25834 new Roo.bootstrap.HtmlEditorToolbar1({
25835 disable : { fonts: 1 , format: 1, ..., ... , ...],
25841 * @cfg {Object} disable List of elements to disable..
25842 * @cfg {Array} btns List of additional buttons.
25846 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25849 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25852 Roo.apply(this, config);
25854 // default disabled, based on 'good practice'..
25855 this.disable = this.disable || {};
25856 Roo.applyIf(this.disable, {
25859 specialElements : true
25861 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25863 this.editor = config.editor;
25864 this.editorcore = config.editor.editorcore;
25866 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25868 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25869 // dont call parent... till later.
25871 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
25876 editorcore : false,
25881 "h1","h2","h3","h4","h5","h6",
25883 "abbr", "acronym", "address", "cite", "samp", "var",
25887 onRender : function(ct, position)
25889 // Roo.log("Call onRender: " + this.xtype);
25891 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25893 this.el.dom.style.marginBottom = '0';
25895 var editorcore = this.editorcore;
25896 var editor= this.editor;
25899 var btn = function(id,cmd , toggle, handler, html){
25901 var event = toggle ? 'toggle' : 'click';
25906 xns: Roo.bootstrap,
25910 enableToggle:toggle !== false,
25912 pressed : toggle ? false : null,
25915 a.listeners[toggle ? 'toggle' : 'click'] = function() {
25916 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
25922 // var cb_box = function...
25927 xns: Roo.bootstrap,
25932 xns: Roo.bootstrap,
25936 Roo.each(this.formats, function(f) {
25937 style.menu.items.push({
25939 xns: Roo.bootstrap,
25940 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25945 editorcore.insertTag(this.tagname);
25952 children.push(style);
25954 btn('bold',false,true);
25955 btn('italic',false,true);
25956 btn('align-left', 'justifyleft',true);
25957 btn('align-center', 'justifycenter',true);
25958 btn('align-right' , 'justifyright',true);
25959 btn('link', false, false, function(btn) {
25960 //Roo.log("create link?");
25961 var url = prompt(this.createLinkText, this.defaultLinkValue);
25962 if(url && url != 'http:/'+'/'){
25963 this.editorcore.relayCmd('createlink', url);
25966 btn('list','insertunorderedlist',true);
25967 btn('pencil', false,true, function(btn){
25969 this.toggleSourceEdit(btn.pressed);
25972 if (this.editor.btns.length > 0) {
25973 for (var i = 0; i<this.editor.btns.length; i++) {
25974 children.push(this.editor.btns[i]);
25982 xns: Roo.bootstrap,
25987 xns: Roo.bootstrap,
25992 cog.menu.items.push({
25994 xns: Roo.bootstrap,
25995 html : Clean styles,
26000 editorcore.insertTag(this.tagname);
26009 this.xtype = 'NavSimplebar';
26011 for(var i=0;i< children.length;i++) {
26013 this.buttons.add(this.addxtypeChild(children[i]));
26017 editor.on('editorevent', this.updateToolbar, this);
26019 onBtnClick : function(id)
26021 this.editorcore.relayCmd(id);
26022 this.editorcore.focus();
26026 * Protected method that will not generally be called directly. It triggers
26027 * a toolbar update by reading the markup state of the current selection in the editor.
26029 updateToolbar: function(){
26031 if(!this.editorcore.activated){
26032 this.editor.onFirstFocus(); // is this neeed?
26036 var btns = this.buttons;
26037 var doc = this.editorcore.doc;
26038 btns.get('bold').setActive(doc.queryCommandState('bold'));
26039 btns.get('italic').setActive(doc.queryCommandState('italic'));
26040 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26042 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26043 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26044 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26046 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26047 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26050 var ans = this.editorcore.getAllAncestors();
26051 if (this.formatCombo) {
26054 var store = this.formatCombo.store;
26055 this.formatCombo.setValue("");
26056 for (var i =0; i < ans.length;i++) {
26057 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26059 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26067 // hides menus... - so this cant be on a menu...
26068 Roo.bootstrap.MenuMgr.hideAll();
26070 Roo.bootstrap.MenuMgr.hideAll();
26071 //this.editorsyncValue();
26073 onFirstFocus: function() {
26074 this.buttons.each(function(item){
26078 toggleSourceEdit : function(sourceEditMode){
26081 if(sourceEditMode){
26082 Roo.log("disabling buttons");
26083 this.buttons.each( function(item){
26084 if(item.cmd != 'pencil'){
26090 Roo.log("enabling buttons");
26091 if(this.editorcore.initialized){
26092 this.buttons.each( function(item){
26098 Roo.log("calling toggole on editor");
26099 // tell the editor that it's been pressed..
26100 this.editor.toggleSourceEdit(sourceEditMode);
26114 * @class Roo.bootstrap.Markdown
26115 * @extends Roo.bootstrap.TextArea
26116 * Bootstrap Showdown editable area
26117 * @cfg {string} content
26120 * Create a new Showdown
26123 Roo.bootstrap.Markdown = function(config){
26124 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26128 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26132 initEvents : function()
26135 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26136 this.markdownEl = this.el.createChild({
26137 cls : 'roo-markdown-area'
26139 this.inputEl().addClass('d-none');
26140 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26141 this.markdownEl.on('click', this.toggleTextEdit, this);
26142 this.on('blur', this.toggleTextEdit, this);
26143 this.on('specialkey', this.resizeTextArea, this);
26146 toggleTextEdit : function()
26148 var sh = this.markdownEl.getHeight();
26149 this.inputEl().addClass('d-none');
26150 this.markdownEl.addClass('d-none');
26151 if (!this.editing) {
26153 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26154 this.inputEl().removeClass('d-none');
26155 this.inputEl().focus();
26156 this.editing = true;
26159 // show showdown...
26160 this.updateMarkdown();
26161 this.markdownEl.removeClass('d-none');
26162 this.editing = false;
26165 updateMarkdown : function()
26167 if (this.getValue() == '') {
26168 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder);
26171 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26174 resizeTextArea: function () {
26177 Roo.log([sh, this.getValue().split("\n").length * 30]);
26178 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26180 setValue : function(val)
26182 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26183 if (!this.editing) {
26184 this.updateMarkdown();
26190 if (!this.editing) {
26191 this.toggleTextEdit();
26199 * @class Roo.bootstrap.Table.AbstractSelectionModel
26200 * @extends Roo.util.Observable
26201 * Abstract base class for grid SelectionModels. It provides the interface that should be
26202 * implemented by descendant classes. This class should not be directly instantiated.
26205 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26206 this.locked = false;
26207 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26211 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26212 /** @ignore Called by the grid automatically. Do not call directly. */
26213 init : function(grid){
26219 * Locks the selections.
26222 this.locked = true;
26226 * Unlocks the selections.
26228 unlock : function(){
26229 this.locked = false;
26233 * Returns true if the selections are locked.
26234 * @return {Boolean}
26236 isLocked : function(){
26237 return this.locked;
26241 initEvents : function ()
26247 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26248 * @class Roo.bootstrap.Table.RowSelectionModel
26249 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26250 * It supports multiple selections and keyboard selection/navigation.
26252 * @param {Object} config
26255 Roo.bootstrap.Table.RowSelectionModel = function(config){
26256 Roo.apply(this, config);
26257 this.selections = new Roo.util.MixedCollection(false, function(o){
26262 this.lastActive = false;
26266 * @event selectionchange
26267 * Fires when the selection changes
26268 * @param {SelectionModel} this
26270 "selectionchange" : true,
26272 * @event afterselectionchange
26273 * Fires after the selection changes (eg. by key press or clicking)
26274 * @param {SelectionModel} this
26276 "afterselectionchange" : true,
26278 * @event beforerowselect
26279 * Fires when a row is selected being selected, return false to cancel.
26280 * @param {SelectionModel} this
26281 * @param {Number} rowIndex The selected index
26282 * @param {Boolean} keepExisting False if other selections will be cleared
26284 "beforerowselect" : true,
26287 * Fires when a row is selected.
26288 * @param {SelectionModel} this
26289 * @param {Number} rowIndex The selected index
26290 * @param {Roo.data.Record} r The record
26292 "rowselect" : true,
26294 * @event rowdeselect
26295 * Fires when a row is deselected.
26296 * @param {SelectionModel} this
26297 * @param {Number} rowIndex The selected index
26299 "rowdeselect" : true
26301 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26302 this.locked = false;
26305 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26307 * @cfg {Boolean} singleSelect
26308 * True to allow selection of only one row at a time (defaults to false)
26310 singleSelect : false,
26313 initEvents : function()
26316 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26317 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26318 //}else{ // allow click to work like normal
26319 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26321 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26322 this.grid.on("rowclick", this.handleMouseDown, this);
26324 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26325 "up" : function(e){
26327 this.selectPrevious(e.shiftKey);
26328 }else if(this.last !== false && this.lastActive !== false){
26329 var last = this.last;
26330 this.selectRange(this.last, this.lastActive-1);
26331 this.grid.getView().focusRow(this.lastActive);
26332 if(last !== false){
26336 this.selectFirstRow();
26338 this.fireEvent("afterselectionchange", this);
26340 "down" : function(e){
26342 this.selectNext(e.shiftKey);
26343 }else if(this.last !== false && this.lastActive !== false){
26344 var last = this.last;
26345 this.selectRange(this.last, this.lastActive+1);
26346 this.grid.getView().focusRow(this.lastActive);
26347 if(last !== false){
26351 this.selectFirstRow();
26353 this.fireEvent("afterselectionchange", this);
26357 this.grid.store.on('load', function(){
26358 this.selections.clear();
26361 var view = this.grid.view;
26362 view.on("refresh", this.onRefresh, this);
26363 view.on("rowupdated", this.onRowUpdated, this);
26364 view.on("rowremoved", this.onRemove, this);
26369 onRefresh : function()
26371 var ds = this.grid.store, i, v = this.grid.view;
26372 var s = this.selections;
26373 s.each(function(r){
26374 if((i = ds.indexOfId(r.id)) != -1){
26383 onRemove : function(v, index, r){
26384 this.selections.remove(r);
26388 onRowUpdated : function(v, index, r){
26389 if(this.isSelected(r)){
26390 v.onRowSelect(index);
26396 * @param {Array} records The records to select
26397 * @param {Boolean} keepExisting (optional) True to keep existing selections
26399 selectRecords : function(records, keepExisting)
26402 this.clearSelections();
26404 var ds = this.grid.store;
26405 for(var i = 0, len = records.length; i < len; i++){
26406 this.selectRow(ds.indexOf(records[i]), true);
26411 * Gets the number of selected rows.
26414 getCount : function(){
26415 return this.selections.length;
26419 * Selects the first row in the grid.
26421 selectFirstRow : function(){
26426 * Select the last row.
26427 * @param {Boolean} keepExisting (optional) True to keep existing selections
26429 selectLastRow : function(keepExisting){
26430 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26431 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26435 * Selects the row immediately following the last selected row.
26436 * @param {Boolean} keepExisting (optional) True to keep existing selections
26438 selectNext : function(keepExisting)
26440 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26441 this.selectRow(this.last+1, keepExisting);
26442 this.grid.getView().focusRow(this.last);
26447 * Selects the row that precedes the last selected row.
26448 * @param {Boolean} keepExisting (optional) True to keep existing selections
26450 selectPrevious : function(keepExisting){
26452 this.selectRow(this.last-1, keepExisting);
26453 this.grid.getView().focusRow(this.last);
26458 * Returns the selected records
26459 * @return {Array} Array of selected records
26461 getSelections : function(){
26462 return [].concat(this.selections.items);
26466 * Returns the first selected record.
26469 getSelected : function(){
26470 return this.selections.itemAt(0);
26475 * Clears all selections.
26477 clearSelections : function(fast)
26483 var ds = this.grid.store;
26484 var s = this.selections;
26485 s.each(function(r){
26486 this.deselectRow(ds.indexOfId(r.id));
26490 this.selections.clear();
26497 * Selects all rows.
26499 selectAll : function(){
26503 this.selections.clear();
26504 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26505 this.selectRow(i, true);
26510 * Returns True if there is a selection.
26511 * @return {Boolean}
26513 hasSelection : function(){
26514 return this.selections.length > 0;
26518 * Returns True if the specified row is selected.
26519 * @param {Number/Record} record The record or index of the record to check
26520 * @return {Boolean}
26522 isSelected : function(index){
26523 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26524 return (r && this.selections.key(r.id) ? true : false);
26528 * Returns True if the specified record id is selected.
26529 * @param {String} id The id of record to check
26530 * @return {Boolean}
26532 isIdSelected : function(id){
26533 return (this.selections.key(id) ? true : false);
26538 handleMouseDBClick : function(e, t){
26542 handleMouseDown : function(e, t)
26544 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26545 if(this.isLocked() || rowIndex < 0 ){
26548 if(e.shiftKey && this.last !== false){
26549 var last = this.last;
26550 this.selectRange(last, rowIndex, e.ctrlKey);
26551 this.last = last; // reset the last
26555 var isSelected = this.isSelected(rowIndex);
26556 //Roo.log("select row:" + rowIndex);
26558 this.deselectRow(rowIndex);
26560 this.selectRow(rowIndex, true);
26564 if(e.button !== 0 && isSelected){
26565 alert('rowIndex 2: ' + rowIndex);
26566 view.focusRow(rowIndex);
26567 }else if(e.ctrlKey && isSelected){
26568 this.deselectRow(rowIndex);
26569 }else if(!isSelected){
26570 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26571 view.focusRow(rowIndex);
26575 this.fireEvent("afterselectionchange", this);
26578 handleDragableRowClick : function(grid, rowIndex, e)
26580 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26581 this.selectRow(rowIndex, false);
26582 grid.view.focusRow(rowIndex);
26583 this.fireEvent("afterselectionchange", this);
26588 * Selects multiple rows.
26589 * @param {Array} rows Array of the indexes of the row to select
26590 * @param {Boolean} keepExisting (optional) True to keep existing selections
26592 selectRows : function(rows, keepExisting){
26594 this.clearSelections();
26596 for(var i = 0, len = rows.length; i < len; i++){
26597 this.selectRow(rows[i], true);
26602 * Selects a range of rows. All rows in between startRow and endRow are also selected.
26603 * @param {Number} startRow The index of the first row in the range
26604 * @param {Number} endRow The index of the last row in the range
26605 * @param {Boolean} keepExisting (optional) True to retain existing selections
26607 selectRange : function(startRow, endRow, keepExisting){
26612 this.clearSelections();
26614 if(startRow <= endRow){
26615 for(var i = startRow; i <= endRow; i++){
26616 this.selectRow(i, true);
26619 for(var i = startRow; i >= endRow; i--){
26620 this.selectRow(i, true);
26626 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26627 * @param {Number} startRow The index of the first row in the range
26628 * @param {Number} endRow The index of the last row in the range
26630 deselectRange : function(startRow, endRow, preventViewNotify){
26634 for(var i = startRow; i <= endRow; i++){
26635 this.deselectRow(i, preventViewNotify);
26641 * @param {Number} row The index of the row to select
26642 * @param {Boolean} keepExisting (optional) True to keep existing selections
26644 selectRow : function(index, keepExisting, preventViewNotify)
26646 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26649 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26650 if(!keepExisting || this.singleSelect){
26651 this.clearSelections();
26654 var r = this.grid.store.getAt(index);
26655 //console.log('selectRow - record id :' + r.id);
26657 this.selections.add(r);
26658 this.last = this.lastActive = index;
26659 if(!preventViewNotify){
26660 var proxy = new Roo.Element(
26661 this.grid.getRowDom(index)
26663 proxy.addClass('bg-info info');
26665 this.fireEvent("rowselect", this, index, r);
26666 this.fireEvent("selectionchange", this);
26672 * @param {Number} row The index of the row to deselect
26674 deselectRow : function(index, preventViewNotify)
26679 if(this.last == index){
26682 if(this.lastActive == index){
26683 this.lastActive = false;
26686 var r = this.grid.store.getAt(index);
26691 this.selections.remove(r);
26692 //.console.log('deselectRow - record id :' + r.id);
26693 if(!preventViewNotify){
26695 var proxy = new Roo.Element(
26696 this.grid.getRowDom(index)
26698 proxy.removeClass('bg-info info');
26700 this.fireEvent("rowdeselect", this, index);
26701 this.fireEvent("selectionchange", this);
26705 restoreLast : function(){
26707 this.last = this._last;
26712 acceptsNav : function(row, col, cm){
26713 return !cm.isHidden(col) && cm.isCellEditable(col, row);
26717 onEditorKey : function(field, e){
26718 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26723 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26725 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26727 }else if(k == e.ENTER && !e.ctrlKey){
26731 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26733 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26735 }else if(k == e.ESC){
26739 g.startEditing(newCell[0], newCell[1]);
26745 * Ext JS Library 1.1.1
26746 * Copyright(c) 2006-2007, Ext JS, LLC.
26748 * Originally Released Under LGPL - original licence link has changed is not relivant.
26751 * <script type="text/javascript">
26755 * @class Roo.bootstrap.PagingToolbar
26756 * @extends Roo.bootstrap.NavSimplebar
26757 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26759 * Create a new PagingToolbar
26760 * @param {Object} config The config object
26761 * @param {Roo.data.Store} store
26763 Roo.bootstrap.PagingToolbar = function(config)
26765 // old args format still supported... - xtype is prefered..
26766 // created from xtype...
26768 this.ds = config.dataSource;
26770 if (config.store && !this.ds) {
26771 this.store= Roo.factory(config.store, Roo.data);
26772 this.ds = this.store;
26773 this.ds.xmodule = this.xmodule || false;
26776 this.toolbarItems = [];
26777 if (config.items) {
26778 this.toolbarItems = config.items;
26781 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26786 this.bind(this.ds);
26789 if (Roo.bootstrap.version == 4) {
26790 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26792 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26797 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26799 * @cfg {Roo.data.Store} dataSource
26800 * The underlying data store providing the paged data
26803 * @cfg {String/HTMLElement/Element} container
26804 * container The id or element that will contain the toolbar
26807 * @cfg {Boolean} displayInfo
26808 * True to display the displayMsg (defaults to false)
26811 * @cfg {Number} pageSize
26812 * The number of records to display per page (defaults to 20)
26816 * @cfg {String} displayMsg
26817 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26819 displayMsg : 'Displaying {0} - {1} of {2}',
26821 * @cfg {String} emptyMsg
26822 * The message to display when no records are found (defaults to "No data to display")
26824 emptyMsg : 'No data to display',
26826 * Customizable piece of the default paging text (defaults to "Page")
26829 beforePageText : "Page",
26831 * Customizable piece of the default paging text (defaults to "of %0")
26834 afterPageText : "of {0}",
26836 * Customizable piece of the default paging text (defaults to "First Page")
26839 firstText : "First Page",
26841 * Customizable piece of the default paging text (defaults to "Previous Page")
26844 prevText : "Previous Page",
26846 * Customizable piece of the default paging text (defaults to "Next Page")
26849 nextText : "Next Page",
26851 * Customizable piece of the default paging text (defaults to "Last Page")
26854 lastText : "Last Page",
26856 * Customizable piece of the default paging text (defaults to "Refresh")
26859 refreshText : "Refresh",
26863 onRender : function(ct, position)
26865 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26866 this.navgroup.parentId = this.id;
26867 this.navgroup.onRender(this.el, null);
26868 // add the buttons to the navgroup
26870 if(this.displayInfo){
26871 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26872 this.displayEl = this.el.select('.x-paging-info', true).first();
26873 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26874 // this.displayEl = navel.el.select('span',true).first();
26880 Roo.each(_this.buttons, function(e){ // this might need to use render????
26881 Roo.factory(e).render(_this.el);
26885 Roo.each(_this.toolbarItems, function(e) {
26886 _this.navgroup.addItem(e);
26890 this.first = this.navgroup.addItem({
26891 tooltip: this.firstText,
26892 cls: "prev btn-outline-secondary",
26893 html : ' <i class="fa fa-step-backward"></i>',
26895 preventDefault: true,
26896 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26899 this.prev = this.navgroup.addItem({
26900 tooltip: this.prevText,
26901 cls: "prev btn-outline-secondary",
26902 html : ' <i class="fa fa-backward"></i>',
26904 preventDefault: true,
26905 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
26907 //this.addSeparator();
26910 var field = this.navgroup.addItem( {
26912 cls : 'x-paging-position btn-outline-secondary',
26914 html : this.beforePageText +
26915 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26916 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
26919 this.field = field.el.select('input', true).first();
26920 this.field.on("keydown", this.onPagingKeydown, this);
26921 this.field.on("focus", function(){this.dom.select();});
26924 this.afterTextEl = field.el.select('.x-paging-after',true).first();
26925 //this.field.setHeight(18);
26926 //this.addSeparator();
26927 this.next = this.navgroup.addItem({
26928 tooltip: this.nextText,
26929 cls: "next btn-outline-secondary",
26930 html : ' <i class="fa fa-forward"></i>',
26932 preventDefault: true,
26933 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
26935 this.last = this.navgroup.addItem({
26936 tooltip: this.lastText,
26937 html : ' <i class="fa fa-step-forward"></i>',
26938 cls: "next btn-outline-secondary",
26940 preventDefault: true,
26941 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
26943 //this.addSeparator();
26944 this.loading = this.navgroup.addItem({
26945 tooltip: this.refreshText,
26946 cls: "btn-outline-secondary",
26947 html : ' <i class="fa fa-refresh"></i>',
26948 preventDefault: true,
26949 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26955 updateInfo : function(){
26956 if(this.displayEl){
26957 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26958 var msg = count == 0 ?
26962 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
26964 this.displayEl.update(msg);
26969 onLoad : function(ds, r, o)
26971 this.cursor = o.params.start ? o.params.start : 0;
26973 var d = this.getPageData(),
26978 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26979 this.field.dom.value = ap;
26980 this.first.setDisabled(ap == 1);
26981 this.prev.setDisabled(ap == 1);
26982 this.next.setDisabled(ap == ps);
26983 this.last.setDisabled(ap == ps);
26984 this.loading.enable();
26989 getPageData : function(){
26990 var total = this.ds.getTotalCount();
26993 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26994 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26999 onLoadError : function(){
27000 this.loading.enable();
27004 onPagingKeydown : function(e){
27005 var k = e.getKey();
27006 var d = this.getPageData();
27008 var v = this.field.dom.value, pageNum;
27009 if(!v || isNaN(pageNum = parseInt(v, 10))){
27010 this.field.dom.value = d.activePage;
27013 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27014 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27017 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))
27019 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27020 this.field.dom.value = pageNum;
27021 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27024 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27026 var v = this.field.dom.value, pageNum;
27027 var increment = (e.shiftKey) ? 10 : 1;
27028 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27031 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27032 this.field.dom.value = d.activePage;
27035 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27037 this.field.dom.value = parseInt(v, 10) + increment;
27038 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27039 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27046 beforeLoad : function(){
27048 this.loading.disable();
27053 onClick : function(which){
27062 ds.load({params:{start: 0, limit: this.pageSize}});
27065 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27068 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27071 var total = ds.getTotalCount();
27072 var extra = total % this.pageSize;
27073 var lastStart = extra ? (total - extra) : total-this.pageSize;
27074 ds.load({params:{start: lastStart, limit: this.pageSize}});
27077 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27083 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27084 * @param {Roo.data.Store} store The data store to unbind
27086 unbind : function(ds){
27087 ds.un("beforeload", this.beforeLoad, this);
27088 ds.un("load", this.onLoad, this);
27089 ds.un("loadexception", this.onLoadError, this);
27090 ds.un("remove", this.updateInfo, this);
27091 ds.un("add", this.updateInfo, this);
27092 this.ds = undefined;
27096 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27097 * @param {Roo.data.Store} store The data store to bind
27099 bind : function(ds){
27100 ds.on("beforeload", this.beforeLoad, this);
27101 ds.on("load", this.onLoad, this);
27102 ds.on("loadexception", this.onLoadError, this);
27103 ds.on("remove", this.updateInfo, this);
27104 ds.on("add", this.updateInfo, this);
27115 * @class Roo.bootstrap.MessageBar
27116 * @extends Roo.bootstrap.Component
27117 * Bootstrap MessageBar class
27118 * @cfg {String} html contents of the MessageBar
27119 * @cfg {String} weight (info | success | warning | danger) default info
27120 * @cfg {String} beforeClass insert the bar before the given class
27121 * @cfg {Boolean} closable (true | false) default false
27122 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27125 * Create a new Element
27126 * @param {Object} config The config object
27129 Roo.bootstrap.MessageBar = function(config){
27130 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27133 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27139 beforeClass: 'bootstrap-sticky-wrap',
27141 getAutoCreate : function(){
27145 cls: 'alert alert-dismissable alert-' + this.weight,
27150 html: this.html || ''
27156 cfg.cls += ' alert-messages-fixed';
27170 onRender : function(ct, position)
27172 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27175 var cfg = Roo.apply({}, this.getAutoCreate());
27179 cfg.cls += ' ' + this.cls;
27182 cfg.style = this.style;
27184 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27186 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27189 this.el.select('>button.close').on('click', this.hide, this);
27195 if (!this.rendered) {
27201 this.fireEvent('show', this);
27207 if (!this.rendered) {
27213 this.fireEvent('hide', this);
27216 update : function()
27218 // var e = this.el.dom.firstChild;
27220 // if(this.closable){
27221 // e = e.nextSibling;
27224 // e.data = this.html || '';
27226 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27242 * @class Roo.bootstrap.Graph
27243 * @extends Roo.bootstrap.Component
27244 * Bootstrap Graph class
27248 @cfg {String} graphtype bar | vbar | pie
27249 @cfg {number} g_x coodinator | centre x (pie)
27250 @cfg {number} g_y coodinator | centre y (pie)
27251 @cfg {number} g_r radius (pie)
27252 @cfg {number} g_height height of the chart (respected by all elements in the set)
27253 @cfg {number} g_width width of the chart (respected by all elements in the set)
27254 @cfg {Object} title The title of the chart
27257 -opts (object) options for the chart
27259 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27260 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27262 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.
27263 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27265 o stretch (boolean)
27267 -opts (object) options for the pie
27270 o startAngle (number)
27271 o endAngle (number)
27275 * Create a new Input
27276 * @param {Object} config The config object
27279 Roo.bootstrap.Graph = function(config){
27280 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27286 * The img click event for the img.
27287 * @param {Roo.EventObject} e
27293 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27304 //g_colors: this.colors,
27311 getAutoCreate : function(){
27322 onRender : function(ct,position){
27325 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27327 if (typeof(Raphael) == 'undefined') {
27328 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27332 this.raphael = Raphael(this.el.dom);
27334 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27335 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27336 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27337 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27339 r.text(160, 10, "Single Series Chart").attr(txtattr);
27340 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27341 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27342 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27344 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27345 r.barchart(330, 10, 300, 220, data1);
27346 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27347 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27350 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27351 // r.barchart(30, 30, 560, 250, xdata, {
27352 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27353 // axis : "0 0 1 1",
27354 // axisxlabels : xdata
27355 // //yvalues : cols,
27358 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27360 // this.load(null,xdata,{
27361 // axis : "0 0 1 1",
27362 // axisxlabels : xdata
27367 load : function(graphtype,xdata,opts)
27369 this.raphael.clear();
27371 graphtype = this.graphtype;
27376 var r = this.raphael,
27377 fin = function () {
27378 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27380 fout = function () {
27381 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27383 pfin = function() {
27384 this.sector.stop();
27385 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27388 this.label[0].stop();
27389 this.label[0].attr({ r: 7.5 });
27390 this.label[1].attr({ "font-weight": 800 });
27393 pfout = function() {
27394 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27397 this.label[0].animate({ r: 5 }, 500, "bounce");
27398 this.label[1].attr({ "font-weight": 400 });
27404 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27407 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27410 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27411 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27413 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27420 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27425 setTitle: function(o)
27430 initEvents: function() {
27433 this.el.on('click', this.onClick, this);
27437 onClick : function(e)
27439 Roo.log('img onclick');
27440 this.fireEvent('click', this, e);
27452 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27455 * @class Roo.bootstrap.dash.NumberBox
27456 * @extends Roo.bootstrap.Component
27457 * Bootstrap NumberBox class
27458 * @cfg {String} headline Box headline
27459 * @cfg {String} content Box content
27460 * @cfg {String} icon Box icon
27461 * @cfg {String} footer Footer text
27462 * @cfg {String} fhref Footer href
27465 * Create a new NumberBox
27466 * @param {Object} config The config object
27470 Roo.bootstrap.dash.NumberBox = function(config){
27471 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27475 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27484 getAutoCreate : function(){
27488 cls : 'small-box ',
27496 cls : 'roo-headline',
27497 html : this.headline
27501 cls : 'roo-content',
27502 html : this.content
27516 cls : 'ion ' + this.icon
27525 cls : 'small-box-footer',
27526 href : this.fhref || '#',
27530 cfg.cn.push(footer);
27537 onRender : function(ct,position){
27538 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27545 setHeadline: function (value)
27547 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27550 setFooter: function (value, href)
27552 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27555 this.el.select('a.small-box-footer',true).first().attr('href', href);
27560 setContent: function (value)
27562 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27565 initEvents: function()
27579 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27582 * @class Roo.bootstrap.dash.TabBox
27583 * @extends Roo.bootstrap.Component
27584 * Bootstrap TabBox class
27585 * @cfg {String} title Title of the TabBox
27586 * @cfg {String} icon Icon of the TabBox
27587 * @cfg {Boolean} showtabs (true|false) show the tabs default true
27588 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27591 * Create a new TabBox
27592 * @param {Object} config The config object
27596 Roo.bootstrap.dash.TabBox = function(config){
27597 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27602 * When a pane is added
27603 * @param {Roo.bootstrap.dash.TabPane} pane
27607 * @event activatepane
27608 * When a pane is activated
27609 * @param {Roo.bootstrap.dash.TabPane} pane
27611 "activatepane" : true
27619 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
27624 tabScrollable : false,
27626 getChildContainer : function()
27628 return this.el.select('.tab-content', true).first();
27631 getAutoCreate : function(){
27635 cls: 'pull-left header',
27643 cls: 'fa ' + this.icon
27649 cls: 'nav nav-tabs pull-right',
27655 if(this.tabScrollable){
27662 cls: 'nav nav-tabs pull-right',
27673 cls: 'nav-tabs-custom',
27678 cls: 'tab-content no-padding',
27686 initEvents : function()
27688 //Roo.log('add add pane handler');
27689 this.on('addpane', this.onAddPane, this);
27692 * Updates the box title
27693 * @param {String} html to set the title to.
27695 setTitle : function(value)
27697 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27699 onAddPane : function(pane)
27701 this.panes.push(pane);
27702 //Roo.log('addpane');
27704 // tabs are rendere left to right..
27705 if(!this.showtabs){
27709 var ctr = this.el.select('.nav-tabs', true).first();
27712 var existing = ctr.select('.nav-tab',true);
27713 var qty = existing.getCount();;
27716 var tab = ctr.createChild({
27718 cls : 'nav-tab' + (qty ? '' : ' active'),
27726 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27729 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27731 pane.el.addClass('active');
27736 onTabClick : function(ev,un,ob,pane)
27738 //Roo.log('tab - prev default');
27739 ev.preventDefault();
27742 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27743 pane.tab.addClass('active');
27744 //Roo.log(pane.title);
27745 this.getChildContainer().select('.tab-pane',true).removeClass('active');
27746 // technically we should have a deactivate event.. but maybe add later.
27747 // and it should not de-activate the selected tab...
27748 this.fireEvent('activatepane', pane);
27749 pane.el.addClass('active');
27750 pane.fireEvent('activate');
27755 getActivePane : function()
27758 Roo.each(this.panes, function(p) {
27759 if(p.el.hasClass('active')){
27780 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27782 * @class Roo.bootstrap.TabPane
27783 * @extends Roo.bootstrap.Component
27784 * Bootstrap TabPane class
27785 * @cfg {Boolean} active (false | true) Default false
27786 * @cfg {String} title title of panel
27790 * Create a new TabPane
27791 * @param {Object} config The config object
27794 Roo.bootstrap.dash.TabPane = function(config){
27795 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27801 * When a pane is activated
27802 * @param {Roo.bootstrap.dash.TabPane} pane
27809 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
27814 // the tabBox that this is attached to.
27817 getAutoCreate : function()
27825 cfg.cls += ' active';
27830 initEvents : function()
27832 //Roo.log('trigger add pane handler');
27833 this.parent().fireEvent('addpane', this)
27837 * Updates the tab title
27838 * @param {String} html to set the title to.
27840 setTitle: function(str)
27846 this.tab.select('a', true).first().dom.innerHTML = str;
27863 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27866 * @class Roo.bootstrap.menu.Menu
27867 * @extends Roo.bootstrap.Component
27868 * Bootstrap Menu class - container for Menu
27869 * @cfg {String} html Text of the menu
27870 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27871 * @cfg {String} icon Font awesome icon
27872 * @cfg {String} pos Menu align to (top | bottom) default bottom
27876 * Create a new Menu
27877 * @param {Object} config The config object
27881 Roo.bootstrap.menu.Menu = function(config){
27882 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27886 * @event beforeshow
27887 * Fires before this menu is displayed
27888 * @param {Roo.bootstrap.menu.Menu} this
27892 * @event beforehide
27893 * Fires before this menu is hidden
27894 * @param {Roo.bootstrap.menu.Menu} this
27899 * Fires after this menu is displayed
27900 * @param {Roo.bootstrap.menu.Menu} this
27905 * Fires after this menu is hidden
27906 * @param {Roo.bootstrap.menu.Menu} this
27911 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27912 * @param {Roo.bootstrap.menu.Menu} this
27913 * @param {Roo.EventObject} e
27920 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
27924 weight : 'default',
27929 getChildContainer : function() {
27930 if(this.isSubMenu){
27934 return this.el.select('ul.dropdown-menu', true).first();
27937 getAutoCreate : function()
27942 cls : 'roo-menu-text',
27950 cls : 'fa ' + this.icon
27961 cls : 'dropdown-button btn btn-' + this.weight,
27966 cls : 'dropdown-toggle btn btn-' + this.weight,
27976 cls : 'dropdown-menu'
27982 if(this.pos == 'top'){
27983 cfg.cls += ' dropup';
27986 if(this.isSubMenu){
27989 cls : 'dropdown-menu'
27996 onRender : function(ct, position)
27998 this.isSubMenu = ct.hasClass('dropdown-submenu');
28000 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28003 initEvents : function()
28005 if(this.isSubMenu){
28009 this.hidden = true;
28011 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28012 this.triggerEl.on('click', this.onTriggerPress, this);
28014 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28015 this.buttonEl.on('click', this.onClick, this);
28021 if(this.isSubMenu){
28025 return this.el.select('ul.dropdown-menu', true).first();
28028 onClick : function(e)
28030 this.fireEvent("click", this, e);
28033 onTriggerPress : function(e)
28035 if (this.isVisible()) {
28042 isVisible : function(){
28043 return !this.hidden;
28048 this.fireEvent("beforeshow", this);
28050 this.hidden = false;
28051 this.el.addClass('open');
28053 Roo.get(document).on("mouseup", this.onMouseUp, this);
28055 this.fireEvent("show", this);
28062 this.fireEvent("beforehide", this);
28064 this.hidden = true;
28065 this.el.removeClass('open');
28067 Roo.get(document).un("mouseup", this.onMouseUp);
28069 this.fireEvent("hide", this);
28072 onMouseUp : function()
28086 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28089 * @class Roo.bootstrap.menu.Item
28090 * @extends Roo.bootstrap.Component
28091 * Bootstrap MenuItem class
28092 * @cfg {Boolean} submenu (true | false) default false
28093 * @cfg {String} html text of the item
28094 * @cfg {String} href the link
28095 * @cfg {Boolean} disable (true | false) default false
28096 * @cfg {Boolean} preventDefault (true | false) default true
28097 * @cfg {String} icon Font awesome icon
28098 * @cfg {String} pos Submenu align to (left | right) default right
28102 * Create a new Item
28103 * @param {Object} config The config object
28107 Roo.bootstrap.menu.Item = function(config){
28108 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28112 * Fires when the mouse is hovering over this menu
28113 * @param {Roo.bootstrap.menu.Item} this
28114 * @param {Roo.EventObject} e
28119 * Fires when the mouse exits this menu
28120 * @param {Roo.bootstrap.menu.Item} this
28121 * @param {Roo.EventObject} e
28127 * The raw click event for the entire grid.
28128 * @param {Roo.EventObject} e
28134 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28139 preventDefault: true,
28144 getAutoCreate : function()
28149 cls : 'roo-menu-item-text',
28157 cls : 'fa ' + this.icon
28166 href : this.href || '#',
28173 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28177 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28179 if(this.pos == 'left'){
28180 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28187 initEvents : function()
28189 this.el.on('mouseover', this.onMouseOver, this);
28190 this.el.on('mouseout', this.onMouseOut, this);
28192 this.el.select('a', true).first().on('click', this.onClick, this);
28196 onClick : function(e)
28198 if(this.preventDefault){
28199 e.preventDefault();
28202 this.fireEvent("click", this, e);
28205 onMouseOver : function(e)
28207 if(this.submenu && this.pos == 'left'){
28208 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28211 this.fireEvent("mouseover", this, e);
28214 onMouseOut : function(e)
28216 this.fireEvent("mouseout", this, e);
28228 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28231 * @class Roo.bootstrap.menu.Separator
28232 * @extends Roo.bootstrap.Component
28233 * Bootstrap Separator class
28236 * Create a new Separator
28237 * @param {Object} config The config object
28241 Roo.bootstrap.menu.Separator = function(config){
28242 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28245 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28247 getAutoCreate : function(){
28268 * @class Roo.bootstrap.Tooltip
28269 * Bootstrap Tooltip class
28270 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28271 * to determine which dom element triggers the tooltip.
28273 * It needs to add support for additional attributes like tooltip-position
28276 * Create a new Toolti
28277 * @param {Object} config The config object
28280 Roo.bootstrap.Tooltip = function(config){
28281 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28283 this.alignment = Roo.bootstrap.Tooltip.alignment;
28285 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28286 this.alignment = config.alignment;
28291 Roo.apply(Roo.bootstrap.Tooltip, {
28293 * @function init initialize tooltip monitoring.
28297 currentTip : false,
28298 currentRegion : false,
28304 Roo.get(document).on('mouseover', this.enter ,this);
28305 Roo.get(document).on('mouseout', this.leave, this);
28308 this.currentTip = new Roo.bootstrap.Tooltip();
28311 enter : function(ev)
28313 var dom = ev.getTarget();
28315 //Roo.log(['enter',dom]);
28316 var el = Roo.fly(dom);
28317 if (this.currentEl) {
28319 //Roo.log(this.currentEl);
28320 //Roo.log(this.currentEl.contains(dom));
28321 if (this.currentEl == el) {
28324 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28330 if (this.currentTip.el) {
28331 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28335 if(!el || el.dom == document){
28341 // you can not look for children, as if el is the body.. then everythign is the child..
28342 if (!el.attr('tooltip')) { //
28343 if (!el.select("[tooltip]").elements.length) {
28346 // is the mouse over this child...?
28347 bindEl = el.select("[tooltip]").first();
28348 var xy = ev.getXY();
28349 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28350 //Roo.log("not in region.");
28353 //Roo.log("child element over..");
28356 this.currentEl = bindEl;
28357 this.currentTip.bind(bindEl);
28358 this.currentRegion = Roo.lib.Region.getRegion(dom);
28359 this.currentTip.enter();
28362 leave : function(ev)
28364 var dom = ev.getTarget();
28365 //Roo.log(['leave',dom]);
28366 if (!this.currentEl) {
28371 if (dom != this.currentEl.dom) {
28374 var xy = ev.getXY();
28375 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28378 // only activate leave if mouse cursor is outside... bounding box..
28383 if (this.currentTip) {
28384 this.currentTip.leave();
28386 //Roo.log('clear currentEl');
28387 this.currentEl = false;
28392 'left' : ['r-l', [-2,0], 'right'],
28393 'right' : ['l-r', [2,0], 'left'],
28394 'bottom' : ['t-b', [0,2], 'top'],
28395 'top' : [ 'b-t', [0,-2], 'bottom']
28401 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28406 delay : null, // can be { show : 300 , hide: 500}
28410 hoverState : null, //???
28412 placement : 'bottom',
28416 getAutoCreate : function(){
28423 cls : 'tooltip-arrow arrow'
28426 cls : 'tooltip-inner'
28433 bind : function(el)
28438 initEvents : function()
28440 this.arrowEl = this.el.select('.arrow', true).first();
28441 this.innerEl = this.el.select('.tooltip-inner', true).first();
28444 enter : function () {
28446 if (this.timeout != null) {
28447 clearTimeout(this.timeout);
28450 this.hoverState = 'in';
28451 //Roo.log("enter - show");
28452 if (!this.delay || !this.delay.show) {
28457 this.timeout = setTimeout(function () {
28458 if (_t.hoverState == 'in') {
28461 }, this.delay.show);
28465 clearTimeout(this.timeout);
28467 this.hoverState = 'out';
28468 if (!this.delay || !this.delay.hide) {
28474 this.timeout = setTimeout(function () {
28475 //Roo.log("leave - timeout");
28477 if (_t.hoverState == 'out') {
28479 Roo.bootstrap.Tooltip.currentEl = false;
28484 show : function (msg)
28487 this.render(document.body);
28490 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28492 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28494 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28496 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28497 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28499 var placement = typeof this.placement == 'function' ?
28500 this.placement.call(this, this.el, on_el) :
28503 var autoToken = /\s?auto?\s?/i;
28504 var autoPlace = autoToken.test(placement);
28506 placement = placement.replace(autoToken, '') || 'top';
28510 //this.el.setXY([0,0]);
28512 //this.el.dom.style.display='block';
28514 //this.el.appendTo(on_el);
28516 var p = this.getPosition();
28517 var box = this.el.getBox();
28523 var align = this.alignment[placement];
28525 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28527 if(placement == 'top' || placement == 'bottom'){
28529 placement = 'right';
28532 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28533 placement = 'left';
28536 var scroll = Roo.select('body', true).first().getScroll();
28538 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28542 align = this.alignment[placement];
28544 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28548 this.el.alignTo(this.bindEl, align[0],align[1]);
28549 //var arrow = this.el.select('.arrow',true).first();
28550 //arrow.set(align[2],
28552 this.el.addClass(placement);
28553 this.el.addClass("bs-tooltip-"+ placement);
28555 this.el.addClass('in fade show');
28557 this.hoverState = null;
28559 if (this.el.hasClass('fade')) {
28574 //this.el.setXY([0,0]);
28575 this.el.removeClass(['show', 'in']);
28591 * @class Roo.bootstrap.LocationPicker
28592 * @extends Roo.bootstrap.Component
28593 * Bootstrap LocationPicker class
28594 * @cfg {Number} latitude Position when init default 0
28595 * @cfg {Number} longitude Position when init default 0
28596 * @cfg {Number} zoom default 15
28597 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28598 * @cfg {Boolean} mapTypeControl default false
28599 * @cfg {Boolean} disableDoubleClickZoom default false
28600 * @cfg {Boolean} scrollwheel default true
28601 * @cfg {Boolean} streetViewControl default false
28602 * @cfg {Number} radius default 0
28603 * @cfg {String} locationName
28604 * @cfg {Boolean} draggable default true
28605 * @cfg {Boolean} enableAutocomplete default false
28606 * @cfg {Boolean} enableReverseGeocode default true
28607 * @cfg {String} markerTitle
28610 * Create a new LocationPicker
28611 * @param {Object} config The config object
28615 Roo.bootstrap.LocationPicker = function(config){
28617 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28622 * Fires when the picker initialized.
28623 * @param {Roo.bootstrap.LocationPicker} this
28624 * @param {Google Location} location
28628 * @event positionchanged
28629 * Fires when the picker position changed.
28630 * @param {Roo.bootstrap.LocationPicker} this
28631 * @param {Google Location} location
28633 positionchanged : true,
28636 * Fires when the map resize.
28637 * @param {Roo.bootstrap.LocationPicker} this
28642 * Fires when the map show.
28643 * @param {Roo.bootstrap.LocationPicker} this
28648 * Fires when the map hide.
28649 * @param {Roo.bootstrap.LocationPicker} this
28654 * Fires when click the map.
28655 * @param {Roo.bootstrap.LocationPicker} this
28656 * @param {Map event} e
28660 * @event mapRightClick
28661 * Fires when right click the map.
28662 * @param {Roo.bootstrap.LocationPicker} this
28663 * @param {Map event} e
28665 mapRightClick : true,
28667 * @event markerClick
28668 * Fires when click the marker.
28669 * @param {Roo.bootstrap.LocationPicker} this
28670 * @param {Map event} e
28672 markerClick : true,
28674 * @event markerRightClick
28675 * Fires when right click the marker.
28676 * @param {Roo.bootstrap.LocationPicker} this
28677 * @param {Map event} e
28679 markerRightClick : true,
28681 * @event OverlayViewDraw
28682 * Fires when OverlayView Draw
28683 * @param {Roo.bootstrap.LocationPicker} this
28685 OverlayViewDraw : true,
28687 * @event OverlayViewOnAdd
28688 * Fires when OverlayView Draw
28689 * @param {Roo.bootstrap.LocationPicker} this
28691 OverlayViewOnAdd : true,
28693 * @event OverlayViewOnRemove
28694 * Fires when OverlayView Draw
28695 * @param {Roo.bootstrap.LocationPicker} this
28697 OverlayViewOnRemove : true,
28699 * @event OverlayViewShow
28700 * Fires when OverlayView Draw
28701 * @param {Roo.bootstrap.LocationPicker} this
28702 * @param {Pixel} cpx
28704 OverlayViewShow : true,
28706 * @event OverlayViewHide
28707 * Fires when OverlayView Draw
28708 * @param {Roo.bootstrap.LocationPicker} this
28710 OverlayViewHide : true,
28712 * @event loadexception
28713 * Fires when load google lib failed.
28714 * @param {Roo.bootstrap.LocationPicker} this
28716 loadexception : true
28721 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
28723 gMapContext: false,
28729 mapTypeControl: false,
28730 disableDoubleClickZoom: false,
28732 streetViewControl: false,
28736 enableAutocomplete: false,
28737 enableReverseGeocode: true,
28740 getAutoCreate: function()
28745 cls: 'roo-location-picker'
28751 initEvents: function(ct, position)
28753 if(!this.el.getWidth() || this.isApplied()){
28757 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28762 initial: function()
28764 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28765 this.fireEvent('loadexception', this);
28769 if(!this.mapTypeId){
28770 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28773 this.gMapContext = this.GMapContext();
28775 this.initOverlayView();
28777 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28781 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28782 _this.setPosition(_this.gMapContext.marker.position);
28785 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28786 _this.fireEvent('mapClick', this, event);
28790 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28791 _this.fireEvent('mapRightClick', this, event);
28795 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28796 _this.fireEvent('markerClick', this, event);
28800 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28801 _this.fireEvent('markerRightClick', this, event);
28805 this.setPosition(this.gMapContext.location);
28807 this.fireEvent('initial', this, this.gMapContext.location);
28810 initOverlayView: function()
28814 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28818 _this.fireEvent('OverlayViewDraw', _this);
28823 _this.fireEvent('OverlayViewOnAdd', _this);
28826 onRemove: function()
28828 _this.fireEvent('OverlayViewOnRemove', _this);
28831 show: function(cpx)
28833 _this.fireEvent('OverlayViewShow', _this, cpx);
28838 _this.fireEvent('OverlayViewHide', _this);
28844 fromLatLngToContainerPixel: function(event)
28846 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28849 isApplied: function()
28851 return this.getGmapContext() == false ? false : true;
28854 getGmapContext: function()
28856 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28859 GMapContext: function()
28861 var position = new google.maps.LatLng(this.latitude, this.longitude);
28863 var _map = new google.maps.Map(this.el.dom, {
28866 mapTypeId: this.mapTypeId,
28867 mapTypeControl: this.mapTypeControl,
28868 disableDoubleClickZoom: this.disableDoubleClickZoom,
28869 scrollwheel: this.scrollwheel,
28870 streetViewControl: this.streetViewControl,
28871 locationName: this.locationName,
28872 draggable: this.draggable,
28873 enableAutocomplete: this.enableAutocomplete,
28874 enableReverseGeocode: this.enableReverseGeocode
28877 var _marker = new google.maps.Marker({
28878 position: position,
28880 title: this.markerTitle,
28881 draggable: this.draggable
28888 location: position,
28889 radius: this.radius,
28890 locationName: this.locationName,
28891 addressComponents: {
28892 formatted_address: null,
28893 addressLine1: null,
28894 addressLine2: null,
28896 streetNumber: null,
28900 stateOrProvince: null
28903 domContainer: this.el.dom,
28904 geodecoder: new google.maps.Geocoder()
28908 drawCircle: function(center, radius, options)
28910 if (this.gMapContext.circle != null) {
28911 this.gMapContext.circle.setMap(null);
28915 options = Roo.apply({}, options, {
28916 strokeColor: "#0000FF",
28917 strokeOpacity: .35,
28919 fillColor: "#0000FF",
28923 options.map = this.gMapContext.map;
28924 options.radius = radius;
28925 options.center = center;
28926 this.gMapContext.circle = new google.maps.Circle(options);
28927 return this.gMapContext.circle;
28933 setPosition: function(location)
28935 this.gMapContext.location = location;
28936 this.gMapContext.marker.setPosition(location);
28937 this.gMapContext.map.panTo(location);
28938 this.drawCircle(location, this.gMapContext.radius, {});
28942 if (this.gMapContext.settings.enableReverseGeocode) {
28943 this.gMapContext.geodecoder.geocode({
28944 latLng: this.gMapContext.location
28945 }, function(results, status) {
28947 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28948 _this.gMapContext.locationName = results[0].formatted_address;
28949 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28951 _this.fireEvent('positionchanged', this, location);
28958 this.fireEvent('positionchanged', this, location);
28963 google.maps.event.trigger(this.gMapContext.map, "resize");
28965 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28967 this.fireEvent('resize', this);
28970 setPositionByLatLng: function(latitude, longitude)
28972 this.setPosition(new google.maps.LatLng(latitude, longitude));
28975 getCurrentPosition: function()
28978 latitude: this.gMapContext.location.lat(),
28979 longitude: this.gMapContext.location.lng()
28983 getAddressName: function()
28985 return this.gMapContext.locationName;
28988 getAddressComponents: function()
28990 return this.gMapContext.addressComponents;
28993 address_component_from_google_geocode: function(address_components)
28997 for (var i = 0; i < address_components.length; i++) {
28998 var component = address_components[i];
28999 if (component.types.indexOf("postal_code") >= 0) {
29000 result.postalCode = component.short_name;
29001 } else if (component.types.indexOf("street_number") >= 0) {
29002 result.streetNumber = component.short_name;
29003 } else if (component.types.indexOf("route") >= 0) {
29004 result.streetName = component.short_name;
29005 } else if (component.types.indexOf("neighborhood") >= 0) {
29006 result.city = component.short_name;
29007 } else if (component.types.indexOf("locality") >= 0) {
29008 result.city = component.short_name;
29009 } else if (component.types.indexOf("sublocality") >= 0) {
29010 result.district = component.short_name;
29011 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29012 result.stateOrProvince = component.short_name;
29013 } else if (component.types.indexOf("country") >= 0) {
29014 result.country = component.short_name;
29018 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29019 result.addressLine2 = "";
29023 setZoomLevel: function(zoom)
29025 this.gMapContext.map.setZoom(zoom);
29038 this.fireEvent('show', this);
29049 this.fireEvent('hide', this);
29054 Roo.apply(Roo.bootstrap.LocationPicker, {
29056 OverlayView : function(map, options)
29058 options = options || {};
29065 * @class Roo.bootstrap.Alert
29066 * @extends Roo.bootstrap.Component
29067 * Bootstrap Alert class - shows an alert area box
29069 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29070 Enter a valid email address
29073 * @cfg {String} title The title of alert
29074 * @cfg {String} html The content of alert
29075 * @cfg {String} weight ( success | info | warning | danger )
29076 * @cfg {String} faicon font-awesomeicon
29079 * Create a new alert
29080 * @param {Object} config The config object
29084 Roo.bootstrap.Alert = function(config){
29085 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29089 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29096 getAutoCreate : function()
29105 cls : 'roo-alert-icon'
29110 cls : 'roo-alert-title',
29115 cls : 'roo-alert-text',
29122 cfg.cn[0].cls += ' fa ' + this.faicon;
29126 cfg.cls += ' alert-' + this.weight;
29132 initEvents: function()
29134 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29137 setTitle : function(str)
29139 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29142 setText : function(str)
29144 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29147 setWeight : function(weight)
29150 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29153 this.weight = weight;
29155 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29158 setIcon : function(icon)
29161 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29164 this.faicon = icon;
29166 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29187 * @class Roo.bootstrap.UploadCropbox
29188 * @extends Roo.bootstrap.Component
29189 * Bootstrap UploadCropbox class
29190 * @cfg {String} emptyText show when image has been loaded
29191 * @cfg {String} rotateNotify show when image too small to rotate
29192 * @cfg {Number} errorTimeout default 3000
29193 * @cfg {Number} minWidth default 300
29194 * @cfg {Number} minHeight default 300
29195 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29196 * @cfg {Boolean} isDocument (true|false) default false
29197 * @cfg {String} url action url
29198 * @cfg {String} paramName default 'imageUpload'
29199 * @cfg {String} method default POST
29200 * @cfg {Boolean} loadMask (true|false) default true
29201 * @cfg {Boolean} loadingText default 'Loading...'
29204 * Create a new UploadCropbox
29205 * @param {Object} config The config object
29208 Roo.bootstrap.UploadCropbox = function(config){
29209 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29213 * @event beforeselectfile
29214 * Fire before select file
29215 * @param {Roo.bootstrap.UploadCropbox} this
29217 "beforeselectfile" : true,
29220 * Fire after initEvent
29221 * @param {Roo.bootstrap.UploadCropbox} this
29226 * Fire after initEvent
29227 * @param {Roo.bootstrap.UploadCropbox} this
29228 * @param {String} data
29233 * Fire when preparing the file data
29234 * @param {Roo.bootstrap.UploadCropbox} this
29235 * @param {Object} file
29240 * Fire when get exception
29241 * @param {Roo.bootstrap.UploadCropbox} this
29242 * @param {XMLHttpRequest} xhr
29244 "exception" : true,
29246 * @event beforeloadcanvas
29247 * Fire before load the canvas
29248 * @param {Roo.bootstrap.UploadCropbox} this
29249 * @param {String} src
29251 "beforeloadcanvas" : true,
29254 * Fire when trash image
29255 * @param {Roo.bootstrap.UploadCropbox} this
29260 * Fire when download the image
29261 * @param {Roo.bootstrap.UploadCropbox} this
29265 * @event footerbuttonclick
29266 * Fire when footerbuttonclick
29267 * @param {Roo.bootstrap.UploadCropbox} this
29268 * @param {String} type
29270 "footerbuttonclick" : true,
29274 * @param {Roo.bootstrap.UploadCropbox} this
29279 * Fire when rotate the image
29280 * @param {Roo.bootstrap.UploadCropbox} this
29281 * @param {String} pos
29286 * Fire when inspect the file
29287 * @param {Roo.bootstrap.UploadCropbox} this
29288 * @param {Object} file
29293 * Fire when xhr upload the file
29294 * @param {Roo.bootstrap.UploadCropbox} this
29295 * @param {Object} data
29300 * Fire when arrange the file data
29301 * @param {Roo.bootstrap.UploadCropbox} this
29302 * @param {Object} formData
29307 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29310 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29312 emptyText : 'Click to upload image',
29313 rotateNotify : 'Image is too small to rotate',
29314 errorTimeout : 3000,
29328 cropType : 'image/jpeg',
29330 canvasLoaded : false,
29331 isDocument : false,
29333 paramName : 'imageUpload',
29335 loadingText : 'Loading...',
29338 getAutoCreate : function()
29342 cls : 'roo-upload-cropbox',
29346 cls : 'roo-upload-cropbox-selector',
29351 cls : 'roo-upload-cropbox-body',
29352 style : 'cursor:pointer',
29356 cls : 'roo-upload-cropbox-preview'
29360 cls : 'roo-upload-cropbox-thumb'
29364 cls : 'roo-upload-cropbox-empty-notify',
29365 html : this.emptyText
29369 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29370 html : this.rotateNotify
29376 cls : 'roo-upload-cropbox-footer',
29379 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29389 onRender : function(ct, position)
29391 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29393 if (this.buttons.length) {
29395 Roo.each(this.buttons, function(bb) {
29397 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29399 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29405 this.maskEl = this.el;
29409 initEvents : function()
29411 this.urlAPI = (window.createObjectURL && window) ||
29412 (window.URL && URL.revokeObjectURL && URL) ||
29413 (window.webkitURL && webkitURL);
29415 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29416 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29418 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29419 this.selectorEl.hide();
29421 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29422 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29424 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29425 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29426 this.thumbEl.hide();
29428 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29429 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29431 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29432 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29433 this.errorEl.hide();
29435 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29436 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29437 this.footerEl.hide();
29439 this.setThumbBoxSize();
29445 this.fireEvent('initial', this);
29452 window.addEventListener("resize", function() { _this.resize(); } );
29454 this.bodyEl.on('click', this.beforeSelectFile, this);
29457 this.bodyEl.on('touchstart', this.onTouchStart, this);
29458 this.bodyEl.on('touchmove', this.onTouchMove, this);
29459 this.bodyEl.on('touchend', this.onTouchEnd, this);
29463 this.bodyEl.on('mousedown', this.onMouseDown, this);
29464 this.bodyEl.on('mousemove', this.onMouseMove, this);
29465 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29466 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29467 Roo.get(document).on('mouseup', this.onMouseUp, this);
29470 this.selectorEl.on('change', this.onFileSelected, this);
29476 this.baseScale = 1;
29478 this.baseRotate = 1;
29479 this.dragable = false;
29480 this.pinching = false;
29483 this.cropData = false;
29484 this.notifyEl.dom.innerHTML = this.emptyText;
29486 this.selectorEl.dom.value = '';
29490 resize : function()
29492 if(this.fireEvent('resize', this) != false){
29493 this.setThumbBoxPosition();
29494 this.setCanvasPosition();
29498 onFooterButtonClick : function(e, el, o, type)
29501 case 'rotate-left' :
29502 this.onRotateLeft(e);
29504 case 'rotate-right' :
29505 this.onRotateRight(e);
29508 this.beforeSelectFile(e);
29523 this.fireEvent('footerbuttonclick', this, type);
29526 beforeSelectFile : function(e)
29528 e.preventDefault();
29530 if(this.fireEvent('beforeselectfile', this) != false){
29531 this.selectorEl.dom.click();
29535 onFileSelected : function(e)
29537 e.preventDefault();
29539 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29543 var file = this.selectorEl.dom.files[0];
29545 if(this.fireEvent('inspect', this, file) != false){
29546 this.prepare(file);
29551 trash : function(e)
29553 this.fireEvent('trash', this);
29556 download : function(e)
29558 this.fireEvent('download', this);
29561 loadCanvas : function(src)
29563 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29567 this.imageEl = document.createElement('img');
29571 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29573 this.imageEl.src = src;
29577 onLoadCanvas : function()
29579 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29580 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29582 this.bodyEl.un('click', this.beforeSelectFile, this);
29584 this.notifyEl.hide();
29585 this.thumbEl.show();
29586 this.footerEl.show();
29588 this.baseRotateLevel();
29590 if(this.isDocument){
29591 this.setThumbBoxSize();
29594 this.setThumbBoxPosition();
29596 this.baseScaleLevel();
29602 this.canvasLoaded = true;
29605 this.maskEl.unmask();
29610 setCanvasPosition : function()
29612 if(!this.canvasEl){
29616 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29617 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29619 this.previewEl.setLeft(pw);
29620 this.previewEl.setTop(ph);
29624 onMouseDown : function(e)
29628 this.dragable = true;
29629 this.pinching = false;
29631 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29632 this.dragable = false;
29636 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29637 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29641 onMouseMove : function(e)
29645 if(!this.canvasLoaded){
29649 if (!this.dragable){
29653 var minX = Math.ceil(this.thumbEl.getLeft(true));
29654 var minY = Math.ceil(this.thumbEl.getTop(true));
29656 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29657 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29659 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29660 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29662 x = x - this.mouseX;
29663 y = y - this.mouseY;
29665 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29666 var bgY = Math.ceil(y + this.previewEl.getTop(true));
29668 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29669 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29671 this.previewEl.setLeft(bgX);
29672 this.previewEl.setTop(bgY);
29674 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29675 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29678 onMouseUp : function(e)
29682 this.dragable = false;
29685 onMouseWheel : function(e)
29689 this.startScale = this.scale;
29691 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29693 if(!this.zoomable()){
29694 this.scale = this.startScale;
29703 zoomable : function()
29705 var minScale = this.thumbEl.getWidth() / this.minWidth;
29707 if(this.minWidth < this.minHeight){
29708 minScale = this.thumbEl.getHeight() / this.minHeight;
29711 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29712 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29716 (this.rotate == 0 || this.rotate == 180) &&
29718 width > this.imageEl.OriginWidth ||
29719 height > this.imageEl.OriginHeight ||
29720 (width < this.minWidth && height < this.minHeight)
29728 (this.rotate == 90 || this.rotate == 270) &&
29730 width > this.imageEl.OriginWidth ||
29731 height > this.imageEl.OriginHeight ||
29732 (width < this.minHeight && height < this.minWidth)
29739 !this.isDocument &&
29740 (this.rotate == 0 || this.rotate == 180) &&
29742 width < this.minWidth ||
29743 width > this.imageEl.OriginWidth ||
29744 height < this.minHeight ||
29745 height > this.imageEl.OriginHeight
29752 !this.isDocument &&
29753 (this.rotate == 90 || this.rotate == 270) &&
29755 width < this.minHeight ||
29756 width > this.imageEl.OriginWidth ||
29757 height < this.minWidth ||
29758 height > this.imageEl.OriginHeight
29768 onRotateLeft : function(e)
29770 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29772 var minScale = this.thumbEl.getWidth() / this.minWidth;
29774 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29775 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29777 this.startScale = this.scale;
29779 while (this.getScaleLevel() < minScale){
29781 this.scale = this.scale + 1;
29783 if(!this.zoomable()){
29788 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29789 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29794 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29801 this.scale = this.startScale;
29803 this.onRotateFail();
29808 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29810 if(this.isDocument){
29811 this.setThumbBoxSize();
29812 this.setThumbBoxPosition();
29813 this.setCanvasPosition();
29818 this.fireEvent('rotate', this, 'left');
29822 onRotateRight : function(e)
29824 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29826 var minScale = this.thumbEl.getWidth() / this.minWidth;
29828 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29829 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29831 this.startScale = this.scale;
29833 while (this.getScaleLevel() < minScale){
29835 this.scale = this.scale + 1;
29837 if(!this.zoomable()){
29842 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29843 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29848 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29855 this.scale = this.startScale;
29857 this.onRotateFail();
29862 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29864 if(this.isDocument){
29865 this.setThumbBoxSize();
29866 this.setThumbBoxPosition();
29867 this.setCanvasPosition();
29872 this.fireEvent('rotate', this, 'right');
29875 onRotateFail : function()
29877 this.errorEl.show(true);
29881 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29886 this.previewEl.dom.innerHTML = '';
29888 var canvasEl = document.createElement("canvas");
29890 var contextEl = canvasEl.getContext("2d");
29892 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29893 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29894 var center = this.imageEl.OriginWidth / 2;
29896 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29897 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29898 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29899 center = this.imageEl.OriginHeight / 2;
29902 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29904 contextEl.translate(center, center);
29905 contextEl.rotate(this.rotate * Math.PI / 180);
29907 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29909 this.canvasEl = document.createElement("canvas");
29911 this.contextEl = this.canvasEl.getContext("2d");
29913 switch (this.rotate) {
29916 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29917 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29919 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29924 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29925 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29927 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29928 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);
29932 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29937 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29938 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29940 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29941 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);
29945 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);
29950 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29951 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29953 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29954 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29958 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);
29965 this.previewEl.appendChild(this.canvasEl);
29967 this.setCanvasPosition();
29972 if(!this.canvasLoaded){
29976 var imageCanvas = document.createElement("canvas");
29978 var imageContext = imageCanvas.getContext("2d");
29980 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29981 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29983 var center = imageCanvas.width / 2;
29985 imageContext.translate(center, center);
29987 imageContext.rotate(this.rotate * Math.PI / 180);
29989 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29991 var canvas = document.createElement("canvas");
29993 var context = canvas.getContext("2d");
29995 canvas.width = this.minWidth;
29996 canvas.height = this.minHeight;
29998 switch (this.rotate) {
30001 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30002 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30004 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30005 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30007 var targetWidth = this.minWidth - 2 * x;
30008 var targetHeight = this.minHeight - 2 * y;
30012 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30013 scale = targetWidth / width;
30016 if(x > 0 && y == 0){
30017 scale = targetHeight / height;
30020 if(x > 0 && y > 0){
30021 scale = targetWidth / width;
30023 if(width < height){
30024 scale = targetHeight / height;
30028 context.scale(scale, scale);
30030 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30031 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30033 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30034 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30036 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30041 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30042 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30044 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30045 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30047 var targetWidth = this.minWidth - 2 * x;
30048 var targetHeight = this.minHeight - 2 * y;
30052 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30053 scale = targetWidth / width;
30056 if(x > 0 && y == 0){
30057 scale = targetHeight / height;
30060 if(x > 0 && y > 0){
30061 scale = targetWidth / width;
30063 if(width < height){
30064 scale = targetHeight / height;
30068 context.scale(scale, scale);
30070 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30071 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30073 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30074 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30076 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30078 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30083 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30084 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30086 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30087 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30089 var targetWidth = this.minWidth - 2 * x;
30090 var targetHeight = this.minHeight - 2 * y;
30094 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30095 scale = targetWidth / width;
30098 if(x > 0 && y == 0){
30099 scale = targetHeight / height;
30102 if(x > 0 && y > 0){
30103 scale = targetWidth / width;
30105 if(width < height){
30106 scale = targetHeight / height;
30110 context.scale(scale, scale);
30112 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30113 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30115 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30116 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30118 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30119 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30121 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30126 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30127 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30129 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30130 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30132 var targetWidth = this.minWidth - 2 * x;
30133 var targetHeight = this.minHeight - 2 * y;
30137 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30138 scale = targetWidth / width;
30141 if(x > 0 && y == 0){
30142 scale = targetHeight / height;
30145 if(x > 0 && y > 0){
30146 scale = targetWidth / width;
30148 if(width < height){
30149 scale = targetHeight / height;
30153 context.scale(scale, scale);
30155 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30156 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30158 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30159 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30161 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30163 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30170 this.cropData = canvas.toDataURL(this.cropType);
30172 if(this.fireEvent('crop', this, this.cropData) !== false){
30173 this.process(this.file, this.cropData);
30180 setThumbBoxSize : function()
30184 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30185 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30186 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30188 this.minWidth = width;
30189 this.minHeight = height;
30191 if(this.rotate == 90 || this.rotate == 270){
30192 this.minWidth = height;
30193 this.minHeight = width;
30198 width = Math.ceil(this.minWidth * height / this.minHeight);
30200 if(this.minWidth > this.minHeight){
30202 height = Math.ceil(this.minHeight * width / this.minWidth);
30205 this.thumbEl.setStyle({
30206 width : width + 'px',
30207 height : height + 'px'
30214 setThumbBoxPosition : function()
30216 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30217 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30219 this.thumbEl.setLeft(x);
30220 this.thumbEl.setTop(y);
30224 baseRotateLevel : function()
30226 this.baseRotate = 1;
30229 typeof(this.exif) != 'undefined' &&
30230 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30231 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30233 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30236 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30240 baseScaleLevel : function()
30244 if(this.isDocument){
30246 if(this.baseRotate == 6 || this.baseRotate == 8){
30248 height = this.thumbEl.getHeight();
30249 this.baseScale = height / this.imageEl.OriginWidth;
30251 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30252 width = this.thumbEl.getWidth();
30253 this.baseScale = width / this.imageEl.OriginHeight;
30259 height = this.thumbEl.getHeight();
30260 this.baseScale = height / this.imageEl.OriginHeight;
30262 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30263 width = this.thumbEl.getWidth();
30264 this.baseScale = width / this.imageEl.OriginWidth;
30270 if(this.baseRotate == 6 || this.baseRotate == 8){
30272 width = this.thumbEl.getHeight();
30273 this.baseScale = width / this.imageEl.OriginHeight;
30275 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30276 height = this.thumbEl.getWidth();
30277 this.baseScale = height / this.imageEl.OriginHeight;
30280 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30281 height = this.thumbEl.getWidth();
30282 this.baseScale = height / this.imageEl.OriginHeight;
30284 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30285 width = this.thumbEl.getHeight();
30286 this.baseScale = width / this.imageEl.OriginWidth;
30293 width = this.thumbEl.getWidth();
30294 this.baseScale = width / this.imageEl.OriginWidth;
30296 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30297 height = this.thumbEl.getHeight();
30298 this.baseScale = height / this.imageEl.OriginHeight;
30301 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30303 height = this.thumbEl.getHeight();
30304 this.baseScale = height / this.imageEl.OriginHeight;
30306 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30307 width = this.thumbEl.getWidth();
30308 this.baseScale = width / this.imageEl.OriginWidth;
30316 getScaleLevel : function()
30318 return this.baseScale * Math.pow(1.1, this.scale);
30321 onTouchStart : function(e)
30323 if(!this.canvasLoaded){
30324 this.beforeSelectFile(e);
30328 var touches = e.browserEvent.touches;
30334 if(touches.length == 1){
30335 this.onMouseDown(e);
30339 if(touches.length != 2){
30345 for(var i = 0, finger; finger = touches[i]; i++){
30346 coords.push(finger.pageX, finger.pageY);
30349 var x = Math.pow(coords[0] - coords[2], 2);
30350 var y = Math.pow(coords[1] - coords[3], 2);
30352 this.startDistance = Math.sqrt(x + y);
30354 this.startScale = this.scale;
30356 this.pinching = true;
30357 this.dragable = false;
30361 onTouchMove : function(e)
30363 if(!this.pinching && !this.dragable){
30367 var touches = e.browserEvent.touches;
30374 this.onMouseMove(e);
30380 for(var i = 0, finger; finger = touches[i]; i++){
30381 coords.push(finger.pageX, finger.pageY);
30384 var x = Math.pow(coords[0] - coords[2], 2);
30385 var y = Math.pow(coords[1] - coords[3], 2);
30387 this.endDistance = Math.sqrt(x + y);
30389 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30391 if(!this.zoomable()){
30392 this.scale = this.startScale;
30400 onTouchEnd : function(e)
30402 this.pinching = false;
30403 this.dragable = false;
30407 process : function(file, crop)
30410 this.maskEl.mask(this.loadingText);
30413 this.xhr = new XMLHttpRequest();
30415 file.xhr = this.xhr;
30417 this.xhr.open(this.method, this.url, true);
30420 "Accept": "application/json",
30421 "Cache-Control": "no-cache",
30422 "X-Requested-With": "XMLHttpRequest"
30425 for (var headerName in headers) {
30426 var headerValue = headers[headerName];
30428 this.xhr.setRequestHeader(headerName, headerValue);
30434 this.xhr.onload = function()
30436 _this.xhrOnLoad(_this.xhr);
30439 this.xhr.onerror = function()
30441 _this.xhrOnError(_this.xhr);
30444 var formData = new FormData();
30446 formData.append('returnHTML', 'NO');
30449 formData.append('crop', crop);
30452 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30453 formData.append(this.paramName, file, file.name);
30456 if(typeof(file.filename) != 'undefined'){
30457 formData.append('filename', file.filename);
30460 if(typeof(file.mimetype) != 'undefined'){
30461 formData.append('mimetype', file.mimetype);
30464 if(this.fireEvent('arrange', this, formData) != false){
30465 this.xhr.send(formData);
30469 xhrOnLoad : function(xhr)
30472 this.maskEl.unmask();
30475 if (xhr.readyState !== 4) {
30476 this.fireEvent('exception', this, xhr);
30480 var response = Roo.decode(xhr.responseText);
30482 if(!response.success){
30483 this.fireEvent('exception', this, xhr);
30487 var response = Roo.decode(xhr.responseText);
30489 this.fireEvent('upload', this, response);
30493 xhrOnError : function()
30496 this.maskEl.unmask();
30499 Roo.log('xhr on error');
30501 var response = Roo.decode(xhr.responseText);
30507 prepare : function(file)
30510 this.maskEl.mask(this.loadingText);
30516 if(typeof(file) === 'string'){
30517 this.loadCanvas(file);
30521 if(!file || !this.urlAPI){
30526 this.cropType = file.type;
30530 if(this.fireEvent('prepare', this, this.file) != false){
30532 var reader = new FileReader();
30534 reader.onload = function (e) {
30535 if (e.target.error) {
30536 Roo.log(e.target.error);
30540 var buffer = e.target.result,
30541 dataView = new DataView(buffer),
30543 maxOffset = dataView.byteLength - 4,
30547 if (dataView.getUint16(0) === 0xffd8) {
30548 while (offset < maxOffset) {
30549 markerBytes = dataView.getUint16(offset);
30551 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30552 markerLength = dataView.getUint16(offset + 2) + 2;
30553 if (offset + markerLength > dataView.byteLength) {
30554 Roo.log('Invalid meta data: Invalid segment size.');
30558 if(markerBytes == 0xffe1){
30559 _this.parseExifData(
30566 offset += markerLength;
30576 var url = _this.urlAPI.createObjectURL(_this.file);
30578 _this.loadCanvas(url);
30583 reader.readAsArrayBuffer(this.file);
30589 parseExifData : function(dataView, offset, length)
30591 var tiffOffset = offset + 10,
30595 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30596 // No Exif data, might be XMP data instead
30600 // Check for the ASCII code for "Exif" (0x45786966):
30601 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30602 // No Exif data, might be XMP data instead
30605 if (tiffOffset + 8 > dataView.byteLength) {
30606 Roo.log('Invalid Exif data: Invalid segment size.');
30609 // Check for the two null bytes:
30610 if (dataView.getUint16(offset + 8) !== 0x0000) {
30611 Roo.log('Invalid Exif data: Missing byte alignment offset.');
30614 // Check the byte alignment:
30615 switch (dataView.getUint16(tiffOffset)) {
30617 littleEndian = true;
30620 littleEndian = false;
30623 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30626 // Check for the TIFF tag marker (0x002A):
30627 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30628 Roo.log('Invalid Exif data: Missing TIFF marker.');
30631 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30632 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30634 this.parseExifTags(
30637 tiffOffset + dirOffset,
30642 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30647 if (dirOffset + 6 > dataView.byteLength) {
30648 Roo.log('Invalid Exif data: Invalid directory offset.');
30651 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30652 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30653 if (dirEndOffset + 4 > dataView.byteLength) {
30654 Roo.log('Invalid Exif data: Invalid directory size.');
30657 for (i = 0; i < tagsNumber; i += 1) {
30661 dirOffset + 2 + 12 * i, // tag offset
30665 // Return the offset to the next directory:
30666 return dataView.getUint32(dirEndOffset, littleEndian);
30669 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
30671 var tag = dataView.getUint16(offset, littleEndian);
30673 this.exif[tag] = this.getExifValue(
30677 dataView.getUint16(offset + 2, littleEndian), // tag type
30678 dataView.getUint32(offset + 4, littleEndian), // tag length
30683 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30685 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30694 Roo.log('Invalid Exif data: Invalid tag type.');
30698 tagSize = tagType.size * length;
30699 // Determine if the value is contained in the dataOffset bytes,
30700 // or if the value at the dataOffset is a pointer to the actual data:
30701 dataOffset = tagSize > 4 ?
30702 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30703 if (dataOffset + tagSize > dataView.byteLength) {
30704 Roo.log('Invalid Exif data: Invalid data offset.');
30707 if (length === 1) {
30708 return tagType.getValue(dataView, dataOffset, littleEndian);
30711 for (i = 0; i < length; i += 1) {
30712 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30715 if (tagType.ascii) {
30717 // Concatenate the chars:
30718 for (i = 0; i < values.length; i += 1) {
30720 // Ignore the terminating NULL byte(s):
30721 if (c === '\u0000') {
30733 Roo.apply(Roo.bootstrap.UploadCropbox, {
30735 'Orientation': 0x0112
30739 1: 0, //'top-left',
30741 3: 180, //'bottom-right',
30742 // 4: 'bottom-left',
30744 6: 90, //'right-top',
30745 // 7: 'right-bottom',
30746 8: 270 //'left-bottom'
30750 // byte, 8-bit unsigned int:
30752 getValue: function (dataView, dataOffset) {
30753 return dataView.getUint8(dataOffset);
30757 // ascii, 8-bit byte:
30759 getValue: function (dataView, dataOffset) {
30760 return String.fromCharCode(dataView.getUint8(dataOffset));
30765 // short, 16 bit int:
30767 getValue: function (dataView, dataOffset, littleEndian) {
30768 return dataView.getUint16(dataOffset, littleEndian);
30772 // long, 32 bit int:
30774 getValue: function (dataView, dataOffset, littleEndian) {
30775 return dataView.getUint32(dataOffset, littleEndian);
30779 // rational = two long values, first is numerator, second is denominator:
30781 getValue: function (dataView, dataOffset, littleEndian) {
30782 return dataView.getUint32(dataOffset, littleEndian) /
30783 dataView.getUint32(dataOffset + 4, littleEndian);
30787 // slong, 32 bit signed int:
30789 getValue: function (dataView, dataOffset, littleEndian) {
30790 return dataView.getInt32(dataOffset, littleEndian);
30794 // srational, two slongs, first is numerator, second is denominator:
30796 getValue: function (dataView, dataOffset, littleEndian) {
30797 return dataView.getInt32(dataOffset, littleEndian) /
30798 dataView.getInt32(dataOffset + 4, littleEndian);
30808 cls : 'btn-group roo-upload-cropbox-rotate-left',
30809 action : 'rotate-left',
30813 cls : 'btn btn-default',
30814 html : '<i class="fa fa-undo"></i>'
30820 cls : 'btn-group roo-upload-cropbox-picture',
30821 action : 'picture',
30825 cls : 'btn btn-default',
30826 html : '<i class="fa fa-picture-o"></i>'
30832 cls : 'btn-group roo-upload-cropbox-rotate-right',
30833 action : 'rotate-right',
30837 cls : 'btn btn-default',
30838 html : '<i class="fa fa-repeat"></i>'
30846 cls : 'btn-group roo-upload-cropbox-rotate-left',
30847 action : 'rotate-left',
30851 cls : 'btn btn-default',
30852 html : '<i class="fa fa-undo"></i>'
30858 cls : 'btn-group roo-upload-cropbox-download',
30859 action : 'download',
30863 cls : 'btn btn-default',
30864 html : '<i class="fa fa-download"></i>'
30870 cls : 'btn-group roo-upload-cropbox-crop',
30875 cls : 'btn btn-default',
30876 html : '<i class="fa fa-crop"></i>'
30882 cls : 'btn-group roo-upload-cropbox-trash',
30887 cls : 'btn btn-default',
30888 html : '<i class="fa fa-trash"></i>'
30894 cls : 'btn-group roo-upload-cropbox-rotate-right',
30895 action : 'rotate-right',
30899 cls : 'btn btn-default',
30900 html : '<i class="fa fa-repeat"></i>'
30908 cls : 'btn-group roo-upload-cropbox-rotate-left',
30909 action : 'rotate-left',
30913 cls : 'btn btn-default',
30914 html : '<i class="fa fa-undo"></i>'
30920 cls : 'btn-group roo-upload-cropbox-rotate-right',
30921 action : 'rotate-right',
30925 cls : 'btn btn-default',
30926 html : '<i class="fa fa-repeat"></i>'
30939 * @class Roo.bootstrap.DocumentManager
30940 * @extends Roo.bootstrap.Component
30941 * Bootstrap DocumentManager class
30942 * @cfg {String} paramName default 'imageUpload'
30943 * @cfg {String} toolTipName default 'filename'
30944 * @cfg {String} method default POST
30945 * @cfg {String} url action url
30946 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30947 * @cfg {Boolean} multiple multiple upload default true
30948 * @cfg {Number} thumbSize default 300
30949 * @cfg {String} fieldLabel
30950 * @cfg {Number} labelWidth default 4
30951 * @cfg {String} labelAlign (left|top) default left
30952 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30953 * @cfg {Number} labellg set the width of label (1-12)
30954 * @cfg {Number} labelmd set the width of label (1-12)
30955 * @cfg {Number} labelsm set the width of label (1-12)
30956 * @cfg {Number} labelxs set the width of label (1-12)
30959 * Create a new DocumentManager
30960 * @param {Object} config The config object
30963 Roo.bootstrap.DocumentManager = function(config){
30964 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30967 this.delegates = [];
30972 * Fire when initial the DocumentManager
30973 * @param {Roo.bootstrap.DocumentManager} this
30978 * inspect selected file
30979 * @param {Roo.bootstrap.DocumentManager} this
30980 * @param {File} file
30985 * Fire when xhr load exception
30986 * @param {Roo.bootstrap.DocumentManager} this
30987 * @param {XMLHttpRequest} xhr
30989 "exception" : true,
30991 * @event afterupload
30992 * Fire when xhr load exception
30993 * @param {Roo.bootstrap.DocumentManager} this
30994 * @param {XMLHttpRequest} xhr
30996 "afterupload" : true,
30999 * prepare the form data
31000 * @param {Roo.bootstrap.DocumentManager} this
31001 * @param {Object} formData
31006 * Fire when remove the file
31007 * @param {Roo.bootstrap.DocumentManager} this
31008 * @param {Object} file
31013 * Fire after refresh the file
31014 * @param {Roo.bootstrap.DocumentManager} this
31019 * Fire after click the image
31020 * @param {Roo.bootstrap.DocumentManager} this
31021 * @param {Object} file
31026 * Fire when upload a image and editable set to true
31027 * @param {Roo.bootstrap.DocumentManager} this
31028 * @param {Object} file
31032 * @event beforeselectfile
31033 * Fire before select file
31034 * @param {Roo.bootstrap.DocumentManager} this
31036 "beforeselectfile" : true,
31039 * Fire before process file
31040 * @param {Roo.bootstrap.DocumentManager} this
31041 * @param {Object} file
31045 * @event previewrendered
31046 * Fire when preview rendered
31047 * @param {Roo.bootstrap.DocumentManager} this
31048 * @param {Object} file
31050 "previewrendered" : true,
31053 "previewResize" : true
31058 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31067 paramName : 'imageUpload',
31068 toolTipName : 'filename',
31071 labelAlign : 'left',
31081 getAutoCreate : function()
31083 var managerWidget = {
31085 cls : 'roo-document-manager',
31089 cls : 'roo-document-manager-selector',
31094 cls : 'roo-document-manager-uploader',
31098 cls : 'roo-document-manager-upload-btn',
31099 html : '<i class="fa fa-plus"></i>'
31110 cls : 'column col-md-12',
31115 if(this.fieldLabel.length){
31120 cls : 'column col-md-12',
31121 html : this.fieldLabel
31125 cls : 'column col-md-12',
31130 if(this.labelAlign == 'left'){
31135 html : this.fieldLabel
31144 if(this.labelWidth > 12){
31145 content[0].style = "width: " + this.labelWidth + 'px';
31148 if(this.labelWidth < 13 && this.labelmd == 0){
31149 this.labelmd = this.labelWidth;
31152 if(this.labellg > 0){
31153 content[0].cls += ' col-lg-' + this.labellg;
31154 content[1].cls += ' col-lg-' + (12 - this.labellg);
31157 if(this.labelmd > 0){
31158 content[0].cls += ' col-md-' + this.labelmd;
31159 content[1].cls += ' col-md-' + (12 - this.labelmd);
31162 if(this.labelsm > 0){
31163 content[0].cls += ' col-sm-' + this.labelsm;
31164 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31167 if(this.labelxs > 0){
31168 content[0].cls += ' col-xs-' + this.labelxs;
31169 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31177 cls : 'row clearfix',
31185 initEvents : function()
31187 this.managerEl = this.el.select('.roo-document-manager', true).first();
31188 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31190 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31191 this.selectorEl.hide();
31194 this.selectorEl.attr('multiple', 'multiple');
31197 this.selectorEl.on('change', this.onFileSelected, this);
31199 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31200 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31202 this.uploader.on('click', this.onUploaderClick, this);
31204 this.renderProgressDialog();
31208 window.addEventListener("resize", function() { _this.refresh(); } );
31210 this.fireEvent('initial', this);
31213 renderProgressDialog : function()
31217 this.progressDialog = new Roo.bootstrap.Modal({
31218 cls : 'roo-document-manager-progress-dialog',
31219 allow_close : false,
31230 btnclick : function() {
31231 _this.uploadCancel();
31237 this.progressDialog.render(Roo.get(document.body));
31239 this.progress = new Roo.bootstrap.Progress({
31240 cls : 'roo-document-manager-progress',
31245 this.progress.render(this.progressDialog.getChildContainer());
31247 this.progressBar = new Roo.bootstrap.ProgressBar({
31248 cls : 'roo-document-manager-progress-bar',
31251 aria_valuemax : 12,
31255 this.progressBar.render(this.progress.getChildContainer());
31258 onUploaderClick : function(e)
31260 e.preventDefault();
31262 if(this.fireEvent('beforeselectfile', this) != false){
31263 this.selectorEl.dom.click();
31268 onFileSelected : function(e)
31270 e.preventDefault();
31272 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31276 Roo.each(this.selectorEl.dom.files, function(file){
31277 if(this.fireEvent('inspect', this, file) != false){
31278 this.files.push(file);
31288 this.selectorEl.dom.value = '';
31290 if(!this.files || !this.files.length){
31294 if(this.boxes > 0 && this.files.length > this.boxes){
31295 this.files = this.files.slice(0, this.boxes);
31298 this.uploader.show();
31300 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31301 this.uploader.hide();
31310 Roo.each(this.files, function(file){
31312 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31313 var f = this.renderPreview(file);
31318 if(file.type.indexOf('image') != -1){
31319 this.delegates.push(
31321 _this.process(file);
31322 }).createDelegate(this)
31330 _this.process(file);
31331 }).createDelegate(this)
31336 this.files = files;
31338 this.delegates = this.delegates.concat(docs);
31340 if(!this.delegates.length){
31345 this.progressBar.aria_valuemax = this.delegates.length;
31352 arrange : function()
31354 if(!this.delegates.length){
31355 this.progressDialog.hide();
31360 var delegate = this.delegates.shift();
31362 this.progressDialog.show();
31364 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31366 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31371 refresh : function()
31373 this.uploader.show();
31375 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31376 this.uploader.hide();
31379 Roo.isTouch ? this.closable(false) : this.closable(true);
31381 this.fireEvent('refresh', this);
31384 onRemove : function(e, el, o)
31386 e.preventDefault();
31388 this.fireEvent('remove', this, o);
31392 remove : function(o)
31396 Roo.each(this.files, function(file){
31397 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31406 this.files = files;
31413 Roo.each(this.files, function(file){
31418 file.target.remove();
31427 onClick : function(e, el, o)
31429 e.preventDefault();
31431 this.fireEvent('click', this, o);
31435 closable : function(closable)
31437 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31439 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31451 xhrOnLoad : function(xhr)
31453 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31457 if (xhr.readyState !== 4) {
31459 this.fireEvent('exception', this, xhr);
31463 var response = Roo.decode(xhr.responseText);
31465 if(!response.success){
31467 this.fireEvent('exception', this, xhr);
31471 var file = this.renderPreview(response.data);
31473 this.files.push(file);
31477 this.fireEvent('afterupload', this, xhr);
31481 xhrOnError : function(xhr)
31483 Roo.log('xhr on error');
31485 var response = Roo.decode(xhr.responseText);
31492 process : function(file)
31494 if(this.fireEvent('process', this, file) !== false){
31495 if(this.editable && file.type.indexOf('image') != -1){
31496 this.fireEvent('edit', this, file);
31500 this.uploadStart(file, false);
31507 uploadStart : function(file, crop)
31509 this.xhr = new XMLHttpRequest();
31511 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31516 file.xhr = this.xhr;
31518 this.managerEl.createChild({
31520 cls : 'roo-document-manager-loading',
31524 tooltip : file.name,
31525 cls : 'roo-document-manager-thumb',
31526 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31532 this.xhr.open(this.method, this.url, true);
31535 "Accept": "application/json",
31536 "Cache-Control": "no-cache",
31537 "X-Requested-With": "XMLHttpRequest"
31540 for (var headerName in headers) {
31541 var headerValue = headers[headerName];
31543 this.xhr.setRequestHeader(headerName, headerValue);
31549 this.xhr.onload = function()
31551 _this.xhrOnLoad(_this.xhr);
31554 this.xhr.onerror = function()
31556 _this.xhrOnError(_this.xhr);
31559 var formData = new FormData();
31561 formData.append('returnHTML', 'NO');
31564 formData.append('crop', crop);
31567 formData.append(this.paramName, file, file.name);
31574 if(this.fireEvent('prepare', this, formData, options) != false){
31576 if(options.manually){
31580 this.xhr.send(formData);
31584 this.uploadCancel();
31587 uploadCancel : function()
31593 this.delegates = [];
31595 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31602 renderPreview : function(file)
31604 if(typeof(file.target) != 'undefined' && file.target){
31608 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31610 var previewEl = this.managerEl.createChild({
31612 cls : 'roo-document-manager-preview',
31616 tooltip : file[this.toolTipName],
31617 cls : 'roo-document-manager-thumb',
31618 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31623 html : '<i class="fa fa-times-circle"></i>'
31628 var close = previewEl.select('button.close', true).first();
31630 close.on('click', this.onRemove, this, file);
31632 file.target = previewEl;
31634 var image = previewEl.select('img', true).first();
31638 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31640 image.on('click', this.onClick, this, file);
31642 this.fireEvent('previewrendered', this, file);
31648 onPreviewLoad : function(file, image)
31650 if(typeof(file.target) == 'undefined' || !file.target){
31654 var width = image.dom.naturalWidth || image.dom.width;
31655 var height = image.dom.naturalHeight || image.dom.height;
31657 if(!this.previewResize) {
31661 if(width > height){
31662 file.target.addClass('wide');
31666 file.target.addClass('tall');
31671 uploadFromSource : function(file, crop)
31673 this.xhr = new XMLHttpRequest();
31675 this.managerEl.createChild({
31677 cls : 'roo-document-manager-loading',
31681 tooltip : file.name,
31682 cls : 'roo-document-manager-thumb',
31683 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31689 this.xhr.open(this.method, this.url, true);
31692 "Accept": "application/json",
31693 "Cache-Control": "no-cache",
31694 "X-Requested-With": "XMLHttpRequest"
31697 for (var headerName in headers) {
31698 var headerValue = headers[headerName];
31700 this.xhr.setRequestHeader(headerName, headerValue);
31706 this.xhr.onload = function()
31708 _this.xhrOnLoad(_this.xhr);
31711 this.xhr.onerror = function()
31713 _this.xhrOnError(_this.xhr);
31716 var formData = new FormData();
31718 formData.append('returnHTML', 'NO');
31720 formData.append('crop', crop);
31722 if(typeof(file.filename) != 'undefined'){
31723 formData.append('filename', file.filename);
31726 if(typeof(file.mimetype) != 'undefined'){
31727 formData.append('mimetype', file.mimetype);
31732 if(this.fireEvent('prepare', this, formData) != false){
31733 this.xhr.send(formData);
31743 * @class Roo.bootstrap.DocumentViewer
31744 * @extends Roo.bootstrap.Component
31745 * Bootstrap DocumentViewer class
31746 * @cfg {Boolean} showDownload (true|false) show download button (default true)
31747 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31750 * Create a new DocumentViewer
31751 * @param {Object} config The config object
31754 Roo.bootstrap.DocumentViewer = function(config){
31755 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31760 * Fire after initEvent
31761 * @param {Roo.bootstrap.DocumentViewer} this
31767 * @param {Roo.bootstrap.DocumentViewer} this
31772 * Fire after download button
31773 * @param {Roo.bootstrap.DocumentViewer} this
31778 * Fire after trash button
31779 * @param {Roo.bootstrap.DocumentViewer} this
31786 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
31788 showDownload : true,
31792 getAutoCreate : function()
31796 cls : 'roo-document-viewer',
31800 cls : 'roo-document-viewer-body',
31804 cls : 'roo-document-viewer-thumb',
31808 cls : 'roo-document-viewer-image'
31816 cls : 'roo-document-viewer-footer',
31819 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31823 cls : 'btn-group roo-document-viewer-download',
31827 cls : 'btn btn-default',
31828 html : '<i class="fa fa-download"></i>'
31834 cls : 'btn-group roo-document-viewer-trash',
31838 cls : 'btn btn-default',
31839 html : '<i class="fa fa-trash"></i>'
31852 initEvents : function()
31854 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31855 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31857 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31858 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31860 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31861 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31863 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31864 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31866 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31867 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31869 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31870 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31872 this.bodyEl.on('click', this.onClick, this);
31873 this.downloadBtn.on('click', this.onDownload, this);
31874 this.trashBtn.on('click', this.onTrash, this);
31876 this.downloadBtn.hide();
31877 this.trashBtn.hide();
31879 if(this.showDownload){
31880 this.downloadBtn.show();
31883 if(this.showTrash){
31884 this.trashBtn.show();
31887 if(!this.showDownload && !this.showTrash) {
31888 this.footerEl.hide();
31893 initial : function()
31895 this.fireEvent('initial', this);
31899 onClick : function(e)
31901 e.preventDefault();
31903 this.fireEvent('click', this);
31906 onDownload : function(e)
31908 e.preventDefault();
31910 this.fireEvent('download', this);
31913 onTrash : function(e)
31915 e.preventDefault();
31917 this.fireEvent('trash', this);
31929 * @class Roo.bootstrap.NavProgressBar
31930 * @extends Roo.bootstrap.Component
31931 * Bootstrap NavProgressBar class
31934 * Create a new nav progress bar
31935 * @param {Object} config The config object
31938 Roo.bootstrap.NavProgressBar = function(config){
31939 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31941 this.bullets = this.bullets || [];
31943 // Roo.bootstrap.NavProgressBar.register(this);
31947 * Fires when the active item changes
31948 * @param {Roo.bootstrap.NavProgressBar} this
31949 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31950 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
31957 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
31962 getAutoCreate : function()
31964 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31968 cls : 'roo-navigation-bar-group',
31972 cls : 'roo-navigation-top-bar'
31976 cls : 'roo-navigation-bullets-bar',
31980 cls : 'roo-navigation-bar'
31987 cls : 'roo-navigation-bottom-bar'
31997 initEvents: function()
32002 onRender : function(ct, position)
32004 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32006 if(this.bullets.length){
32007 Roo.each(this.bullets, function(b){
32016 addItem : function(cfg)
32018 var item = new Roo.bootstrap.NavProgressItem(cfg);
32020 item.parentId = this.id;
32021 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32024 var top = new Roo.bootstrap.Element({
32026 cls : 'roo-navigation-bar-text'
32029 var bottom = new Roo.bootstrap.Element({
32031 cls : 'roo-navigation-bar-text'
32034 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32035 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32037 var topText = new Roo.bootstrap.Element({
32039 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32042 var bottomText = new Roo.bootstrap.Element({
32044 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32047 topText.onRender(top.el, null);
32048 bottomText.onRender(bottom.el, null);
32051 item.bottomEl = bottom;
32054 this.barItems.push(item);
32059 getActive : function()
32061 var active = false;
32063 Roo.each(this.barItems, function(v){
32065 if (!v.isActive()) {
32077 setActiveItem : function(item)
32081 Roo.each(this.barItems, function(v){
32082 if (v.rid == item.rid) {
32086 if (v.isActive()) {
32087 v.setActive(false);
32092 item.setActive(true);
32094 this.fireEvent('changed', this, item, prev);
32097 getBarItem: function(rid)
32101 Roo.each(this.barItems, function(e) {
32102 if (e.rid != rid) {
32113 indexOfItem : function(item)
32117 Roo.each(this.barItems, function(v, i){
32119 if (v.rid != item.rid) {
32130 setActiveNext : function()
32132 var i = this.indexOfItem(this.getActive());
32134 if (i > this.barItems.length) {
32138 this.setActiveItem(this.barItems[i+1]);
32141 setActivePrev : function()
32143 var i = this.indexOfItem(this.getActive());
32149 this.setActiveItem(this.barItems[i-1]);
32152 format : function()
32154 if(!this.barItems.length){
32158 var width = 100 / this.barItems.length;
32160 Roo.each(this.barItems, function(i){
32161 i.el.setStyle('width', width + '%');
32162 i.topEl.el.setStyle('width', width + '%');
32163 i.bottomEl.el.setStyle('width', width + '%');
32172 * Nav Progress Item
32177 * @class Roo.bootstrap.NavProgressItem
32178 * @extends Roo.bootstrap.Component
32179 * Bootstrap NavProgressItem class
32180 * @cfg {String} rid the reference id
32181 * @cfg {Boolean} active (true|false) Is item active default false
32182 * @cfg {Boolean} disabled (true|false) Is item active default false
32183 * @cfg {String} html
32184 * @cfg {String} position (top|bottom) text position default bottom
32185 * @cfg {String} icon show icon instead of number
32188 * Create a new NavProgressItem
32189 * @param {Object} config The config object
32191 Roo.bootstrap.NavProgressItem = function(config){
32192 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32197 * The raw click event for the entire grid.
32198 * @param {Roo.bootstrap.NavProgressItem} this
32199 * @param {Roo.EventObject} e
32206 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32212 position : 'bottom',
32215 getAutoCreate : function()
32217 var iconCls = 'roo-navigation-bar-item-icon';
32219 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32223 cls: 'roo-navigation-bar-item',
32233 cfg.cls += ' active';
32236 cfg.cls += ' disabled';
32242 disable : function()
32244 this.setDisabled(true);
32247 enable : function()
32249 this.setDisabled(false);
32252 initEvents: function()
32254 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32256 this.iconEl.on('click', this.onClick, this);
32259 onClick : function(e)
32261 e.preventDefault();
32267 if(this.fireEvent('click', this, e) === false){
32271 this.parent().setActiveItem(this);
32274 isActive: function ()
32276 return this.active;
32279 setActive : function(state)
32281 if(this.active == state){
32285 this.active = state;
32288 this.el.addClass('active');
32292 this.el.removeClass('active');
32297 setDisabled : function(state)
32299 if(this.disabled == state){
32303 this.disabled = state;
32306 this.el.addClass('disabled');
32310 this.el.removeClass('disabled');
32313 tooltipEl : function()
32315 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32328 * @class Roo.bootstrap.FieldLabel
32329 * @extends Roo.bootstrap.Component
32330 * Bootstrap FieldLabel class
32331 * @cfg {String} html contents of the element
32332 * @cfg {String} tag tag of the element default label
32333 * @cfg {String} cls class of the element
32334 * @cfg {String} target label target
32335 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32336 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32337 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32338 * @cfg {String} iconTooltip default "This field is required"
32339 * @cfg {String} indicatorpos (left|right) default left
32342 * Create a new FieldLabel
32343 * @param {Object} config The config object
32346 Roo.bootstrap.FieldLabel = function(config){
32347 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32352 * Fires after the field has been marked as invalid.
32353 * @param {Roo.form.FieldLabel} this
32354 * @param {String} msg The validation message
32359 * Fires after the field has been validated with no errors.
32360 * @param {Roo.form.FieldLabel} this
32366 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32373 invalidClass : 'has-warning',
32374 validClass : 'has-success',
32375 iconTooltip : 'This field is required',
32376 indicatorpos : 'left',
32378 getAutoCreate : function(){
32381 if (!this.allowBlank) {
32387 cls : 'roo-bootstrap-field-label ' + this.cls,
32392 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32393 tooltip : this.iconTooltip
32402 if(this.indicatorpos == 'right'){
32405 cls : 'roo-bootstrap-field-label ' + this.cls,
32414 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32415 tooltip : this.iconTooltip
32424 initEvents: function()
32426 Roo.bootstrap.Element.superclass.initEvents.call(this);
32428 this.indicator = this.indicatorEl();
32430 if(this.indicator){
32431 this.indicator.removeClass('visible');
32432 this.indicator.addClass('invisible');
32435 Roo.bootstrap.FieldLabel.register(this);
32438 indicatorEl : function()
32440 var indicator = this.el.select('i.roo-required-indicator',true).first();
32451 * Mark this field as valid
32453 markValid : function()
32455 if(this.indicator){
32456 this.indicator.removeClass('visible');
32457 this.indicator.addClass('invisible');
32459 if (Roo.bootstrap.version == 3) {
32460 this.el.removeClass(this.invalidClass);
32461 this.el.addClass(this.validClass);
32463 this.el.removeClass('is-invalid');
32464 this.el.addClass('is-valid');
32468 this.fireEvent('valid', this);
32472 * Mark this field as invalid
32473 * @param {String} msg The validation message
32475 markInvalid : function(msg)
32477 if(this.indicator){
32478 this.indicator.removeClass('invisible');
32479 this.indicator.addClass('visible');
32481 if (Roo.bootstrap.version == 3) {
32482 this.el.removeClass(this.validClass);
32483 this.el.addClass(this.invalidClass);
32485 this.el.removeClass('is-valid');
32486 this.el.addClass('is-invalid');
32490 this.fireEvent('invalid', this, msg);
32496 Roo.apply(Roo.bootstrap.FieldLabel, {
32501 * register a FieldLabel Group
32502 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32504 register : function(label)
32506 if(this.groups.hasOwnProperty(label.target)){
32510 this.groups[label.target] = label;
32514 * fetch a FieldLabel Group based on the target
32515 * @param {string} target
32516 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32518 get: function(target) {
32519 if (typeof(this.groups[target]) == 'undefined') {
32523 return this.groups[target] ;
32532 * page DateSplitField.
32538 * @class Roo.bootstrap.DateSplitField
32539 * @extends Roo.bootstrap.Component
32540 * Bootstrap DateSplitField class
32541 * @cfg {string} fieldLabel - the label associated
32542 * @cfg {Number} labelWidth set the width of label (0-12)
32543 * @cfg {String} labelAlign (top|left)
32544 * @cfg {Boolean} dayAllowBlank (true|false) default false
32545 * @cfg {Boolean} monthAllowBlank (true|false) default false
32546 * @cfg {Boolean} yearAllowBlank (true|false) default false
32547 * @cfg {string} dayPlaceholder
32548 * @cfg {string} monthPlaceholder
32549 * @cfg {string} yearPlaceholder
32550 * @cfg {string} dayFormat default 'd'
32551 * @cfg {string} monthFormat default 'm'
32552 * @cfg {string} yearFormat default 'Y'
32553 * @cfg {Number} labellg set the width of label (1-12)
32554 * @cfg {Number} labelmd set the width of label (1-12)
32555 * @cfg {Number} labelsm set the width of label (1-12)
32556 * @cfg {Number} labelxs set the width of label (1-12)
32560 * Create a new DateSplitField
32561 * @param {Object} config The config object
32564 Roo.bootstrap.DateSplitField = function(config){
32565 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32571 * getting the data of years
32572 * @param {Roo.bootstrap.DateSplitField} this
32573 * @param {Object} years
32578 * getting the data of days
32579 * @param {Roo.bootstrap.DateSplitField} this
32580 * @param {Object} days
32585 * Fires after the field has been marked as invalid.
32586 * @param {Roo.form.Field} this
32587 * @param {String} msg The validation message
32592 * Fires after the field has been validated with no errors.
32593 * @param {Roo.form.Field} this
32599 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
32602 labelAlign : 'top',
32604 dayAllowBlank : false,
32605 monthAllowBlank : false,
32606 yearAllowBlank : false,
32607 dayPlaceholder : '',
32608 monthPlaceholder : '',
32609 yearPlaceholder : '',
32613 isFormField : true,
32619 getAutoCreate : function()
32623 cls : 'row roo-date-split-field-group',
32628 cls : 'form-hidden-field roo-date-split-field-group-value',
32634 var labelCls = 'col-md-12';
32635 var contentCls = 'col-md-4';
32637 if(this.fieldLabel){
32641 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32645 html : this.fieldLabel
32650 if(this.labelAlign == 'left'){
32652 if(this.labelWidth > 12){
32653 label.style = "width: " + this.labelWidth + 'px';
32656 if(this.labelWidth < 13 && this.labelmd == 0){
32657 this.labelmd = this.labelWidth;
32660 if(this.labellg > 0){
32661 labelCls = ' col-lg-' + this.labellg;
32662 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32665 if(this.labelmd > 0){
32666 labelCls = ' col-md-' + this.labelmd;
32667 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32670 if(this.labelsm > 0){
32671 labelCls = ' col-sm-' + this.labelsm;
32672 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32675 if(this.labelxs > 0){
32676 labelCls = ' col-xs-' + this.labelxs;
32677 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32681 label.cls += ' ' + labelCls;
32683 cfg.cn.push(label);
32686 Roo.each(['day', 'month', 'year'], function(t){
32689 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32696 inputEl: function ()
32698 return this.el.select('.roo-date-split-field-group-value', true).first();
32701 onRender : function(ct, position)
32705 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32707 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32709 this.dayField = new Roo.bootstrap.ComboBox({
32710 allowBlank : this.dayAllowBlank,
32711 alwaysQuery : true,
32712 displayField : 'value',
32715 forceSelection : true,
32717 placeholder : this.dayPlaceholder,
32718 selectOnFocus : true,
32719 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32720 triggerAction : 'all',
32722 valueField : 'value',
32723 store : new Roo.data.SimpleStore({
32724 data : (function() {
32726 _this.fireEvent('days', _this, days);
32729 fields : [ 'value' ]
32732 select : function (_self, record, index)
32734 _this.setValue(_this.getValue());
32739 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32741 this.monthField = new Roo.bootstrap.MonthField({
32742 after : '<i class=\"fa fa-calendar\"></i>',
32743 allowBlank : this.monthAllowBlank,
32744 placeholder : this.monthPlaceholder,
32747 render : function (_self)
32749 this.el.select('span.input-group-addon', true).first().on('click', function(e){
32750 e.preventDefault();
32754 select : function (_self, oldvalue, newvalue)
32756 _this.setValue(_this.getValue());
32761 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32763 this.yearField = new Roo.bootstrap.ComboBox({
32764 allowBlank : this.yearAllowBlank,
32765 alwaysQuery : true,
32766 displayField : 'value',
32769 forceSelection : true,
32771 placeholder : this.yearPlaceholder,
32772 selectOnFocus : true,
32773 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32774 triggerAction : 'all',
32776 valueField : 'value',
32777 store : new Roo.data.SimpleStore({
32778 data : (function() {
32780 _this.fireEvent('years', _this, years);
32783 fields : [ 'value' ]
32786 select : function (_self, record, index)
32788 _this.setValue(_this.getValue());
32793 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32796 setValue : function(v, format)
32798 this.inputEl.dom.value = v;
32800 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32802 var d = Date.parseDate(v, f);
32809 this.setDay(d.format(this.dayFormat));
32810 this.setMonth(d.format(this.monthFormat));
32811 this.setYear(d.format(this.yearFormat));
32818 setDay : function(v)
32820 this.dayField.setValue(v);
32821 this.inputEl.dom.value = this.getValue();
32826 setMonth : function(v)
32828 this.monthField.setValue(v, true);
32829 this.inputEl.dom.value = this.getValue();
32834 setYear : function(v)
32836 this.yearField.setValue(v);
32837 this.inputEl.dom.value = this.getValue();
32842 getDay : function()
32844 return this.dayField.getValue();
32847 getMonth : function()
32849 return this.monthField.getValue();
32852 getYear : function()
32854 return this.yearField.getValue();
32857 getValue : function()
32859 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32861 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32871 this.inputEl.dom.value = '';
32876 validate : function()
32878 var d = this.dayField.validate();
32879 var m = this.monthField.validate();
32880 var y = this.yearField.validate();
32885 (!this.dayAllowBlank && !d) ||
32886 (!this.monthAllowBlank && !m) ||
32887 (!this.yearAllowBlank && !y)
32892 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32901 this.markInvalid();
32906 markValid : function()
32909 var label = this.el.select('label', true).first();
32910 var icon = this.el.select('i.fa-star', true).first();
32916 this.fireEvent('valid', this);
32920 * Mark this field as invalid
32921 * @param {String} msg The validation message
32923 markInvalid : function(msg)
32926 var label = this.el.select('label', true).first();
32927 var icon = this.el.select('i.fa-star', true).first();
32929 if(label && !icon){
32930 this.el.select('.roo-date-split-field-label', true).createChild({
32932 cls : 'text-danger fa fa-lg fa-star',
32933 tooltip : 'This field is required',
32934 style : 'margin-right:5px;'
32938 this.fireEvent('invalid', this, msg);
32941 clearInvalid : function()
32943 var label = this.el.select('label', true).first();
32944 var icon = this.el.select('i.fa-star', true).first();
32950 this.fireEvent('valid', this);
32953 getName: function()
32963 * http://masonry.desandro.com
32965 * The idea is to render all the bricks based on vertical width...
32967 * The original code extends 'outlayer' - we might need to use that....
32973 * @class Roo.bootstrap.LayoutMasonry
32974 * @extends Roo.bootstrap.Component
32975 * Bootstrap Layout Masonry class
32978 * Create a new Element
32979 * @param {Object} config The config object
32982 Roo.bootstrap.LayoutMasonry = function(config){
32984 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32988 Roo.bootstrap.LayoutMasonry.register(this);
32994 * Fire after layout the items
32995 * @param {Roo.bootstrap.LayoutMasonry} this
32996 * @param {Roo.EventObject} e
33003 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33006 * @cfg {Boolean} isLayoutInstant = no animation?
33008 isLayoutInstant : false, // needed?
33011 * @cfg {Number} boxWidth width of the columns
33016 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33021 * @cfg {Number} padWidth padding below box..
33026 * @cfg {Number} gutter gutter width..
33031 * @cfg {Number} maxCols maximum number of columns
33037 * @cfg {Boolean} isAutoInitial defalut true
33039 isAutoInitial : true,
33044 * @cfg {Boolean} isHorizontal defalut false
33046 isHorizontal : false,
33048 currentSize : null,
33054 bricks: null, //CompositeElement
33058 _isLayoutInited : false,
33060 // isAlternative : false, // only use for vertical layout...
33063 * @cfg {Number} alternativePadWidth padding below box..
33065 alternativePadWidth : 50,
33067 selectedBrick : [],
33069 getAutoCreate : function(){
33071 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33075 cls: 'blog-masonary-wrapper ' + this.cls,
33077 cls : 'mas-boxes masonary'
33084 getChildContainer: function( )
33086 if (this.boxesEl) {
33087 return this.boxesEl;
33090 this.boxesEl = this.el.select('.mas-boxes').first();
33092 return this.boxesEl;
33096 initEvents : function()
33100 if(this.isAutoInitial){
33101 Roo.log('hook children rendered');
33102 this.on('childrenrendered', function() {
33103 Roo.log('children rendered');
33109 initial : function()
33111 this.selectedBrick = [];
33113 this.currentSize = this.el.getBox(true);
33115 Roo.EventManager.onWindowResize(this.resize, this);
33117 if(!this.isAutoInitial){
33125 //this.layout.defer(500,this);
33129 resize : function()
33131 var cs = this.el.getBox(true);
33134 this.currentSize.width == cs.width &&
33135 this.currentSize.x == cs.x &&
33136 this.currentSize.height == cs.height &&
33137 this.currentSize.y == cs.y
33139 Roo.log("no change in with or X or Y");
33143 this.currentSize = cs;
33149 layout : function()
33151 this._resetLayout();
33153 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33155 this.layoutItems( isInstant );
33157 this._isLayoutInited = true;
33159 this.fireEvent('layout', this);
33163 _resetLayout : function()
33165 if(this.isHorizontal){
33166 this.horizontalMeasureColumns();
33170 this.verticalMeasureColumns();
33174 verticalMeasureColumns : function()
33176 this.getContainerWidth();
33178 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33179 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33183 var boxWidth = this.boxWidth + this.padWidth;
33185 if(this.containerWidth < this.boxWidth){
33186 boxWidth = this.containerWidth
33189 var containerWidth = this.containerWidth;
33191 var cols = Math.floor(containerWidth / boxWidth);
33193 this.cols = Math.max( cols, 1 );
33195 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33197 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33199 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33201 this.colWidth = boxWidth + avail - this.padWidth;
33203 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33204 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33207 horizontalMeasureColumns : function()
33209 this.getContainerWidth();
33211 var boxWidth = this.boxWidth;
33213 if(this.containerWidth < boxWidth){
33214 boxWidth = this.containerWidth;
33217 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33219 this.el.setHeight(boxWidth);
33223 getContainerWidth : function()
33225 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33228 layoutItems : function( isInstant )
33230 Roo.log(this.bricks);
33232 var items = Roo.apply([], this.bricks);
33234 if(this.isHorizontal){
33235 this._horizontalLayoutItems( items , isInstant );
33239 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33240 // this._verticalAlternativeLayoutItems( items , isInstant );
33244 this._verticalLayoutItems( items , isInstant );
33248 _verticalLayoutItems : function ( items , isInstant)
33250 if ( !items || !items.length ) {
33255 ['xs', 'xs', 'xs', 'tall'],
33256 ['xs', 'xs', 'tall'],
33257 ['xs', 'xs', 'sm'],
33258 ['xs', 'xs', 'xs'],
33264 ['sm', 'xs', 'xs'],
33268 ['tall', 'xs', 'xs', 'xs'],
33269 ['tall', 'xs', 'xs'],
33281 Roo.each(items, function(item, k){
33283 switch (item.size) {
33284 // these layouts take up a full box,
33295 boxes.push([item]);
33318 var filterPattern = function(box, length)
33326 var pattern = box.slice(0, length);
33330 Roo.each(pattern, function(i){
33331 format.push(i.size);
33334 Roo.each(standard, function(s){
33336 if(String(s) != String(format)){
33345 if(!match && length == 1){
33350 filterPattern(box, length - 1);
33354 queue.push(pattern);
33356 box = box.slice(length, box.length);
33358 filterPattern(box, 4);
33364 Roo.each(boxes, function(box, k){
33370 if(box.length == 1){
33375 filterPattern(box, 4);
33379 this._processVerticalLayoutQueue( queue, isInstant );
33383 // _verticalAlternativeLayoutItems : function( items , isInstant )
33385 // if ( !items || !items.length ) {
33389 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33393 _horizontalLayoutItems : function ( items , isInstant)
33395 if ( !items || !items.length || items.length < 3) {
33401 var eItems = items.slice(0, 3);
33403 items = items.slice(3, items.length);
33406 ['xs', 'xs', 'xs', 'wide'],
33407 ['xs', 'xs', 'wide'],
33408 ['xs', 'xs', 'sm'],
33409 ['xs', 'xs', 'xs'],
33415 ['sm', 'xs', 'xs'],
33419 ['wide', 'xs', 'xs', 'xs'],
33420 ['wide', 'xs', 'xs'],
33433 Roo.each(items, function(item, k){
33435 switch (item.size) {
33446 boxes.push([item]);
33470 var filterPattern = function(box, length)
33478 var pattern = box.slice(0, length);
33482 Roo.each(pattern, function(i){
33483 format.push(i.size);
33486 Roo.each(standard, function(s){
33488 if(String(s) != String(format)){
33497 if(!match && length == 1){
33502 filterPattern(box, length - 1);
33506 queue.push(pattern);
33508 box = box.slice(length, box.length);
33510 filterPattern(box, 4);
33516 Roo.each(boxes, function(box, k){
33522 if(box.length == 1){
33527 filterPattern(box, 4);
33534 var pos = this.el.getBox(true);
33538 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33540 var hit_end = false;
33542 Roo.each(queue, function(box){
33546 Roo.each(box, function(b){
33548 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33558 Roo.each(box, function(b){
33560 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33563 mx = Math.max(mx, b.x);
33567 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33571 Roo.each(box, function(b){
33573 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33587 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33590 /** Sets position of item in DOM
33591 * @param {Element} item
33592 * @param {Number} x - horizontal position
33593 * @param {Number} y - vertical position
33594 * @param {Boolean} isInstant - disables transitions
33596 _processVerticalLayoutQueue : function( queue, isInstant )
33598 var pos = this.el.getBox(true);
33603 for (var i = 0; i < this.cols; i++){
33607 Roo.each(queue, function(box, k){
33609 var col = k % this.cols;
33611 Roo.each(box, function(b,kk){
33613 b.el.position('absolute');
33615 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33616 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33618 if(b.size == 'md-left' || b.size == 'md-right'){
33619 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33620 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33623 b.el.setWidth(width);
33624 b.el.setHeight(height);
33626 b.el.select('iframe',true).setSize(width,height);
33630 for (var i = 0; i < this.cols; i++){
33632 if(maxY[i] < maxY[col]){
33637 col = Math.min(col, i);
33641 x = pos.x + col * (this.colWidth + this.padWidth);
33645 var positions = [];
33647 switch (box.length){
33649 positions = this.getVerticalOneBoxColPositions(x, y, box);
33652 positions = this.getVerticalTwoBoxColPositions(x, y, box);
33655 positions = this.getVerticalThreeBoxColPositions(x, y, box);
33658 positions = this.getVerticalFourBoxColPositions(x, y, box);
33664 Roo.each(box, function(b,kk){
33666 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33668 var sz = b.el.getSize();
33670 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33678 for (var i = 0; i < this.cols; i++){
33679 mY = Math.max(mY, maxY[i]);
33682 this.el.setHeight(mY - pos.y);
33686 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33688 // var pos = this.el.getBox(true);
33691 // var maxX = pos.right;
33693 // var maxHeight = 0;
33695 // Roo.each(items, function(item, k){
33699 // item.el.position('absolute');
33701 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33703 // item.el.setWidth(width);
33705 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33707 // item.el.setHeight(height);
33710 // item.el.setXY([x, y], isInstant ? false : true);
33712 // item.el.setXY([maxX - width, y], isInstant ? false : true);
33715 // y = y + height + this.alternativePadWidth;
33717 // maxHeight = maxHeight + height + this.alternativePadWidth;
33721 // this.el.setHeight(maxHeight);
33725 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33727 var pos = this.el.getBox(true);
33732 var maxX = pos.right;
33734 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33736 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33738 Roo.each(queue, function(box, k){
33740 Roo.each(box, function(b, kk){
33742 b.el.position('absolute');
33744 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33745 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33747 if(b.size == 'md-left' || b.size == 'md-right'){
33748 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33749 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33752 b.el.setWidth(width);
33753 b.el.setHeight(height);
33761 var positions = [];
33763 switch (box.length){
33765 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33768 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33771 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33774 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33780 Roo.each(box, function(b,kk){
33782 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33784 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33792 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33794 Roo.each(eItems, function(b,k){
33796 b.size = (k == 0) ? 'sm' : 'xs';
33797 b.x = (k == 0) ? 2 : 1;
33798 b.y = (k == 0) ? 2 : 1;
33800 b.el.position('absolute');
33802 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33804 b.el.setWidth(width);
33806 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33808 b.el.setHeight(height);
33812 var positions = [];
33815 x : maxX - this.unitWidth * 2 - this.gutter,
33820 x : maxX - this.unitWidth,
33821 y : minY + (this.unitWidth + this.gutter) * 2
33825 x : maxX - this.unitWidth * 3 - this.gutter * 2,
33829 Roo.each(eItems, function(b,k){
33831 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33837 getVerticalOneBoxColPositions : function(x, y, box)
33841 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33843 if(box[0].size == 'md-left'){
33847 if(box[0].size == 'md-right'){
33852 x : x + (this.unitWidth + this.gutter) * rand,
33859 getVerticalTwoBoxColPositions : function(x, y, box)
33863 if(box[0].size == 'xs'){
33867 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33871 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33885 x : x + (this.unitWidth + this.gutter) * 2,
33886 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33893 getVerticalThreeBoxColPositions : function(x, y, box)
33897 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33905 x : x + (this.unitWidth + this.gutter) * 1,
33910 x : x + (this.unitWidth + this.gutter) * 2,
33918 if(box[0].size == 'xs' && box[1].size == 'xs'){
33927 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33931 x : x + (this.unitWidth + this.gutter) * 1,
33945 x : x + (this.unitWidth + this.gutter) * 2,
33950 x : x + (this.unitWidth + this.gutter) * 2,
33951 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33958 getVerticalFourBoxColPositions : function(x, y, box)
33962 if(box[0].size == 'xs'){
33971 y : y + (this.unitHeight + this.gutter) * 1
33976 y : y + (this.unitHeight + this.gutter) * 2
33980 x : x + (this.unitWidth + this.gutter) * 1,
33994 x : x + (this.unitWidth + this.gutter) * 2,
33999 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34000 y : y + (this.unitHeight + this.gutter) * 1
34004 x : x + (this.unitWidth + this.gutter) * 2,
34005 y : y + (this.unitWidth + this.gutter) * 2
34012 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34016 if(box[0].size == 'md-left'){
34018 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34025 if(box[0].size == 'md-right'){
34027 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34028 y : minY + (this.unitWidth + this.gutter) * 1
34034 var rand = Math.floor(Math.random() * (4 - box[0].y));
34037 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34038 y : minY + (this.unitWidth + this.gutter) * rand
34045 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34049 if(box[0].size == 'xs'){
34052 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34057 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34058 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34066 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34071 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34072 y : minY + (this.unitWidth + this.gutter) * 2
34079 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34083 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34086 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34091 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34092 y : minY + (this.unitWidth + this.gutter) * 1
34096 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34097 y : minY + (this.unitWidth + this.gutter) * 2
34104 if(box[0].size == 'xs' && box[1].size == 'xs'){
34107 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34112 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34117 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34118 y : minY + (this.unitWidth + this.gutter) * 1
34126 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34131 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34132 y : minY + (this.unitWidth + this.gutter) * 2
34136 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34137 y : minY + (this.unitWidth + this.gutter) * 2
34144 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34148 if(box[0].size == 'xs'){
34151 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34156 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34161 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),
34166 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34167 y : minY + (this.unitWidth + this.gutter) * 1
34175 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34180 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34181 y : minY + (this.unitWidth + this.gutter) * 2
34185 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34186 y : minY + (this.unitWidth + this.gutter) * 2
34190 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),
34191 y : minY + (this.unitWidth + this.gutter) * 2
34199 * remove a Masonry Brick
34200 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34202 removeBrick : function(brick_id)
34208 for (var i = 0; i<this.bricks.length; i++) {
34209 if (this.bricks[i].id == brick_id) {
34210 this.bricks.splice(i,1);
34211 this.el.dom.removeChild(Roo.get(brick_id).dom);
34218 * adds a Masonry Brick
34219 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34221 addBrick : function(cfg)
34223 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34224 //this.register(cn);
34225 cn.parentId = this.id;
34226 cn.render(this.el);
34231 * register a Masonry Brick
34232 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34235 register : function(brick)
34237 this.bricks.push(brick);
34238 brick.masonryId = this.id;
34242 * clear all the Masonry Brick
34244 clearAll : function()
34247 //this.getChildContainer().dom.innerHTML = "";
34248 this.el.dom.innerHTML = '';
34251 getSelected : function()
34253 if (!this.selectedBrick) {
34257 return this.selectedBrick;
34261 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34265 * register a Masonry Layout
34266 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34269 register : function(layout)
34271 this.groups[layout.id] = layout;
34274 * fetch a Masonry Layout based on the masonry layout ID
34275 * @param {string} the masonry layout to add
34276 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34279 get: function(layout_id) {
34280 if (typeof(this.groups[layout_id]) == 'undefined') {
34283 return this.groups[layout_id] ;
34295 * http://masonry.desandro.com
34297 * The idea is to render all the bricks based on vertical width...
34299 * The original code extends 'outlayer' - we might need to use that....
34305 * @class Roo.bootstrap.LayoutMasonryAuto
34306 * @extends Roo.bootstrap.Component
34307 * Bootstrap Layout Masonry class
34310 * Create a new Element
34311 * @param {Object} config The config object
34314 Roo.bootstrap.LayoutMasonryAuto = function(config){
34315 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34318 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34321 * @cfg {Boolean} isFitWidth - resize the width..
34323 isFitWidth : false, // options..
34325 * @cfg {Boolean} isOriginLeft = left align?
34327 isOriginLeft : true,
34329 * @cfg {Boolean} isOriginTop = top align?
34331 isOriginTop : false,
34333 * @cfg {Boolean} isLayoutInstant = no animation?
34335 isLayoutInstant : false, // needed?
34337 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34339 isResizingContainer : true,
34341 * @cfg {Number} columnWidth width of the columns
34347 * @cfg {Number} maxCols maximum number of columns
34352 * @cfg {Number} padHeight padding below box..
34358 * @cfg {Boolean} isAutoInitial defalut true
34361 isAutoInitial : true,
34367 initialColumnWidth : 0,
34368 currentSize : null,
34370 colYs : null, // array.
34377 bricks: null, //CompositeElement
34378 cols : 0, // array?
34379 // element : null, // wrapped now this.el
34380 _isLayoutInited : null,
34383 getAutoCreate : function(){
34387 cls: 'blog-masonary-wrapper ' + this.cls,
34389 cls : 'mas-boxes masonary'
34396 getChildContainer: function( )
34398 if (this.boxesEl) {
34399 return this.boxesEl;
34402 this.boxesEl = this.el.select('.mas-boxes').first();
34404 return this.boxesEl;
34408 initEvents : function()
34412 if(this.isAutoInitial){
34413 Roo.log('hook children rendered');
34414 this.on('childrenrendered', function() {
34415 Roo.log('children rendered');
34422 initial : function()
34424 this.reloadItems();
34426 this.currentSize = this.el.getBox(true);
34428 /// was window resize... - let's see if this works..
34429 Roo.EventManager.onWindowResize(this.resize, this);
34431 if(!this.isAutoInitial){
34436 this.layout.defer(500,this);
34439 reloadItems: function()
34441 this.bricks = this.el.select('.masonry-brick', true);
34443 this.bricks.each(function(b) {
34444 //Roo.log(b.getSize());
34445 if (!b.attr('originalwidth')) {
34446 b.attr('originalwidth', b.getSize().width);
34451 Roo.log(this.bricks.elements.length);
34454 resize : function()
34457 var cs = this.el.getBox(true);
34459 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34460 Roo.log("no change in with or X");
34463 this.currentSize = cs;
34467 layout : function()
34470 this._resetLayout();
34471 //this._manageStamps();
34473 // don't animate first layout
34474 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34475 this.layoutItems( isInstant );
34477 // flag for initalized
34478 this._isLayoutInited = true;
34481 layoutItems : function( isInstant )
34483 //var items = this._getItemsForLayout( this.items );
34484 // original code supports filtering layout items.. we just ignore it..
34486 this._layoutItems( this.bricks , isInstant );
34488 this._postLayout();
34490 _layoutItems : function ( items , isInstant)
34492 //this.fireEvent( 'layout', this, items );
34495 if ( !items || !items.elements.length ) {
34496 // no items, emit event with empty array
34501 items.each(function(item) {
34502 Roo.log("layout item");
34504 // get x/y object from method
34505 var position = this._getItemLayoutPosition( item );
34507 position.item = item;
34508 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34509 queue.push( position );
34512 this._processLayoutQueue( queue );
34514 /** Sets position of item in DOM
34515 * @param {Element} item
34516 * @param {Number} x - horizontal position
34517 * @param {Number} y - vertical position
34518 * @param {Boolean} isInstant - disables transitions
34520 _processLayoutQueue : function( queue )
34522 for ( var i=0, len = queue.length; i < len; i++ ) {
34523 var obj = queue[i];
34524 obj.item.position('absolute');
34525 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34531 * Any logic you want to do after each layout,
34532 * i.e. size the container
34534 _postLayout : function()
34536 this.resizeContainer();
34539 resizeContainer : function()
34541 if ( !this.isResizingContainer ) {
34544 var size = this._getContainerSize();
34546 this.el.setSize(size.width,size.height);
34547 this.boxesEl.setSize(size.width,size.height);
34553 _resetLayout : function()
34555 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34556 this.colWidth = this.el.getWidth();
34557 //this.gutter = this.el.getWidth();
34559 this.measureColumns();
34565 this.colYs.push( 0 );
34571 measureColumns : function()
34573 this.getContainerWidth();
34574 // if columnWidth is 0, default to outerWidth of first item
34575 if ( !this.columnWidth ) {
34576 var firstItem = this.bricks.first();
34577 Roo.log(firstItem);
34578 this.columnWidth = this.containerWidth;
34579 if (firstItem && firstItem.attr('originalwidth') ) {
34580 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34582 // columnWidth fall back to item of first element
34583 Roo.log("set column width?");
34584 this.initialColumnWidth = this.columnWidth ;
34586 // if first elem has no width, default to size of container
34591 if (this.initialColumnWidth) {
34592 this.columnWidth = this.initialColumnWidth;
34597 // column width is fixed at the top - however if container width get's smaller we should
34600 // this bit calcs how man columns..
34602 var columnWidth = this.columnWidth += this.gutter;
34604 // calculate columns
34605 var containerWidth = this.containerWidth + this.gutter;
34607 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34608 // fix rounding errors, typically with gutters
34609 var excess = columnWidth - containerWidth % columnWidth;
34612 // if overshoot is less than a pixel, round up, otherwise floor it
34613 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34614 cols = Math[ mathMethod ]( cols );
34615 this.cols = Math.max( cols, 1 );
34616 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34618 // padding positioning..
34619 var totalColWidth = this.cols * this.columnWidth;
34620 var padavail = this.containerWidth - totalColWidth;
34621 // so for 2 columns - we need 3 'pads'
34623 var padNeeded = (1+this.cols) * this.padWidth;
34625 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34627 this.columnWidth += padExtra
34628 //this.padWidth = Math.floor(padavail / ( this.cols));
34630 // adjust colum width so that padding is fixed??
34632 // we have 3 columns ... total = width * 3
34633 // we have X left over... that should be used by
34635 //if (this.expandC) {
34643 getContainerWidth : function()
34645 /* // container is parent if fit width
34646 var container = this.isFitWidth ? this.element.parentNode : this.element;
34647 // check that this.size and size are there
34648 // IE8 triggers resize on body size change, so they might not be
34650 var size = getSize( container ); //FIXME
34651 this.containerWidth = size && size.innerWidth; //FIXME
34654 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34658 _getItemLayoutPosition : function( item ) // what is item?
34660 // we resize the item to our columnWidth..
34662 item.setWidth(this.columnWidth);
34663 item.autoBoxAdjust = false;
34665 var sz = item.getSize();
34667 // how many columns does this brick span
34668 var remainder = this.containerWidth % this.columnWidth;
34670 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34671 // round if off by 1 pixel, otherwise use ceil
34672 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
34673 colSpan = Math.min( colSpan, this.cols );
34675 // normally this should be '1' as we dont' currently allow multi width columns..
34677 var colGroup = this._getColGroup( colSpan );
34678 // get the minimum Y value from the columns
34679 var minimumY = Math.min.apply( Math, colGroup );
34680 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34682 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
34684 // position the brick
34686 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34687 y: this.currentSize.y + minimumY + this.padHeight
34691 // apply setHeight to necessary columns
34692 var setHeight = minimumY + sz.height + this.padHeight;
34693 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34695 var setSpan = this.cols + 1 - colGroup.length;
34696 for ( var i = 0; i < setSpan; i++ ) {
34697 this.colYs[ shortColIndex + i ] = setHeight ;
34704 * @param {Number} colSpan - number of columns the element spans
34705 * @returns {Array} colGroup
34707 _getColGroup : function( colSpan )
34709 if ( colSpan < 2 ) {
34710 // if brick spans only one column, use all the column Ys
34715 // how many different places could this brick fit horizontally
34716 var groupCount = this.cols + 1 - colSpan;
34717 // for each group potential horizontal position
34718 for ( var i = 0; i < groupCount; i++ ) {
34719 // make an array of colY values for that one group
34720 var groupColYs = this.colYs.slice( i, i + colSpan );
34721 // and get the max value of the array
34722 colGroup[i] = Math.max.apply( Math, groupColYs );
34727 _manageStamp : function( stamp )
34729 var stampSize = stamp.getSize();
34730 var offset = stamp.getBox();
34731 // get the columns that this stamp affects
34732 var firstX = this.isOriginLeft ? offset.x : offset.right;
34733 var lastX = firstX + stampSize.width;
34734 var firstCol = Math.floor( firstX / this.columnWidth );
34735 firstCol = Math.max( 0, firstCol );
34737 var lastCol = Math.floor( lastX / this.columnWidth );
34738 // lastCol should not go over if multiple of columnWidth #425
34739 lastCol -= lastX % this.columnWidth ? 0 : 1;
34740 lastCol = Math.min( this.cols - 1, lastCol );
34742 // set colYs to bottom of the stamp
34743 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34746 for ( var i = firstCol; i <= lastCol; i++ ) {
34747 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34752 _getContainerSize : function()
34754 this.maxY = Math.max.apply( Math, this.colYs );
34759 if ( this.isFitWidth ) {
34760 size.width = this._getContainerFitWidth();
34766 _getContainerFitWidth : function()
34768 var unusedCols = 0;
34769 // count unused columns
34772 if ( this.colYs[i] !== 0 ) {
34777 // fit container to columns that have been used
34778 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34781 needsResizeLayout : function()
34783 var previousWidth = this.containerWidth;
34784 this.getContainerWidth();
34785 return previousWidth !== this.containerWidth;
34800 * @class Roo.bootstrap.MasonryBrick
34801 * @extends Roo.bootstrap.Component
34802 * Bootstrap MasonryBrick class
34805 * Create a new MasonryBrick
34806 * @param {Object} config The config object
34809 Roo.bootstrap.MasonryBrick = function(config){
34811 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34813 Roo.bootstrap.MasonryBrick.register(this);
34819 * When a MasonryBrick is clcik
34820 * @param {Roo.bootstrap.MasonryBrick} this
34821 * @param {Roo.EventObject} e
34827 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
34830 * @cfg {String} title
34834 * @cfg {String} html
34838 * @cfg {String} bgimage
34842 * @cfg {String} videourl
34846 * @cfg {String} cls
34850 * @cfg {String} href
34854 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34859 * @cfg {String} placetitle (center|bottom)
34864 * @cfg {Boolean} isFitContainer defalut true
34866 isFitContainer : true,
34869 * @cfg {Boolean} preventDefault defalut false
34871 preventDefault : false,
34874 * @cfg {Boolean} inverse defalut false
34876 maskInverse : false,
34878 getAutoCreate : function()
34880 if(!this.isFitContainer){
34881 return this.getSplitAutoCreate();
34884 var cls = 'masonry-brick masonry-brick-full';
34886 if(this.href.length){
34887 cls += ' masonry-brick-link';
34890 if(this.bgimage.length){
34891 cls += ' masonry-brick-image';
34894 if(this.maskInverse){
34895 cls += ' mask-inverse';
34898 if(!this.html.length && !this.maskInverse && !this.videourl.length){
34899 cls += ' enable-mask';
34903 cls += ' masonry-' + this.size + '-brick';
34906 if(this.placetitle.length){
34908 switch (this.placetitle) {
34910 cls += ' masonry-center-title';
34913 cls += ' masonry-bottom-title';
34920 if(!this.html.length && !this.bgimage.length){
34921 cls += ' masonry-center-title';
34924 if(!this.html.length && this.bgimage.length){
34925 cls += ' masonry-bottom-title';
34930 cls += ' ' + this.cls;
34934 tag: (this.href.length) ? 'a' : 'div',
34939 cls: 'masonry-brick-mask'
34943 cls: 'masonry-brick-paragraph',
34949 if(this.href.length){
34950 cfg.href = this.href;
34953 var cn = cfg.cn[1].cn;
34955 if(this.title.length){
34958 cls: 'masonry-brick-title',
34963 if(this.html.length){
34966 cls: 'masonry-brick-text',
34971 if (!this.title.length && !this.html.length) {
34972 cfg.cn[1].cls += ' hide';
34975 if(this.bgimage.length){
34978 cls: 'masonry-brick-image-view',
34983 if(this.videourl.length){
34984 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34985 // youtube support only?
34988 cls: 'masonry-brick-image-view',
34991 allowfullscreen : true
34999 getSplitAutoCreate : function()
35001 var cls = 'masonry-brick masonry-brick-split';
35003 if(this.href.length){
35004 cls += ' masonry-brick-link';
35007 if(this.bgimage.length){
35008 cls += ' masonry-brick-image';
35012 cls += ' masonry-' + this.size + '-brick';
35015 switch (this.placetitle) {
35017 cls += ' masonry-center-title';
35020 cls += ' masonry-bottom-title';
35023 if(!this.bgimage.length){
35024 cls += ' masonry-center-title';
35027 if(this.bgimage.length){
35028 cls += ' masonry-bottom-title';
35034 cls += ' ' + this.cls;
35038 tag: (this.href.length) ? 'a' : 'div',
35043 cls: 'masonry-brick-split-head',
35047 cls: 'masonry-brick-paragraph',
35054 cls: 'masonry-brick-split-body',
35060 if(this.href.length){
35061 cfg.href = this.href;
35064 if(this.title.length){
35065 cfg.cn[0].cn[0].cn.push({
35067 cls: 'masonry-brick-title',
35072 if(this.html.length){
35073 cfg.cn[1].cn.push({
35075 cls: 'masonry-brick-text',
35080 if(this.bgimage.length){
35081 cfg.cn[0].cn.push({
35083 cls: 'masonry-brick-image-view',
35088 if(this.videourl.length){
35089 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35090 // youtube support only?
35091 cfg.cn[0].cn.cn.push({
35093 cls: 'masonry-brick-image-view',
35096 allowfullscreen : true
35103 initEvents: function()
35105 switch (this.size) {
35138 this.el.on('touchstart', this.onTouchStart, this);
35139 this.el.on('touchmove', this.onTouchMove, this);
35140 this.el.on('touchend', this.onTouchEnd, this);
35141 this.el.on('contextmenu', this.onContextMenu, this);
35143 this.el.on('mouseenter' ,this.enter, this);
35144 this.el.on('mouseleave', this.leave, this);
35145 this.el.on('click', this.onClick, this);
35148 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35149 this.parent().bricks.push(this);
35154 onClick: function(e, el)
35156 var time = this.endTimer - this.startTimer;
35157 // Roo.log(e.preventDefault());
35160 e.preventDefault();
35165 if(!this.preventDefault){
35169 e.preventDefault();
35171 if (this.activeClass != '') {
35172 this.selectBrick();
35175 this.fireEvent('click', this, e);
35178 enter: function(e, el)
35180 e.preventDefault();
35182 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35186 if(this.bgimage.length && this.html.length){
35187 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35191 leave: function(e, el)
35193 e.preventDefault();
35195 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35199 if(this.bgimage.length && this.html.length){
35200 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35204 onTouchStart: function(e, el)
35206 // e.preventDefault();
35208 this.touchmoved = false;
35210 if(!this.isFitContainer){
35214 if(!this.bgimage.length || !this.html.length){
35218 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35220 this.timer = new Date().getTime();
35224 onTouchMove: function(e, el)
35226 this.touchmoved = true;
35229 onContextMenu : function(e,el)
35231 e.preventDefault();
35232 e.stopPropagation();
35236 onTouchEnd: function(e, el)
35238 // e.preventDefault();
35240 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35247 if(!this.bgimage.length || !this.html.length){
35249 if(this.href.length){
35250 window.location.href = this.href;
35256 if(!this.isFitContainer){
35260 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35262 window.location.href = this.href;
35265 //selection on single brick only
35266 selectBrick : function() {
35268 if (!this.parentId) {
35272 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35273 var index = m.selectedBrick.indexOf(this.id);
35276 m.selectedBrick.splice(index,1);
35277 this.el.removeClass(this.activeClass);
35281 for(var i = 0; i < m.selectedBrick.length; i++) {
35282 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35283 b.el.removeClass(b.activeClass);
35286 m.selectedBrick = [];
35288 m.selectedBrick.push(this.id);
35289 this.el.addClass(this.activeClass);
35293 isSelected : function(){
35294 return this.el.hasClass(this.activeClass);
35299 Roo.apply(Roo.bootstrap.MasonryBrick, {
35302 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35304 * register a Masonry Brick
35305 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35308 register : function(brick)
35310 //this.groups[brick.id] = brick;
35311 this.groups.add(brick.id, brick);
35314 * fetch a masonry brick based on the masonry brick ID
35315 * @param {string} the masonry brick to add
35316 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35319 get: function(brick_id)
35321 // if (typeof(this.groups[brick_id]) == 'undefined') {
35324 // return this.groups[brick_id] ;
35326 if(this.groups.key(brick_id)) {
35327 return this.groups.key(brick_id);
35345 * @class Roo.bootstrap.Brick
35346 * @extends Roo.bootstrap.Component
35347 * Bootstrap Brick class
35350 * Create a new Brick
35351 * @param {Object} config The config object
35354 Roo.bootstrap.Brick = function(config){
35355 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35361 * When a Brick is click
35362 * @param {Roo.bootstrap.Brick} this
35363 * @param {Roo.EventObject} e
35369 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35372 * @cfg {String} title
35376 * @cfg {String} html
35380 * @cfg {String} bgimage
35384 * @cfg {String} cls
35388 * @cfg {String} href
35392 * @cfg {String} video
35396 * @cfg {Boolean} square
35400 getAutoCreate : function()
35402 var cls = 'roo-brick';
35404 if(this.href.length){
35405 cls += ' roo-brick-link';
35408 if(this.bgimage.length){
35409 cls += ' roo-brick-image';
35412 if(!this.html.length && !this.bgimage.length){
35413 cls += ' roo-brick-center-title';
35416 if(!this.html.length && this.bgimage.length){
35417 cls += ' roo-brick-bottom-title';
35421 cls += ' ' + this.cls;
35425 tag: (this.href.length) ? 'a' : 'div',
35430 cls: 'roo-brick-paragraph',
35436 if(this.href.length){
35437 cfg.href = this.href;
35440 var cn = cfg.cn[0].cn;
35442 if(this.title.length){
35445 cls: 'roo-brick-title',
35450 if(this.html.length){
35453 cls: 'roo-brick-text',
35460 if(this.bgimage.length){
35463 cls: 'roo-brick-image-view',
35471 initEvents: function()
35473 if(this.title.length || this.html.length){
35474 this.el.on('mouseenter' ,this.enter, this);
35475 this.el.on('mouseleave', this.leave, this);
35478 Roo.EventManager.onWindowResize(this.resize, this);
35480 if(this.bgimage.length){
35481 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35482 this.imageEl.on('load', this.onImageLoad, this);
35489 onImageLoad : function()
35494 resize : function()
35496 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35498 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35500 if(this.bgimage.length){
35501 var image = this.el.select('.roo-brick-image-view', true).first();
35503 image.setWidth(paragraph.getWidth());
35506 image.setHeight(paragraph.getWidth());
35509 this.el.setHeight(image.getHeight());
35510 paragraph.setHeight(image.getHeight());
35516 enter: function(e, el)
35518 e.preventDefault();
35520 if(this.bgimage.length){
35521 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35522 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35526 leave: function(e, el)
35528 e.preventDefault();
35530 if(this.bgimage.length){
35531 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35532 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35547 * @class Roo.bootstrap.NumberField
35548 * @extends Roo.bootstrap.Input
35549 * Bootstrap NumberField class
35555 * Create a new NumberField
35556 * @param {Object} config The config object
35559 Roo.bootstrap.NumberField = function(config){
35560 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35563 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35566 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35568 allowDecimals : true,
35570 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35572 decimalSeparator : ".",
35574 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35576 decimalPrecision : 2,
35578 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35580 allowNegative : true,
35583 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35587 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35589 minValue : Number.NEGATIVE_INFINITY,
35591 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35593 maxValue : Number.MAX_VALUE,
35595 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35597 minText : "The minimum value for this field is {0}",
35599 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35601 maxText : "The maximum value for this field is {0}",
35603 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
35604 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35606 nanText : "{0} is not a valid number",
35608 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35610 thousandsDelimiter : false,
35612 * @cfg {String} valueAlign alignment of value
35614 valueAlign : "left",
35616 getAutoCreate : function()
35618 var hiddenInput = {
35622 cls: 'hidden-number-input'
35626 hiddenInput.name = this.name;
35631 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35633 this.name = hiddenInput.name;
35635 if(cfg.cn.length > 0) {
35636 cfg.cn.push(hiddenInput);
35643 initEvents : function()
35645 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35647 var allowed = "0123456789";
35649 if(this.allowDecimals){
35650 allowed += this.decimalSeparator;
35653 if(this.allowNegative){
35657 if(this.thousandsDelimiter) {
35661 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35663 var keyPress = function(e){
35665 var k = e.getKey();
35667 var c = e.getCharCode();
35670 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35671 allowed.indexOf(String.fromCharCode(c)) === -1
35677 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35681 if(allowed.indexOf(String.fromCharCode(c)) === -1){
35686 this.el.on("keypress", keyPress, this);
35689 validateValue : function(value)
35692 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35696 var num = this.parseValue(value);
35699 this.markInvalid(String.format(this.nanText, value));
35703 if(num < this.minValue){
35704 this.markInvalid(String.format(this.minText, this.minValue));
35708 if(num > this.maxValue){
35709 this.markInvalid(String.format(this.maxText, this.maxValue));
35716 getValue : function()
35718 var v = this.hiddenEl().getValue();
35720 return this.fixPrecision(this.parseValue(v));
35723 parseValue : function(value)
35725 if(this.thousandsDelimiter) {
35727 r = new RegExp(",", "g");
35728 value = value.replace(r, "");
35731 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35732 return isNaN(value) ? '' : value;
35735 fixPrecision : function(value)
35737 if(this.thousandsDelimiter) {
35739 r = new RegExp(",", "g");
35740 value = value.replace(r, "");
35743 var nan = isNaN(value);
35745 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35746 return nan ? '' : value;
35748 return parseFloat(value).toFixed(this.decimalPrecision);
35751 setValue : function(v)
35753 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35759 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35761 this.inputEl().dom.value = (v == '') ? '' :
35762 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35764 if(!this.allowZero && v === '0') {
35765 this.hiddenEl().dom.value = '';
35766 this.inputEl().dom.value = '';
35773 decimalPrecisionFcn : function(v)
35775 return Math.floor(v);
35778 beforeBlur : function()
35780 var v = this.parseValue(this.getRawValue());
35782 if(v || v === 0 || v === ''){
35787 hiddenEl : function()
35789 return this.el.select('input.hidden-number-input',true).first();
35801 * @class Roo.bootstrap.DocumentSlider
35802 * @extends Roo.bootstrap.Component
35803 * Bootstrap DocumentSlider class
35806 * Create a new DocumentViewer
35807 * @param {Object} config The config object
35810 Roo.bootstrap.DocumentSlider = function(config){
35811 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35818 * Fire after initEvent
35819 * @param {Roo.bootstrap.DocumentSlider} this
35824 * Fire after update
35825 * @param {Roo.bootstrap.DocumentSlider} this
35831 * @param {Roo.bootstrap.DocumentSlider} this
35837 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
35843 getAutoCreate : function()
35847 cls : 'roo-document-slider',
35851 cls : 'roo-document-slider-header',
35855 cls : 'roo-document-slider-header-title'
35861 cls : 'roo-document-slider-body',
35865 cls : 'roo-document-slider-prev',
35869 cls : 'fa fa-chevron-left'
35875 cls : 'roo-document-slider-thumb',
35879 cls : 'roo-document-slider-image'
35885 cls : 'roo-document-slider-next',
35889 cls : 'fa fa-chevron-right'
35901 initEvents : function()
35903 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35904 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35906 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35907 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35909 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35910 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35912 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35913 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35915 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35916 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35918 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35919 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35921 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35922 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35924 this.thumbEl.on('click', this.onClick, this);
35926 this.prevIndicator.on('click', this.prev, this);
35928 this.nextIndicator.on('click', this.next, this);
35932 initial : function()
35934 if(this.files.length){
35935 this.indicator = 1;
35939 this.fireEvent('initial', this);
35942 update : function()
35944 this.imageEl.attr('src', this.files[this.indicator - 1]);
35946 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35948 this.prevIndicator.show();
35950 if(this.indicator == 1){
35951 this.prevIndicator.hide();
35954 this.nextIndicator.show();
35956 if(this.indicator == this.files.length){
35957 this.nextIndicator.hide();
35960 this.thumbEl.scrollTo('top');
35962 this.fireEvent('update', this);
35965 onClick : function(e)
35967 e.preventDefault();
35969 this.fireEvent('click', this);
35974 e.preventDefault();
35976 this.indicator = Math.max(1, this.indicator - 1);
35983 e.preventDefault();
35985 this.indicator = Math.min(this.files.length, this.indicator + 1);
35999 * @class Roo.bootstrap.RadioSet
36000 * @extends Roo.bootstrap.Input
36001 * Bootstrap RadioSet class
36002 * @cfg {String} indicatorpos (left|right) default left
36003 * @cfg {Boolean} inline (true|false) inline the element (default true)
36004 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36006 * Create a new RadioSet
36007 * @param {Object} config The config object
36010 Roo.bootstrap.RadioSet = function(config){
36012 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36016 Roo.bootstrap.RadioSet.register(this);
36021 * Fires when the element is checked or unchecked.
36022 * @param {Roo.bootstrap.RadioSet} this This radio
36023 * @param {Roo.bootstrap.Radio} item The checked item
36028 * Fires when the element is click.
36029 * @param {Roo.bootstrap.RadioSet} this This radio set
36030 * @param {Roo.bootstrap.Radio} item The checked item
36031 * @param {Roo.EventObject} e The event object
36038 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36046 indicatorpos : 'left',
36048 getAutoCreate : function()
36052 cls : 'roo-radio-set-label',
36056 html : this.fieldLabel
36060 if (Roo.bootstrap.version == 3) {
36063 if(this.indicatorpos == 'left'){
36066 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36067 tooltip : 'This field is required'
36072 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36073 tooltip : 'This field is required'
36079 cls : 'roo-radio-set-items'
36082 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36084 if (align === 'left' && this.fieldLabel.length) {
36087 cls : "roo-radio-set-right",
36093 if(this.labelWidth > 12){
36094 label.style = "width: " + this.labelWidth + 'px';
36097 if(this.labelWidth < 13 && this.labelmd == 0){
36098 this.labelmd = this.labelWidth;
36101 if(this.labellg > 0){
36102 label.cls += ' col-lg-' + this.labellg;
36103 items.cls += ' col-lg-' + (12 - this.labellg);
36106 if(this.labelmd > 0){
36107 label.cls += ' col-md-' + this.labelmd;
36108 items.cls += ' col-md-' + (12 - this.labelmd);
36111 if(this.labelsm > 0){
36112 label.cls += ' col-sm-' + this.labelsm;
36113 items.cls += ' col-sm-' + (12 - this.labelsm);
36116 if(this.labelxs > 0){
36117 label.cls += ' col-xs-' + this.labelxs;
36118 items.cls += ' col-xs-' + (12 - this.labelxs);
36124 cls : 'roo-radio-set',
36128 cls : 'roo-radio-set-input',
36131 value : this.value ? this.value : ''
36138 if(this.weight.length){
36139 cfg.cls += ' roo-radio-' + this.weight;
36143 cfg.cls += ' roo-radio-set-inline';
36147 ['xs','sm','md','lg'].map(function(size){
36148 if (settings[size]) {
36149 cfg.cls += ' col-' + size + '-' + settings[size];
36157 initEvents : function()
36159 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36160 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36162 if(!this.fieldLabel.length){
36163 this.labelEl.hide();
36166 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36167 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36169 this.indicator = this.indicatorEl();
36171 if(this.indicator){
36172 this.indicator.addClass('invisible');
36175 this.originalValue = this.getValue();
36179 inputEl: function ()
36181 return this.el.select('.roo-radio-set-input', true).first();
36184 getChildContainer : function()
36186 return this.itemsEl;
36189 register : function(item)
36191 this.radioes.push(item);
36195 validate : function()
36197 if(this.getVisibilityEl().hasClass('hidden')){
36203 Roo.each(this.radioes, function(i){
36212 if(this.allowBlank) {
36216 if(this.disabled || valid){
36221 this.markInvalid();
36226 markValid : function()
36228 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36229 this.indicatorEl().removeClass('visible');
36230 this.indicatorEl().addClass('invisible');
36234 if (Roo.bootstrap.version == 3) {
36235 this.el.removeClass([this.invalidClass, this.validClass]);
36236 this.el.addClass(this.validClass);
36238 this.el.removeClass(['is-invalid','is-valid']);
36239 this.el.addClass(['is-valid']);
36241 this.fireEvent('valid', this);
36244 markInvalid : function(msg)
36246 if(this.allowBlank || this.disabled){
36250 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36251 this.indicatorEl().removeClass('invisible');
36252 this.indicatorEl().addClass('visible');
36254 if (Roo.bootstrap.version == 3) {
36255 this.el.removeClass([this.invalidClass, this.validClass]);
36256 this.el.addClass(this.invalidClass);
36258 this.el.removeClass(['is-invalid','is-valid']);
36259 this.el.addClass(['is-invalid']);
36262 this.fireEvent('invalid', this, msg);
36266 setValue : function(v, suppressEvent)
36268 if(this.value === v){
36275 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36278 Roo.each(this.radioes, function(i){
36280 i.el.removeClass('checked');
36283 Roo.each(this.radioes, function(i){
36285 if(i.value === v || i.value.toString() === v.toString()){
36287 i.el.addClass('checked');
36289 if(suppressEvent !== true){
36290 this.fireEvent('check', this, i);
36301 clearInvalid : function(){
36303 if(!this.el || this.preventMark){
36307 this.el.removeClass([this.invalidClass]);
36309 this.fireEvent('valid', this);
36314 Roo.apply(Roo.bootstrap.RadioSet, {
36318 register : function(set)
36320 this.groups[set.name] = set;
36323 get: function(name)
36325 if (typeof(this.groups[name]) == 'undefined') {
36329 return this.groups[name] ;
36335 * Ext JS Library 1.1.1
36336 * Copyright(c) 2006-2007, Ext JS, LLC.
36338 * Originally Released Under LGPL - original licence link has changed is not relivant.
36341 * <script type="text/javascript">
36346 * @class Roo.bootstrap.SplitBar
36347 * @extends Roo.util.Observable
36348 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36352 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36353 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36354 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36355 split.minSize = 100;
36356 split.maxSize = 600;
36357 split.animate = true;
36358 split.on('moved', splitterMoved);
36361 * Create a new SplitBar
36362 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36363 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36364 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36365 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36366 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36367 position of the SplitBar).
36369 Roo.bootstrap.SplitBar = function(cfg){
36374 // dragElement : elm
36375 // resizingElement: el,
36377 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36378 // placement : Roo.bootstrap.SplitBar.LEFT ,
36379 // existingProxy ???
36382 this.el = Roo.get(cfg.dragElement, true);
36383 this.el.dom.unselectable = "on";
36385 this.resizingEl = Roo.get(cfg.resizingElement, true);
36389 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36390 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36393 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36396 * The minimum size of the resizing element. (Defaults to 0)
36402 * The maximum size of the resizing element. (Defaults to 2000)
36405 this.maxSize = 2000;
36408 * Whether to animate the transition to the new size
36411 this.animate = false;
36414 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36417 this.useShim = false;
36422 if(!cfg.existingProxy){
36424 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36426 this.proxy = Roo.get(cfg.existingProxy).dom;
36429 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36432 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36435 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36438 this.dragSpecs = {};
36441 * @private The adapter to use to positon and resize elements
36443 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36444 this.adapter.init(this);
36446 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36448 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36449 this.el.addClass("roo-splitbar-h");
36452 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36453 this.el.addClass("roo-splitbar-v");
36459 * Fires when the splitter is moved (alias for {@link #event-moved})
36460 * @param {Roo.bootstrap.SplitBar} this
36461 * @param {Number} newSize the new width or height
36466 * Fires when the splitter is moved
36467 * @param {Roo.bootstrap.SplitBar} this
36468 * @param {Number} newSize the new width or height
36472 * @event beforeresize
36473 * Fires before the splitter is dragged
36474 * @param {Roo.bootstrap.SplitBar} this
36476 "beforeresize" : true,
36478 "beforeapply" : true
36481 Roo.util.Observable.call(this);
36484 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36485 onStartProxyDrag : function(x, y){
36486 this.fireEvent("beforeresize", this);
36488 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36490 o.enableDisplayMode("block");
36491 // all splitbars share the same overlay
36492 Roo.bootstrap.SplitBar.prototype.overlay = o;
36494 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36495 this.overlay.show();
36496 Roo.get(this.proxy).setDisplayed("block");
36497 var size = this.adapter.getElementSize(this);
36498 this.activeMinSize = this.getMinimumSize();;
36499 this.activeMaxSize = this.getMaximumSize();;
36500 var c1 = size - this.activeMinSize;
36501 var c2 = Math.max(this.activeMaxSize - size, 0);
36502 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36503 this.dd.resetConstraints();
36504 this.dd.setXConstraint(
36505 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36506 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36508 this.dd.setYConstraint(0, 0);
36510 this.dd.resetConstraints();
36511 this.dd.setXConstraint(0, 0);
36512 this.dd.setYConstraint(
36513 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36514 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36517 this.dragSpecs.startSize = size;
36518 this.dragSpecs.startPoint = [x, y];
36519 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36523 * @private Called after the drag operation by the DDProxy
36525 onEndProxyDrag : function(e){
36526 Roo.get(this.proxy).setDisplayed(false);
36527 var endPoint = Roo.lib.Event.getXY(e);
36529 this.overlay.hide();
36532 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36533 newSize = this.dragSpecs.startSize +
36534 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36535 endPoint[0] - this.dragSpecs.startPoint[0] :
36536 this.dragSpecs.startPoint[0] - endPoint[0]
36539 newSize = this.dragSpecs.startSize +
36540 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36541 endPoint[1] - this.dragSpecs.startPoint[1] :
36542 this.dragSpecs.startPoint[1] - endPoint[1]
36545 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36546 if(newSize != this.dragSpecs.startSize){
36547 if(this.fireEvent('beforeapply', this, newSize) !== false){
36548 this.adapter.setElementSize(this, newSize);
36549 this.fireEvent("moved", this, newSize);
36550 this.fireEvent("resize", this, newSize);
36556 * Get the adapter this SplitBar uses
36557 * @return The adapter object
36559 getAdapter : function(){
36560 return this.adapter;
36564 * Set the adapter this SplitBar uses
36565 * @param {Object} adapter A SplitBar adapter object
36567 setAdapter : function(adapter){
36568 this.adapter = adapter;
36569 this.adapter.init(this);
36573 * Gets the minimum size for the resizing element
36574 * @return {Number} The minimum size
36576 getMinimumSize : function(){
36577 return this.minSize;
36581 * Sets the minimum size for the resizing element
36582 * @param {Number} minSize The minimum size
36584 setMinimumSize : function(minSize){
36585 this.minSize = minSize;
36589 * Gets the maximum size for the resizing element
36590 * @return {Number} The maximum size
36592 getMaximumSize : function(){
36593 return this.maxSize;
36597 * Sets the maximum size for the resizing element
36598 * @param {Number} maxSize The maximum size
36600 setMaximumSize : function(maxSize){
36601 this.maxSize = maxSize;
36605 * Sets the initialize size for the resizing element
36606 * @param {Number} size The initial size
36608 setCurrentSize : function(size){
36609 var oldAnimate = this.animate;
36610 this.animate = false;
36611 this.adapter.setElementSize(this, size);
36612 this.animate = oldAnimate;
36616 * Destroy this splitbar.
36617 * @param {Boolean} removeEl True to remove the element
36619 destroy : function(removeEl){
36621 this.shim.remove();
36624 this.proxy.parentNode.removeChild(this.proxy);
36632 * @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.
36634 Roo.bootstrap.SplitBar.createProxy = function(dir){
36635 var proxy = new Roo.Element(document.createElement("div"));
36636 proxy.unselectable();
36637 var cls = 'roo-splitbar-proxy';
36638 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36639 document.body.appendChild(proxy.dom);
36644 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36645 * Default Adapter. It assumes the splitter and resizing element are not positioned
36646 * elements and only gets/sets the width of the element. Generally used for table based layouts.
36648 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36651 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36652 // do nothing for now
36653 init : function(s){
36657 * Called before drag operations to get the current size of the resizing element.
36658 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36660 getElementSize : function(s){
36661 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36662 return s.resizingEl.getWidth();
36664 return s.resizingEl.getHeight();
36669 * Called after drag operations to set the size of the resizing element.
36670 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36671 * @param {Number} newSize The new size to set
36672 * @param {Function} onComplete A function to be invoked when resizing is complete
36674 setElementSize : function(s, newSize, onComplete){
36675 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36677 s.resizingEl.setWidth(newSize);
36679 onComplete(s, newSize);
36682 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36687 s.resizingEl.setHeight(newSize);
36689 onComplete(s, newSize);
36692 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36699 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36700 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36701 * Adapter that moves the splitter element to align with the resized sizing element.
36702 * Used with an absolute positioned SplitBar.
36703 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36704 * document.body, make sure you assign an id to the body element.
36706 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36707 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36708 this.container = Roo.get(container);
36711 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36712 init : function(s){
36713 this.basic.init(s);
36716 getElementSize : function(s){
36717 return this.basic.getElementSize(s);
36720 setElementSize : function(s, newSize, onComplete){
36721 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36724 moveSplitter : function(s){
36725 var yes = Roo.bootstrap.SplitBar;
36726 switch(s.placement){
36728 s.el.setX(s.resizingEl.getRight());
36731 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36734 s.el.setY(s.resizingEl.getBottom());
36737 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36744 * Orientation constant - Create a vertical SplitBar
36748 Roo.bootstrap.SplitBar.VERTICAL = 1;
36751 * Orientation constant - Create a horizontal SplitBar
36755 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36758 * Placement constant - The resizing element is to the left of the splitter element
36762 Roo.bootstrap.SplitBar.LEFT = 1;
36765 * Placement constant - The resizing element is to the right of the splitter element
36769 Roo.bootstrap.SplitBar.RIGHT = 2;
36772 * Placement constant - The resizing element is positioned above the splitter element
36776 Roo.bootstrap.SplitBar.TOP = 3;
36779 * Placement constant - The resizing element is positioned under splitter element
36783 Roo.bootstrap.SplitBar.BOTTOM = 4;
36784 Roo.namespace("Roo.bootstrap.layout");/*
36786 * Ext JS Library 1.1.1
36787 * Copyright(c) 2006-2007, Ext JS, LLC.
36789 * Originally Released Under LGPL - original licence link has changed is not relivant.
36792 * <script type="text/javascript">
36796 * @class Roo.bootstrap.layout.Manager
36797 * @extends Roo.bootstrap.Component
36798 * Base class for layout managers.
36800 Roo.bootstrap.layout.Manager = function(config)
36802 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36808 /** false to disable window resize monitoring @type Boolean */
36809 this.monitorWindowResize = true;
36814 * Fires when a layout is performed.
36815 * @param {Roo.LayoutManager} this
36819 * @event regionresized
36820 * Fires when the user resizes a region.
36821 * @param {Roo.LayoutRegion} region The resized region
36822 * @param {Number} newSize The new size (width for east/west, height for north/south)
36824 "regionresized" : true,
36826 * @event regioncollapsed
36827 * Fires when a region is collapsed.
36828 * @param {Roo.LayoutRegion} region The collapsed region
36830 "regioncollapsed" : true,
36832 * @event regionexpanded
36833 * Fires when a region is expanded.
36834 * @param {Roo.LayoutRegion} region The expanded region
36836 "regionexpanded" : true
36838 this.updating = false;
36841 this.el = Roo.get(config.el);
36847 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36852 monitorWindowResize : true,
36858 onRender : function(ct, position)
36861 this.el = Roo.get(ct);
36864 //this.fireEvent('render',this);
36868 initEvents: function()
36872 // ie scrollbar fix
36873 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36874 document.body.scroll = "no";
36875 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36876 this.el.position('relative');
36878 this.id = this.el.id;
36879 this.el.addClass("roo-layout-container");
36880 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36881 if(this.el.dom != document.body ) {
36882 this.el.on('resize', this.layout,this);
36883 this.el.on('show', this.layout,this);
36889 * Returns true if this layout is currently being updated
36890 * @return {Boolean}
36892 isUpdating : function(){
36893 return this.updating;
36897 * Suspend the LayoutManager from doing auto-layouts while
36898 * making multiple add or remove calls
36900 beginUpdate : function(){
36901 this.updating = true;
36905 * Restore auto-layouts and optionally disable the manager from performing a layout
36906 * @param {Boolean} noLayout true to disable a layout update
36908 endUpdate : function(noLayout){
36909 this.updating = false;
36915 layout: function(){
36919 onRegionResized : function(region, newSize){
36920 this.fireEvent("regionresized", region, newSize);
36924 onRegionCollapsed : function(region){
36925 this.fireEvent("regioncollapsed", region);
36928 onRegionExpanded : function(region){
36929 this.fireEvent("regionexpanded", region);
36933 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36934 * performs box-model adjustments.
36935 * @return {Object} The size as an object {width: (the width), height: (the height)}
36937 getViewSize : function()
36940 if(this.el.dom != document.body){
36941 size = this.el.getSize();
36943 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36945 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36946 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36951 * Returns the Element this layout is bound to.
36952 * @return {Roo.Element}
36954 getEl : function(){
36959 * Returns the specified region.
36960 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36961 * @return {Roo.LayoutRegion}
36963 getRegion : function(target){
36964 return this.regions[target.toLowerCase()];
36967 onWindowResize : function(){
36968 if(this.monitorWindowResize){
36975 * Ext JS Library 1.1.1
36976 * Copyright(c) 2006-2007, Ext JS, LLC.
36978 * Originally Released Under LGPL - original licence link has changed is not relivant.
36981 * <script type="text/javascript">
36984 * @class Roo.bootstrap.layout.Border
36985 * @extends Roo.bootstrap.layout.Manager
36986 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36987 * please see: examples/bootstrap/nested.html<br><br>
36989 <b>The container the layout is rendered into can be either the body element or any other element.
36990 If it is not the body element, the container needs to either be an absolute positioned element,
36991 or you will need to add "position:relative" to the css of the container. You will also need to specify
36992 the container size if it is not the body element.</b>
36995 * Create a new Border
36996 * @param {Object} config Configuration options
36998 Roo.bootstrap.layout.Border = function(config){
36999 config = config || {};
37000 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37004 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37005 if(config[region]){
37006 config[region].region = region;
37007 this.addRegion(config[region]);
37013 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
37015 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37017 parent : false, // this might point to a 'nest' or a ???
37020 * Creates and adds a new region if it doesn't already exist.
37021 * @param {String} target The target region key (north, south, east, west or center).
37022 * @param {Object} config The regions config object
37023 * @return {BorderLayoutRegion} The new region
37025 addRegion : function(config)
37027 if(!this.regions[config.region]){
37028 var r = this.factory(config);
37029 this.bindRegion(r);
37031 return this.regions[config.region];
37035 bindRegion : function(r){
37036 this.regions[r.config.region] = r;
37038 r.on("visibilitychange", this.layout, this);
37039 r.on("paneladded", this.layout, this);
37040 r.on("panelremoved", this.layout, this);
37041 r.on("invalidated", this.layout, this);
37042 r.on("resized", this.onRegionResized, this);
37043 r.on("collapsed", this.onRegionCollapsed, this);
37044 r.on("expanded", this.onRegionExpanded, this);
37048 * Performs a layout update.
37050 layout : function()
37052 if(this.updating) {
37056 // render all the rebions if they have not been done alreayd?
37057 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37058 if(this.regions[region] && !this.regions[region].bodyEl){
37059 this.regions[region].onRender(this.el)
37063 var size = this.getViewSize();
37064 var w = size.width;
37065 var h = size.height;
37070 //var x = 0, y = 0;
37072 var rs = this.regions;
37073 var north = rs["north"];
37074 var south = rs["south"];
37075 var west = rs["west"];
37076 var east = rs["east"];
37077 var center = rs["center"];
37078 //if(this.hideOnLayout){ // not supported anymore
37079 //c.el.setStyle("display", "none");
37081 if(north && north.isVisible()){
37082 var b = north.getBox();
37083 var m = north.getMargins();
37084 b.width = w - (m.left+m.right);
37087 centerY = b.height + b.y + m.bottom;
37088 centerH -= centerY;
37089 north.updateBox(this.safeBox(b));
37091 if(south && south.isVisible()){
37092 var b = south.getBox();
37093 var m = south.getMargins();
37094 b.width = w - (m.left+m.right);
37096 var totalHeight = (b.height + m.top + m.bottom);
37097 b.y = h - totalHeight + m.top;
37098 centerH -= totalHeight;
37099 south.updateBox(this.safeBox(b));
37101 if(west && west.isVisible()){
37102 var b = west.getBox();
37103 var m = west.getMargins();
37104 b.height = centerH - (m.top+m.bottom);
37106 b.y = centerY + m.top;
37107 var totalWidth = (b.width + m.left + m.right);
37108 centerX += totalWidth;
37109 centerW -= totalWidth;
37110 west.updateBox(this.safeBox(b));
37112 if(east && east.isVisible()){
37113 var b = east.getBox();
37114 var m = east.getMargins();
37115 b.height = centerH - (m.top+m.bottom);
37116 var totalWidth = (b.width + m.left + m.right);
37117 b.x = w - totalWidth + m.left;
37118 b.y = centerY + m.top;
37119 centerW -= totalWidth;
37120 east.updateBox(this.safeBox(b));
37123 var m = center.getMargins();
37125 x: centerX + m.left,
37126 y: centerY + m.top,
37127 width: centerW - (m.left+m.right),
37128 height: centerH - (m.top+m.bottom)
37130 //if(this.hideOnLayout){
37131 //center.el.setStyle("display", "block");
37133 center.updateBox(this.safeBox(centerBox));
37136 this.fireEvent("layout", this);
37140 safeBox : function(box){
37141 box.width = Math.max(0, box.width);
37142 box.height = Math.max(0, box.height);
37147 * Adds a ContentPanel (or subclass) to this layout.
37148 * @param {String} target The target region key (north, south, east, west or center).
37149 * @param {Roo.ContentPanel} panel The panel to add
37150 * @return {Roo.ContentPanel} The added panel
37152 add : function(target, panel){
37154 target = target.toLowerCase();
37155 return this.regions[target].add(panel);
37159 * Remove a ContentPanel (or subclass) to this layout.
37160 * @param {String} target The target region key (north, south, east, west or center).
37161 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37162 * @return {Roo.ContentPanel} The removed panel
37164 remove : function(target, panel){
37165 target = target.toLowerCase();
37166 return this.regions[target].remove(panel);
37170 * Searches all regions for a panel with the specified id
37171 * @param {String} panelId
37172 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37174 findPanel : function(panelId){
37175 var rs = this.regions;
37176 for(var target in rs){
37177 if(typeof rs[target] != "function"){
37178 var p = rs[target].getPanel(panelId);
37188 * Searches all regions for a panel with the specified id and activates (shows) it.
37189 * @param {String/ContentPanel} panelId The panels id or the panel itself
37190 * @return {Roo.ContentPanel} The shown panel or null
37192 showPanel : function(panelId) {
37193 var rs = this.regions;
37194 for(var target in rs){
37195 var r = rs[target];
37196 if(typeof r != "function"){
37197 if(r.hasPanel(panelId)){
37198 return r.showPanel(panelId);
37206 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37207 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37210 restoreState : function(provider){
37212 provider = Roo.state.Manager;
37214 var sm = new Roo.LayoutStateManager();
37215 sm.init(this, provider);
37221 * Adds a xtype elements to the layout.
37225 xtype : 'ContentPanel',
37232 xtype : 'NestedLayoutPanel',
37238 items : [ ... list of content panels or nested layout panels.. ]
37242 * @param {Object} cfg Xtype definition of item to add.
37244 addxtype : function(cfg)
37246 // basically accepts a pannel...
37247 // can accept a layout region..!?!?
37248 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37251 // theory? children can only be panels??
37253 //if (!cfg.xtype.match(/Panel$/)) {
37258 if (typeof(cfg.region) == 'undefined') {
37259 Roo.log("Failed to add Panel, region was not set");
37263 var region = cfg.region;
37269 xitems = cfg.items;
37274 if ( region == 'center') {
37275 Roo.log("Center: " + cfg.title);
37281 case 'Content': // ContentPanel (el, cfg)
37282 case 'Scroll': // ContentPanel (el, cfg)
37284 cfg.autoCreate = cfg.autoCreate || true;
37285 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37287 // var el = this.el.createChild();
37288 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37291 this.add(region, ret);
37295 case 'TreePanel': // our new panel!
37296 cfg.el = this.el.createChild();
37297 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37298 this.add(region, ret);
37303 // create a new Layout (which is a Border Layout...
37305 var clayout = cfg.layout;
37306 clayout.el = this.el.createChild();
37307 clayout.items = clayout.items || [];
37311 // replace this exitems with the clayout ones..
37312 xitems = clayout.items;
37314 // force background off if it's in center...
37315 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37316 cfg.background = false;
37318 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37321 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37322 //console.log('adding nested layout panel ' + cfg.toSource());
37323 this.add(region, ret);
37324 nb = {}; /// find first...
37329 // needs grid and region
37331 //var el = this.getRegion(region).el.createChild();
37333 *var el = this.el.createChild();
37334 // create the grid first...
37335 cfg.grid.container = el;
37336 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37339 if (region == 'center' && this.active ) {
37340 cfg.background = false;
37343 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37345 this.add(region, ret);
37347 if (cfg.background) {
37348 // render grid on panel activation (if panel background)
37349 ret.on('activate', function(gp) {
37350 if (!gp.grid.rendered) {
37351 // gp.grid.render(el);
37355 // cfg.grid.render(el);
37361 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37362 // it was the old xcomponent building that caused this before.
37363 // espeically if border is the top element in the tree.
37373 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37375 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37376 this.add(region, ret);
37380 throw "Can not add '" + cfg.xtype + "' to Border";
37386 this.beginUpdate();
37390 Roo.each(xitems, function(i) {
37391 region = nb && i.region ? i.region : false;
37393 var add = ret.addxtype(i);
37396 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37397 if (!i.background) {
37398 abn[region] = nb[region] ;
37405 // make the last non-background panel active..
37406 //if (nb) { Roo.log(abn); }
37409 for(var r in abn) {
37410 region = this.getRegion(r);
37412 // tried using nb[r], but it does not work..
37414 region.showPanel(abn[r]);
37425 factory : function(cfg)
37428 var validRegions = Roo.bootstrap.layout.Border.regions;
37430 var target = cfg.region;
37433 var r = Roo.bootstrap.layout;
37437 return new r.North(cfg);
37439 return new r.South(cfg);
37441 return new r.East(cfg);
37443 return new r.West(cfg);
37445 return new r.Center(cfg);
37447 throw 'Layout region "'+target+'" not supported.';
37454 * Ext JS Library 1.1.1
37455 * Copyright(c) 2006-2007, Ext JS, LLC.
37457 * Originally Released Under LGPL - original licence link has changed is not relivant.
37460 * <script type="text/javascript">
37464 * @class Roo.bootstrap.layout.Basic
37465 * @extends Roo.util.Observable
37466 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37467 * and does not have a titlebar, tabs or any other features. All it does is size and position
37468 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37469 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37470 * @cfg {string} region the region that it inhabits..
37471 * @cfg {bool} skipConfig skip config?
37475 Roo.bootstrap.layout.Basic = function(config){
37477 this.mgr = config.mgr;
37479 this.position = config.region;
37481 var skipConfig = config.skipConfig;
37485 * @scope Roo.BasicLayoutRegion
37489 * @event beforeremove
37490 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37491 * @param {Roo.LayoutRegion} this
37492 * @param {Roo.ContentPanel} panel The panel
37493 * @param {Object} e The cancel event object
37495 "beforeremove" : true,
37497 * @event invalidated
37498 * Fires when the layout for this region is changed.
37499 * @param {Roo.LayoutRegion} this
37501 "invalidated" : true,
37503 * @event visibilitychange
37504 * Fires when this region is shown or hidden
37505 * @param {Roo.LayoutRegion} this
37506 * @param {Boolean} visibility true or false
37508 "visibilitychange" : true,
37510 * @event paneladded
37511 * Fires when a panel is added.
37512 * @param {Roo.LayoutRegion} this
37513 * @param {Roo.ContentPanel} panel The panel
37515 "paneladded" : true,
37517 * @event panelremoved
37518 * Fires when a panel is removed.
37519 * @param {Roo.LayoutRegion} this
37520 * @param {Roo.ContentPanel} panel The panel
37522 "panelremoved" : true,
37524 * @event beforecollapse
37525 * Fires when this region before collapse.
37526 * @param {Roo.LayoutRegion} this
37528 "beforecollapse" : true,
37531 * Fires when this region is collapsed.
37532 * @param {Roo.LayoutRegion} this
37534 "collapsed" : true,
37537 * Fires when this region is expanded.
37538 * @param {Roo.LayoutRegion} this
37543 * Fires when this region is slid into view.
37544 * @param {Roo.LayoutRegion} this
37546 "slideshow" : true,
37549 * Fires when this region slides out of view.
37550 * @param {Roo.LayoutRegion} this
37552 "slidehide" : true,
37554 * @event panelactivated
37555 * Fires when a panel is activated.
37556 * @param {Roo.LayoutRegion} this
37557 * @param {Roo.ContentPanel} panel The activated panel
37559 "panelactivated" : true,
37562 * Fires when the user resizes this region.
37563 * @param {Roo.LayoutRegion} this
37564 * @param {Number} newSize The new size (width for east/west, height for north/south)
37568 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37569 this.panels = new Roo.util.MixedCollection();
37570 this.panels.getKey = this.getPanelId.createDelegate(this);
37572 this.activePanel = null;
37573 // ensure listeners are added...
37575 if (config.listeners || config.events) {
37576 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37577 listeners : config.listeners || {},
37578 events : config.events || {}
37582 if(skipConfig !== true){
37583 this.applyConfig(config);
37587 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37589 getPanelId : function(p){
37593 applyConfig : function(config){
37594 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37595 this.config = config;
37600 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
37601 * the width, for horizontal (north, south) the height.
37602 * @param {Number} newSize The new width or height
37604 resizeTo : function(newSize){
37605 var el = this.el ? this.el :
37606 (this.activePanel ? this.activePanel.getEl() : null);
37608 switch(this.position){
37611 el.setWidth(newSize);
37612 this.fireEvent("resized", this, newSize);
37616 el.setHeight(newSize);
37617 this.fireEvent("resized", this, newSize);
37623 getBox : function(){
37624 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37627 getMargins : function(){
37628 return this.margins;
37631 updateBox : function(box){
37633 var el = this.activePanel.getEl();
37634 el.dom.style.left = box.x + "px";
37635 el.dom.style.top = box.y + "px";
37636 this.activePanel.setSize(box.width, box.height);
37640 * Returns the container element for this region.
37641 * @return {Roo.Element}
37643 getEl : function(){
37644 return this.activePanel;
37648 * Returns true if this region is currently visible.
37649 * @return {Boolean}
37651 isVisible : function(){
37652 return this.activePanel ? true : false;
37655 setActivePanel : function(panel){
37656 panel = this.getPanel(panel);
37657 if(this.activePanel && this.activePanel != panel){
37658 this.activePanel.setActiveState(false);
37659 this.activePanel.getEl().setLeftTop(-10000,-10000);
37661 this.activePanel = panel;
37662 panel.setActiveState(true);
37664 panel.setSize(this.box.width, this.box.height);
37666 this.fireEvent("panelactivated", this, panel);
37667 this.fireEvent("invalidated");
37671 * Show the specified panel.
37672 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37673 * @return {Roo.ContentPanel} The shown panel or null
37675 showPanel : function(panel){
37676 panel = this.getPanel(panel);
37678 this.setActivePanel(panel);
37684 * Get the active panel for this region.
37685 * @return {Roo.ContentPanel} The active panel or null
37687 getActivePanel : function(){
37688 return this.activePanel;
37692 * Add the passed ContentPanel(s)
37693 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37694 * @return {Roo.ContentPanel} The panel added (if only one was added)
37696 add : function(panel){
37697 if(arguments.length > 1){
37698 for(var i = 0, len = arguments.length; i < len; i++) {
37699 this.add(arguments[i]);
37703 if(this.hasPanel(panel)){
37704 this.showPanel(panel);
37707 var el = panel.getEl();
37708 if(el.dom.parentNode != this.mgr.el.dom){
37709 this.mgr.el.dom.appendChild(el.dom);
37711 if(panel.setRegion){
37712 panel.setRegion(this);
37714 this.panels.add(panel);
37715 el.setStyle("position", "absolute");
37716 if(!panel.background){
37717 this.setActivePanel(panel);
37718 if(this.config.initialSize && this.panels.getCount()==1){
37719 this.resizeTo(this.config.initialSize);
37722 this.fireEvent("paneladded", this, panel);
37727 * Returns true if the panel is in this region.
37728 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37729 * @return {Boolean}
37731 hasPanel : function(panel){
37732 if(typeof panel == "object"){ // must be panel obj
37733 panel = panel.getId();
37735 return this.getPanel(panel) ? true : false;
37739 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37740 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37741 * @param {Boolean} preservePanel Overrides the config preservePanel option
37742 * @return {Roo.ContentPanel} The panel that was removed
37744 remove : function(panel, preservePanel){
37745 panel = this.getPanel(panel);
37750 this.fireEvent("beforeremove", this, panel, e);
37751 if(e.cancel === true){
37754 var panelId = panel.getId();
37755 this.panels.removeKey(panelId);
37760 * Returns the panel specified or null if it's not in this region.
37761 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37762 * @return {Roo.ContentPanel}
37764 getPanel : function(id){
37765 if(typeof id == "object"){ // must be panel obj
37768 return this.panels.get(id);
37772 * Returns this regions position (north/south/east/west/center).
37775 getPosition: function(){
37776 return this.position;
37780 * Ext JS Library 1.1.1
37781 * Copyright(c) 2006-2007, Ext JS, LLC.
37783 * Originally Released Under LGPL - original licence link has changed is not relivant.
37786 * <script type="text/javascript">
37790 * @class Roo.bootstrap.layout.Region
37791 * @extends Roo.bootstrap.layout.Basic
37792 * This class represents a region in a layout manager.
37794 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37795 * @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})
37796 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
37797 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
37798 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
37799 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
37800 * @cfg {String} title The title for the region (overrides panel titles)
37801 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
37802 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37803 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
37804 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37805 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
37806 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37807 * the space available, similar to FireFox 1.5 tabs (defaults to false)
37808 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
37809 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
37810 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
37812 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
37813 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
37814 * @cfg {Boolean} disableTabTips True to disable tab tooltips
37815 * @cfg {Number} width For East/West panels
37816 * @cfg {Number} height For North/South panels
37817 * @cfg {Boolean} split To show the splitter
37818 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
37820 * @cfg {string} cls Extra CSS classes to add to region
37822 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37823 * @cfg {string} region the region that it inhabits..
37826 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
37827 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
37829 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
37830 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
37831 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
37833 Roo.bootstrap.layout.Region = function(config)
37835 this.applyConfig(config);
37837 var mgr = config.mgr;
37838 var pos = config.region;
37839 config.skipConfig = true;
37840 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37843 this.onRender(mgr.el);
37846 this.visible = true;
37847 this.collapsed = false;
37848 this.unrendered_panels = [];
37851 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37853 position: '', // set by wrapper (eg. north/south etc..)
37854 unrendered_panels : null, // unrendered panels.
37856 tabPosition : false,
37858 mgr: false, // points to 'Border'
37861 createBody : function(){
37862 /** This region's body element
37863 * @type Roo.Element */
37864 this.bodyEl = this.el.createChild({
37866 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37870 onRender: function(ctr, pos)
37872 var dh = Roo.DomHelper;
37873 /** This region's container element
37874 * @type Roo.Element */
37875 this.el = dh.append(ctr.dom, {
37877 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37879 /** This region's title element
37880 * @type Roo.Element */
37882 this.titleEl = dh.append(this.el.dom, {
37884 unselectable: "on",
37885 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37887 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
37888 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37892 this.titleEl.enableDisplayMode();
37893 /** This region's title text element
37894 * @type HTMLElement */
37895 this.titleTextEl = this.titleEl.dom.firstChild;
37896 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37898 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37899 this.closeBtn.enableDisplayMode();
37900 this.closeBtn.on("click", this.closeClicked, this);
37901 this.closeBtn.hide();
37903 this.createBody(this.config);
37904 if(this.config.hideWhenEmpty){
37906 this.on("paneladded", this.validateVisibility, this);
37907 this.on("panelremoved", this.validateVisibility, this);
37909 if(this.autoScroll){
37910 this.bodyEl.setStyle("overflow", "auto");
37912 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37914 //if(c.titlebar !== false){
37915 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37916 this.titleEl.hide();
37918 this.titleEl.show();
37919 if(this.config.title){
37920 this.titleTextEl.innerHTML = this.config.title;
37924 if(this.config.collapsed){
37925 this.collapse(true);
37927 if(this.config.hidden){
37931 if (this.unrendered_panels && this.unrendered_panels.length) {
37932 for (var i =0;i< this.unrendered_panels.length; i++) {
37933 this.add(this.unrendered_panels[i]);
37935 this.unrendered_panels = null;
37941 applyConfig : function(c)
37944 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37945 var dh = Roo.DomHelper;
37946 if(c.titlebar !== false){
37947 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37948 this.collapseBtn.on("click", this.collapse, this);
37949 this.collapseBtn.enableDisplayMode();
37951 if(c.showPin === true || this.showPin){
37952 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37953 this.stickBtn.enableDisplayMode();
37954 this.stickBtn.on("click", this.expand, this);
37955 this.stickBtn.hide();
37960 /** This region's collapsed element
37961 * @type Roo.Element */
37964 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37965 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37968 if(c.floatable !== false){
37969 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37970 this.collapsedEl.on("click", this.collapseClick, this);
37973 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37974 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37975 id: "message", unselectable: "on", style:{"float":"left"}});
37976 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37978 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37979 this.expandBtn.on("click", this.expand, this);
37983 if(this.collapseBtn){
37984 this.collapseBtn.setVisible(c.collapsible == true);
37987 this.cmargins = c.cmargins || this.cmargins ||
37988 (this.position == "west" || this.position == "east" ?
37989 {top: 0, left: 2, right:2, bottom: 0} :
37990 {top: 2, left: 0, right:0, bottom: 2});
37992 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37995 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37997 this.autoScroll = c.autoScroll || false;
38002 this.duration = c.duration || .30;
38003 this.slideDuration = c.slideDuration || .45;
38008 * Returns true if this region is currently visible.
38009 * @return {Boolean}
38011 isVisible : function(){
38012 return this.visible;
38016 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38017 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38019 //setCollapsedTitle : function(title){
38020 // title = title || " ";
38021 // if(this.collapsedTitleTextEl){
38022 // this.collapsedTitleTextEl.innerHTML = title;
38026 getBox : function(){
38028 // if(!this.collapsed){
38029 b = this.el.getBox(false, true);
38031 // b = this.collapsedEl.getBox(false, true);
38036 getMargins : function(){
38037 return this.margins;
38038 //return this.collapsed ? this.cmargins : this.margins;
38041 highlight : function(){
38042 this.el.addClass("x-layout-panel-dragover");
38045 unhighlight : function(){
38046 this.el.removeClass("x-layout-panel-dragover");
38049 updateBox : function(box)
38051 if (!this.bodyEl) {
38052 return; // not rendered yet..
38056 if(!this.collapsed){
38057 this.el.dom.style.left = box.x + "px";
38058 this.el.dom.style.top = box.y + "px";
38059 this.updateBody(box.width, box.height);
38061 this.collapsedEl.dom.style.left = box.x + "px";
38062 this.collapsedEl.dom.style.top = box.y + "px";
38063 this.collapsedEl.setSize(box.width, box.height);
38066 this.tabs.autoSizeTabs();
38070 updateBody : function(w, h)
38073 this.el.setWidth(w);
38074 w -= this.el.getBorderWidth("rl");
38075 if(this.config.adjustments){
38076 w += this.config.adjustments[0];
38079 if(h !== null && h > 0){
38080 this.el.setHeight(h);
38081 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38082 h -= this.el.getBorderWidth("tb");
38083 if(this.config.adjustments){
38084 h += this.config.adjustments[1];
38086 this.bodyEl.setHeight(h);
38088 h = this.tabs.syncHeight(h);
38091 if(this.panelSize){
38092 w = w !== null ? w : this.panelSize.width;
38093 h = h !== null ? h : this.panelSize.height;
38095 if(this.activePanel){
38096 var el = this.activePanel.getEl();
38097 w = w !== null ? w : el.getWidth();
38098 h = h !== null ? h : el.getHeight();
38099 this.panelSize = {width: w, height: h};
38100 this.activePanel.setSize(w, h);
38102 if(Roo.isIE && this.tabs){
38103 this.tabs.el.repaint();
38108 * Returns the container element for this region.
38109 * @return {Roo.Element}
38111 getEl : function(){
38116 * Hides this region.
38119 //if(!this.collapsed){
38120 this.el.dom.style.left = "-2000px";
38123 // this.collapsedEl.dom.style.left = "-2000px";
38124 // this.collapsedEl.hide();
38126 this.visible = false;
38127 this.fireEvent("visibilitychange", this, false);
38131 * Shows this region if it was previously hidden.
38134 //if(!this.collapsed){
38137 // this.collapsedEl.show();
38139 this.visible = true;
38140 this.fireEvent("visibilitychange", this, true);
38143 closeClicked : function(){
38144 if(this.activePanel){
38145 this.remove(this.activePanel);
38149 collapseClick : function(e){
38151 e.stopPropagation();
38154 e.stopPropagation();
38160 * Collapses this region.
38161 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38164 collapse : function(skipAnim, skipCheck = false){
38165 if(this.collapsed) {
38169 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38171 this.collapsed = true;
38173 this.split.el.hide();
38175 if(this.config.animate && skipAnim !== true){
38176 this.fireEvent("invalidated", this);
38177 this.animateCollapse();
38179 this.el.setLocation(-20000,-20000);
38181 this.collapsedEl.show();
38182 this.fireEvent("collapsed", this);
38183 this.fireEvent("invalidated", this);
38189 animateCollapse : function(){
38194 * Expands this region if it was previously collapsed.
38195 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38196 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38199 expand : function(e, skipAnim){
38201 e.stopPropagation();
38203 if(!this.collapsed || this.el.hasActiveFx()) {
38207 this.afterSlideIn();
38210 this.collapsed = false;
38211 if(this.config.animate && skipAnim !== true){
38212 this.animateExpand();
38216 this.split.el.show();
38218 this.collapsedEl.setLocation(-2000,-2000);
38219 this.collapsedEl.hide();
38220 this.fireEvent("invalidated", this);
38221 this.fireEvent("expanded", this);
38225 animateExpand : function(){
38229 initTabs : function()
38231 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38233 var ts = new Roo.bootstrap.panel.Tabs({
38234 el: this.bodyEl.dom,
38236 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38237 disableTooltips: this.config.disableTabTips,
38238 toolbar : this.config.toolbar
38241 if(this.config.hideTabs){
38242 ts.stripWrap.setDisplayed(false);
38245 ts.resizeTabs = this.config.resizeTabs === true;
38246 ts.minTabWidth = this.config.minTabWidth || 40;
38247 ts.maxTabWidth = this.config.maxTabWidth || 250;
38248 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38249 ts.monitorResize = false;
38250 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38251 ts.bodyEl.addClass('roo-layout-tabs-body');
38252 this.panels.each(this.initPanelAsTab, this);
38255 initPanelAsTab : function(panel){
38256 var ti = this.tabs.addTab(
38260 this.config.closeOnTab && panel.isClosable(),
38263 if(panel.tabTip !== undefined){
38264 ti.setTooltip(panel.tabTip);
38266 ti.on("activate", function(){
38267 this.setActivePanel(panel);
38270 if(this.config.closeOnTab){
38271 ti.on("beforeclose", function(t, e){
38273 this.remove(panel);
38277 panel.tabItem = ti;
38282 updatePanelTitle : function(panel, title)
38284 if(this.activePanel == panel){
38285 this.updateTitle(title);
38288 var ti = this.tabs.getTab(panel.getEl().id);
38290 if(panel.tabTip !== undefined){
38291 ti.setTooltip(panel.tabTip);
38296 updateTitle : function(title){
38297 if(this.titleTextEl && !this.config.title){
38298 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38302 setActivePanel : function(panel)
38304 panel = this.getPanel(panel);
38305 if(this.activePanel && this.activePanel != panel){
38306 if(this.activePanel.setActiveState(false) === false){
38310 this.activePanel = panel;
38311 panel.setActiveState(true);
38312 if(this.panelSize){
38313 panel.setSize(this.panelSize.width, this.panelSize.height);
38316 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38318 this.updateTitle(panel.getTitle());
38320 this.fireEvent("invalidated", this);
38322 this.fireEvent("panelactivated", this, panel);
38326 * Shows the specified panel.
38327 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38328 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38330 showPanel : function(panel)
38332 panel = this.getPanel(panel);
38335 var tab = this.tabs.getTab(panel.getEl().id);
38336 if(tab.isHidden()){
38337 this.tabs.unhideTab(tab.id);
38341 this.setActivePanel(panel);
38348 * Get the active panel for this region.
38349 * @return {Roo.ContentPanel} The active panel or null
38351 getActivePanel : function(){
38352 return this.activePanel;
38355 validateVisibility : function(){
38356 if(this.panels.getCount() < 1){
38357 this.updateTitle(" ");
38358 this.closeBtn.hide();
38361 if(!this.isVisible()){
38368 * Adds the passed ContentPanel(s) to this region.
38369 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38370 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38372 add : function(panel)
38374 if(arguments.length > 1){
38375 for(var i = 0, len = arguments.length; i < len; i++) {
38376 this.add(arguments[i]);
38381 // if we have not been rendered yet, then we can not really do much of this..
38382 if (!this.bodyEl) {
38383 this.unrendered_panels.push(panel);
38390 if(this.hasPanel(panel)){
38391 this.showPanel(panel);
38394 panel.setRegion(this);
38395 this.panels.add(panel);
38396 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38397 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38398 // and hide them... ???
38399 this.bodyEl.dom.appendChild(panel.getEl().dom);
38400 if(panel.background !== true){
38401 this.setActivePanel(panel);
38403 this.fireEvent("paneladded", this, panel);
38410 this.initPanelAsTab(panel);
38414 if(panel.background !== true){
38415 this.tabs.activate(panel.getEl().id);
38417 this.fireEvent("paneladded", this, panel);
38422 * Hides the tab for the specified panel.
38423 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38425 hidePanel : function(panel){
38426 if(this.tabs && (panel = this.getPanel(panel))){
38427 this.tabs.hideTab(panel.getEl().id);
38432 * Unhides the tab for a previously hidden panel.
38433 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38435 unhidePanel : function(panel){
38436 if(this.tabs && (panel = this.getPanel(panel))){
38437 this.tabs.unhideTab(panel.getEl().id);
38441 clearPanels : function(){
38442 while(this.panels.getCount() > 0){
38443 this.remove(this.panels.first());
38448 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38449 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38450 * @param {Boolean} preservePanel Overrides the config preservePanel option
38451 * @return {Roo.ContentPanel} The panel that was removed
38453 remove : function(panel, preservePanel)
38455 panel = this.getPanel(panel);
38460 this.fireEvent("beforeremove", this, panel, e);
38461 if(e.cancel === true){
38464 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38465 var panelId = panel.getId();
38466 this.panels.removeKey(panelId);
38468 document.body.appendChild(panel.getEl().dom);
38471 this.tabs.removeTab(panel.getEl().id);
38472 }else if (!preservePanel){
38473 this.bodyEl.dom.removeChild(panel.getEl().dom);
38475 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38476 var p = this.panels.first();
38477 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38478 tempEl.appendChild(p.getEl().dom);
38479 this.bodyEl.update("");
38480 this.bodyEl.dom.appendChild(p.getEl().dom);
38482 this.updateTitle(p.getTitle());
38484 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38485 this.setActivePanel(p);
38487 panel.setRegion(null);
38488 if(this.activePanel == panel){
38489 this.activePanel = null;
38491 if(this.config.autoDestroy !== false && preservePanel !== true){
38492 try{panel.destroy();}catch(e){}
38494 this.fireEvent("panelremoved", this, panel);
38499 * Returns the TabPanel component used by this region
38500 * @return {Roo.TabPanel}
38502 getTabs : function(){
38506 createTool : function(parentEl, className){
38507 var btn = Roo.DomHelper.append(parentEl, {
38509 cls: "x-layout-tools-button",
38512 cls: "roo-layout-tools-button-inner " + className,
38516 btn.addClassOnOver("roo-layout-tools-button-over");
38521 * Ext JS Library 1.1.1
38522 * Copyright(c) 2006-2007, Ext JS, LLC.
38524 * Originally Released Under LGPL - original licence link has changed is not relivant.
38527 * <script type="text/javascript">
38533 * @class Roo.SplitLayoutRegion
38534 * @extends Roo.LayoutRegion
38535 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38537 Roo.bootstrap.layout.Split = function(config){
38538 this.cursor = config.cursor;
38539 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38542 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38544 splitTip : "Drag to resize.",
38545 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38546 useSplitTips : false,
38548 applyConfig : function(config){
38549 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38552 onRender : function(ctr,pos) {
38554 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38555 if(!this.config.split){
38560 var splitEl = Roo.DomHelper.append(ctr.dom, {
38562 id: this.el.id + "-split",
38563 cls: "roo-layout-split roo-layout-split-"+this.position,
38566 /** The SplitBar for this region
38567 * @type Roo.SplitBar */
38568 // does not exist yet...
38569 Roo.log([this.position, this.orientation]);
38571 this.split = new Roo.bootstrap.SplitBar({
38572 dragElement : splitEl,
38573 resizingElement: this.el,
38574 orientation : this.orientation
38577 this.split.on("moved", this.onSplitMove, this);
38578 this.split.useShim = this.config.useShim === true;
38579 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38580 if(this.useSplitTips){
38581 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38583 //if(config.collapsible){
38584 // this.split.el.on("dblclick", this.collapse, this);
38587 if(typeof this.config.minSize != "undefined"){
38588 this.split.minSize = this.config.minSize;
38590 if(typeof this.config.maxSize != "undefined"){
38591 this.split.maxSize = this.config.maxSize;
38593 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38594 this.hideSplitter();
38599 getHMaxSize : function(){
38600 var cmax = this.config.maxSize || 10000;
38601 var center = this.mgr.getRegion("center");
38602 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38605 getVMaxSize : function(){
38606 var cmax = this.config.maxSize || 10000;
38607 var center = this.mgr.getRegion("center");
38608 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38611 onSplitMove : function(split, newSize){
38612 this.fireEvent("resized", this, newSize);
38616 * Returns the {@link Roo.SplitBar} for this region.
38617 * @return {Roo.SplitBar}
38619 getSplitBar : function(){
38624 this.hideSplitter();
38625 Roo.bootstrap.layout.Split.superclass.hide.call(this);
38628 hideSplitter : function(){
38630 this.split.el.setLocation(-2000,-2000);
38631 this.split.el.hide();
38637 this.split.el.show();
38639 Roo.bootstrap.layout.Split.superclass.show.call(this);
38642 beforeSlide: function(){
38643 if(Roo.isGecko){// firefox overflow auto bug workaround
38644 this.bodyEl.clip();
38646 this.tabs.bodyEl.clip();
38648 if(this.activePanel){
38649 this.activePanel.getEl().clip();
38651 if(this.activePanel.beforeSlide){
38652 this.activePanel.beforeSlide();
38658 afterSlide : function(){
38659 if(Roo.isGecko){// firefox overflow auto bug workaround
38660 this.bodyEl.unclip();
38662 this.tabs.bodyEl.unclip();
38664 if(this.activePanel){
38665 this.activePanel.getEl().unclip();
38666 if(this.activePanel.afterSlide){
38667 this.activePanel.afterSlide();
38673 initAutoHide : function(){
38674 if(this.autoHide !== false){
38675 if(!this.autoHideHd){
38676 var st = new Roo.util.DelayedTask(this.slideIn, this);
38677 this.autoHideHd = {
38678 "mouseout": function(e){
38679 if(!e.within(this.el, true)){
38683 "mouseover" : function(e){
38689 this.el.on(this.autoHideHd);
38693 clearAutoHide : function(){
38694 if(this.autoHide !== false){
38695 this.el.un("mouseout", this.autoHideHd.mouseout);
38696 this.el.un("mouseover", this.autoHideHd.mouseover);
38700 clearMonitor : function(){
38701 Roo.get(document).un("click", this.slideInIf, this);
38704 // these names are backwards but not changed for compat
38705 slideOut : function(){
38706 if(this.isSlid || this.el.hasActiveFx()){
38709 this.isSlid = true;
38710 if(this.collapseBtn){
38711 this.collapseBtn.hide();
38713 this.closeBtnState = this.closeBtn.getStyle('display');
38714 this.closeBtn.hide();
38716 this.stickBtn.show();
38719 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38720 this.beforeSlide();
38721 this.el.setStyle("z-index", 10001);
38722 this.el.slideIn(this.getSlideAnchor(), {
38723 callback: function(){
38725 this.initAutoHide();
38726 Roo.get(document).on("click", this.slideInIf, this);
38727 this.fireEvent("slideshow", this);
38734 afterSlideIn : function(){
38735 this.clearAutoHide();
38736 this.isSlid = false;
38737 this.clearMonitor();
38738 this.el.setStyle("z-index", "");
38739 if(this.collapseBtn){
38740 this.collapseBtn.show();
38742 this.closeBtn.setStyle('display', this.closeBtnState);
38744 this.stickBtn.hide();
38746 this.fireEvent("slidehide", this);
38749 slideIn : function(cb){
38750 if(!this.isSlid || this.el.hasActiveFx()){
38754 this.isSlid = false;
38755 this.beforeSlide();
38756 this.el.slideOut(this.getSlideAnchor(), {
38757 callback: function(){
38758 this.el.setLeftTop(-10000, -10000);
38760 this.afterSlideIn();
38768 slideInIf : function(e){
38769 if(!e.within(this.el)){
38774 animateCollapse : function(){
38775 this.beforeSlide();
38776 this.el.setStyle("z-index", 20000);
38777 var anchor = this.getSlideAnchor();
38778 this.el.slideOut(anchor, {
38779 callback : function(){
38780 this.el.setStyle("z-index", "");
38781 this.collapsedEl.slideIn(anchor, {duration:.3});
38783 this.el.setLocation(-10000,-10000);
38785 this.fireEvent("collapsed", this);
38792 animateExpand : function(){
38793 this.beforeSlide();
38794 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38795 this.el.setStyle("z-index", 20000);
38796 this.collapsedEl.hide({
38799 this.el.slideIn(this.getSlideAnchor(), {
38800 callback : function(){
38801 this.el.setStyle("z-index", "");
38804 this.split.el.show();
38806 this.fireEvent("invalidated", this);
38807 this.fireEvent("expanded", this);
38835 getAnchor : function(){
38836 return this.anchors[this.position];
38839 getCollapseAnchor : function(){
38840 return this.canchors[this.position];
38843 getSlideAnchor : function(){
38844 return this.sanchors[this.position];
38847 getAlignAdj : function(){
38848 var cm = this.cmargins;
38849 switch(this.position){
38865 getExpandAdj : function(){
38866 var c = this.collapsedEl, cm = this.cmargins;
38867 switch(this.position){
38869 return [-(cm.right+c.getWidth()+cm.left), 0];
38872 return [cm.right+c.getWidth()+cm.left, 0];
38875 return [0, -(cm.top+cm.bottom+c.getHeight())];
38878 return [0, cm.top+cm.bottom+c.getHeight()];
38884 * Ext JS Library 1.1.1
38885 * Copyright(c) 2006-2007, Ext JS, LLC.
38887 * Originally Released Under LGPL - original licence link has changed is not relivant.
38890 * <script type="text/javascript">
38893 * These classes are private internal classes
38895 Roo.bootstrap.layout.Center = function(config){
38896 config.region = "center";
38897 Roo.bootstrap.layout.Region.call(this, config);
38898 this.visible = true;
38899 this.minWidth = config.minWidth || 20;
38900 this.minHeight = config.minHeight || 20;
38903 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38905 // center panel can't be hidden
38909 // center panel can't be hidden
38912 getMinWidth: function(){
38913 return this.minWidth;
38916 getMinHeight: function(){
38917 return this.minHeight;
38931 Roo.bootstrap.layout.North = function(config)
38933 config.region = 'north';
38934 config.cursor = 'n-resize';
38936 Roo.bootstrap.layout.Split.call(this, config);
38940 this.split.placement = Roo.bootstrap.SplitBar.TOP;
38941 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38942 this.split.el.addClass("roo-layout-split-v");
38944 var size = config.initialSize || config.height;
38945 if(typeof size != "undefined"){
38946 this.el.setHeight(size);
38949 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38951 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38955 getBox : function(){
38956 if(this.collapsed){
38957 return this.collapsedEl.getBox();
38959 var box = this.el.getBox();
38961 box.height += this.split.el.getHeight();
38966 updateBox : function(box){
38967 if(this.split && !this.collapsed){
38968 box.height -= this.split.el.getHeight();
38969 this.split.el.setLeft(box.x);
38970 this.split.el.setTop(box.y+box.height);
38971 this.split.el.setWidth(box.width);
38973 if(this.collapsed){
38974 this.updateBody(box.width, null);
38976 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38984 Roo.bootstrap.layout.South = function(config){
38985 config.region = 'south';
38986 config.cursor = 's-resize';
38987 Roo.bootstrap.layout.Split.call(this, config);
38989 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38990 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38991 this.split.el.addClass("roo-layout-split-v");
38993 var size = config.initialSize || config.height;
38994 if(typeof size != "undefined"){
38995 this.el.setHeight(size);
38999 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39000 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39001 getBox : function(){
39002 if(this.collapsed){
39003 return this.collapsedEl.getBox();
39005 var box = this.el.getBox();
39007 var sh = this.split.el.getHeight();
39014 updateBox : function(box){
39015 if(this.split && !this.collapsed){
39016 var sh = this.split.el.getHeight();
39019 this.split.el.setLeft(box.x);
39020 this.split.el.setTop(box.y-sh);
39021 this.split.el.setWidth(box.width);
39023 if(this.collapsed){
39024 this.updateBody(box.width, null);
39026 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39030 Roo.bootstrap.layout.East = function(config){
39031 config.region = "east";
39032 config.cursor = "e-resize";
39033 Roo.bootstrap.layout.Split.call(this, config);
39035 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39036 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39037 this.split.el.addClass("roo-layout-split-h");
39039 var size = config.initialSize || config.width;
39040 if(typeof size != "undefined"){
39041 this.el.setWidth(size);
39044 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39045 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39046 getBox : function(){
39047 if(this.collapsed){
39048 return this.collapsedEl.getBox();
39050 var box = this.el.getBox();
39052 var sw = this.split.el.getWidth();
39059 updateBox : function(box){
39060 if(this.split && !this.collapsed){
39061 var sw = this.split.el.getWidth();
39063 this.split.el.setLeft(box.x);
39064 this.split.el.setTop(box.y);
39065 this.split.el.setHeight(box.height);
39068 if(this.collapsed){
39069 this.updateBody(null, box.height);
39071 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39075 Roo.bootstrap.layout.West = function(config){
39076 config.region = "west";
39077 config.cursor = "w-resize";
39079 Roo.bootstrap.layout.Split.call(this, config);
39081 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39082 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39083 this.split.el.addClass("roo-layout-split-h");
39087 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39088 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39090 onRender: function(ctr, pos)
39092 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39093 var size = this.config.initialSize || this.config.width;
39094 if(typeof size != "undefined"){
39095 this.el.setWidth(size);
39099 getBox : function(){
39100 if(this.collapsed){
39101 return this.collapsedEl.getBox();
39103 var box = this.el.getBox();
39105 box.width += this.split.el.getWidth();
39110 updateBox : function(box){
39111 if(this.split && !this.collapsed){
39112 var sw = this.split.el.getWidth();
39114 this.split.el.setLeft(box.x+box.width);
39115 this.split.el.setTop(box.y);
39116 this.split.el.setHeight(box.height);
39118 if(this.collapsed){
39119 this.updateBody(null, box.height);
39121 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39123 });Roo.namespace("Roo.bootstrap.panel");/*
39125 * Ext JS Library 1.1.1
39126 * Copyright(c) 2006-2007, Ext JS, LLC.
39128 * Originally Released Under LGPL - original licence link has changed is not relivant.
39131 * <script type="text/javascript">
39134 * @class Roo.ContentPanel
39135 * @extends Roo.util.Observable
39136 * A basic ContentPanel element.
39137 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39138 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39139 * @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
39140 * @cfg {Boolean} closable True if the panel can be closed/removed
39141 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39142 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39143 * @cfg {Toolbar} toolbar A toolbar for this panel
39144 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39145 * @cfg {String} title The title for this panel
39146 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39147 * @cfg {String} url Calls {@link #setUrl} with this value
39148 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39149 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39150 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39151 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39152 * @cfg {Boolean} badges render the badges
39155 * Create a new ContentPanel.
39156 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39157 * @param {String/Object} config A string to set only the title or a config object
39158 * @param {String} content (optional) Set the HTML content for this panel
39159 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39161 Roo.bootstrap.panel.Content = function( config){
39163 this.tpl = config.tpl || false;
39165 var el = config.el;
39166 var content = config.content;
39168 if(config.autoCreate){ // xtype is available if this is called from factory
39171 this.el = Roo.get(el);
39172 if(!this.el && config && config.autoCreate){
39173 if(typeof config.autoCreate == "object"){
39174 if(!config.autoCreate.id){
39175 config.autoCreate.id = config.id||el;
39177 this.el = Roo.DomHelper.append(document.body,
39178 config.autoCreate, true);
39180 var elcfg = { tag: "div",
39181 cls: "roo-layout-inactive-content",
39185 elcfg.html = config.html;
39189 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39192 this.closable = false;
39193 this.loaded = false;
39194 this.active = false;
39197 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39199 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39201 this.wrapEl = this.el; //this.el.wrap();
39203 if (config.toolbar.items) {
39204 ti = config.toolbar.items ;
39205 delete config.toolbar.items ;
39209 this.toolbar.render(this.wrapEl, 'before');
39210 for(var i =0;i < ti.length;i++) {
39211 // Roo.log(['add child', items[i]]);
39212 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39214 this.toolbar.items = nitems;
39215 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39216 delete config.toolbar;
39220 // xtype created footer. - not sure if will work as we normally have to render first..
39221 if (this.footer && !this.footer.el && this.footer.xtype) {
39222 if (!this.wrapEl) {
39223 this.wrapEl = this.el.wrap();
39226 this.footer.container = this.wrapEl.createChild();
39228 this.footer = Roo.factory(this.footer, Roo);
39233 if(typeof config == "string"){
39234 this.title = config;
39236 Roo.apply(this, config);
39240 this.resizeEl = Roo.get(this.resizeEl, true);
39242 this.resizeEl = this.el;
39244 // handle view.xtype
39252 * Fires when this panel is activated.
39253 * @param {Roo.ContentPanel} this
39257 * @event deactivate
39258 * Fires when this panel is activated.
39259 * @param {Roo.ContentPanel} this
39261 "deactivate" : true,
39265 * Fires when this panel is resized if fitToFrame is true.
39266 * @param {Roo.ContentPanel} this
39267 * @param {Number} width The width after any component adjustments
39268 * @param {Number} height The height after any component adjustments
39274 * Fires when this tab is created
39275 * @param {Roo.ContentPanel} this
39286 if(this.autoScroll){
39287 this.resizeEl.setStyle("overflow", "auto");
39289 // fix randome scrolling
39290 //this.el.on('scroll', function() {
39291 // Roo.log('fix random scolling');
39292 // this.scrollTo('top',0);
39295 content = content || this.content;
39297 this.setContent(content);
39299 if(config && config.url){
39300 this.setUrl(this.url, this.params, this.loadOnce);
39305 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39307 if (this.view && typeof(this.view.xtype) != 'undefined') {
39308 this.view.el = this.el.appendChild(document.createElement("div"));
39309 this.view = Roo.factory(this.view);
39310 this.view.render && this.view.render(false, '');
39314 this.fireEvent('render', this);
39317 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39321 setRegion : function(region){
39322 this.region = region;
39323 this.setActiveClass(region && !this.background);
39327 setActiveClass: function(state)
39330 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39331 this.el.setStyle('position','relative');
39333 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39334 this.el.setStyle('position', 'absolute');
39339 * Returns the toolbar for this Panel if one was configured.
39340 * @return {Roo.Toolbar}
39342 getToolbar : function(){
39343 return this.toolbar;
39346 setActiveState : function(active)
39348 this.active = active;
39349 this.setActiveClass(active);
39351 if(this.fireEvent("deactivate", this) === false){
39356 this.fireEvent("activate", this);
39360 * Updates this panel's element
39361 * @param {String} content The new content
39362 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39364 setContent : function(content, loadScripts){
39365 this.el.update(content, loadScripts);
39368 ignoreResize : function(w, h){
39369 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39372 this.lastSize = {width: w, height: h};
39377 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39378 * @return {Roo.UpdateManager} The UpdateManager
39380 getUpdateManager : function(){
39381 return this.el.getUpdateManager();
39384 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39385 * @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:
39388 url: "your-url.php",
39389 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39390 callback: yourFunction,
39391 scope: yourObject, //(optional scope)
39394 text: "Loading...",
39399 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39400 * 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.
39401 * @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}
39402 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39403 * @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.
39404 * @return {Roo.ContentPanel} this
39407 var um = this.el.getUpdateManager();
39408 um.update.apply(um, arguments);
39414 * 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.
39415 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39416 * @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)
39417 * @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)
39418 * @return {Roo.UpdateManager} The UpdateManager
39420 setUrl : function(url, params, loadOnce){
39421 if(this.refreshDelegate){
39422 this.removeListener("activate", this.refreshDelegate);
39424 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39425 this.on("activate", this.refreshDelegate);
39426 return this.el.getUpdateManager();
39429 _handleRefresh : function(url, params, loadOnce){
39430 if(!loadOnce || !this.loaded){
39431 var updater = this.el.getUpdateManager();
39432 updater.update(url, params, this._setLoaded.createDelegate(this));
39436 _setLoaded : function(){
39437 this.loaded = true;
39441 * Returns this panel's id
39444 getId : function(){
39449 * Returns this panel's element - used by regiosn to add.
39450 * @return {Roo.Element}
39452 getEl : function(){
39453 return this.wrapEl || this.el;
39458 adjustForComponents : function(width, height)
39460 //Roo.log('adjustForComponents ');
39461 if(this.resizeEl != this.el){
39462 width -= this.el.getFrameWidth('lr');
39463 height -= this.el.getFrameWidth('tb');
39466 var te = this.toolbar.getEl();
39467 te.setWidth(width);
39468 height -= te.getHeight();
39471 var te = this.footer.getEl();
39472 te.setWidth(width);
39473 height -= te.getHeight();
39477 if(this.adjustments){
39478 width += this.adjustments[0];
39479 height += this.adjustments[1];
39481 return {"width": width, "height": height};
39484 setSize : function(width, height){
39485 if(this.fitToFrame && !this.ignoreResize(width, height)){
39486 if(this.fitContainer && this.resizeEl != this.el){
39487 this.el.setSize(width, height);
39489 var size = this.adjustForComponents(width, height);
39490 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39491 this.fireEvent('resize', this, size.width, size.height);
39496 * Returns this panel's title
39499 getTitle : function(){
39501 if (typeof(this.title) != 'object') {
39506 for (var k in this.title) {
39507 if (!this.title.hasOwnProperty(k)) {
39511 if (k.indexOf('-') >= 0) {
39512 var s = k.split('-');
39513 for (var i = 0; i<s.length; i++) {
39514 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39517 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39524 * Set this panel's title
39525 * @param {String} title
39527 setTitle : function(title){
39528 this.title = title;
39530 this.region.updatePanelTitle(this, title);
39535 * Returns true is this panel was configured to be closable
39536 * @return {Boolean}
39538 isClosable : function(){
39539 return this.closable;
39542 beforeSlide : function(){
39544 this.resizeEl.clip();
39547 afterSlide : function(){
39549 this.resizeEl.unclip();
39553 * Force a content refresh from the URL specified in the {@link #setUrl} method.
39554 * Will fail silently if the {@link #setUrl} method has not been called.
39555 * This does not activate the panel, just updates its content.
39557 refresh : function(){
39558 if(this.refreshDelegate){
39559 this.loaded = false;
39560 this.refreshDelegate();
39565 * Destroys this panel
39567 destroy : function(){
39568 this.el.removeAllListeners();
39569 var tempEl = document.createElement("span");
39570 tempEl.appendChild(this.el.dom);
39571 tempEl.innerHTML = "";
39577 * form - if the content panel contains a form - this is a reference to it.
39578 * @type {Roo.form.Form}
39582 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39583 * This contains a reference to it.
39589 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39599 * @param {Object} cfg Xtype definition of item to add.
39603 getChildContainer: function () {
39604 return this.getEl();
39609 var ret = new Roo.factory(cfg);
39614 if (cfg.xtype.match(/^Form$/)) {
39617 //if (this.footer) {
39618 // el = this.footer.container.insertSibling(false, 'before');
39620 el = this.el.createChild();
39623 this.form = new Roo.form.Form(cfg);
39626 if ( this.form.allItems.length) {
39627 this.form.render(el.dom);
39631 // should only have one of theses..
39632 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39633 // views.. should not be just added - used named prop 'view''
39635 cfg.el = this.el.appendChild(document.createElement("div"));
39638 var ret = new Roo.factory(cfg);
39640 ret.render && ret.render(false, ''); // render blank..
39650 * @class Roo.bootstrap.panel.Grid
39651 * @extends Roo.bootstrap.panel.Content
39653 * Create a new GridPanel.
39654 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39655 * @param {Object} config A the config object
39661 Roo.bootstrap.panel.Grid = function(config)
39665 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39666 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39668 config.el = this.wrapper;
39669 //this.el = this.wrapper;
39671 if (config.container) {
39672 // ctor'ed from a Border/panel.grid
39675 this.wrapper.setStyle("overflow", "hidden");
39676 this.wrapper.addClass('roo-grid-container');
39681 if(config.toolbar){
39682 var tool_el = this.wrapper.createChild();
39683 this.toolbar = Roo.factory(config.toolbar);
39685 if (config.toolbar.items) {
39686 ti = config.toolbar.items ;
39687 delete config.toolbar.items ;
39691 this.toolbar.render(tool_el);
39692 for(var i =0;i < ti.length;i++) {
39693 // Roo.log(['add child', items[i]]);
39694 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39696 this.toolbar.items = nitems;
39698 delete config.toolbar;
39701 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39702 config.grid.scrollBody = true;;
39703 config.grid.monitorWindowResize = false; // turn off autosizing
39704 config.grid.autoHeight = false;
39705 config.grid.autoWidth = false;
39707 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39709 if (config.background) {
39710 // render grid on panel activation (if panel background)
39711 this.on('activate', function(gp) {
39712 if (!gp.grid.rendered) {
39713 gp.grid.render(this.wrapper);
39714 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39719 this.grid.render(this.wrapper);
39720 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39723 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39724 // ??? needed ??? config.el = this.wrapper;
39729 // xtype created footer. - not sure if will work as we normally have to render first..
39730 if (this.footer && !this.footer.el && this.footer.xtype) {
39732 var ctr = this.grid.getView().getFooterPanel(true);
39733 this.footer.dataSource = this.grid.dataSource;
39734 this.footer = Roo.factory(this.footer, Roo);
39735 this.footer.render(ctr);
39745 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39746 getId : function(){
39747 return this.grid.id;
39751 * Returns the grid for this panel
39752 * @return {Roo.bootstrap.Table}
39754 getGrid : function(){
39758 setSize : function(width, height){
39759 if(!this.ignoreResize(width, height)){
39760 var grid = this.grid;
39761 var size = this.adjustForComponents(width, height);
39762 // tfoot is not a footer?
39765 var gridel = grid.getGridEl();
39766 gridel.setSize(size.width, size.height);
39768 var tbd = grid.getGridEl().select('tbody', true).first();
39769 var thd = grid.getGridEl().select('thead',true).first();
39770 var tbf= grid.getGridEl().select('tfoot', true).first();
39773 size.height -= thd.getHeight();
39776 size.height -= thd.getHeight();
39779 tbd.setSize(size.width, size.height );
39780 // this is for the account management tab -seems to work there.
39781 var thd = grid.getGridEl().select('thead',true).first();
39783 // tbd.setSize(size.width, size.height - thd.getHeight());
39792 beforeSlide : function(){
39793 this.grid.getView().scroller.clip();
39796 afterSlide : function(){
39797 this.grid.getView().scroller.unclip();
39800 destroy : function(){
39801 this.grid.destroy();
39803 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
39808 * @class Roo.bootstrap.panel.Nest
39809 * @extends Roo.bootstrap.panel.Content
39811 * Create a new Panel, that can contain a layout.Border.
39814 * @param {Roo.BorderLayout} layout The layout for this panel
39815 * @param {String/Object} config A string to set only the title or a config object
39817 Roo.bootstrap.panel.Nest = function(config)
39819 // construct with only one argument..
39820 /* FIXME - implement nicer consturctors
39821 if (layout.layout) {
39823 layout = config.layout;
39824 delete config.layout;
39826 if (layout.xtype && !layout.getEl) {
39827 // then layout needs constructing..
39828 layout = Roo.factory(layout, Roo);
39832 config.el = config.layout.getEl();
39834 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39836 config.layout.monitorWindowResize = false; // turn off autosizing
39837 this.layout = config.layout;
39838 this.layout.getEl().addClass("roo-layout-nested-layout");
39839 this.layout.parent = this;
39846 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39848 setSize : function(width, height){
39849 if(!this.ignoreResize(width, height)){
39850 var size = this.adjustForComponents(width, height);
39851 var el = this.layout.getEl();
39852 if (size.height < 1) {
39853 el.setWidth(size.width);
39855 el.setSize(size.width, size.height);
39857 var touch = el.dom.offsetWidth;
39858 this.layout.layout();
39859 // ie requires a double layout on the first pass
39860 if(Roo.isIE && !this.initialized){
39861 this.initialized = true;
39862 this.layout.layout();
39867 // activate all subpanels if not currently active..
39869 setActiveState : function(active){
39870 this.active = active;
39871 this.setActiveClass(active);
39874 this.fireEvent("deactivate", this);
39878 this.fireEvent("activate", this);
39879 // not sure if this should happen before or after..
39880 if (!this.layout) {
39881 return; // should not happen..
39884 for (var r in this.layout.regions) {
39885 reg = this.layout.getRegion(r);
39886 if (reg.getActivePanel()) {
39887 //reg.showPanel(reg.getActivePanel()); // force it to activate..
39888 reg.setActivePanel(reg.getActivePanel());
39891 if (!reg.panels.length) {
39894 reg.showPanel(reg.getPanel(0));
39903 * Returns the nested BorderLayout for this panel
39904 * @return {Roo.BorderLayout}
39906 getLayout : function(){
39907 return this.layout;
39911 * Adds a xtype elements to the layout of the nested panel
39915 xtype : 'ContentPanel',
39922 xtype : 'NestedLayoutPanel',
39928 items : [ ... list of content panels or nested layout panels.. ]
39932 * @param {Object} cfg Xtype definition of item to add.
39934 addxtype : function(cfg) {
39935 return this.layout.addxtype(cfg);
39940 * Ext JS Library 1.1.1
39941 * Copyright(c) 2006-2007, Ext JS, LLC.
39943 * Originally Released Under LGPL - original licence link has changed is not relivant.
39946 * <script type="text/javascript">
39949 * @class Roo.TabPanel
39950 * @extends Roo.util.Observable
39951 * A lightweight tab container.
39955 // basic tabs 1, built from existing content
39956 var tabs = new Roo.TabPanel("tabs1");
39957 tabs.addTab("script", "View Script");
39958 tabs.addTab("markup", "View Markup");
39959 tabs.activate("script");
39961 // more advanced tabs, built from javascript
39962 var jtabs = new Roo.TabPanel("jtabs");
39963 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39965 // set up the UpdateManager
39966 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39967 var updater = tab2.getUpdateManager();
39968 updater.setDefaultUrl("ajax1.htm");
39969 tab2.on('activate', updater.refresh, updater, true);
39971 // Use setUrl for Ajax loading
39972 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39973 tab3.setUrl("ajax2.htm", null, true);
39976 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39979 jtabs.activate("jtabs-1");
39982 * Create a new TabPanel.
39983 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39984 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39986 Roo.bootstrap.panel.Tabs = function(config){
39988 * The container element for this TabPanel.
39989 * @type Roo.Element
39991 this.el = Roo.get(config.el);
39994 if(typeof config == "boolean"){
39995 this.tabPosition = config ? "bottom" : "top";
39997 Roo.apply(this, config);
40001 if(this.tabPosition == "bottom"){
40002 // if tabs are at the bottom = create the body first.
40003 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40004 this.el.addClass("roo-tabs-bottom");
40006 // next create the tabs holders
40008 if (this.tabPosition == "west"){
40010 var reg = this.region; // fake it..
40012 if (!reg.mgr.parent) {
40015 reg = reg.mgr.parent.region;
40017 Roo.log("got nest?");
40019 if (reg.mgr.getRegion('west')) {
40020 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40021 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40022 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40023 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40024 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40032 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40033 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40034 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40035 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40040 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40043 // finally - if tabs are at the top, then create the body last..
40044 if(this.tabPosition != "bottom"){
40045 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40046 * @type Roo.Element
40048 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40049 this.el.addClass("roo-tabs-top");
40053 this.bodyEl.setStyle("position", "relative");
40055 this.active = null;
40056 this.activateDelegate = this.activate.createDelegate(this);
40061 * Fires when the active tab changes
40062 * @param {Roo.TabPanel} this
40063 * @param {Roo.TabPanelItem} activePanel The new active tab
40067 * @event beforetabchange
40068 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40069 * @param {Roo.TabPanel} this
40070 * @param {Object} e Set cancel to true on this object to cancel the tab change
40071 * @param {Roo.TabPanelItem} tab The tab being changed to
40073 "beforetabchange" : true
40076 Roo.EventManager.onWindowResize(this.onResize, this);
40077 this.cpad = this.el.getPadding("lr");
40078 this.hiddenCount = 0;
40081 // toolbar on the tabbar support...
40082 if (this.toolbar) {
40083 alert("no toolbar support yet");
40084 this.toolbar = false;
40086 var tcfg = this.toolbar;
40087 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40088 this.toolbar = new Roo.Toolbar(tcfg);
40089 if (Roo.isSafari) {
40090 var tbl = tcfg.container.child('table', true);
40091 tbl.setAttribute('width', '100%');
40099 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40102 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40104 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40106 tabPosition : "top",
40108 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40110 currentTabWidth : 0,
40112 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40116 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40120 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40122 preferredTabWidth : 175,
40124 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40126 resizeTabs : false,
40128 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40130 monitorResize : true,
40132 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40134 toolbar : false, // set by caller..
40136 region : false, /// set by caller
40138 disableTooltips : true, // not used yet...
40141 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40142 * @param {String} id The id of the div to use <b>or create</b>
40143 * @param {String} text The text for the tab
40144 * @param {String} content (optional) Content to put in the TabPanelItem body
40145 * @param {Boolean} closable (optional) True to create a close icon on the tab
40146 * @return {Roo.TabPanelItem} The created TabPanelItem
40148 addTab : function(id, text, content, closable, tpl)
40150 var item = new Roo.bootstrap.panel.TabItem({
40154 closable : closable,
40157 this.addTabItem(item);
40159 item.setContent(content);
40165 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40166 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40167 * @return {Roo.TabPanelItem}
40169 getTab : function(id){
40170 return this.items[id];
40174 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40175 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40177 hideTab : function(id){
40178 var t = this.items[id];
40181 this.hiddenCount++;
40182 this.autoSizeTabs();
40187 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40188 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40190 unhideTab : function(id){
40191 var t = this.items[id];
40193 t.setHidden(false);
40194 this.hiddenCount--;
40195 this.autoSizeTabs();
40200 * Adds an existing {@link Roo.TabPanelItem}.
40201 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40203 addTabItem : function(item)
40205 this.items[item.id] = item;
40206 this.items.push(item);
40207 this.autoSizeTabs();
40208 // if(this.resizeTabs){
40209 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40210 // this.autoSizeTabs();
40212 // item.autoSize();
40217 * Removes a {@link Roo.TabPanelItem}.
40218 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40220 removeTab : function(id){
40221 var items = this.items;
40222 var tab = items[id];
40223 if(!tab) { return; }
40224 var index = items.indexOf(tab);
40225 if(this.active == tab && items.length > 1){
40226 var newTab = this.getNextAvailable(index);
40231 this.stripEl.dom.removeChild(tab.pnode.dom);
40232 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40233 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40235 items.splice(index, 1);
40236 delete this.items[tab.id];
40237 tab.fireEvent("close", tab);
40238 tab.purgeListeners();
40239 this.autoSizeTabs();
40242 getNextAvailable : function(start){
40243 var items = this.items;
40245 // look for a next tab that will slide over to
40246 // replace the one being removed
40247 while(index < items.length){
40248 var item = items[++index];
40249 if(item && !item.isHidden()){
40253 // if one isn't found select the previous tab (on the left)
40256 var item = items[--index];
40257 if(item && !item.isHidden()){
40265 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40266 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40268 disableTab : function(id){
40269 var tab = this.items[id];
40270 if(tab && this.active != tab){
40276 * Enables a {@link Roo.TabPanelItem} that is disabled.
40277 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40279 enableTab : function(id){
40280 var tab = this.items[id];
40285 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40286 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40287 * @return {Roo.TabPanelItem} The TabPanelItem.
40289 activate : function(id)
40291 //Roo.log('activite:' + id);
40293 var tab = this.items[id];
40297 if(tab == this.active || tab.disabled){
40301 this.fireEvent("beforetabchange", this, e, tab);
40302 if(e.cancel !== true && !tab.disabled){
40304 this.active.hide();
40306 this.active = this.items[id];
40307 this.active.show();
40308 this.fireEvent("tabchange", this, this.active);
40314 * Gets the active {@link Roo.TabPanelItem}.
40315 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40317 getActiveTab : function(){
40318 return this.active;
40322 * Updates the tab body element to fit the height of the container element
40323 * for overflow scrolling
40324 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40326 syncHeight : function(targetHeight){
40327 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40328 var bm = this.bodyEl.getMargins();
40329 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40330 this.bodyEl.setHeight(newHeight);
40334 onResize : function(){
40335 if(this.monitorResize){
40336 this.autoSizeTabs();
40341 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40343 beginUpdate : function(){
40344 this.updating = true;
40348 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40350 endUpdate : function(){
40351 this.updating = false;
40352 this.autoSizeTabs();
40356 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40358 autoSizeTabs : function()
40360 var count = this.items.length;
40361 var vcount = count - this.hiddenCount;
40364 this.stripEl.hide();
40366 this.stripEl.show();
40369 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40374 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40375 var availWidth = Math.floor(w / vcount);
40376 var b = this.stripBody;
40377 if(b.getWidth() > w){
40378 var tabs = this.items;
40379 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40380 if(availWidth < this.minTabWidth){
40381 /*if(!this.sleft){ // incomplete scrolling code
40382 this.createScrollButtons();
40385 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40388 if(this.currentTabWidth < this.preferredTabWidth){
40389 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40395 * Returns the number of tabs in this TabPanel.
40398 getCount : function(){
40399 return this.items.length;
40403 * Resizes all the tabs to the passed width
40404 * @param {Number} The new width
40406 setTabWidth : function(width){
40407 this.currentTabWidth = width;
40408 for(var i = 0, len = this.items.length; i < len; i++) {
40409 if(!this.items[i].isHidden()) {
40410 this.items[i].setWidth(width);
40416 * Destroys this TabPanel
40417 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40419 destroy : function(removeEl){
40420 Roo.EventManager.removeResizeListener(this.onResize, this);
40421 for(var i = 0, len = this.items.length; i < len; i++){
40422 this.items[i].purgeListeners();
40424 if(removeEl === true){
40425 this.el.update("");
40430 createStrip : function(container)
40432 var strip = document.createElement("nav");
40433 strip.className = Roo.bootstrap.version == 4 ?
40434 "navbar-light bg-light" :
40435 "navbar navbar-default"; //"x-tabs-wrap";
40436 container.appendChild(strip);
40440 createStripList : function(strip)
40442 // div wrapper for retard IE
40443 // returns the "tr" element.
40444 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40445 //'<div class="x-tabs-strip-wrap">'+
40446 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40447 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40448 return strip.firstChild; //.firstChild.firstChild.firstChild;
40450 createBody : function(container)
40452 var body = document.createElement("div");
40453 Roo.id(body, "tab-body");
40454 //Roo.fly(body).addClass("x-tabs-body");
40455 Roo.fly(body).addClass("tab-content");
40456 container.appendChild(body);
40459 createItemBody :function(bodyEl, id){
40460 var body = Roo.getDom(id);
40462 body = document.createElement("div");
40465 //Roo.fly(body).addClass("x-tabs-item-body");
40466 Roo.fly(body).addClass("tab-pane");
40467 bodyEl.insertBefore(body, bodyEl.firstChild);
40471 createStripElements : function(stripEl, text, closable, tpl)
40473 var td = document.createElement("li"); // was td..
40474 td.className = 'nav-item';
40476 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40479 stripEl.appendChild(td);
40481 td.className = "x-tabs-closable";
40482 if(!this.closeTpl){
40483 this.closeTpl = new Roo.Template(
40484 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40485 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40486 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40489 var el = this.closeTpl.overwrite(td, {"text": text});
40490 var close = el.getElementsByTagName("div")[0];
40491 var inner = el.getElementsByTagName("em")[0];
40492 return {"el": el, "close": close, "inner": inner};
40495 // not sure what this is..
40496 // if(!this.tabTpl){
40497 //this.tabTpl = new Roo.Template(
40498 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40499 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40501 // this.tabTpl = new Roo.Template(
40502 // '<a href="#">' +
40503 // '<span unselectable="on"' +
40504 // (this.disableTooltips ? '' : ' title="{text}"') +
40505 // ' >{text}</span></a>'
40511 var template = tpl || this.tabTpl || false;
40514 template = new Roo.Template(
40515 Roo.bootstrap.version == 4 ?
40517 '<a class="nav-link" href="#" unselectable="on"' +
40518 (this.disableTooltips ? '' : ' title="{text}"') +
40521 '<a class="nav-link" href="#">' +
40522 '<span unselectable="on"' +
40523 (this.disableTooltips ? '' : ' title="{text}"') +
40524 ' >{text}</span></a>'
40529 switch (typeof(template)) {
40533 template = new Roo.Template(template);
40539 var el = template.overwrite(td, {"text": text});
40541 var inner = el.getElementsByTagName("span")[0];
40543 return {"el": el, "inner": inner};
40551 * @class Roo.TabPanelItem
40552 * @extends Roo.util.Observable
40553 * Represents an individual item (tab plus body) in a TabPanel.
40554 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40555 * @param {String} id The id of this TabPanelItem
40556 * @param {String} text The text for the tab of this TabPanelItem
40557 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40559 Roo.bootstrap.panel.TabItem = function(config){
40561 * The {@link Roo.TabPanel} this TabPanelItem belongs to
40562 * @type Roo.TabPanel
40564 this.tabPanel = config.panel;
40566 * The id for this TabPanelItem
40569 this.id = config.id;
40571 this.disabled = false;
40573 this.text = config.text;
40575 this.loaded = false;
40576 this.closable = config.closable;
40579 * The body element for this TabPanelItem.
40580 * @type Roo.Element
40582 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40583 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40584 this.bodyEl.setStyle("display", "block");
40585 this.bodyEl.setStyle("zoom", "1");
40586 //this.hideAction();
40588 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40590 this.el = Roo.get(els.el);
40591 this.inner = Roo.get(els.inner, true);
40592 this.textEl = Roo.bootstrap.version == 4 ?
40593 this.el : Roo.get(this.el.dom.firstChild, true);
40595 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40596 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40599 // this.el.on("mousedown", this.onTabMouseDown, this);
40600 this.el.on("click", this.onTabClick, this);
40602 if(config.closable){
40603 var c = Roo.get(els.close, true);
40604 c.dom.title = this.closeText;
40605 c.addClassOnOver("close-over");
40606 c.on("click", this.closeClick, this);
40612 * Fires when this tab becomes the active tab.
40613 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40614 * @param {Roo.TabPanelItem} this
40618 * @event beforeclose
40619 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40620 * @param {Roo.TabPanelItem} this
40621 * @param {Object} e Set cancel to true on this object to cancel the close.
40623 "beforeclose": true,
40626 * Fires when this tab is closed.
40627 * @param {Roo.TabPanelItem} this
40631 * @event deactivate
40632 * Fires when this tab is no longer the active tab.
40633 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40634 * @param {Roo.TabPanelItem} this
40636 "deactivate" : true
40638 this.hidden = false;
40640 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40643 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40645 purgeListeners : function(){
40646 Roo.util.Observable.prototype.purgeListeners.call(this);
40647 this.el.removeAllListeners();
40650 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40653 this.status_node.addClass("active");
40656 this.tabPanel.stripWrap.repaint();
40658 this.fireEvent("activate", this.tabPanel, this);
40662 * Returns true if this tab is the active tab.
40663 * @return {Boolean}
40665 isActive : function(){
40666 return this.tabPanel.getActiveTab() == this;
40670 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40673 this.status_node.removeClass("active");
40675 this.fireEvent("deactivate", this.tabPanel, this);
40678 hideAction : function(){
40679 this.bodyEl.hide();
40680 this.bodyEl.setStyle("position", "absolute");
40681 this.bodyEl.setLeft("-20000px");
40682 this.bodyEl.setTop("-20000px");
40685 showAction : function(){
40686 this.bodyEl.setStyle("position", "relative");
40687 this.bodyEl.setTop("");
40688 this.bodyEl.setLeft("");
40689 this.bodyEl.show();
40693 * Set the tooltip for the tab.
40694 * @param {String} tooltip The tab's tooltip
40696 setTooltip : function(text){
40697 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40698 this.textEl.dom.qtip = text;
40699 this.textEl.dom.removeAttribute('title');
40701 this.textEl.dom.title = text;
40705 onTabClick : function(e){
40706 e.preventDefault();
40707 this.tabPanel.activate(this.id);
40710 onTabMouseDown : function(e){
40711 e.preventDefault();
40712 this.tabPanel.activate(this.id);
40715 getWidth : function(){
40716 return this.inner.getWidth();
40719 setWidth : function(width){
40720 var iwidth = width - this.linode.getPadding("lr");
40721 this.inner.setWidth(iwidth);
40722 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40723 this.linode.setWidth(width);
40727 * Show or hide the tab
40728 * @param {Boolean} hidden True to hide or false to show.
40730 setHidden : function(hidden){
40731 this.hidden = hidden;
40732 this.linode.setStyle("display", hidden ? "none" : "");
40736 * Returns true if this tab is "hidden"
40737 * @return {Boolean}
40739 isHidden : function(){
40740 return this.hidden;
40744 * Returns the text for this tab
40747 getText : function(){
40751 autoSize : function(){
40752 //this.el.beginMeasure();
40753 this.textEl.setWidth(1);
40755 * #2804 [new] Tabs in Roojs
40756 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40758 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40759 //this.el.endMeasure();
40763 * Sets the text for the tab (Note: this also sets the tooltip text)
40764 * @param {String} text The tab's text and tooltip
40766 setText : function(text){
40768 this.textEl.update(text);
40769 this.setTooltip(text);
40770 //if(!this.tabPanel.resizeTabs){
40771 // this.autoSize();
40775 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40777 activate : function(){
40778 this.tabPanel.activate(this.id);
40782 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40784 disable : function(){
40785 if(this.tabPanel.active != this){
40786 this.disabled = true;
40787 this.status_node.addClass("disabled");
40792 * Enables this TabPanelItem if it was previously disabled.
40794 enable : function(){
40795 this.disabled = false;
40796 this.status_node.removeClass("disabled");
40800 * Sets the content for this TabPanelItem.
40801 * @param {String} content The content
40802 * @param {Boolean} loadScripts true to look for and load scripts
40804 setContent : function(content, loadScripts){
40805 this.bodyEl.update(content, loadScripts);
40809 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40810 * @return {Roo.UpdateManager} The UpdateManager
40812 getUpdateManager : function(){
40813 return this.bodyEl.getUpdateManager();
40817 * Set a URL to be used to load the content for this TabPanelItem.
40818 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40819 * @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)
40820 * @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)
40821 * @return {Roo.UpdateManager} The UpdateManager
40823 setUrl : function(url, params, loadOnce){
40824 if(this.refreshDelegate){
40825 this.un('activate', this.refreshDelegate);
40827 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40828 this.on("activate", this.refreshDelegate);
40829 return this.bodyEl.getUpdateManager();
40833 _handleRefresh : function(url, params, loadOnce){
40834 if(!loadOnce || !this.loaded){
40835 var updater = this.bodyEl.getUpdateManager();
40836 updater.update(url, params, this._setLoaded.createDelegate(this));
40841 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
40842 * Will fail silently if the setUrl method has not been called.
40843 * This does not activate the panel, just updates its content.
40845 refresh : function(){
40846 if(this.refreshDelegate){
40847 this.loaded = false;
40848 this.refreshDelegate();
40853 _setLoaded : function(){
40854 this.loaded = true;
40858 closeClick : function(e){
40861 this.fireEvent("beforeclose", this, o);
40862 if(o.cancel !== true){
40863 this.tabPanel.removeTab(this.id);
40867 * The text displayed in the tooltip for the close icon.
40870 closeText : "Close this tab"
40873 * This script refer to:
40874 * Title: International Telephone Input
40875 * Author: Jack O'Connor
40876 * Code version: v12.1.12
40877 * Availability: https://github.com/jackocnr/intl-tel-input.git
40880 Roo.bootstrap.PhoneInputData = function() {
40883 "Afghanistan (افغانستان)",
40888 "Albania (Shqipëri)",
40893 "Algeria (الجزائر)",
40918 "Antigua and Barbuda",
40928 "Armenia (Հայաստան)",
40944 "Austria (Österreich)",
40949 "Azerbaijan (Azərbaycan)",
40959 "Bahrain (البحرين)",
40964 "Bangladesh (বাংলাদেশ)",
40974 "Belarus (Беларусь)",
40979 "Belgium (België)",
41009 "Bosnia and Herzegovina (Босна и Херцеговина)",
41024 "British Indian Ocean Territory",
41029 "British Virgin Islands",
41039 "Bulgaria (България)",
41049 "Burundi (Uburundi)",
41054 "Cambodia (កម្ពុជា)",
41059 "Cameroon (Cameroun)",
41068 ["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"]
41071 "Cape Verde (Kabu Verdi)",
41076 "Caribbean Netherlands",
41087 "Central African Republic (République centrafricaine)",
41107 "Christmas Island",
41113 "Cocos (Keeling) Islands",
41124 "Comoros (جزر القمر)",
41129 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41134 "Congo (Republic) (Congo-Brazzaville)",
41154 "Croatia (Hrvatska)",
41175 "Czech Republic (Česká republika)",
41180 "Denmark (Danmark)",
41195 "Dominican Republic (República Dominicana)",
41199 ["809", "829", "849"]
41217 "Equatorial Guinea (Guinea Ecuatorial)",
41237 "Falkland Islands (Islas Malvinas)",
41242 "Faroe Islands (Føroyar)",
41263 "French Guiana (Guyane française)",
41268 "French Polynesia (Polynésie française)",
41283 "Georgia (საქართველო)",
41288 "Germany (Deutschland)",
41308 "Greenland (Kalaallit Nunaat)",
41345 "Guinea-Bissau (Guiné Bissau)",
41370 "Hungary (Magyarország)",
41375 "Iceland (Ísland)",
41395 "Iraq (العراق)",
41411 "Israel (ישראל)",
41438 "Jordan (الأردن)",
41443 "Kazakhstan (Казахстан)",
41464 "Kuwait (الكويت)",
41469 "Kyrgyzstan (Кыргызстан)",
41479 "Latvia (Latvija)",
41484 "Lebanon (لبنان)",
41499 "Libya (ليبيا)",
41509 "Lithuania (Lietuva)",
41524 "Macedonia (FYROM) (Македонија)",
41529 "Madagascar (Madagasikara)",
41559 "Marshall Islands",
41569 "Mauritania (موريتانيا)",
41574 "Mauritius (Moris)",
41595 "Moldova (Republica Moldova)",
41605 "Mongolia (Монгол)",
41610 "Montenegro (Crna Gora)",
41620 "Morocco (المغرب)",
41626 "Mozambique (Moçambique)",
41631 "Myanmar (Burma) (မြန်မာ)",
41636 "Namibia (Namibië)",
41651 "Netherlands (Nederland)",
41656 "New Caledonia (Nouvelle-Calédonie)",
41691 "North Korea (조선 민주주의 인민 공화국)",
41696 "Northern Mariana Islands",
41712 "Pakistan (پاکستان)",
41722 "Palestine (فلسطين)",
41732 "Papua New Guinea",
41774 "Réunion (La Réunion)",
41780 "Romania (România)",
41796 "Saint Barthélemy",
41807 "Saint Kitts and Nevis",
41817 "Saint Martin (Saint-Martin (partie française))",
41823 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41828 "Saint Vincent and the Grenadines",
41843 "São Tomé and Príncipe (São Tomé e Príncipe)",
41848 "Saudi Arabia (المملكة العربية السعودية)",
41853 "Senegal (Sénégal)",
41883 "Slovakia (Slovensko)",
41888 "Slovenia (Slovenija)",
41898 "Somalia (Soomaaliya)",
41908 "South Korea (대한민국)",
41913 "South Sudan (جنوب السودان)",
41923 "Sri Lanka (ශ්රී ලංකාව)",
41928 "Sudan (السودان)",
41938 "Svalbard and Jan Mayen",
41949 "Sweden (Sverige)",
41954 "Switzerland (Schweiz)",
41959 "Syria (سوريا)",
42004 "Trinidad and Tobago",
42009 "Tunisia (تونس)",
42014 "Turkey (Türkiye)",
42024 "Turks and Caicos Islands",
42034 "U.S. Virgin Islands",
42044 "Ukraine (Україна)",
42049 "United Arab Emirates (الإمارات العربية المتحدة)",
42071 "Uzbekistan (Oʻzbekiston)",
42081 "Vatican City (Città del Vaticano)",
42092 "Vietnam (Việt Nam)",
42097 "Wallis and Futuna (Wallis-et-Futuna)",
42102 "Western Sahara (الصحراء الغربية)",
42108 "Yemen (اليمن)",
42132 * This script refer to:
42133 * Title: International Telephone Input
42134 * Author: Jack O'Connor
42135 * Code version: v12.1.12
42136 * Availability: https://github.com/jackocnr/intl-tel-input.git
42140 * @class Roo.bootstrap.PhoneInput
42141 * @extends Roo.bootstrap.TriggerField
42142 * An input with International dial-code selection
42144 * @cfg {String} defaultDialCode default '+852'
42145 * @cfg {Array} preferedCountries default []
42148 * Create a new PhoneInput.
42149 * @param {Object} config Configuration options
42152 Roo.bootstrap.PhoneInput = function(config) {
42153 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42156 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42158 listWidth: undefined,
42160 selectedClass: 'active',
42162 invalidClass : "has-warning",
42164 validClass: 'has-success',
42166 allowed: '0123456789',
42171 * @cfg {String} defaultDialCode The default dial code when initializing the input
42173 defaultDialCode: '+852',
42176 * @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
42178 preferedCountries: false,
42180 getAutoCreate : function()
42182 var data = Roo.bootstrap.PhoneInputData();
42183 var align = this.labelAlign || this.parentLabelAlign();
42186 this.allCountries = [];
42187 this.dialCodeMapping = [];
42189 for (var i = 0; i < data.length; i++) {
42191 this.allCountries[i] = {
42195 priority: c[3] || 0,
42196 areaCodes: c[4] || null
42198 this.dialCodeMapping[c[2]] = {
42201 priority: c[3] || 0,
42202 areaCodes: c[4] || null
42214 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42215 maxlength: this.max_length,
42216 cls : 'form-control tel-input',
42217 autocomplete: 'new-password'
42220 var hiddenInput = {
42223 cls: 'hidden-tel-input'
42227 hiddenInput.name = this.name;
42230 if (this.disabled) {
42231 input.disabled = true;
42234 var flag_container = {
42251 cls: this.hasFeedback ? 'has-feedback' : '',
42257 cls: 'dial-code-holder',
42264 cls: 'roo-select2-container input-group',
42271 if (this.fieldLabel.length) {
42274 tooltip: 'This field is required'
42280 cls: 'control-label',
42286 html: this.fieldLabel
42289 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42295 if(this.indicatorpos == 'right') {
42296 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42303 if(align == 'left') {
42311 if(this.labelWidth > 12){
42312 label.style = "width: " + this.labelWidth + 'px';
42314 if(this.labelWidth < 13 && this.labelmd == 0){
42315 this.labelmd = this.labelWidth;
42317 if(this.labellg > 0){
42318 label.cls += ' col-lg-' + this.labellg;
42319 input.cls += ' col-lg-' + (12 - this.labellg);
42321 if(this.labelmd > 0){
42322 label.cls += ' col-md-' + this.labelmd;
42323 container.cls += ' col-md-' + (12 - this.labelmd);
42325 if(this.labelsm > 0){
42326 label.cls += ' col-sm-' + this.labelsm;
42327 container.cls += ' col-sm-' + (12 - this.labelsm);
42329 if(this.labelxs > 0){
42330 label.cls += ' col-xs-' + this.labelxs;
42331 container.cls += ' col-xs-' + (12 - this.labelxs);
42341 var settings = this;
42343 ['xs','sm','md','lg'].map(function(size){
42344 if (settings[size]) {
42345 cfg.cls += ' col-' + size + '-' + settings[size];
42349 this.store = new Roo.data.Store({
42350 proxy : new Roo.data.MemoryProxy({}),
42351 reader : new Roo.data.JsonReader({
42362 'name' : 'dialCode',
42366 'name' : 'priority',
42370 'name' : 'areaCodes',
42377 if(!this.preferedCountries) {
42378 this.preferedCountries = [
42385 var p = this.preferedCountries.reverse();
42388 for (var i = 0; i < p.length; i++) {
42389 for (var j = 0; j < this.allCountries.length; j++) {
42390 if(this.allCountries[j].iso2 == p[i]) {
42391 var t = this.allCountries[j];
42392 this.allCountries.splice(j,1);
42393 this.allCountries.unshift(t);
42399 this.store.proxy.data = {
42401 data: this.allCountries
42407 initEvents : function()
42410 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42412 this.indicator = this.indicatorEl();
42413 this.flag = this.flagEl();
42414 this.dialCodeHolder = this.dialCodeHolderEl();
42416 this.trigger = this.el.select('div.flag-box',true).first();
42417 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42422 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42423 _this.list.setWidth(lw);
42426 this.list.on('mouseover', this.onViewOver, this);
42427 this.list.on('mousemove', this.onViewMove, this);
42428 this.inputEl().on("keyup", this.onKeyUp, this);
42429 this.inputEl().on("keypress", this.onKeyPress, this);
42431 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42433 this.view = new Roo.View(this.list, this.tpl, {
42434 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42437 this.view.on('click', this.onViewClick, this);
42438 this.setValue(this.defaultDialCode);
42441 onTriggerClick : function(e)
42443 Roo.log('trigger click');
42448 if(this.isExpanded()){
42450 this.hasFocus = false;
42452 this.store.load({});
42453 this.hasFocus = true;
42458 isExpanded : function()
42460 return this.list.isVisible();
42463 collapse : function()
42465 if(!this.isExpanded()){
42469 Roo.get(document).un('mousedown', this.collapseIf, this);
42470 Roo.get(document).un('mousewheel', this.collapseIf, this);
42471 this.fireEvent('collapse', this);
42475 expand : function()
42479 if(this.isExpanded() || !this.hasFocus){
42483 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42484 this.list.setWidth(lw);
42487 this.restrictHeight();
42489 Roo.get(document).on('mousedown', this.collapseIf, this);
42490 Roo.get(document).on('mousewheel', this.collapseIf, this);
42492 this.fireEvent('expand', this);
42495 restrictHeight : function()
42497 this.list.alignTo(this.inputEl(), this.listAlign);
42498 this.list.alignTo(this.inputEl(), this.listAlign);
42501 onViewOver : function(e, t)
42503 if(this.inKeyMode){
42506 var item = this.view.findItemFromChild(t);
42509 var index = this.view.indexOf(item);
42510 this.select(index, false);
42515 onViewClick : function(view, doFocus, el, e)
42517 var index = this.view.getSelectedIndexes()[0];
42519 var r = this.store.getAt(index);
42522 this.onSelect(r, index);
42524 if(doFocus !== false && !this.blockFocus){
42525 this.inputEl().focus();
42529 onViewMove : function(e, t)
42531 this.inKeyMode = false;
42534 select : function(index, scrollIntoView)
42536 this.selectedIndex = index;
42537 this.view.select(index);
42538 if(scrollIntoView !== false){
42539 var el = this.view.getNode(index);
42541 this.list.scrollChildIntoView(el, false);
42546 createList : function()
42548 this.list = Roo.get(document.body).createChild({
42550 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42551 style: 'display:none'
42554 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42557 collapseIf : function(e)
42559 var in_combo = e.within(this.el);
42560 var in_list = e.within(this.list);
42561 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42563 if (in_combo || in_list || is_list) {
42569 onSelect : function(record, index)
42571 if(this.fireEvent('beforeselect', this, record, index) !== false){
42573 this.setFlagClass(record.data.iso2);
42574 this.setDialCode(record.data.dialCode);
42575 this.hasFocus = false;
42577 this.fireEvent('select', this, record, index);
42581 flagEl : function()
42583 var flag = this.el.select('div.flag',true).first();
42590 dialCodeHolderEl : function()
42592 var d = this.el.select('input.dial-code-holder',true).first();
42599 setDialCode : function(v)
42601 this.dialCodeHolder.dom.value = '+'+v;
42604 setFlagClass : function(n)
42606 this.flag.dom.className = 'flag '+n;
42609 getValue : function()
42611 var v = this.inputEl().getValue();
42612 if(this.dialCodeHolder) {
42613 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42618 setValue : function(v)
42620 var d = this.getDialCode(v);
42622 //invalid dial code
42623 if(v.length == 0 || !d || d.length == 0) {
42625 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42626 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42632 this.setFlagClass(this.dialCodeMapping[d].iso2);
42633 this.setDialCode(d);
42634 this.inputEl().dom.value = v.replace('+'+d,'');
42635 this.hiddenEl().dom.value = this.getValue();
42640 getDialCode : function(v)
42644 if (v.length == 0) {
42645 return this.dialCodeHolder.dom.value;
42649 if (v.charAt(0) != "+") {
42652 var numericChars = "";
42653 for (var i = 1; i < v.length; i++) {
42654 var c = v.charAt(i);
42657 if (this.dialCodeMapping[numericChars]) {
42658 dialCode = v.substr(1, i);
42660 if (numericChars.length == 4) {
42670 this.setValue(this.defaultDialCode);
42674 hiddenEl : function()
42676 return this.el.select('input.hidden-tel-input',true).first();
42679 // after setting val
42680 onKeyUp : function(e){
42681 this.setValue(this.getValue());
42684 onKeyPress : function(e){
42685 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42692 * @class Roo.bootstrap.MoneyField
42693 * @extends Roo.bootstrap.ComboBox
42694 * Bootstrap MoneyField class
42697 * Create a new MoneyField.
42698 * @param {Object} config Configuration options
42701 Roo.bootstrap.MoneyField = function(config) {
42703 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42707 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42710 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42712 allowDecimals : true,
42714 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42716 decimalSeparator : ".",
42718 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42720 decimalPrecision : 0,
42722 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42724 allowNegative : true,
42726 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42730 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42732 minValue : Number.NEGATIVE_INFINITY,
42734 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42736 maxValue : Number.MAX_VALUE,
42738 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42740 minText : "The minimum value for this field is {0}",
42742 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42744 maxText : "The maximum value for this field is {0}",
42746 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
42747 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42749 nanText : "{0} is not a valid number",
42751 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42755 * @cfg {String} defaults currency of the MoneyField
42756 * value should be in lkey
42758 defaultCurrency : false,
42760 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42762 thousandsDelimiter : false,
42764 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42775 getAutoCreate : function()
42777 var align = this.labelAlign || this.parentLabelAlign();
42789 cls : 'form-control roo-money-amount-input',
42790 autocomplete: 'new-password'
42793 var hiddenInput = {
42797 cls: 'hidden-number-input'
42800 if(this.max_length) {
42801 input.maxlength = this.max_length;
42805 hiddenInput.name = this.name;
42808 if (this.disabled) {
42809 input.disabled = true;
42812 var clg = 12 - this.inputlg;
42813 var cmd = 12 - this.inputmd;
42814 var csm = 12 - this.inputsm;
42815 var cxs = 12 - this.inputxs;
42819 cls : 'row roo-money-field',
42823 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42827 cls: 'roo-select2-container input-group',
42831 cls : 'form-control roo-money-currency-input',
42832 autocomplete: 'new-password',
42834 name : this.currencyName
42838 cls : 'input-group-addon',
42852 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42856 cls: this.hasFeedback ? 'has-feedback' : '',
42867 if (this.fieldLabel.length) {
42870 tooltip: 'This field is required'
42876 cls: 'control-label',
42882 html: this.fieldLabel
42885 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42891 if(this.indicatorpos == 'right') {
42892 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42899 if(align == 'left') {
42907 if(this.labelWidth > 12){
42908 label.style = "width: " + this.labelWidth + 'px';
42910 if(this.labelWidth < 13 && this.labelmd == 0){
42911 this.labelmd = this.labelWidth;
42913 if(this.labellg > 0){
42914 label.cls += ' col-lg-' + this.labellg;
42915 input.cls += ' col-lg-' + (12 - this.labellg);
42917 if(this.labelmd > 0){
42918 label.cls += ' col-md-' + this.labelmd;
42919 container.cls += ' col-md-' + (12 - this.labelmd);
42921 if(this.labelsm > 0){
42922 label.cls += ' col-sm-' + this.labelsm;
42923 container.cls += ' col-sm-' + (12 - this.labelsm);
42925 if(this.labelxs > 0){
42926 label.cls += ' col-xs-' + this.labelxs;
42927 container.cls += ' col-xs-' + (12 - this.labelxs);
42938 var settings = this;
42940 ['xs','sm','md','lg'].map(function(size){
42941 if (settings[size]) {
42942 cfg.cls += ' col-' + size + '-' + settings[size];
42949 initEvents : function()
42951 this.indicator = this.indicatorEl();
42953 this.initCurrencyEvent();
42955 this.initNumberEvent();
42958 initCurrencyEvent : function()
42961 throw "can not find store for combo";
42964 this.store = Roo.factory(this.store, Roo.data);
42965 this.store.parent = this;
42969 this.triggerEl = this.el.select('.input-group-addon', true).first();
42971 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42976 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42977 _this.list.setWidth(lw);
42980 this.list.on('mouseover', this.onViewOver, this);
42981 this.list.on('mousemove', this.onViewMove, this);
42982 this.list.on('scroll', this.onViewScroll, this);
42985 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42988 this.view = new Roo.View(this.list, this.tpl, {
42989 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42992 this.view.on('click', this.onViewClick, this);
42994 this.store.on('beforeload', this.onBeforeLoad, this);
42995 this.store.on('load', this.onLoad, this);
42996 this.store.on('loadexception', this.onLoadException, this);
42998 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42999 "up" : function(e){
43000 this.inKeyMode = true;
43004 "down" : function(e){
43005 if(!this.isExpanded()){
43006 this.onTriggerClick();
43008 this.inKeyMode = true;
43013 "enter" : function(e){
43016 if(this.fireEvent("specialkey", this, e)){
43017 this.onViewClick(false);
43023 "esc" : function(e){
43027 "tab" : function(e){
43030 if(this.fireEvent("specialkey", this, e)){
43031 this.onViewClick(false);
43039 doRelay : function(foo, bar, hname){
43040 if(hname == 'down' || this.scope.isExpanded()){
43041 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43049 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43053 initNumberEvent : function(e)
43055 this.inputEl().on("keydown" , this.fireKey, this);
43056 this.inputEl().on("focus", this.onFocus, this);
43057 this.inputEl().on("blur", this.onBlur, this);
43059 this.inputEl().relayEvent('keyup', this);
43061 if(this.indicator){
43062 this.indicator.addClass('invisible');
43065 this.originalValue = this.getValue();
43067 if(this.validationEvent == 'keyup'){
43068 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43069 this.inputEl().on('keyup', this.filterValidation, this);
43071 else if(this.validationEvent !== false){
43072 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43075 if(this.selectOnFocus){
43076 this.on("focus", this.preFocus, this);
43079 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43080 this.inputEl().on("keypress", this.filterKeys, this);
43082 this.inputEl().relayEvent('keypress', this);
43085 var allowed = "0123456789";
43087 if(this.allowDecimals){
43088 allowed += this.decimalSeparator;
43091 if(this.allowNegative){
43095 if(this.thousandsDelimiter) {
43099 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43101 var keyPress = function(e){
43103 var k = e.getKey();
43105 var c = e.getCharCode();
43108 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43109 allowed.indexOf(String.fromCharCode(c)) === -1
43115 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43119 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43124 this.inputEl().on("keypress", keyPress, this);
43128 onTriggerClick : function(e)
43135 this.loadNext = false;
43137 if(this.isExpanded()){
43142 this.hasFocus = true;
43144 if(this.triggerAction == 'all') {
43145 this.doQuery(this.allQuery, true);
43149 this.doQuery(this.getRawValue());
43152 getCurrency : function()
43154 var v = this.currencyEl().getValue();
43159 restrictHeight : function()
43161 this.list.alignTo(this.currencyEl(), this.listAlign);
43162 this.list.alignTo(this.currencyEl(), this.listAlign);
43165 onViewClick : function(view, doFocus, el, e)
43167 var index = this.view.getSelectedIndexes()[0];
43169 var r = this.store.getAt(index);
43172 this.onSelect(r, index);
43176 onSelect : function(record, index){
43178 if(this.fireEvent('beforeselect', this, record, index) !== false){
43180 this.setFromCurrencyData(index > -1 ? record.data : false);
43184 this.fireEvent('select', this, record, index);
43188 setFromCurrencyData : function(o)
43192 this.lastCurrency = o;
43194 if (this.currencyField) {
43195 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43197 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43200 this.lastSelectionText = currency;
43202 //setting default currency
43203 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43204 this.setCurrency(this.defaultCurrency);
43208 this.setCurrency(currency);
43211 setFromData : function(o)
43215 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43217 this.setFromCurrencyData(c);
43222 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43224 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43227 this.setValue(value);
43231 setCurrency : function(v)
43233 this.currencyValue = v;
43236 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43241 setValue : function(v)
43243 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43249 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43251 this.inputEl().dom.value = (v == '') ? '' :
43252 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43254 if(!this.allowZero && v === '0') {
43255 this.hiddenEl().dom.value = '';
43256 this.inputEl().dom.value = '';
43263 getRawValue : function()
43265 var v = this.inputEl().getValue();
43270 getValue : function()
43272 return this.fixPrecision(this.parseValue(this.getRawValue()));
43275 parseValue : function(value)
43277 if(this.thousandsDelimiter) {
43279 r = new RegExp(",", "g");
43280 value = value.replace(r, "");
43283 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43284 return isNaN(value) ? '' : value;
43288 fixPrecision : function(value)
43290 if(this.thousandsDelimiter) {
43292 r = new RegExp(",", "g");
43293 value = value.replace(r, "");
43296 var nan = isNaN(value);
43298 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43299 return nan ? '' : value;
43301 return parseFloat(value).toFixed(this.decimalPrecision);
43304 decimalPrecisionFcn : function(v)
43306 return Math.floor(v);
43309 validateValue : function(value)
43311 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43315 var num = this.parseValue(value);
43318 this.markInvalid(String.format(this.nanText, value));
43322 if(num < this.minValue){
43323 this.markInvalid(String.format(this.minText, this.minValue));
43327 if(num > this.maxValue){
43328 this.markInvalid(String.format(this.maxText, this.maxValue));
43335 validate : function()
43337 if(this.disabled || this.allowBlank){
43342 var currency = this.getCurrency();
43344 if(this.validateValue(this.getRawValue()) && currency.length){
43349 this.markInvalid();
43353 getName: function()
43358 beforeBlur : function()
43364 var v = this.parseValue(this.getRawValue());
43371 onBlur : function()
43375 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43376 //this.el.removeClass(this.focusClass);
43379 this.hasFocus = false;
43381 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43385 var v = this.getValue();
43387 if(String(v) !== String(this.startValue)){
43388 this.fireEvent('change', this, v, this.startValue);
43391 this.fireEvent("blur", this);
43394 inputEl : function()
43396 return this.el.select('.roo-money-amount-input', true).first();
43399 currencyEl : function()
43401 return this.el.select('.roo-money-currency-input', true).first();
43404 hiddenEl : function()
43406 return this.el.select('input.hidden-number-input',true).first();
43410 * @class Roo.bootstrap.BezierSignature
43411 * @extends Roo.bootstrap.Component
43412 * Bootstrap BezierSignature class
43413 * This script refer to:
43414 * Title: Signature Pad
43416 * Availability: https://github.com/szimek/signature_pad
43419 * Create a new BezierSignature
43420 * @param {Object} config The config object
43423 Roo.bootstrap.BezierSignature = function(config){
43424 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43430 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43437 mouse_btn_down: true,
43440 * @cfg {int} canvas height
43442 canvas_height: '200px',
43445 * @cfg {float|function} Radius of a single dot.
43450 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43455 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43460 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43465 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43470 * @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.
43472 bg_color: 'rgba(0, 0, 0, 0)',
43475 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43477 dot_color: 'black',
43480 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43482 velocity_filter_weight: 0.7,
43485 * @cfg {function} Callback when stroke begin.
43490 * @cfg {function} Callback when stroke end.
43494 getAutoCreate : function()
43496 var cls = 'roo-signature column';
43499 cls += ' ' + this.cls;
43509 for(var i = 0; i < col_sizes.length; i++) {
43510 if(this[col_sizes[i]]) {
43511 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43521 cls: 'roo-signature-body',
43525 cls: 'roo-signature-body-canvas',
43526 height: this.canvas_height,
43527 width: this.canvas_width
43534 style: 'display: none'
43542 initEvents: function()
43544 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43546 var canvas = this.canvasEl();
43548 // mouse && touch event swapping...
43549 canvas.dom.style.touchAction = 'none';
43550 canvas.dom.style.msTouchAction = 'none';
43552 this.mouse_btn_down = false;
43553 canvas.on('mousedown', this._handleMouseDown, this);
43554 canvas.on('mousemove', this._handleMouseMove, this);
43555 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43557 if (window.PointerEvent) {
43558 canvas.on('pointerdown', this._handleMouseDown, this);
43559 canvas.on('pointermove', this._handleMouseMove, this);
43560 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43563 if ('ontouchstart' in window) {
43564 canvas.on('touchstart', this._handleTouchStart, this);
43565 canvas.on('touchmove', this._handleTouchMove, this);
43566 canvas.on('touchend', this._handleTouchEnd, this);
43569 Roo.EventManager.onWindowResize(this.resize, this, true);
43571 // file input event
43572 this.fileEl().on('change', this.uploadImage, this);
43579 resize: function(){
43581 var canvas = this.canvasEl().dom;
43582 var ctx = this.canvasElCtx();
43583 var img_data = false;
43585 if(canvas.width > 0) {
43586 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43588 // setting canvas width will clean img data
43591 var style = window.getComputedStyle ?
43592 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43594 var padding_left = parseInt(style.paddingLeft) || 0;
43595 var padding_right = parseInt(style.paddingRight) || 0;
43597 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43600 ctx.putImageData(img_data, 0, 0);
43604 _handleMouseDown: function(e)
43606 if (e.browserEvent.which === 1) {
43607 this.mouse_btn_down = true;
43608 this.strokeBegin(e);
43612 _handleMouseMove: function (e)
43614 if (this.mouse_btn_down) {
43615 this.strokeMoveUpdate(e);
43619 _handleMouseUp: function (e)
43621 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43622 this.mouse_btn_down = false;
43627 _handleTouchStart: function (e) {
43629 e.preventDefault();
43630 if (e.browserEvent.targetTouches.length === 1) {
43631 // var touch = e.browserEvent.changedTouches[0];
43632 // this.strokeBegin(touch);
43634 this.strokeBegin(e); // assume e catching the correct xy...
43638 _handleTouchMove: function (e) {
43639 e.preventDefault();
43640 // var touch = event.targetTouches[0];
43641 // _this._strokeMoveUpdate(touch);
43642 this.strokeMoveUpdate(e);
43645 _handleTouchEnd: function (e) {
43646 var wasCanvasTouched = e.target === this.canvasEl().dom;
43647 if (wasCanvasTouched) {
43648 e.preventDefault();
43649 // var touch = event.changedTouches[0];
43650 // _this._strokeEnd(touch);
43655 reset: function () {
43656 this._lastPoints = [];
43657 this._lastVelocity = 0;
43658 this._lastWidth = (this.min_width + this.max_width) / 2;
43659 this.canvasElCtx().fillStyle = this.dot_color;
43662 strokeMoveUpdate: function(e)
43664 this.strokeUpdate(e);
43666 if (this.throttle) {
43667 this.throttleStroke(this.strokeUpdate, this.throttle);
43670 this.strokeUpdate(e);
43674 strokeBegin: function(e)
43676 var newPointGroup = {
43677 color: this.dot_color,
43681 if (typeof this.onBegin === 'function') {
43685 this.curve_data.push(newPointGroup);
43687 this.strokeUpdate(e);
43690 strokeUpdate: function(e)
43692 var rect = this.canvasEl().dom.getBoundingClientRect();
43693 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43694 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43695 var lastPoints = lastPointGroup.points;
43696 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43697 var isLastPointTooClose = lastPoint
43698 ? point.distanceTo(lastPoint) <= this.min_distance
43700 var color = lastPointGroup.color;
43701 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43702 var curve = this.addPoint(point);
43704 this.drawDot({color: color, point: point});
43707 this.drawCurve({color: color, curve: curve});
43717 strokeEnd: function(e)
43719 this.strokeUpdate(e);
43720 if (typeof this.onEnd === 'function') {
43725 addPoint: function (point) {
43726 var _lastPoints = this._lastPoints;
43727 _lastPoints.push(point);
43728 if (_lastPoints.length > 2) {
43729 if (_lastPoints.length === 3) {
43730 _lastPoints.unshift(_lastPoints[0]);
43732 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43733 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43734 _lastPoints.shift();
43740 calculateCurveWidths: function (startPoint, endPoint) {
43741 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43742 (1 - this.velocity_filter_weight) * this._lastVelocity;
43744 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43747 start: this._lastWidth
43750 this._lastVelocity = velocity;
43751 this._lastWidth = newWidth;
43755 drawDot: function (_a) {
43756 var color = _a.color, point = _a.point;
43757 var ctx = this.canvasElCtx();
43758 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43760 this.drawCurveSegment(point.x, point.y, width);
43762 ctx.fillStyle = color;
43766 drawCurve: function (_a) {
43767 var color = _a.color, curve = _a.curve;
43768 var ctx = this.canvasElCtx();
43769 var widthDelta = curve.endWidth - curve.startWidth;
43770 var drawSteps = Math.floor(curve.length()) * 2;
43772 ctx.fillStyle = color;
43773 for (var i = 0; i < drawSteps; i += 1) {
43774 var t = i / drawSteps;
43780 var x = uuu * curve.startPoint.x;
43781 x += 3 * uu * t * curve.control1.x;
43782 x += 3 * u * tt * curve.control2.x;
43783 x += ttt * curve.endPoint.x;
43784 var y = uuu * curve.startPoint.y;
43785 y += 3 * uu * t * curve.control1.y;
43786 y += 3 * u * tt * curve.control2.y;
43787 y += ttt * curve.endPoint.y;
43788 var width = curve.startWidth + ttt * widthDelta;
43789 this.drawCurveSegment(x, y, width);
43795 drawCurveSegment: function (x, y, width) {
43796 var ctx = this.canvasElCtx();
43798 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43799 this.is_empty = false;
43804 var ctx = this.canvasElCtx();
43805 var canvas = this.canvasEl().dom;
43806 ctx.fillStyle = this.bg_color;
43807 ctx.clearRect(0, 0, canvas.width, canvas.height);
43808 ctx.fillRect(0, 0, canvas.width, canvas.height);
43809 this.curve_data = [];
43811 this.is_empty = true;
43816 return this.el.select('input',true).first();
43819 canvasEl: function()
43821 return this.el.select('canvas',true).first();
43824 canvasElCtx: function()
43826 return this.el.select('canvas',true).first().dom.getContext('2d');
43829 getImage: function(type)
43831 if(this.is_empty) {
43836 return this.canvasEl().dom.toDataURL('image/'+type, 1);
43839 drawFromImage: function(img_src)
43841 var img = new Image();
43843 img.onload = function(){
43844 this.canvasElCtx().drawImage(img, 0, 0);
43849 this.is_empty = false;
43852 selectImage: function()
43854 this.fileEl().dom.click();
43857 uploadImage: function(e)
43859 var reader = new FileReader();
43861 reader.onload = function(e){
43862 var img = new Image();
43863 img.onload = function(){
43865 this.canvasElCtx().drawImage(img, 0, 0);
43867 img.src = e.target.result;
43870 reader.readAsDataURL(e.target.files[0]);
43873 // Bezier Point Constructor
43874 Point: (function () {
43875 function Point(x, y, time) {
43878 this.time = time || Date.now();
43880 Point.prototype.distanceTo = function (start) {
43881 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43883 Point.prototype.equals = function (other) {
43884 return this.x === other.x && this.y === other.y && this.time === other.time;
43886 Point.prototype.velocityFrom = function (start) {
43887 return this.time !== start.time
43888 ? this.distanceTo(start) / (this.time - start.time)
43895 // Bezier Constructor
43896 Bezier: (function () {
43897 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43898 this.startPoint = startPoint;
43899 this.control2 = control2;
43900 this.control1 = control1;
43901 this.endPoint = endPoint;
43902 this.startWidth = startWidth;
43903 this.endWidth = endWidth;
43905 Bezier.fromPoints = function (points, widths, scope) {
43906 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43907 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43908 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43910 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43911 var dx1 = s1.x - s2.x;
43912 var dy1 = s1.y - s2.y;
43913 var dx2 = s2.x - s3.x;
43914 var dy2 = s2.y - s3.y;
43915 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43916 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43917 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43918 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43919 var dxm = m1.x - m2.x;
43920 var dym = m1.y - m2.y;
43921 var k = l2 / (l1 + l2);
43922 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43923 var tx = s2.x - cm.x;
43924 var ty = s2.y - cm.y;
43926 c1: new scope.Point(m1.x + tx, m1.y + ty),
43927 c2: new scope.Point(m2.x + tx, m2.y + ty)
43930 Bezier.prototype.length = function () {
43935 for (var i = 0; i <= steps; i += 1) {
43937 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43938 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43940 var xdiff = cx - px;
43941 var ydiff = cy - py;
43942 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43949 Bezier.prototype.point = function (t, start, c1, c2, end) {
43950 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43951 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43952 + (3.0 * c2 * (1.0 - t) * t * t)
43953 + (end * t * t * t);
43958 throttleStroke: function(fn, wait) {
43959 if (wait === void 0) { wait = 250; }
43961 var timeout = null;
43965 var later = function () {
43966 previous = Date.now();
43968 result = fn.apply(storedContext, storedArgs);
43970 storedContext = null;
43974 return function wrapper() {
43976 for (var _i = 0; _i < arguments.length; _i++) {
43977 args[_i] = arguments[_i];
43979 var now = Date.now();
43980 var remaining = wait - (now - previous);
43981 storedContext = this;
43983 if (remaining <= 0 || remaining > wait) {
43985 clearTimeout(timeout);
43989 result = fn.apply(storedContext, storedArgs);
43991 storedContext = null;
43995 else if (!timeout) {
43996 timeout = window.setTimeout(later, remaining);