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)
23490 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23493 if (value.length == 0) {
23494 if (this.allowBlank) {
23495 this.clearInvalid();
23499 this.markInvalid(this.errors.PwdEmpty);
23500 this.errorMsg = this.errors.PwdEmpty;
23508 if ('[\x21-\x7e]*'.match(value)) {
23509 this.markInvalid(this.errors.PwdBadChar);
23510 this.errorMsg = this.errors.PwdBadChar;
23513 if (value.length < 6) {
23514 this.markInvalid(this.errors.PwdShort);
23515 this.errorMsg = this.errors.PwdShort;
23518 if (value.length > 16) {
23519 this.markInvalid(this.errors.PwdLong);
23520 this.errorMsg = this.errors.PwdLong;
23524 if (this.ClientSideStrongPassword(value)) {
23526 } else if (this.ClientSideMediumPassword(value)) {
23528 } else if (this.ClientSideWeakPassword(value)) {
23535 if (strength < 2) {
23536 //this.markInvalid(this.errors.TooWeak);
23537 this.errorMsg = this.errors.TooWeak;
23542 console.log('strength2: ' + strength);
23544 //var pm = this.trigger.child('div/div/div').dom;
23546 var pm = this.trigger.child('div/div');
23547 pm.removeClass(this.meterClass);
23548 pm.addClass(this.meterClass[strength]);
23550 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23552 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23554 this.errorMsg = '';
23558 CharacterSetChecks: function (type)
23561 this.fResult = false;
23564 isctype: function (character, type)
23567 case this.kCapitalLetter:
23568 if (character >= 'A' && character <= 'Z') {
23573 case this.kSmallLetter:
23574 if (character >= 'a' && character <= 'z') {
23580 if (character >= '0' && character <= '9') {
23585 case this.kPunctuation:
23586 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23597 IsLongEnough: function (pwd, size)
23599 return !(pwd == null || isNaN(size) || pwd.length < size);
23602 SpansEnoughCharacterSets: function (word, nb)
23604 if (!this.IsLongEnough(word, nb))
23609 var characterSetChecks = new Array(
23610 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23611 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23614 for (var index = 0; index < word.length; ++index) {
23615 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23616 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23617 characterSetChecks[nCharSet].fResult = true;
23624 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23625 if (characterSetChecks[nCharSet].fResult) {
23630 if (nCharSets < nb) {
23636 ClientSideStrongPassword: function (pwd)
23638 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23641 ClientSideMediumPassword: function (pwd)
23643 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23646 ClientSideWeakPassword: function (pwd)
23648 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23651 })//<script type="text/javascript">
23654 * Based Ext JS Library 1.1.1
23655 * Copyright(c) 2006-2007, Ext JS, LLC.
23661 * @class Roo.HtmlEditorCore
23662 * @extends Roo.Component
23663 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23665 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23668 Roo.HtmlEditorCore = function(config){
23671 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23676 * @event initialize
23677 * Fires when the editor is fully initialized (including the iframe)
23678 * @param {Roo.HtmlEditorCore} this
23683 * Fires when the editor is first receives the focus. Any insertion must wait
23684 * until after this event.
23685 * @param {Roo.HtmlEditorCore} this
23689 * @event beforesync
23690 * Fires before the textarea is updated with content from the editor iframe. Return false
23691 * to cancel the sync.
23692 * @param {Roo.HtmlEditorCore} this
23693 * @param {String} html
23697 * @event beforepush
23698 * Fires before the iframe editor is updated with content from the textarea. Return false
23699 * to cancel the push.
23700 * @param {Roo.HtmlEditorCore} this
23701 * @param {String} html
23706 * Fires when the textarea is updated with content from the editor iframe.
23707 * @param {Roo.HtmlEditorCore} this
23708 * @param {String} html
23713 * Fires when the iframe editor is updated with content from the textarea.
23714 * @param {Roo.HtmlEditorCore} this
23715 * @param {String} html
23720 * @event editorevent
23721 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23722 * @param {Roo.HtmlEditorCore} this
23728 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23730 // defaults : white / black...
23731 this.applyBlacklists();
23738 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
23742 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
23748 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23753 * @cfg {Number} height (in pixels)
23757 * @cfg {Number} width (in pixels)
23762 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23765 stylesheets: false,
23770 // private properties
23771 validationEvent : false,
23773 initialized : false,
23775 sourceEditMode : false,
23776 onFocus : Roo.emptyFn,
23778 hideMode:'offsets',
23782 // blacklist + whitelisted elements..
23789 * Protected method that will not generally be called directly. It
23790 * is called when the editor initializes the iframe with HTML contents. Override this method if you
23791 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23793 getDocMarkup : function(){
23797 // inherit styels from page...??
23798 if (this.stylesheets === false) {
23800 Roo.get(document.head).select('style').each(function(node) {
23801 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23804 Roo.get(document.head).select('link').each(function(node) {
23805 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23808 } else if (!this.stylesheets.length) {
23810 st = '<style type="text/css">' +
23811 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23814 for (var i in this.stylesheets) {
23815 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
23820 st += '<style type="text/css">' +
23821 'IMG { cursor: pointer } ' +
23824 var cls = 'roo-htmleditor-body';
23826 if(this.bodyCls.length){
23827 cls += ' ' + this.bodyCls;
23830 return '<html><head>' + st +
23831 //<style type="text/css">' +
23832 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23834 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
23838 onRender : function(ct, position)
23841 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23842 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23845 this.el.dom.style.border = '0 none';
23846 this.el.dom.setAttribute('tabIndex', -1);
23847 this.el.addClass('x-hidden hide');
23851 if(Roo.isIE){ // fix IE 1px bogus margin
23852 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23856 this.frameId = Roo.id();
23860 var iframe = this.owner.wrap.createChild({
23862 cls: 'form-control', // bootstrap..
23864 name: this.frameId,
23865 frameBorder : 'no',
23866 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
23871 this.iframe = iframe.dom;
23873 this.assignDocWin();
23875 this.doc.designMode = 'on';
23878 this.doc.write(this.getDocMarkup());
23882 var task = { // must defer to wait for browser to be ready
23884 //console.log("run task?" + this.doc.readyState);
23885 this.assignDocWin();
23886 if(this.doc.body || this.doc.readyState == 'complete'){
23888 this.doc.designMode="on";
23892 Roo.TaskMgr.stop(task);
23893 this.initEditor.defer(10, this);
23900 Roo.TaskMgr.start(task);
23905 onResize : function(w, h)
23907 Roo.log('resize: ' +w + ',' + h );
23908 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23912 if(typeof w == 'number'){
23914 this.iframe.style.width = w + 'px';
23916 if(typeof h == 'number'){
23918 this.iframe.style.height = h + 'px';
23920 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23927 * Toggles the editor between standard and source edit mode.
23928 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23930 toggleSourceEdit : function(sourceEditMode){
23932 this.sourceEditMode = sourceEditMode === true;
23934 if(this.sourceEditMode){
23936 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
23939 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23940 //this.iframe.className = '';
23943 //this.setSize(this.owner.wrap.getSize());
23944 //this.fireEvent('editmodechange', this, this.sourceEditMode);
23951 * Protected method that will not generally be called directly. If you need/want
23952 * custom HTML cleanup, this is the method you should override.
23953 * @param {String} html The HTML to be cleaned
23954 * return {String} The cleaned HTML
23956 cleanHtml : function(html){
23957 html = String(html);
23958 if(html.length > 5){
23959 if(Roo.isSafari){ // strip safari nonsense
23960 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23963 if(html == ' '){
23970 * HTML Editor -> Textarea
23971 * Protected method that will not generally be called directly. Syncs the contents
23972 * of the editor iframe with the textarea.
23974 syncValue : function(){
23975 if(this.initialized){
23976 var bd = (this.doc.body || this.doc.documentElement);
23977 //this.cleanUpPaste(); -- this is done else where and causes havoc..
23978 var html = bd.innerHTML;
23980 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23981 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23983 html = '<div style="'+m[0]+'">' + html + '</div>';
23986 html = this.cleanHtml(html);
23987 // fix up the special chars.. normaly like back quotes in word...
23988 // however we do not want to do this with chinese..
23989 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23991 var cc = match.charCodeAt();
23993 // Get the character value, handling surrogate pairs
23994 if (match.length == 2) {
23995 // It's a surrogate pair, calculate the Unicode code point
23996 var high = match.charCodeAt(0) - 0xD800;
23997 var low = match.charCodeAt(1) - 0xDC00;
23998 cc = (high * 0x400) + low + 0x10000;
24000 (cc >= 0x4E00 && cc < 0xA000 ) ||
24001 (cc >= 0x3400 && cc < 0x4E00 ) ||
24002 (cc >= 0xf900 && cc < 0xfb00 )
24007 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24008 return "&#" + cc + ";";
24015 if(this.owner.fireEvent('beforesync', this, html) !== false){
24016 this.el.dom.value = html;
24017 this.owner.fireEvent('sync', this, html);
24023 * Protected method that will not generally be called directly. Pushes the value of the textarea
24024 * into the iframe editor.
24026 pushValue : function(){
24027 if(this.initialized){
24028 var v = this.el.dom.value.trim();
24030 // if(v.length < 1){
24034 if(this.owner.fireEvent('beforepush', this, v) !== false){
24035 var d = (this.doc.body || this.doc.documentElement);
24037 this.cleanUpPaste();
24038 this.el.dom.value = d.innerHTML;
24039 this.owner.fireEvent('push', this, v);
24045 deferFocus : function(){
24046 this.focus.defer(10, this);
24050 focus : function(){
24051 if(this.win && !this.sourceEditMode){
24058 assignDocWin: function()
24060 var iframe = this.iframe;
24063 this.doc = iframe.contentWindow.document;
24064 this.win = iframe.contentWindow;
24066 // if (!Roo.get(this.frameId)) {
24069 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24070 // this.win = Roo.get(this.frameId).dom.contentWindow;
24072 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24076 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24077 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24082 initEditor : function(){
24083 //console.log("INIT EDITOR");
24084 this.assignDocWin();
24088 this.doc.designMode="on";
24090 this.doc.write(this.getDocMarkup());
24093 var dbody = (this.doc.body || this.doc.documentElement);
24094 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24095 // this copies styles from the containing element into thsi one..
24096 // not sure why we need all of this..
24097 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24099 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24100 //ss['background-attachment'] = 'fixed'; // w3c
24101 dbody.bgProperties = 'fixed'; // ie
24102 //Roo.DomHelper.applyStyles(dbody, ss);
24103 Roo.EventManager.on(this.doc, {
24104 //'mousedown': this.onEditorEvent,
24105 'mouseup': this.onEditorEvent,
24106 'dblclick': this.onEditorEvent,
24107 'click': this.onEditorEvent,
24108 'keyup': this.onEditorEvent,
24113 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24115 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24116 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24118 this.initialized = true;
24120 this.owner.fireEvent('initialize', this);
24125 onDestroy : function(){
24131 //for (var i =0; i < this.toolbars.length;i++) {
24132 // // fixme - ask toolbars for heights?
24133 // this.toolbars[i].onDestroy();
24136 //this.wrap.dom.innerHTML = '';
24137 //this.wrap.remove();
24142 onFirstFocus : function(){
24144 this.assignDocWin();
24147 this.activated = true;
24150 if(Roo.isGecko){ // prevent silly gecko errors
24152 var s = this.win.getSelection();
24153 if(!s.focusNode || s.focusNode.nodeType != 3){
24154 var r = s.getRangeAt(0);
24155 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24160 this.execCmd('useCSS', true);
24161 this.execCmd('styleWithCSS', false);
24164 this.owner.fireEvent('activate', this);
24168 adjustFont: function(btn){
24169 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24170 //if(Roo.isSafari){ // safari
24173 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24174 if(Roo.isSafari){ // safari
24175 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24176 v = (v < 10) ? 10 : v;
24177 v = (v > 48) ? 48 : v;
24178 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24183 v = Math.max(1, v+adjust);
24185 this.execCmd('FontSize', v );
24188 onEditorEvent : function(e)
24190 this.owner.fireEvent('editorevent', this, e);
24191 // this.updateToolbar();
24192 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24195 insertTag : function(tg)
24197 // could be a bit smarter... -> wrap the current selected tRoo..
24198 if (tg.toLowerCase() == 'span' ||
24199 tg.toLowerCase() == 'code' ||
24200 tg.toLowerCase() == 'sup' ||
24201 tg.toLowerCase() == 'sub'
24204 range = this.createRange(this.getSelection());
24205 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24206 wrappingNode.appendChild(range.extractContents());
24207 range.insertNode(wrappingNode);
24214 this.execCmd("formatblock", tg);
24218 insertText : function(txt)
24222 var range = this.createRange();
24223 range.deleteContents();
24224 //alert(Sender.getAttribute('label'));
24226 range.insertNode(this.doc.createTextNode(txt));
24232 * Executes a Midas editor command on the editor document and performs necessary focus and
24233 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24234 * @param {String} cmd The Midas command
24235 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24237 relayCmd : function(cmd, value){
24239 this.execCmd(cmd, value);
24240 this.owner.fireEvent('editorevent', this);
24241 //this.updateToolbar();
24242 this.owner.deferFocus();
24246 * Executes a Midas editor command directly on the editor document.
24247 * For visual commands, you should use {@link #relayCmd} instead.
24248 * <b>This should only be called after the editor is initialized.</b>
24249 * @param {String} cmd The Midas command
24250 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24252 execCmd : function(cmd, value){
24253 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24260 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24262 * @param {String} text | dom node..
24264 insertAtCursor : function(text)
24267 if(!this.activated){
24273 var r = this.doc.selection.createRange();
24284 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24288 // from jquery ui (MIT licenced)
24290 var win = this.win;
24292 if (win.getSelection && win.getSelection().getRangeAt) {
24293 range = win.getSelection().getRangeAt(0);
24294 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24295 range.insertNode(node);
24296 } else if (win.document.selection && win.document.selection.createRange) {
24297 // no firefox support
24298 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24299 win.document.selection.createRange().pasteHTML(txt);
24301 // no firefox support
24302 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24303 this.execCmd('InsertHTML', txt);
24312 mozKeyPress : function(e){
24314 var c = e.getCharCode(), cmd;
24317 c = String.fromCharCode(c).toLowerCase();
24331 this.cleanUpPaste.defer(100, this);
24339 e.preventDefault();
24347 fixKeys : function(){ // load time branching for fastest keydown performance
24349 return function(e){
24350 var k = e.getKey(), r;
24353 r = this.doc.selection.createRange();
24356 r.pasteHTML('    ');
24363 r = this.doc.selection.createRange();
24365 var target = r.parentElement();
24366 if(!target || target.tagName.toLowerCase() != 'li'){
24368 r.pasteHTML('<br />');
24374 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24375 this.cleanUpPaste.defer(100, this);
24381 }else if(Roo.isOpera){
24382 return function(e){
24383 var k = e.getKey();
24387 this.execCmd('InsertHTML','    ');
24390 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24391 this.cleanUpPaste.defer(100, this);
24396 }else if(Roo.isSafari){
24397 return function(e){
24398 var k = e.getKey();
24402 this.execCmd('InsertText','\t');
24406 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24407 this.cleanUpPaste.defer(100, this);
24415 getAllAncestors: function()
24417 var p = this.getSelectedNode();
24420 a.push(p); // push blank onto stack..
24421 p = this.getParentElement();
24425 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24429 a.push(this.doc.body);
24433 lastSelNode : false,
24436 getSelection : function()
24438 this.assignDocWin();
24439 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24442 getSelectedNode: function()
24444 // this may only work on Gecko!!!
24446 // should we cache this!!!!
24451 var range = this.createRange(this.getSelection()).cloneRange();
24454 var parent = range.parentElement();
24456 var testRange = range.duplicate();
24457 testRange.moveToElementText(parent);
24458 if (testRange.inRange(range)) {
24461 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24464 parent = parent.parentElement;
24469 // is ancestor a text element.
24470 var ac = range.commonAncestorContainer;
24471 if (ac.nodeType == 3) {
24472 ac = ac.parentNode;
24475 var ar = ac.childNodes;
24478 var other_nodes = [];
24479 var has_other_nodes = false;
24480 for (var i=0;i<ar.length;i++) {
24481 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24484 // fullly contained node.
24486 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24491 // probably selected..
24492 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24493 other_nodes.push(ar[i]);
24497 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24502 has_other_nodes = true;
24504 if (!nodes.length && other_nodes.length) {
24505 nodes= other_nodes;
24507 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24513 createRange: function(sel)
24515 // this has strange effects when using with
24516 // top toolbar - not sure if it's a great idea.
24517 //this.editor.contentWindow.focus();
24518 if (typeof sel != "undefined") {
24520 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24522 return this.doc.createRange();
24525 return this.doc.createRange();
24528 getParentElement: function()
24531 this.assignDocWin();
24532 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24534 var range = this.createRange(sel);
24537 var p = range.commonAncestorContainer;
24538 while (p.nodeType == 3) { // text node
24549 * Range intersection.. the hard stuff...
24553 * [ -- selected range --- ]
24557 * if end is before start or hits it. fail.
24558 * if start is after end or hits it fail.
24560 * if either hits (but other is outside. - then it's not
24566 // @see http://www.thismuchiknow.co.uk/?p=64.
24567 rangeIntersectsNode : function(range, node)
24569 var nodeRange = node.ownerDocument.createRange();
24571 nodeRange.selectNode(node);
24573 nodeRange.selectNodeContents(node);
24576 var rangeStartRange = range.cloneRange();
24577 rangeStartRange.collapse(true);
24579 var rangeEndRange = range.cloneRange();
24580 rangeEndRange.collapse(false);
24582 var nodeStartRange = nodeRange.cloneRange();
24583 nodeStartRange.collapse(true);
24585 var nodeEndRange = nodeRange.cloneRange();
24586 nodeEndRange.collapse(false);
24588 return rangeStartRange.compareBoundaryPoints(
24589 Range.START_TO_START, nodeEndRange) == -1 &&
24590 rangeEndRange.compareBoundaryPoints(
24591 Range.START_TO_START, nodeStartRange) == 1;
24595 rangeCompareNode : function(range, node)
24597 var nodeRange = node.ownerDocument.createRange();
24599 nodeRange.selectNode(node);
24601 nodeRange.selectNodeContents(node);
24605 range.collapse(true);
24607 nodeRange.collapse(true);
24609 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24610 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
24612 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24614 var nodeIsBefore = ss == 1;
24615 var nodeIsAfter = ee == -1;
24617 if (nodeIsBefore && nodeIsAfter) {
24620 if (!nodeIsBefore && nodeIsAfter) {
24621 return 1; //right trailed.
24624 if (nodeIsBefore && !nodeIsAfter) {
24625 return 2; // left trailed.
24631 // private? - in a new class?
24632 cleanUpPaste : function()
24634 // cleans up the whole document..
24635 Roo.log('cleanuppaste');
24637 this.cleanUpChildren(this.doc.body);
24638 var clean = this.cleanWordChars(this.doc.body.innerHTML);
24639 if (clean != this.doc.body.innerHTML) {
24640 this.doc.body.innerHTML = clean;
24645 cleanWordChars : function(input) {// change the chars to hex code
24646 var he = Roo.HtmlEditorCore;
24648 var output = input;
24649 Roo.each(he.swapCodes, function(sw) {
24650 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24652 output = output.replace(swapper, sw[1]);
24659 cleanUpChildren : function (n)
24661 if (!n.childNodes.length) {
24664 for (var i = n.childNodes.length-1; i > -1 ; i--) {
24665 this.cleanUpChild(n.childNodes[i]);
24672 cleanUpChild : function (node)
24675 //console.log(node);
24676 if (node.nodeName == "#text") {
24677 // clean up silly Windows -- stuff?
24680 if (node.nodeName == "#comment") {
24681 node.parentNode.removeChild(node);
24682 // clean up silly Windows -- stuff?
24685 var lcname = node.tagName.toLowerCase();
24686 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24687 // whitelist of tags..
24689 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24691 node.parentNode.removeChild(node);
24696 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24698 // spans with no attributes - just remove them..
24699 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
24700 remove_keep_children = true;
24703 // remove <a name=....> as rendering on yahoo mailer is borked with this.
24704 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24706 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24707 // remove_keep_children = true;
24710 if (remove_keep_children) {
24711 this.cleanUpChildren(node);
24712 // inserts everything just before this node...
24713 while (node.childNodes.length) {
24714 var cn = node.childNodes[0];
24715 node.removeChild(cn);
24716 node.parentNode.insertBefore(cn, node);
24718 node.parentNode.removeChild(node);
24722 if (!node.attributes || !node.attributes.length) {
24727 this.cleanUpChildren(node);
24731 function cleanAttr(n,v)
24734 if (v.match(/^\./) || v.match(/^\//)) {
24737 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24740 if (v.match(/^#/)) {
24743 if (v.match(/^\{/)) { // allow template editing.
24746 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24747 node.removeAttribute(n);
24751 var cwhite = this.cwhite;
24752 var cblack = this.cblack;
24754 function cleanStyle(n,v)
24756 if (v.match(/expression/)) { //XSS?? should we even bother..
24757 node.removeAttribute(n);
24761 var parts = v.split(/;/);
24764 Roo.each(parts, function(p) {
24765 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24769 var l = p.split(':').shift().replace(/\s+/g,'');
24770 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24772 if ( cwhite.length && cblack.indexOf(l) > -1) {
24773 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24774 //node.removeAttribute(n);
24778 // only allow 'c whitelisted system attributes'
24779 if ( cwhite.length && cwhite.indexOf(l) < 0) {
24780 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24781 //node.removeAttribute(n);
24791 if (clean.length) {
24792 node.setAttribute(n, clean.join(';'));
24794 node.removeAttribute(n);
24800 for (var i = node.attributes.length-1; i > -1 ; i--) {
24801 var a = node.attributes[i];
24804 if (a.name.toLowerCase().substr(0,2)=='on') {
24805 node.removeAttribute(a.name);
24808 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24809 node.removeAttribute(a.name);
24812 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24813 cleanAttr(a.name,a.value); // fixme..
24816 if (a.name == 'style') {
24817 cleanStyle(a.name,a.value);
24820 /// clean up MS crap..
24821 // tecnically this should be a list of valid class'es..
24824 if (a.name == 'class') {
24825 if (a.value.match(/^Mso/)) {
24826 node.removeAttribute('class');
24829 if (a.value.match(/^body$/)) {
24830 node.removeAttribute('class');
24841 this.cleanUpChildren(node);
24847 * Clean up MS wordisms...
24849 cleanWord : function(node)
24852 this.cleanWord(this.doc.body);
24857 node.nodeName == 'SPAN' &&
24858 !node.hasAttributes() &&
24859 node.childNodes.length == 1 &&
24860 node.firstChild.nodeName == "#text"
24862 var textNode = node.firstChild;
24863 node.removeChild(textNode);
24864 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24865 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24867 node.parentNode.insertBefore(textNode, node);
24868 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24869 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24871 node.parentNode.removeChild(node);
24874 if (node.nodeName == "#text") {
24875 // clean up silly Windows -- stuff?
24878 if (node.nodeName == "#comment") {
24879 node.parentNode.removeChild(node);
24880 // clean up silly Windows -- stuff?
24884 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24885 node.parentNode.removeChild(node);
24888 //Roo.log(node.tagName);
24889 // remove - but keep children..
24890 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24891 //Roo.log('-- removed');
24892 while (node.childNodes.length) {
24893 var cn = node.childNodes[0];
24894 node.removeChild(cn);
24895 node.parentNode.insertBefore(cn, node);
24896 // move node to parent - and clean it..
24897 this.cleanWord(cn);
24899 node.parentNode.removeChild(node);
24900 /// no need to iterate chidlren = it's got none..
24901 //this.iterateChildren(node, this.cleanWord);
24905 if (node.className.length) {
24907 var cn = node.className.split(/\W+/);
24909 Roo.each(cn, function(cls) {
24910 if (cls.match(/Mso[a-zA-Z]+/)) {
24915 node.className = cna.length ? cna.join(' ') : '';
24917 node.removeAttribute("class");
24921 if (node.hasAttribute("lang")) {
24922 node.removeAttribute("lang");
24925 if (node.hasAttribute("style")) {
24927 var styles = node.getAttribute("style").split(";");
24929 Roo.each(styles, function(s) {
24930 if (!s.match(/:/)) {
24933 var kv = s.split(":");
24934 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24937 // what ever is left... we allow.
24940 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24941 if (!nstyle.length) {
24942 node.removeAttribute('style');
24945 this.iterateChildren(node, this.cleanWord);
24951 * iterateChildren of a Node, calling fn each time, using this as the scole..
24952 * @param {DomNode} node node to iterate children of.
24953 * @param {Function} fn method of this class to call on each item.
24955 iterateChildren : function(node, fn)
24957 if (!node.childNodes.length) {
24960 for (var i = node.childNodes.length-1; i > -1 ; i--) {
24961 fn.call(this, node.childNodes[i])
24967 * cleanTableWidths.
24969 * Quite often pasting from word etc.. results in tables with column and widths.
24970 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24973 cleanTableWidths : function(node)
24978 this.cleanTableWidths(this.doc.body);
24983 if (node.nodeName == "#text" || node.nodeName == "#comment") {
24986 Roo.log(node.tagName);
24987 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24988 this.iterateChildren(node, this.cleanTableWidths);
24991 if (node.hasAttribute('width')) {
24992 node.removeAttribute('width');
24996 if (node.hasAttribute("style")) {
24999 var styles = node.getAttribute("style").split(";");
25001 Roo.each(styles, function(s) {
25002 if (!s.match(/:/)) {
25005 var kv = s.split(":");
25006 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25009 // what ever is left... we allow.
25012 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25013 if (!nstyle.length) {
25014 node.removeAttribute('style');
25018 this.iterateChildren(node, this.cleanTableWidths);
25026 domToHTML : function(currentElement, depth, nopadtext) {
25028 depth = depth || 0;
25029 nopadtext = nopadtext || false;
25031 if (!currentElement) {
25032 return this.domToHTML(this.doc.body);
25035 //Roo.log(currentElement);
25037 var allText = false;
25038 var nodeName = currentElement.nodeName;
25039 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25041 if (nodeName == '#text') {
25043 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25048 if (nodeName != 'BODY') {
25051 // Prints the node tagName, such as <A>, <IMG>, etc
25054 for(i = 0; i < currentElement.attributes.length;i++) {
25056 var aname = currentElement.attributes.item(i).name;
25057 if (!currentElement.attributes.item(i).value.length) {
25060 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25063 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25072 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25075 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25080 // Traverse the tree
25082 var currentElementChild = currentElement.childNodes.item(i);
25083 var allText = true;
25084 var innerHTML = '';
25086 while (currentElementChild) {
25087 // Formatting code (indent the tree so it looks nice on the screen)
25088 var nopad = nopadtext;
25089 if (lastnode == 'SPAN') {
25093 if (currentElementChild.nodeName == '#text') {
25094 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25095 toadd = nopadtext ? toadd : toadd.trim();
25096 if (!nopad && toadd.length > 80) {
25097 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25099 innerHTML += toadd;
25102 currentElementChild = currentElement.childNodes.item(i);
25108 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25110 // Recursively traverse the tree structure of the child node
25111 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25112 lastnode = currentElementChild.nodeName;
25114 currentElementChild=currentElement.childNodes.item(i);
25120 // The remaining code is mostly for formatting the tree
25121 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25126 ret+= "</"+tagName+">";
25132 applyBlacklists : function()
25134 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25135 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25139 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25140 if (b.indexOf(tag) > -1) {
25143 this.white.push(tag);
25147 Roo.each(w, function(tag) {
25148 if (b.indexOf(tag) > -1) {
25151 if (this.white.indexOf(tag) > -1) {
25154 this.white.push(tag);
25159 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25160 if (w.indexOf(tag) > -1) {
25163 this.black.push(tag);
25167 Roo.each(b, function(tag) {
25168 if (w.indexOf(tag) > -1) {
25171 if (this.black.indexOf(tag) > -1) {
25174 this.black.push(tag);
25179 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25180 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25184 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25185 if (b.indexOf(tag) > -1) {
25188 this.cwhite.push(tag);
25192 Roo.each(w, function(tag) {
25193 if (b.indexOf(tag) > -1) {
25196 if (this.cwhite.indexOf(tag) > -1) {
25199 this.cwhite.push(tag);
25204 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25205 if (w.indexOf(tag) > -1) {
25208 this.cblack.push(tag);
25212 Roo.each(b, function(tag) {
25213 if (w.indexOf(tag) > -1) {
25216 if (this.cblack.indexOf(tag) > -1) {
25219 this.cblack.push(tag);
25224 setStylesheets : function(stylesheets)
25226 if(typeof(stylesheets) == 'string'){
25227 Roo.get(this.iframe.contentDocument.head).createChild({
25229 rel : 'stylesheet',
25238 Roo.each(stylesheets, function(s) {
25243 Roo.get(_this.iframe.contentDocument.head).createChild({
25245 rel : 'stylesheet',
25254 removeStylesheets : function()
25258 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25263 setStyle : function(style)
25265 Roo.get(this.iframe.contentDocument.head).createChild({
25274 // hide stuff that is not compatible
25288 * @event specialkey
25292 * @cfg {String} fieldClass @hide
25295 * @cfg {String} focusClass @hide
25298 * @cfg {String} autoCreate @hide
25301 * @cfg {String} inputType @hide
25304 * @cfg {String} invalidClass @hide
25307 * @cfg {String} invalidText @hide
25310 * @cfg {String} msgFx @hide
25313 * @cfg {String} validateOnBlur @hide
25317 Roo.HtmlEditorCore.white = [
25318 'area', 'br', 'img', 'input', 'hr', 'wbr',
25320 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25321 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25322 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25323 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25324 'table', 'ul', 'xmp',
25326 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25329 'dir', 'menu', 'ol', 'ul', 'dl',
25335 Roo.HtmlEditorCore.black = [
25336 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25338 'base', 'basefont', 'bgsound', 'blink', 'body',
25339 'frame', 'frameset', 'head', 'html', 'ilayer',
25340 'iframe', 'layer', 'link', 'meta', 'object',
25341 'script', 'style' ,'title', 'xml' // clean later..
25343 Roo.HtmlEditorCore.clean = [
25344 'script', 'style', 'title', 'xml'
25346 Roo.HtmlEditorCore.remove = [
25351 Roo.HtmlEditorCore.ablack = [
25355 Roo.HtmlEditorCore.aclean = [
25356 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25360 Roo.HtmlEditorCore.pwhite= [
25361 'http', 'https', 'mailto'
25364 // white listed style attributes.
25365 Roo.HtmlEditorCore.cwhite= [
25366 // 'text-align', /// default is to allow most things..
25372 // black listed style attributes.
25373 Roo.HtmlEditorCore.cblack= [
25374 // 'font-size' -- this can be set by the project
25378 Roo.HtmlEditorCore.swapCodes =[
25397 * @class Roo.bootstrap.HtmlEditor
25398 * @extends Roo.bootstrap.TextArea
25399 * Bootstrap HtmlEditor class
25402 * Create a new HtmlEditor
25403 * @param {Object} config The config object
25406 Roo.bootstrap.HtmlEditor = function(config){
25407 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25408 if (!this.toolbars) {
25409 this.toolbars = [];
25412 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25415 * @event initialize
25416 * Fires when the editor is fully initialized (including the iframe)
25417 * @param {HtmlEditor} this
25422 * Fires when the editor is first receives the focus. Any insertion must wait
25423 * until after this event.
25424 * @param {HtmlEditor} this
25428 * @event beforesync
25429 * Fires before the textarea is updated with content from the editor iframe. Return false
25430 * to cancel the sync.
25431 * @param {HtmlEditor} this
25432 * @param {String} html
25436 * @event beforepush
25437 * Fires before the iframe editor is updated with content from the textarea. Return false
25438 * to cancel the push.
25439 * @param {HtmlEditor} this
25440 * @param {String} html
25445 * Fires when the textarea is updated with content from the editor iframe.
25446 * @param {HtmlEditor} this
25447 * @param {String} html
25452 * Fires when the iframe editor is updated with content from the textarea.
25453 * @param {HtmlEditor} this
25454 * @param {String} html
25458 * @event editmodechange
25459 * Fires when the editor switches edit modes
25460 * @param {HtmlEditor} this
25461 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25463 editmodechange: true,
25465 * @event editorevent
25466 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25467 * @param {HtmlEditor} this
25471 * @event firstfocus
25472 * Fires when on first focus - needed by toolbars..
25473 * @param {HtmlEditor} this
25478 * Auto save the htmlEditor value as a file into Events
25479 * @param {HtmlEditor} this
25483 * @event savedpreview
25484 * preview the saved version of htmlEditor
25485 * @param {HtmlEditor} this
25492 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25496 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25501 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25506 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25511 * @cfg {Number} height (in pixels)
25515 * @cfg {Number} width (in pixels)
25520 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25523 stylesheets: false,
25528 // private properties
25529 validationEvent : false,
25531 initialized : false,
25534 onFocus : Roo.emptyFn,
25536 hideMode:'offsets',
25538 tbContainer : false,
25542 toolbarContainer :function() {
25543 return this.wrap.select('.x-html-editor-tb',true).first();
25547 * Protected method that will not generally be called directly. It
25548 * is called when the editor creates its toolbar. Override this method if you need to
25549 * add custom toolbar buttons.
25550 * @param {HtmlEditor} editor
25552 createToolbar : function(){
25553 Roo.log('renewing');
25554 Roo.log("create toolbars");
25556 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25557 this.toolbars[0].render(this.toolbarContainer());
25561 // if (!editor.toolbars || !editor.toolbars.length) {
25562 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25565 // for (var i =0 ; i < editor.toolbars.length;i++) {
25566 // editor.toolbars[i] = Roo.factory(
25567 // typeof(editor.toolbars[i]) == 'string' ?
25568 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25569 // Roo.bootstrap.HtmlEditor);
25570 // editor.toolbars[i].init(editor);
25576 onRender : function(ct, position)
25578 // Roo.log("Call onRender: " + this.xtype);
25580 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25582 this.wrap = this.inputEl().wrap({
25583 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25586 this.editorcore.onRender(ct, position);
25588 if (this.resizable) {
25589 this.resizeEl = new Roo.Resizable(this.wrap, {
25593 minHeight : this.height,
25594 height: this.height,
25595 handles : this.resizable,
25598 resize : function(r, w, h) {
25599 _t.onResize(w,h); // -something
25605 this.createToolbar(this);
25608 if(!this.width && this.resizable){
25609 this.setSize(this.wrap.getSize());
25611 if (this.resizeEl) {
25612 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25613 // should trigger onReize..
25619 onResize : function(w, h)
25621 Roo.log('resize: ' +w + ',' + h );
25622 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25626 if(this.inputEl() ){
25627 if(typeof w == 'number'){
25628 var aw = w - this.wrap.getFrameWidth('lr');
25629 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25632 if(typeof h == 'number'){
25633 var tbh = -11; // fixme it needs to tool bar size!
25634 for (var i =0; i < this.toolbars.length;i++) {
25635 // fixme - ask toolbars for heights?
25636 tbh += this.toolbars[i].el.getHeight();
25637 //if (this.toolbars[i].footer) {
25638 // tbh += this.toolbars[i].footer.el.getHeight();
25646 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25647 ah -= 5; // knock a few pixes off for look..
25648 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25652 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25653 this.editorcore.onResize(ew,eh);
25658 * Toggles the editor between standard and source edit mode.
25659 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25661 toggleSourceEdit : function(sourceEditMode)
25663 this.editorcore.toggleSourceEdit(sourceEditMode);
25665 if(this.editorcore.sourceEditMode){
25666 Roo.log('editor - showing textarea');
25669 // Roo.log(this.syncValue());
25671 this.inputEl().removeClass(['hide', 'x-hidden']);
25672 this.inputEl().dom.removeAttribute('tabIndex');
25673 this.inputEl().focus();
25675 Roo.log('editor - hiding textarea');
25677 // Roo.log(this.pushValue());
25680 this.inputEl().addClass(['hide', 'x-hidden']);
25681 this.inputEl().dom.setAttribute('tabIndex', -1);
25682 //this.deferFocus();
25685 if(this.resizable){
25686 this.setSize(this.wrap.getSize());
25689 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25692 // private (for BoxComponent)
25693 adjustSize : Roo.BoxComponent.prototype.adjustSize,
25695 // private (for BoxComponent)
25696 getResizeEl : function(){
25700 // private (for BoxComponent)
25701 getPositionEl : function(){
25706 initEvents : function(){
25707 this.originalValue = this.getValue();
25711 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25714 // markInvalid : Roo.emptyFn,
25716 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25719 // clearInvalid : Roo.emptyFn,
25721 setValue : function(v){
25722 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25723 this.editorcore.pushValue();
25728 deferFocus : function(){
25729 this.focus.defer(10, this);
25733 focus : function(){
25734 this.editorcore.focus();
25740 onDestroy : function(){
25746 for (var i =0; i < this.toolbars.length;i++) {
25747 // fixme - ask toolbars for heights?
25748 this.toolbars[i].onDestroy();
25751 this.wrap.dom.innerHTML = '';
25752 this.wrap.remove();
25757 onFirstFocus : function(){
25758 //Roo.log("onFirstFocus");
25759 this.editorcore.onFirstFocus();
25760 for (var i =0; i < this.toolbars.length;i++) {
25761 this.toolbars[i].onFirstFocus();
25767 syncValue : function()
25769 this.editorcore.syncValue();
25772 pushValue : function()
25774 this.editorcore.pushValue();
25778 // hide stuff that is not compatible
25792 * @event specialkey
25796 * @cfg {String} fieldClass @hide
25799 * @cfg {String} focusClass @hide
25802 * @cfg {String} autoCreate @hide
25805 * @cfg {String} inputType @hide
25809 * @cfg {String} invalidText @hide
25812 * @cfg {String} msgFx @hide
25815 * @cfg {String} validateOnBlur @hide
25824 Roo.namespace('Roo.bootstrap.htmleditor');
25826 * @class Roo.bootstrap.HtmlEditorToolbar1
25832 new Roo.bootstrap.HtmlEditor({
25835 new Roo.bootstrap.HtmlEditorToolbar1({
25836 disable : { fonts: 1 , format: 1, ..., ... , ...],
25842 * @cfg {Object} disable List of elements to disable..
25843 * @cfg {Array} btns List of additional buttons.
25847 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25850 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25853 Roo.apply(this, config);
25855 // default disabled, based on 'good practice'..
25856 this.disable = this.disable || {};
25857 Roo.applyIf(this.disable, {
25860 specialElements : true
25862 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25864 this.editor = config.editor;
25865 this.editorcore = config.editor.editorcore;
25867 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25869 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25870 // dont call parent... till later.
25872 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
25877 editorcore : false,
25882 "h1","h2","h3","h4","h5","h6",
25884 "abbr", "acronym", "address", "cite", "samp", "var",
25888 onRender : function(ct, position)
25890 // Roo.log("Call onRender: " + this.xtype);
25892 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25894 this.el.dom.style.marginBottom = '0';
25896 var editorcore = this.editorcore;
25897 var editor= this.editor;
25900 var btn = function(id,cmd , toggle, handler, html){
25902 var event = toggle ? 'toggle' : 'click';
25907 xns: Roo.bootstrap,
25911 enableToggle:toggle !== false,
25913 pressed : toggle ? false : null,
25916 a.listeners[toggle ? 'toggle' : 'click'] = function() {
25917 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
25923 // var cb_box = function...
25928 xns: Roo.bootstrap,
25933 xns: Roo.bootstrap,
25937 Roo.each(this.formats, function(f) {
25938 style.menu.items.push({
25940 xns: Roo.bootstrap,
25941 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25946 editorcore.insertTag(this.tagname);
25953 children.push(style);
25955 btn('bold',false,true);
25956 btn('italic',false,true);
25957 btn('align-left', 'justifyleft',true);
25958 btn('align-center', 'justifycenter',true);
25959 btn('align-right' , 'justifyright',true);
25960 btn('link', false, false, function(btn) {
25961 //Roo.log("create link?");
25962 var url = prompt(this.createLinkText, this.defaultLinkValue);
25963 if(url && url != 'http:/'+'/'){
25964 this.editorcore.relayCmd('createlink', url);
25967 btn('list','insertunorderedlist',true);
25968 btn('pencil', false,true, function(btn){
25970 this.toggleSourceEdit(btn.pressed);
25973 if (this.editor.btns.length > 0) {
25974 for (var i = 0; i<this.editor.btns.length; i++) {
25975 children.push(this.editor.btns[i]);
25983 xns: Roo.bootstrap,
25988 xns: Roo.bootstrap,
25993 cog.menu.items.push({
25995 xns: Roo.bootstrap,
25996 html : Clean styles,
26001 editorcore.insertTag(this.tagname);
26010 this.xtype = 'NavSimplebar';
26012 for(var i=0;i< children.length;i++) {
26014 this.buttons.add(this.addxtypeChild(children[i]));
26018 editor.on('editorevent', this.updateToolbar, this);
26020 onBtnClick : function(id)
26022 this.editorcore.relayCmd(id);
26023 this.editorcore.focus();
26027 * Protected method that will not generally be called directly. It triggers
26028 * a toolbar update by reading the markup state of the current selection in the editor.
26030 updateToolbar: function(){
26032 if(!this.editorcore.activated){
26033 this.editor.onFirstFocus(); // is this neeed?
26037 var btns = this.buttons;
26038 var doc = this.editorcore.doc;
26039 btns.get('bold').setActive(doc.queryCommandState('bold'));
26040 btns.get('italic').setActive(doc.queryCommandState('italic'));
26041 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26043 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26044 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26045 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26047 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26048 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26051 var ans = this.editorcore.getAllAncestors();
26052 if (this.formatCombo) {
26055 var store = this.formatCombo.store;
26056 this.formatCombo.setValue("");
26057 for (var i =0; i < ans.length;i++) {
26058 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26060 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26068 // hides menus... - so this cant be on a menu...
26069 Roo.bootstrap.MenuMgr.hideAll();
26071 Roo.bootstrap.MenuMgr.hideAll();
26072 //this.editorsyncValue();
26074 onFirstFocus: function() {
26075 this.buttons.each(function(item){
26079 toggleSourceEdit : function(sourceEditMode){
26082 if(sourceEditMode){
26083 Roo.log("disabling buttons");
26084 this.buttons.each( function(item){
26085 if(item.cmd != 'pencil'){
26091 Roo.log("enabling buttons");
26092 if(this.editorcore.initialized){
26093 this.buttons.each( function(item){
26099 Roo.log("calling toggole on editor");
26100 // tell the editor that it's been pressed..
26101 this.editor.toggleSourceEdit(sourceEditMode);
26115 * @class Roo.bootstrap.Markdown
26116 * @extends Roo.bootstrap.TextArea
26117 * Bootstrap Showdown editable area
26118 * @cfg {string} content
26121 * Create a new Showdown
26124 Roo.bootstrap.Markdown = function(config){
26125 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26129 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26133 initEvents : function()
26136 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26137 this.markdownEl = this.el.createChild({
26138 cls : 'roo-markdown-area'
26140 this.inputEl().addClass('d-none');
26141 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26142 this.markdownEl.on('click', this.toggleTextEdit, this);
26143 this.on('blur', this.toggleTextEdit, this);
26144 this.on('specialkey', this.resizeTextArea, this);
26147 toggleTextEdit : function()
26149 var sh = this.markdownEl.getHeight();
26150 this.inputEl().addClass('d-none');
26151 this.markdownEl.addClass('d-none');
26152 if (!this.editing) {
26154 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26155 this.inputEl().removeClass('d-none');
26156 this.inputEl().focus();
26157 this.editing = true;
26160 // show showdown...
26161 this.updateMarkdown();
26162 this.markdownEl.removeClass('d-none');
26163 this.editing = false;
26166 updateMarkdown : function()
26168 if (this.getValue() == '') {
26169 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder);
26172 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26175 resizeTextArea: function () {
26178 Roo.log([sh, this.getValue().split("\n").length * 30]);
26179 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26181 setValue : function(val)
26183 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26184 if (!this.editing) {
26185 this.updateMarkdown();
26191 if (!this.editing) {
26192 this.toggleTextEdit();
26200 * @class Roo.bootstrap.Table.AbstractSelectionModel
26201 * @extends Roo.util.Observable
26202 * Abstract base class for grid SelectionModels. It provides the interface that should be
26203 * implemented by descendant classes. This class should not be directly instantiated.
26206 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26207 this.locked = false;
26208 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26212 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26213 /** @ignore Called by the grid automatically. Do not call directly. */
26214 init : function(grid){
26220 * Locks the selections.
26223 this.locked = true;
26227 * Unlocks the selections.
26229 unlock : function(){
26230 this.locked = false;
26234 * Returns true if the selections are locked.
26235 * @return {Boolean}
26237 isLocked : function(){
26238 return this.locked;
26242 initEvents : function ()
26248 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26249 * @class Roo.bootstrap.Table.RowSelectionModel
26250 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26251 * It supports multiple selections and keyboard selection/navigation.
26253 * @param {Object} config
26256 Roo.bootstrap.Table.RowSelectionModel = function(config){
26257 Roo.apply(this, config);
26258 this.selections = new Roo.util.MixedCollection(false, function(o){
26263 this.lastActive = false;
26267 * @event selectionchange
26268 * Fires when the selection changes
26269 * @param {SelectionModel} this
26271 "selectionchange" : true,
26273 * @event afterselectionchange
26274 * Fires after the selection changes (eg. by key press or clicking)
26275 * @param {SelectionModel} this
26277 "afterselectionchange" : true,
26279 * @event beforerowselect
26280 * Fires when a row is selected being selected, return false to cancel.
26281 * @param {SelectionModel} this
26282 * @param {Number} rowIndex The selected index
26283 * @param {Boolean} keepExisting False if other selections will be cleared
26285 "beforerowselect" : true,
26288 * Fires when a row is selected.
26289 * @param {SelectionModel} this
26290 * @param {Number} rowIndex The selected index
26291 * @param {Roo.data.Record} r The record
26293 "rowselect" : true,
26295 * @event rowdeselect
26296 * Fires when a row is deselected.
26297 * @param {SelectionModel} this
26298 * @param {Number} rowIndex The selected index
26300 "rowdeselect" : true
26302 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26303 this.locked = false;
26306 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26308 * @cfg {Boolean} singleSelect
26309 * True to allow selection of only one row at a time (defaults to false)
26311 singleSelect : false,
26314 initEvents : function()
26317 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26318 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26319 //}else{ // allow click to work like normal
26320 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26322 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26323 this.grid.on("rowclick", this.handleMouseDown, this);
26325 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26326 "up" : function(e){
26328 this.selectPrevious(e.shiftKey);
26329 }else if(this.last !== false && this.lastActive !== false){
26330 var last = this.last;
26331 this.selectRange(this.last, this.lastActive-1);
26332 this.grid.getView().focusRow(this.lastActive);
26333 if(last !== false){
26337 this.selectFirstRow();
26339 this.fireEvent("afterselectionchange", this);
26341 "down" : function(e){
26343 this.selectNext(e.shiftKey);
26344 }else if(this.last !== false && this.lastActive !== false){
26345 var last = this.last;
26346 this.selectRange(this.last, this.lastActive+1);
26347 this.grid.getView().focusRow(this.lastActive);
26348 if(last !== false){
26352 this.selectFirstRow();
26354 this.fireEvent("afterselectionchange", this);
26358 this.grid.store.on('load', function(){
26359 this.selections.clear();
26362 var view = this.grid.view;
26363 view.on("refresh", this.onRefresh, this);
26364 view.on("rowupdated", this.onRowUpdated, this);
26365 view.on("rowremoved", this.onRemove, this);
26370 onRefresh : function()
26372 var ds = this.grid.store, i, v = this.grid.view;
26373 var s = this.selections;
26374 s.each(function(r){
26375 if((i = ds.indexOfId(r.id)) != -1){
26384 onRemove : function(v, index, r){
26385 this.selections.remove(r);
26389 onRowUpdated : function(v, index, r){
26390 if(this.isSelected(r)){
26391 v.onRowSelect(index);
26397 * @param {Array} records The records to select
26398 * @param {Boolean} keepExisting (optional) True to keep existing selections
26400 selectRecords : function(records, keepExisting)
26403 this.clearSelections();
26405 var ds = this.grid.store;
26406 for(var i = 0, len = records.length; i < len; i++){
26407 this.selectRow(ds.indexOf(records[i]), true);
26412 * Gets the number of selected rows.
26415 getCount : function(){
26416 return this.selections.length;
26420 * Selects the first row in the grid.
26422 selectFirstRow : function(){
26427 * Select the last row.
26428 * @param {Boolean} keepExisting (optional) True to keep existing selections
26430 selectLastRow : function(keepExisting){
26431 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26432 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26436 * Selects the row immediately following the last selected row.
26437 * @param {Boolean} keepExisting (optional) True to keep existing selections
26439 selectNext : function(keepExisting)
26441 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26442 this.selectRow(this.last+1, keepExisting);
26443 this.grid.getView().focusRow(this.last);
26448 * Selects the row that precedes the last selected row.
26449 * @param {Boolean} keepExisting (optional) True to keep existing selections
26451 selectPrevious : function(keepExisting){
26453 this.selectRow(this.last-1, keepExisting);
26454 this.grid.getView().focusRow(this.last);
26459 * Returns the selected records
26460 * @return {Array} Array of selected records
26462 getSelections : function(){
26463 return [].concat(this.selections.items);
26467 * Returns the first selected record.
26470 getSelected : function(){
26471 return this.selections.itemAt(0);
26476 * Clears all selections.
26478 clearSelections : function(fast)
26484 var ds = this.grid.store;
26485 var s = this.selections;
26486 s.each(function(r){
26487 this.deselectRow(ds.indexOfId(r.id));
26491 this.selections.clear();
26498 * Selects all rows.
26500 selectAll : function(){
26504 this.selections.clear();
26505 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26506 this.selectRow(i, true);
26511 * Returns True if there is a selection.
26512 * @return {Boolean}
26514 hasSelection : function(){
26515 return this.selections.length > 0;
26519 * Returns True if the specified row is selected.
26520 * @param {Number/Record} record The record or index of the record to check
26521 * @return {Boolean}
26523 isSelected : function(index){
26524 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26525 return (r && this.selections.key(r.id) ? true : false);
26529 * Returns True if the specified record id is selected.
26530 * @param {String} id The id of record to check
26531 * @return {Boolean}
26533 isIdSelected : function(id){
26534 return (this.selections.key(id) ? true : false);
26539 handleMouseDBClick : function(e, t){
26543 handleMouseDown : function(e, t)
26545 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26546 if(this.isLocked() || rowIndex < 0 ){
26549 if(e.shiftKey && this.last !== false){
26550 var last = this.last;
26551 this.selectRange(last, rowIndex, e.ctrlKey);
26552 this.last = last; // reset the last
26556 var isSelected = this.isSelected(rowIndex);
26557 //Roo.log("select row:" + rowIndex);
26559 this.deselectRow(rowIndex);
26561 this.selectRow(rowIndex, true);
26565 if(e.button !== 0 && isSelected){
26566 alert('rowIndex 2: ' + rowIndex);
26567 view.focusRow(rowIndex);
26568 }else if(e.ctrlKey && isSelected){
26569 this.deselectRow(rowIndex);
26570 }else if(!isSelected){
26571 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26572 view.focusRow(rowIndex);
26576 this.fireEvent("afterselectionchange", this);
26579 handleDragableRowClick : function(grid, rowIndex, e)
26581 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26582 this.selectRow(rowIndex, false);
26583 grid.view.focusRow(rowIndex);
26584 this.fireEvent("afterselectionchange", this);
26589 * Selects multiple rows.
26590 * @param {Array} rows Array of the indexes of the row to select
26591 * @param {Boolean} keepExisting (optional) True to keep existing selections
26593 selectRows : function(rows, keepExisting){
26595 this.clearSelections();
26597 for(var i = 0, len = rows.length; i < len; i++){
26598 this.selectRow(rows[i], true);
26603 * Selects a range of rows. All rows in between startRow and endRow are also selected.
26604 * @param {Number} startRow The index of the first row in the range
26605 * @param {Number} endRow The index of the last row in the range
26606 * @param {Boolean} keepExisting (optional) True to retain existing selections
26608 selectRange : function(startRow, endRow, keepExisting){
26613 this.clearSelections();
26615 if(startRow <= endRow){
26616 for(var i = startRow; i <= endRow; i++){
26617 this.selectRow(i, true);
26620 for(var i = startRow; i >= endRow; i--){
26621 this.selectRow(i, true);
26627 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26628 * @param {Number} startRow The index of the first row in the range
26629 * @param {Number} endRow The index of the last row in the range
26631 deselectRange : function(startRow, endRow, preventViewNotify){
26635 for(var i = startRow; i <= endRow; i++){
26636 this.deselectRow(i, preventViewNotify);
26642 * @param {Number} row The index of the row to select
26643 * @param {Boolean} keepExisting (optional) True to keep existing selections
26645 selectRow : function(index, keepExisting, preventViewNotify)
26647 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26650 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26651 if(!keepExisting || this.singleSelect){
26652 this.clearSelections();
26655 var r = this.grid.store.getAt(index);
26656 //console.log('selectRow - record id :' + r.id);
26658 this.selections.add(r);
26659 this.last = this.lastActive = index;
26660 if(!preventViewNotify){
26661 var proxy = new Roo.Element(
26662 this.grid.getRowDom(index)
26664 proxy.addClass('bg-info info');
26666 this.fireEvent("rowselect", this, index, r);
26667 this.fireEvent("selectionchange", this);
26673 * @param {Number} row The index of the row to deselect
26675 deselectRow : function(index, preventViewNotify)
26680 if(this.last == index){
26683 if(this.lastActive == index){
26684 this.lastActive = false;
26687 var r = this.grid.store.getAt(index);
26692 this.selections.remove(r);
26693 //.console.log('deselectRow - record id :' + r.id);
26694 if(!preventViewNotify){
26696 var proxy = new Roo.Element(
26697 this.grid.getRowDom(index)
26699 proxy.removeClass('bg-info info');
26701 this.fireEvent("rowdeselect", this, index);
26702 this.fireEvent("selectionchange", this);
26706 restoreLast : function(){
26708 this.last = this._last;
26713 acceptsNav : function(row, col, cm){
26714 return !cm.isHidden(col) && cm.isCellEditable(col, row);
26718 onEditorKey : function(field, e){
26719 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26724 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26726 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26728 }else if(k == e.ENTER && !e.ctrlKey){
26732 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26734 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26736 }else if(k == e.ESC){
26740 g.startEditing(newCell[0], newCell[1]);
26746 * Ext JS Library 1.1.1
26747 * Copyright(c) 2006-2007, Ext JS, LLC.
26749 * Originally Released Under LGPL - original licence link has changed is not relivant.
26752 * <script type="text/javascript">
26756 * @class Roo.bootstrap.PagingToolbar
26757 * @extends Roo.bootstrap.NavSimplebar
26758 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26760 * Create a new PagingToolbar
26761 * @param {Object} config The config object
26762 * @param {Roo.data.Store} store
26764 Roo.bootstrap.PagingToolbar = function(config)
26766 // old args format still supported... - xtype is prefered..
26767 // created from xtype...
26769 this.ds = config.dataSource;
26771 if (config.store && !this.ds) {
26772 this.store= Roo.factory(config.store, Roo.data);
26773 this.ds = this.store;
26774 this.ds.xmodule = this.xmodule || false;
26777 this.toolbarItems = [];
26778 if (config.items) {
26779 this.toolbarItems = config.items;
26782 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26787 this.bind(this.ds);
26790 if (Roo.bootstrap.version == 4) {
26791 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26793 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26798 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26800 * @cfg {Roo.data.Store} dataSource
26801 * The underlying data store providing the paged data
26804 * @cfg {String/HTMLElement/Element} container
26805 * container The id or element that will contain the toolbar
26808 * @cfg {Boolean} displayInfo
26809 * True to display the displayMsg (defaults to false)
26812 * @cfg {Number} pageSize
26813 * The number of records to display per page (defaults to 20)
26817 * @cfg {String} displayMsg
26818 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26820 displayMsg : 'Displaying {0} - {1} of {2}',
26822 * @cfg {String} emptyMsg
26823 * The message to display when no records are found (defaults to "No data to display")
26825 emptyMsg : 'No data to display',
26827 * Customizable piece of the default paging text (defaults to "Page")
26830 beforePageText : "Page",
26832 * Customizable piece of the default paging text (defaults to "of %0")
26835 afterPageText : "of {0}",
26837 * Customizable piece of the default paging text (defaults to "First Page")
26840 firstText : "First Page",
26842 * Customizable piece of the default paging text (defaults to "Previous Page")
26845 prevText : "Previous Page",
26847 * Customizable piece of the default paging text (defaults to "Next Page")
26850 nextText : "Next Page",
26852 * Customizable piece of the default paging text (defaults to "Last Page")
26855 lastText : "Last Page",
26857 * Customizable piece of the default paging text (defaults to "Refresh")
26860 refreshText : "Refresh",
26864 onRender : function(ct, position)
26866 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26867 this.navgroup.parentId = this.id;
26868 this.navgroup.onRender(this.el, null);
26869 // add the buttons to the navgroup
26871 if(this.displayInfo){
26872 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26873 this.displayEl = this.el.select('.x-paging-info', true).first();
26874 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26875 // this.displayEl = navel.el.select('span',true).first();
26881 Roo.each(_this.buttons, function(e){ // this might need to use render????
26882 Roo.factory(e).render(_this.el);
26886 Roo.each(_this.toolbarItems, function(e) {
26887 _this.navgroup.addItem(e);
26891 this.first = this.navgroup.addItem({
26892 tooltip: this.firstText,
26893 cls: "prev btn-outline-secondary",
26894 html : ' <i class="fa fa-step-backward"></i>',
26896 preventDefault: true,
26897 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26900 this.prev = this.navgroup.addItem({
26901 tooltip: this.prevText,
26902 cls: "prev btn-outline-secondary",
26903 html : ' <i class="fa fa-backward"></i>',
26905 preventDefault: true,
26906 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
26908 //this.addSeparator();
26911 var field = this.navgroup.addItem( {
26913 cls : 'x-paging-position btn-outline-secondary',
26915 html : this.beforePageText +
26916 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26917 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
26920 this.field = field.el.select('input', true).first();
26921 this.field.on("keydown", this.onPagingKeydown, this);
26922 this.field.on("focus", function(){this.dom.select();});
26925 this.afterTextEl = field.el.select('.x-paging-after',true).first();
26926 //this.field.setHeight(18);
26927 //this.addSeparator();
26928 this.next = this.navgroup.addItem({
26929 tooltip: this.nextText,
26930 cls: "next btn-outline-secondary",
26931 html : ' <i class="fa fa-forward"></i>',
26933 preventDefault: true,
26934 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
26936 this.last = this.navgroup.addItem({
26937 tooltip: this.lastText,
26938 html : ' <i class="fa fa-step-forward"></i>',
26939 cls: "next btn-outline-secondary",
26941 preventDefault: true,
26942 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
26944 //this.addSeparator();
26945 this.loading = this.navgroup.addItem({
26946 tooltip: this.refreshText,
26947 cls: "btn-outline-secondary",
26948 html : ' <i class="fa fa-refresh"></i>',
26949 preventDefault: true,
26950 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26956 updateInfo : function(){
26957 if(this.displayEl){
26958 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26959 var msg = count == 0 ?
26963 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
26965 this.displayEl.update(msg);
26970 onLoad : function(ds, r, o)
26972 this.cursor = o.params.start ? o.params.start : 0;
26974 var d = this.getPageData(),
26979 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26980 this.field.dom.value = ap;
26981 this.first.setDisabled(ap == 1);
26982 this.prev.setDisabled(ap == 1);
26983 this.next.setDisabled(ap == ps);
26984 this.last.setDisabled(ap == ps);
26985 this.loading.enable();
26990 getPageData : function(){
26991 var total = this.ds.getTotalCount();
26994 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26995 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27000 onLoadError : function(){
27001 this.loading.enable();
27005 onPagingKeydown : function(e){
27006 var k = e.getKey();
27007 var d = this.getPageData();
27009 var v = this.field.dom.value, pageNum;
27010 if(!v || isNaN(pageNum = parseInt(v, 10))){
27011 this.field.dom.value = d.activePage;
27014 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27015 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27018 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))
27020 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27021 this.field.dom.value = pageNum;
27022 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27025 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27027 var v = this.field.dom.value, pageNum;
27028 var increment = (e.shiftKey) ? 10 : 1;
27029 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27032 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27033 this.field.dom.value = d.activePage;
27036 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27038 this.field.dom.value = parseInt(v, 10) + increment;
27039 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27040 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27047 beforeLoad : function(){
27049 this.loading.disable();
27054 onClick : function(which){
27063 ds.load({params:{start: 0, limit: this.pageSize}});
27066 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27069 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27072 var total = ds.getTotalCount();
27073 var extra = total % this.pageSize;
27074 var lastStart = extra ? (total - extra) : total-this.pageSize;
27075 ds.load({params:{start: lastStart, limit: this.pageSize}});
27078 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27084 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27085 * @param {Roo.data.Store} store The data store to unbind
27087 unbind : function(ds){
27088 ds.un("beforeload", this.beforeLoad, this);
27089 ds.un("load", this.onLoad, this);
27090 ds.un("loadexception", this.onLoadError, this);
27091 ds.un("remove", this.updateInfo, this);
27092 ds.un("add", this.updateInfo, this);
27093 this.ds = undefined;
27097 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27098 * @param {Roo.data.Store} store The data store to bind
27100 bind : function(ds){
27101 ds.on("beforeload", this.beforeLoad, this);
27102 ds.on("load", this.onLoad, this);
27103 ds.on("loadexception", this.onLoadError, this);
27104 ds.on("remove", this.updateInfo, this);
27105 ds.on("add", this.updateInfo, this);
27116 * @class Roo.bootstrap.MessageBar
27117 * @extends Roo.bootstrap.Component
27118 * Bootstrap MessageBar class
27119 * @cfg {String} html contents of the MessageBar
27120 * @cfg {String} weight (info | success | warning | danger) default info
27121 * @cfg {String} beforeClass insert the bar before the given class
27122 * @cfg {Boolean} closable (true | false) default false
27123 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27126 * Create a new Element
27127 * @param {Object} config The config object
27130 Roo.bootstrap.MessageBar = function(config){
27131 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27134 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27140 beforeClass: 'bootstrap-sticky-wrap',
27142 getAutoCreate : function(){
27146 cls: 'alert alert-dismissable alert-' + this.weight,
27151 html: this.html || ''
27157 cfg.cls += ' alert-messages-fixed';
27171 onRender : function(ct, position)
27173 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27176 var cfg = Roo.apply({}, this.getAutoCreate());
27180 cfg.cls += ' ' + this.cls;
27183 cfg.style = this.style;
27185 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27187 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27190 this.el.select('>button.close').on('click', this.hide, this);
27196 if (!this.rendered) {
27202 this.fireEvent('show', this);
27208 if (!this.rendered) {
27214 this.fireEvent('hide', this);
27217 update : function()
27219 // var e = this.el.dom.firstChild;
27221 // if(this.closable){
27222 // e = e.nextSibling;
27225 // e.data = this.html || '';
27227 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27243 * @class Roo.bootstrap.Graph
27244 * @extends Roo.bootstrap.Component
27245 * Bootstrap Graph class
27249 @cfg {String} graphtype bar | vbar | pie
27250 @cfg {number} g_x coodinator | centre x (pie)
27251 @cfg {number} g_y coodinator | centre y (pie)
27252 @cfg {number} g_r radius (pie)
27253 @cfg {number} g_height height of the chart (respected by all elements in the set)
27254 @cfg {number} g_width width of the chart (respected by all elements in the set)
27255 @cfg {Object} title The title of the chart
27258 -opts (object) options for the chart
27260 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27261 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27263 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.
27264 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27266 o stretch (boolean)
27268 -opts (object) options for the pie
27271 o startAngle (number)
27272 o endAngle (number)
27276 * Create a new Input
27277 * @param {Object} config The config object
27280 Roo.bootstrap.Graph = function(config){
27281 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27287 * The img click event for the img.
27288 * @param {Roo.EventObject} e
27294 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27305 //g_colors: this.colors,
27312 getAutoCreate : function(){
27323 onRender : function(ct,position){
27326 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27328 if (typeof(Raphael) == 'undefined') {
27329 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27333 this.raphael = Raphael(this.el.dom);
27335 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27336 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27337 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27338 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27340 r.text(160, 10, "Single Series Chart").attr(txtattr);
27341 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27342 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27343 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27345 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27346 r.barchart(330, 10, 300, 220, data1);
27347 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27348 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27351 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27352 // r.barchart(30, 30, 560, 250, xdata, {
27353 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27354 // axis : "0 0 1 1",
27355 // axisxlabels : xdata
27356 // //yvalues : cols,
27359 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27361 // this.load(null,xdata,{
27362 // axis : "0 0 1 1",
27363 // axisxlabels : xdata
27368 load : function(graphtype,xdata,opts)
27370 this.raphael.clear();
27372 graphtype = this.graphtype;
27377 var r = this.raphael,
27378 fin = function () {
27379 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27381 fout = function () {
27382 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27384 pfin = function() {
27385 this.sector.stop();
27386 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27389 this.label[0].stop();
27390 this.label[0].attr({ r: 7.5 });
27391 this.label[1].attr({ "font-weight": 800 });
27394 pfout = function() {
27395 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27398 this.label[0].animate({ r: 5 }, 500, "bounce");
27399 this.label[1].attr({ "font-weight": 400 });
27405 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27408 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27411 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27412 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27414 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27421 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27426 setTitle: function(o)
27431 initEvents: function() {
27434 this.el.on('click', this.onClick, this);
27438 onClick : function(e)
27440 Roo.log('img onclick');
27441 this.fireEvent('click', this, e);
27453 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27456 * @class Roo.bootstrap.dash.NumberBox
27457 * @extends Roo.bootstrap.Component
27458 * Bootstrap NumberBox class
27459 * @cfg {String} headline Box headline
27460 * @cfg {String} content Box content
27461 * @cfg {String} icon Box icon
27462 * @cfg {String} footer Footer text
27463 * @cfg {String} fhref Footer href
27466 * Create a new NumberBox
27467 * @param {Object} config The config object
27471 Roo.bootstrap.dash.NumberBox = function(config){
27472 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27476 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27485 getAutoCreate : function(){
27489 cls : 'small-box ',
27497 cls : 'roo-headline',
27498 html : this.headline
27502 cls : 'roo-content',
27503 html : this.content
27517 cls : 'ion ' + this.icon
27526 cls : 'small-box-footer',
27527 href : this.fhref || '#',
27531 cfg.cn.push(footer);
27538 onRender : function(ct,position){
27539 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27546 setHeadline: function (value)
27548 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27551 setFooter: function (value, href)
27553 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27556 this.el.select('a.small-box-footer',true).first().attr('href', href);
27561 setContent: function (value)
27563 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27566 initEvents: function()
27580 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27583 * @class Roo.bootstrap.dash.TabBox
27584 * @extends Roo.bootstrap.Component
27585 * Bootstrap TabBox class
27586 * @cfg {String} title Title of the TabBox
27587 * @cfg {String} icon Icon of the TabBox
27588 * @cfg {Boolean} showtabs (true|false) show the tabs default true
27589 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27592 * Create a new TabBox
27593 * @param {Object} config The config object
27597 Roo.bootstrap.dash.TabBox = function(config){
27598 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27603 * When a pane is added
27604 * @param {Roo.bootstrap.dash.TabPane} pane
27608 * @event activatepane
27609 * When a pane is activated
27610 * @param {Roo.bootstrap.dash.TabPane} pane
27612 "activatepane" : true
27620 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
27625 tabScrollable : false,
27627 getChildContainer : function()
27629 return this.el.select('.tab-content', true).first();
27632 getAutoCreate : function(){
27636 cls: 'pull-left header',
27644 cls: 'fa ' + this.icon
27650 cls: 'nav nav-tabs pull-right',
27656 if(this.tabScrollable){
27663 cls: 'nav nav-tabs pull-right',
27674 cls: 'nav-tabs-custom',
27679 cls: 'tab-content no-padding',
27687 initEvents : function()
27689 //Roo.log('add add pane handler');
27690 this.on('addpane', this.onAddPane, this);
27693 * Updates the box title
27694 * @param {String} html to set the title to.
27696 setTitle : function(value)
27698 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27700 onAddPane : function(pane)
27702 this.panes.push(pane);
27703 //Roo.log('addpane');
27705 // tabs are rendere left to right..
27706 if(!this.showtabs){
27710 var ctr = this.el.select('.nav-tabs', true).first();
27713 var existing = ctr.select('.nav-tab',true);
27714 var qty = existing.getCount();;
27717 var tab = ctr.createChild({
27719 cls : 'nav-tab' + (qty ? '' : ' active'),
27727 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27730 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27732 pane.el.addClass('active');
27737 onTabClick : function(ev,un,ob,pane)
27739 //Roo.log('tab - prev default');
27740 ev.preventDefault();
27743 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27744 pane.tab.addClass('active');
27745 //Roo.log(pane.title);
27746 this.getChildContainer().select('.tab-pane',true).removeClass('active');
27747 // technically we should have a deactivate event.. but maybe add later.
27748 // and it should not de-activate the selected tab...
27749 this.fireEvent('activatepane', pane);
27750 pane.el.addClass('active');
27751 pane.fireEvent('activate');
27756 getActivePane : function()
27759 Roo.each(this.panes, function(p) {
27760 if(p.el.hasClass('active')){
27781 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27783 * @class Roo.bootstrap.TabPane
27784 * @extends Roo.bootstrap.Component
27785 * Bootstrap TabPane class
27786 * @cfg {Boolean} active (false | true) Default false
27787 * @cfg {String} title title of panel
27791 * Create a new TabPane
27792 * @param {Object} config The config object
27795 Roo.bootstrap.dash.TabPane = function(config){
27796 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27802 * When a pane is activated
27803 * @param {Roo.bootstrap.dash.TabPane} pane
27810 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
27815 // the tabBox that this is attached to.
27818 getAutoCreate : function()
27826 cfg.cls += ' active';
27831 initEvents : function()
27833 //Roo.log('trigger add pane handler');
27834 this.parent().fireEvent('addpane', this)
27838 * Updates the tab title
27839 * @param {String} html to set the title to.
27841 setTitle: function(str)
27847 this.tab.select('a', true).first().dom.innerHTML = str;
27864 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27867 * @class Roo.bootstrap.menu.Menu
27868 * @extends Roo.bootstrap.Component
27869 * Bootstrap Menu class - container for Menu
27870 * @cfg {String} html Text of the menu
27871 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27872 * @cfg {String} icon Font awesome icon
27873 * @cfg {String} pos Menu align to (top | bottom) default bottom
27877 * Create a new Menu
27878 * @param {Object} config The config object
27882 Roo.bootstrap.menu.Menu = function(config){
27883 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27887 * @event beforeshow
27888 * Fires before this menu is displayed
27889 * @param {Roo.bootstrap.menu.Menu} this
27893 * @event beforehide
27894 * Fires before this menu is hidden
27895 * @param {Roo.bootstrap.menu.Menu} this
27900 * Fires after this menu is displayed
27901 * @param {Roo.bootstrap.menu.Menu} this
27906 * Fires after this menu is hidden
27907 * @param {Roo.bootstrap.menu.Menu} this
27912 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27913 * @param {Roo.bootstrap.menu.Menu} this
27914 * @param {Roo.EventObject} e
27921 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
27925 weight : 'default',
27930 getChildContainer : function() {
27931 if(this.isSubMenu){
27935 return this.el.select('ul.dropdown-menu', true).first();
27938 getAutoCreate : function()
27943 cls : 'roo-menu-text',
27951 cls : 'fa ' + this.icon
27962 cls : 'dropdown-button btn btn-' + this.weight,
27967 cls : 'dropdown-toggle btn btn-' + this.weight,
27977 cls : 'dropdown-menu'
27983 if(this.pos == 'top'){
27984 cfg.cls += ' dropup';
27987 if(this.isSubMenu){
27990 cls : 'dropdown-menu'
27997 onRender : function(ct, position)
27999 this.isSubMenu = ct.hasClass('dropdown-submenu');
28001 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28004 initEvents : function()
28006 if(this.isSubMenu){
28010 this.hidden = true;
28012 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28013 this.triggerEl.on('click', this.onTriggerPress, this);
28015 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28016 this.buttonEl.on('click', this.onClick, this);
28022 if(this.isSubMenu){
28026 return this.el.select('ul.dropdown-menu', true).first();
28029 onClick : function(e)
28031 this.fireEvent("click", this, e);
28034 onTriggerPress : function(e)
28036 if (this.isVisible()) {
28043 isVisible : function(){
28044 return !this.hidden;
28049 this.fireEvent("beforeshow", this);
28051 this.hidden = false;
28052 this.el.addClass('open');
28054 Roo.get(document).on("mouseup", this.onMouseUp, this);
28056 this.fireEvent("show", this);
28063 this.fireEvent("beforehide", this);
28065 this.hidden = true;
28066 this.el.removeClass('open');
28068 Roo.get(document).un("mouseup", this.onMouseUp);
28070 this.fireEvent("hide", this);
28073 onMouseUp : function()
28087 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28090 * @class Roo.bootstrap.menu.Item
28091 * @extends Roo.bootstrap.Component
28092 * Bootstrap MenuItem class
28093 * @cfg {Boolean} submenu (true | false) default false
28094 * @cfg {String} html text of the item
28095 * @cfg {String} href the link
28096 * @cfg {Boolean} disable (true | false) default false
28097 * @cfg {Boolean} preventDefault (true | false) default true
28098 * @cfg {String} icon Font awesome icon
28099 * @cfg {String} pos Submenu align to (left | right) default right
28103 * Create a new Item
28104 * @param {Object} config The config object
28108 Roo.bootstrap.menu.Item = function(config){
28109 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28113 * Fires when the mouse is hovering over this menu
28114 * @param {Roo.bootstrap.menu.Item} this
28115 * @param {Roo.EventObject} e
28120 * Fires when the mouse exits this menu
28121 * @param {Roo.bootstrap.menu.Item} this
28122 * @param {Roo.EventObject} e
28128 * The raw click event for the entire grid.
28129 * @param {Roo.EventObject} e
28135 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28140 preventDefault: true,
28145 getAutoCreate : function()
28150 cls : 'roo-menu-item-text',
28158 cls : 'fa ' + this.icon
28167 href : this.href || '#',
28174 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28178 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28180 if(this.pos == 'left'){
28181 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28188 initEvents : function()
28190 this.el.on('mouseover', this.onMouseOver, this);
28191 this.el.on('mouseout', this.onMouseOut, this);
28193 this.el.select('a', true).first().on('click', this.onClick, this);
28197 onClick : function(e)
28199 if(this.preventDefault){
28200 e.preventDefault();
28203 this.fireEvent("click", this, e);
28206 onMouseOver : function(e)
28208 if(this.submenu && this.pos == 'left'){
28209 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28212 this.fireEvent("mouseover", this, e);
28215 onMouseOut : function(e)
28217 this.fireEvent("mouseout", this, e);
28229 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28232 * @class Roo.bootstrap.menu.Separator
28233 * @extends Roo.bootstrap.Component
28234 * Bootstrap Separator class
28237 * Create a new Separator
28238 * @param {Object} config The config object
28242 Roo.bootstrap.menu.Separator = function(config){
28243 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28246 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28248 getAutoCreate : function(){
28269 * @class Roo.bootstrap.Tooltip
28270 * Bootstrap Tooltip class
28271 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28272 * to determine which dom element triggers the tooltip.
28274 * It needs to add support for additional attributes like tooltip-position
28277 * Create a new Toolti
28278 * @param {Object} config The config object
28281 Roo.bootstrap.Tooltip = function(config){
28282 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28284 this.alignment = Roo.bootstrap.Tooltip.alignment;
28286 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28287 this.alignment = config.alignment;
28292 Roo.apply(Roo.bootstrap.Tooltip, {
28294 * @function init initialize tooltip monitoring.
28298 currentTip : false,
28299 currentRegion : false,
28305 Roo.get(document).on('mouseover', this.enter ,this);
28306 Roo.get(document).on('mouseout', this.leave, this);
28309 this.currentTip = new Roo.bootstrap.Tooltip();
28312 enter : function(ev)
28314 var dom = ev.getTarget();
28316 //Roo.log(['enter',dom]);
28317 var el = Roo.fly(dom);
28318 if (this.currentEl) {
28320 //Roo.log(this.currentEl);
28321 //Roo.log(this.currentEl.contains(dom));
28322 if (this.currentEl == el) {
28325 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28331 if (this.currentTip.el) {
28332 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28336 if(!el || el.dom == document){
28342 // you can not look for children, as if el is the body.. then everythign is the child..
28343 if (!el.attr('tooltip')) { //
28344 if (!el.select("[tooltip]").elements.length) {
28347 // is the mouse over this child...?
28348 bindEl = el.select("[tooltip]").first();
28349 var xy = ev.getXY();
28350 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28351 //Roo.log("not in region.");
28354 //Roo.log("child element over..");
28357 this.currentEl = bindEl;
28358 this.currentTip.bind(bindEl);
28359 this.currentRegion = Roo.lib.Region.getRegion(dom);
28360 this.currentTip.enter();
28363 leave : function(ev)
28365 var dom = ev.getTarget();
28366 //Roo.log(['leave',dom]);
28367 if (!this.currentEl) {
28372 if (dom != this.currentEl.dom) {
28375 var xy = ev.getXY();
28376 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28379 // only activate leave if mouse cursor is outside... bounding box..
28384 if (this.currentTip) {
28385 this.currentTip.leave();
28387 //Roo.log('clear currentEl');
28388 this.currentEl = false;
28393 'left' : ['r-l', [-2,0], 'right'],
28394 'right' : ['l-r', [2,0], 'left'],
28395 'bottom' : ['t-b', [0,2], 'top'],
28396 'top' : [ 'b-t', [0,-2], 'bottom']
28402 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28407 delay : null, // can be { show : 300 , hide: 500}
28411 hoverState : null, //???
28413 placement : 'bottom',
28417 getAutoCreate : function(){
28424 cls : 'tooltip-arrow arrow'
28427 cls : 'tooltip-inner'
28434 bind : function(el)
28439 initEvents : function()
28441 this.arrowEl = this.el.select('.arrow', true).first();
28442 this.innerEl = this.el.select('.tooltip-inner', true).first();
28445 enter : function () {
28447 if (this.timeout != null) {
28448 clearTimeout(this.timeout);
28451 this.hoverState = 'in';
28452 //Roo.log("enter - show");
28453 if (!this.delay || !this.delay.show) {
28458 this.timeout = setTimeout(function () {
28459 if (_t.hoverState == 'in') {
28462 }, this.delay.show);
28466 clearTimeout(this.timeout);
28468 this.hoverState = 'out';
28469 if (!this.delay || !this.delay.hide) {
28475 this.timeout = setTimeout(function () {
28476 //Roo.log("leave - timeout");
28478 if (_t.hoverState == 'out') {
28480 Roo.bootstrap.Tooltip.currentEl = false;
28485 show : function (msg)
28488 this.render(document.body);
28491 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28493 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28495 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28497 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28498 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28500 var placement = typeof this.placement == 'function' ?
28501 this.placement.call(this, this.el, on_el) :
28504 var autoToken = /\s?auto?\s?/i;
28505 var autoPlace = autoToken.test(placement);
28507 placement = placement.replace(autoToken, '') || 'top';
28511 //this.el.setXY([0,0]);
28513 //this.el.dom.style.display='block';
28515 //this.el.appendTo(on_el);
28517 var p = this.getPosition();
28518 var box = this.el.getBox();
28524 var align = this.alignment[placement];
28526 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28528 if(placement == 'top' || placement == 'bottom'){
28530 placement = 'right';
28533 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28534 placement = 'left';
28537 var scroll = Roo.select('body', true).first().getScroll();
28539 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28543 align = this.alignment[placement];
28545 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28549 this.el.alignTo(this.bindEl, align[0],align[1]);
28550 //var arrow = this.el.select('.arrow',true).first();
28551 //arrow.set(align[2],
28553 this.el.addClass(placement);
28554 this.el.addClass("bs-tooltip-"+ placement);
28556 this.el.addClass('in fade show');
28558 this.hoverState = null;
28560 if (this.el.hasClass('fade')) {
28575 //this.el.setXY([0,0]);
28576 this.el.removeClass(['show', 'in']);
28592 * @class Roo.bootstrap.LocationPicker
28593 * @extends Roo.bootstrap.Component
28594 * Bootstrap LocationPicker class
28595 * @cfg {Number} latitude Position when init default 0
28596 * @cfg {Number} longitude Position when init default 0
28597 * @cfg {Number} zoom default 15
28598 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28599 * @cfg {Boolean} mapTypeControl default false
28600 * @cfg {Boolean} disableDoubleClickZoom default false
28601 * @cfg {Boolean} scrollwheel default true
28602 * @cfg {Boolean} streetViewControl default false
28603 * @cfg {Number} radius default 0
28604 * @cfg {String} locationName
28605 * @cfg {Boolean} draggable default true
28606 * @cfg {Boolean} enableAutocomplete default false
28607 * @cfg {Boolean} enableReverseGeocode default true
28608 * @cfg {String} markerTitle
28611 * Create a new LocationPicker
28612 * @param {Object} config The config object
28616 Roo.bootstrap.LocationPicker = function(config){
28618 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28623 * Fires when the picker initialized.
28624 * @param {Roo.bootstrap.LocationPicker} this
28625 * @param {Google Location} location
28629 * @event positionchanged
28630 * Fires when the picker position changed.
28631 * @param {Roo.bootstrap.LocationPicker} this
28632 * @param {Google Location} location
28634 positionchanged : true,
28637 * Fires when the map resize.
28638 * @param {Roo.bootstrap.LocationPicker} this
28643 * Fires when the map show.
28644 * @param {Roo.bootstrap.LocationPicker} this
28649 * Fires when the map hide.
28650 * @param {Roo.bootstrap.LocationPicker} this
28655 * Fires when click the map.
28656 * @param {Roo.bootstrap.LocationPicker} this
28657 * @param {Map event} e
28661 * @event mapRightClick
28662 * Fires when right click the map.
28663 * @param {Roo.bootstrap.LocationPicker} this
28664 * @param {Map event} e
28666 mapRightClick : true,
28668 * @event markerClick
28669 * Fires when click the marker.
28670 * @param {Roo.bootstrap.LocationPicker} this
28671 * @param {Map event} e
28673 markerClick : true,
28675 * @event markerRightClick
28676 * Fires when right click the marker.
28677 * @param {Roo.bootstrap.LocationPicker} this
28678 * @param {Map event} e
28680 markerRightClick : true,
28682 * @event OverlayViewDraw
28683 * Fires when OverlayView Draw
28684 * @param {Roo.bootstrap.LocationPicker} this
28686 OverlayViewDraw : true,
28688 * @event OverlayViewOnAdd
28689 * Fires when OverlayView Draw
28690 * @param {Roo.bootstrap.LocationPicker} this
28692 OverlayViewOnAdd : true,
28694 * @event OverlayViewOnRemove
28695 * Fires when OverlayView Draw
28696 * @param {Roo.bootstrap.LocationPicker} this
28698 OverlayViewOnRemove : true,
28700 * @event OverlayViewShow
28701 * Fires when OverlayView Draw
28702 * @param {Roo.bootstrap.LocationPicker} this
28703 * @param {Pixel} cpx
28705 OverlayViewShow : true,
28707 * @event OverlayViewHide
28708 * Fires when OverlayView Draw
28709 * @param {Roo.bootstrap.LocationPicker} this
28711 OverlayViewHide : true,
28713 * @event loadexception
28714 * Fires when load google lib failed.
28715 * @param {Roo.bootstrap.LocationPicker} this
28717 loadexception : true
28722 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
28724 gMapContext: false,
28730 mapTypeControl: false,
28731 disableDoubleClickZoom: false,
28733 streetViewControl: false,
28737 enableAutocomplete: false,
28738 enableReverseGeocode: true,
28741 getAutoCreate: function()
28746 cls: 'roo-location-picker'
28752 initEvents: function(ct, position)
28754 if(!this.el.getWidth() || this.isApplied()){
28758 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28763 initial: function()
28765 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28766 this.fireEvent('loadexception', this);
28770 if(!this.mapTypeId){
28771 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28774 this.gMapContext = this.GMapContext();
28776 this.initOverlayView();
28778 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28782 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28783 _this.setPosition(_this.gMapContext.marker.position);
28786 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28787 _this.fireEvent('mapClick', this, event);
28791 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28792 _this.fireEvent('mapRightClick', this, event);
28796 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28797 _this.fireEvent('markerClick', this, event);
28801 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28802 _this.fireEvent('markerRightClick', this, event);
28806 this.setPosition(this.gMapContext.location);
28808 this.fireEvent('initial', this, this.gMapContext.location);
28811 initOverlayView: function()
28815 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28819 _this.fireEvent('OverlayViewDraw', _this);
28824 _this.fireEvent('OverlayViewOnAdd', _this);
28827 onRemove: function()
28829 _this.fireEvent('OverlayViewOnRemove', _this);
28832 show: function(cpx)
28834 _this.fireEvent('OverlayViewShow', _this, cpx);
28839 _this.fireEvent('OverlayViewHide', _this);
28845 fromLatLngToContainerPixel: function(event)
28847 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28850 isApplied: function()
28852 return this.getGmapContext() == false ? false : true;
28855 getGmapContext: function()
28857 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28860 GMapContext: function()
28862 var position = new google.maps.LatLng(this.latitude, this.longitude);
28864 var _map = new google.maps.Map(this.el.dom, {
28867 mapTypeId: this.mapTypeId,
28868 mapTypeControl: this.mapTypeControl,
28869 disableDoubleClickZoom: this.disableDoubleClickZoom,
28870 scrollwheel: this.scrollwheel,
28871 streetViewControl: this.streetViewControl,
28872 locationName: this.locationName,
28873 draggable: this.draggable,
28874 enableAutocomplete: this.enableAutocomplete,
28875 enableReverseGeocode: this.enableReverseGeocode
28878 var _marker = new google.maps.Marker({
28879 position: position,
28881 title: this.markerTitle,
28882 draggable: this.draggable
28889 location: position,
28890 radius: this.radius,
28891 locationName: this.locationName,
28892 addressComponents: {
28893 formatted_address: null,
28894 addressLine1: null,
28895 addressLine2: null,
28897 streetNumber: null,
28901 stateOrProvince: null
28904 domContainer: this.el.dom,
28905 geodecoder: new google.maps.Geocoder()
28909 drawCircle: function(center, radius, options)
28911 if (this.gMapContext.circle != null) {
28912 this.gMapContext.circle.setMap(null);
28916 options = Roo.apply({}, options, {
28917 strokeColor: "#0000FF",
28918 strokeOpacity: .35,
28920 fillColor: "#0000FF",
28924 options.map = this.gMapContext.map;
28925 options.radius = radius;
28926 options.center = center;
28927 this.gMapContext.circle = new google.maps.Circle(options);
28928 return this.gMapContext.circle;
28934 setPosition: function(location)
28936 this.gMapContext.location = location;
28937 this.gMapContext.marker.setPosition(location);
28938 this.gMapContext.map.panTo(location);
28939 this.drawCircle(location, this.gMapContext.radius, {});
28943 if (this.gMapContext.settings.enableReverseGeocode) {
28944 this.gMapContext.geodecoder.geocode({
28945 latLng: this.gMapContext.location
28946 }, function(results, status) {
28948 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28949 _this.gMapContext.locationName = results[0].formatted_address;
28950 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28952 _this.fireEvent('positionchanged', this, location);
28959 this.fireEvent('positionchanged', this, location);
28964 google.maps.event.trigger(this.gMapContext.map, "resize");
28966 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28968 this.fireEvent('resize', this);
28971 setPositionByLatLng: function(latitude, longitude)
28973 this.setPosition(new google.maps.LatLng(latitude, longitude));
28976 getCurrentPosition: function()
28979 latitude: this.gMapContext.location.lat(),
28980 longitude: this.gMapContext.location.lng()
28984 getAddressName: function()
28986 return this.gMapContext.locationName;
28989 getAddressComponents: function()
28991 return this.gMapContext.addressComponents;
28994 address_component_from_google_geocode: function(address_components)
28998 for (var i = 0; i < address_components.length; i++) {
28999 var component = address_components[i];
29000 if (component.types.indexOf("postal_code") >= 0) {
29001 result.postalCode = component.short_name;
29002 } else if (component.types.indexOf("street_number") >= 0) {
29003 result.streetNumber = component.short_name;
29004 } else if (component.types.indexOf("route") >= 0) {
29005 result.streetName = component.short_name;
29006 } else if (component.types.indexOf("neighborhood") >= 0) {
29007 result.city = component.short_name;
29008 } else if (component.types.indexOf("locality") >= 0) {
29009 result.city = component.short_name;
29010 } else if (component.types.indexOf("sublocality") >= 0) {
29011 result.district = component.short_name;
29012 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29013 result.stateOrProvince = component.short_name;
29014 } else if (component.types.indexOf("country") >= 0) {
29015 result.country = component.short_name;
29019 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29020 result.addressLine2 = "";
29024 setZoomLevel: function(zoom)
29026 this.gMapContext.map.setZoom(zoom);
29039 this.fireEvent('show', this);
29050 this.fireEvent('hide', this);
29055 Roo.apply(Roo.bootstrap.LocationPicker, {
29057 OverlayView : function(map, options)
29059 options = options || {};
29066 * @class Roo.bootstrap.Alert
29067 * @extends Roo.bootstrap.Component
29068 * Bootstrap Alert class - shows an alert area box
29070 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29071 Enter a valid email address
29074 * @cfg {String} title The title of alert
29075 * @cfg {String} html The content of alert
29076 * @cfg {String} weight ( success | info | warning | danger )
29077 * @cfg {String} faicon font-awesomeicon
29080 * Create a new alert
29081 * @param {Object} config The config object
29085 Roo.bootstrap.Alert = function(config){
29086 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29090 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29097 getAutoCreate : function()
29106 cls : 'roo-alert-icon'
29111 cls : 'roo-alert-title',
29116 cls : 'roo-alert-text',
29123 cfg.cn[0].cls += ' fa ' + this.faicon;
29127 cfg.cls += ' alert-' + this.weight;
29133 initEvents: function()
29135 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29138 setTitle : function(str)
29140 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29143 setText : function(str)
29145 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29148 setWeight : function(weight)
29151 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29154 this.weight = weight;
29156 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29159 setIcon : function(icon)
29162 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29165 this.faicon = icon;
29167 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29188 * @class Roo.bootstrap.UploadCropbox
29189 * @extends Roo.bootstrap.Component
29190 * Bootstrap UploadCropbox class
29191 * @cfg {String} emptyText show when image has been loaded
29192 * @cfg {String} rotateNotify show when image too small to rotate
29193 * @cfg {Number} errorTimeout default 3000
29194 * @cfg {Number} minWidth default 300
29195 * @cfg {Number} minHeight default 300
29196 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29197 * @cfg {Boolean} isDocument (true|false) default false
29198 * @cfg {String} url action url
29199 * @cfg {String} paramName default 'imageUpload'
29200 * @cfg {String} method default POST
29201 * @cfg {Boolean} loadMask (true|false) default true
29202 * @cfg {Boolean} loadingText default 'Loading...'
29205 * Create a new UploadCropbox
29206 * @param {Object} config The config object
29209 Roo.bootstrap.UploadCropbox = function(config){
29210 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29214 * @event beforeselectfile
29215 * Fire before select file
29216 * @param {Roo.bootstrap.UploadCropbox} this
29218 "beforeselectfile" : true,
29221 * Fire after initEvent
29222 * @param {Roo.bootstrap.UploadCropbox} this
29227 * Fire after initEvent
29228 * @param {Roo.bootstrap.UploadCropbox} this
29229 * @param {String} data
29234 * Fire when preparing the file data
29235 * @param {Roo.bootstrap.UploadCropbox} this
29236 * @param {Object} file
29241 * Fire when get exception
29242 * @param {Roo.bootstrap.UploadCropbox} this
29243 * @param {XMLHttpRequest} xhr
29245 "exception" : true,
29247 * @event beforeloadcanvas
29248 * Fire before load the canvas
29249 * @param {Roo.bootstrap.UploadCropbox} this
29250 * @param {String} src
29252 "beforeloadcanvas" : true,
29255 * Fire when trash image
29256 * @param {Roo.bootstrap.UploadCropbox} this
29261 * Fire when download the image
29262 * @param {Roo.bootstrap.UploadCropbox} this
29266 * @event footerbuttonclick
29267 * Fire when footerbuttonclick
29268 * @param {Roo.bootstrap.UploadCropbox} this
29269 * @param {String} type
29271 "footerbuttonclick" : true,
29275 * @param {Roo.bootstrap.UploadCropbox} this
29280 * Fire when rotate the image
29281 * @param {Roo.bootstrap.UploadCropbox} this
29282 * @param {String} pos
29287 * Fire when inspect the file
29288 * @param {Roo.bootstrap.UploadCropbox} this
29289 * @param {Object} file
29294 * Fire when xhr upload the file
29295 * @param {Roo.bootstrap.UploadCropbox} this
29296 * @param {Object} data
29301 * Fire when arrange the file data
29302 * @param {Roo.bootstrap.UploadCropbox} this
29303 * @param {Object} formData
29308 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29311 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29313 emptyText : 'Click to upload image',
29314 rotateNotify : 'Image is too small to rotate',
29315 errorTimeout : 3000,
29329 cropType : 'image/jpeg',
29331 canvasLoaded : false,
29332 isDocument : false,
29334 paramName : 'imageUpload',
29336 loadingText : 'Loading...',
29339 getAutoCreate : function()
29343 cls : 'roo-upload-cropbox',
29347 cls : 'roo-upload-cropbox-selector',
29352 cls : 'roo-upload-cropbox-body',
29353 style : 'cursor:pointer',
29357 cls : 'roo-upload-cropbox-preview'
29361 cls : 'roo-upload-cropbox-thumb'
29365 cls : 'roo-upload-cropbox-empty-notify',
29366 html : this.emptyText
29370 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29371 html : this.rotateNotify
29377 cls : 'roo-upload-cropbox-footer',
29380 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29390 onRender : function(ct, position)
29392 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29394 if (this.buttons.length) {
29396 Roo.each(this.buttons, function(bb) {
29398 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29400 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29406 this.maskEl = this.el;
29410 initEvents : function()
29412 this.urlAPI = (window.createObjectURL && window) ||
29413 (window.URL && URL.revokeObjectURL && URL) ||
29414 (window.webkitURL && webkitURL);
29416 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29417 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29419 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29420 this.selectorEl.hide();
29422 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29423 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29425 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29426 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29427 this.thumbEl.hide();
29429 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29430 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29432 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29433 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29434 this.errorEl.hide();
29436 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29437 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29438 this.footerEl.hide();
29440 this.setThumbBoxSize();
29446 this.fireEvent('initial', this);
29453 window.addEventListener("resize", function() { _this.resize(); } );
29455 this.bodyEl.on('click', this.beforeSelectFile, this);
29458 this.bodyEl.on('touchstart', this.onTouchStart, this);
29459 this.bodyEl.on('touchmove', this.onTouchMove, this);
29460 this.bodyEl.on('touchend', this.onTouchEnd, this);
29464 this.bodyEl.on('mousedown', this.onMouseDown, this);
29465 this.bodyEl.on('mousemove', this.onMouseMove, this);
29466 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29467 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29468 Roo.get(document).on('mouseup', this.onMouseUp, this);
29471 this.selectorEl.on('change', this.onFileSelected, this);
29477 this.baseScale = 1;
29479 this.baseRotate = 1;
29480 this.dragable = false;
29481 this.pinching = false;
29484 this.cropData = false;
29485 this.notifyEl.dom.innerHTML = this.emptyText;
29487 this.selectorEl.dom.value = '';
29491 resize : function()
29493 if(this.fireEvent('resize', this) != false){
29494 this.setThumbBoxPosition();
29495 this.setCanvasPosition();
29499 onFooterButtonClick : function(e, el, o, type)
29502 case 'rotate-left' :
29503 this.onRotateLeft(e);
29505 case 'rotate-right' :
29506 this.onRotateRight(e);
29509 this.beforeSelectFile(e);
29524 this.fireEvent('footerbuttonclick', this, type);
29527 beforeSelectFile : function(e)
29529 e.preventDefault();
29531 if(this.fireEvent('beforeselectfile', this) != false){
29532 this.selectorEl.dom.click();
29536 onFileSelected : function(e)
29538 e.preventDefault();
29540 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29544 var file = this.selectorEl.dom.files[0];
29546 if(this.fireEvent('inspect', this, file) != false){
29547 this.prepare(file);
29552 trash : function(e)
29554 this.fireEvent('trash', this);
29557 download : function(e)
29559 this.fireEvent('download', this);
29562 loadCanvas : function(src)
29564 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29568 this.imageEl = document.createElement('img');
29572 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29574 this.imageEl.src = src;
29578 onLoadCanvas : function()
29580 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29581 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29583 this.bodyEl.un('click', this.beforeSelectFile, this);
29585 this.notifyEl.hide();
29586 this.thumbEl.show();
29587 this.footerEl.show();
29589 this.baseRotateLevel();
29591 if(this.isDocument){
29592 this.setThumbBoxSize();
29595 this.setThumbBoxPosition();
29597 this.baseScaleLevel();
29603 this.canvasLoaded = true;
29606 this.maskEl.unmask();
29611 setCanvasPosition : function()
29613 if(!this.canvasEl){
29617 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29618 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29620 this.previewEl.setLeft(pw);
29621 this.previewEl.setTop(ph);
29625 onMouseDown : function(e)
29629 this.dragable = true;
29630 this.pinching = false;
29632 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29633 this.dragable = false;
29637 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29638 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29642 onMouseMove : function(e)
29646 if(!this.canvasLoaded){
29650 if (!this.dragable){
29654 var minX = Math.ceil(this.thumbEl.getLeft(true));
29655 var minY = Math.ceil(this.thumbEl.getTop(true));
29657 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29658 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29660 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29661 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29663 x = x - this.mouseX;
29664 y = y - this.mouseY;
29666 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29667 var bgY = Math.ceil(y + this.previewEl.getTop(true));
29669 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29670 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29672 this.previewEl.setLeft(bgX);
29673 this.previewEl.setTop(bgY);
29675 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29676 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29679 onMouseUp : function(e)
29683 this.dragable = false;
29686 onMouseWheel : function(e)
29690 this.startScale = this.scale;
29692 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29694 if(!this.zoomable()){
29695 this.scale = this.startScale;
29704 zoomable : function()
29706 var minScale = this.thumbEl.getWidth() / this.minWidth;
29708 if(this.minWidth < this.minHeight){
29709 minScale = this.thumbEl.getHeight() / this.minHeight;
29712 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29713 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29717 (this.rotate == 0 || this.rotate == 180) &&
29719 width > this.imageEl.OriginWidth ||
29720 height > this.imageEl.OriginHeight ||
29721 (width < this.minWidth && height < this.minHeight)
29729 (this.rotate == 90 || this.rotate == 270) &&
29731 width > this.imageEl.OriginWidth ||
29732 height > this.imageEl.OriginHeight ||
29733 (width < this.minHeight && height < this.minWidth)
29740 !this.isDocument &&
29741 (this.rotate == 0 || this.rotate == 180) &&
29743 width < this.minWidth ||
29744 width > this.imageEl.OriginWidth ||
29745 height < this.minHeight ||
29746 height > this.imageEl.OriginHeight
29753 !this.isDocument &&
29754 (this.rotate == 90 || this.rotate == 270) &&
29756 width < this.minHeight ||
29757 width > this.imageEl.OriginWidth ||
29758 height < this.minWidth ||
29759 height > this.imageEl.OriginHeight
29769 onRotateLeft : function(e)
29771 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29773 var minScale = this.thumbEl.getWidth() / this.minWidth;
29775 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29776 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29778 this.startScale = this.scale;
29780 while (this.getScaleLevel() < minScale){
29782 this.scale = this.scale + 1;
29784 if(!this.zoomable()){
29789 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29790 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29795 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29802 this.scale = this.startScale;
29804 this.onRotateFail();
29809 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29811 if(this.isDocument){
29812 this.setThumbBoxSize();
29813 this.setThumbBoxPosition();
29814 this.setCanvasPosition();
29819 this.fireEvent('rotate', this, 'left');
29823 onRotateRight : function(e)
29825 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29827 var minScale = this.thumbEl.getWidth() / this.minWidth;
29829 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29830 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29832 this.startScale = this.scale;
29834 while (this.getScaleLevel() < minScale){
29836 this.scale = this.scale + 1;
29838 if(!this.zoomable()){
29843 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29844 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29849 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29856 this.scale = this.startScale;
29858 this.onRotateFail();
29863 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29865 if(this.isDocument){
29866 this.setThumbBoxSize();
29867 this.setThumbBoxPosition();
29868 this.setCanvasPosition();
29873 this.fireEvent('rotate', this, 'right');
29876 onRotateFail : function()
29878 this.errorEl.show(true);
29882 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29887 this.previewEl.dom.innerHTML = '';
29889 var canvasEl = document.createElement("canvas");
29891 var contextEl = canvasEl.getContext("2d");
29893 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29894 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29895 var center = this.imageEl.OriginWidth / 2;
29897 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29898 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29899 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29900 center = this.imageEl.OriginHeight / 2;
29903 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29905 contextEl.translate(center, center);
29906 contextEl.rotate(this.rotate * Math.PI / 180);
29908 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29910 this.canvasEl = document.createElement("canvas");
29912 this.contextEl = this.canvasEl.getContext("2d");
29914 switch (this.rotate) {
29917 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29918 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29920 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29925 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29926 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29928 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29929 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);
29933 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29938 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29939 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29941 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29942 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);
29946 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);
29951 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29952 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29954 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29955 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29959 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);
29966 this.previewEl.appendChild(this.canvasEl);
29968 this.setCanvasPosition();
29973 if(!this.canvasLoaded){
29977 var imageCanvas = document.createElement("canvas");
29979 var imageContext = imageCanvas.getContext("2d");
29981 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29982 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29984 var center = imageCanvas.width / 2;
29986 imageContext.translate(center, center);
29988 imageContext.rotate(this.rotate * Math.PI / 180);
29990 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29992 var canvas = document.createElement("canvas");
29994 var context = canvas.getContext("2d");
29996 canvas.width = this.minWidth;
29997 canvas.height = this.minHeight;
29999 switch (this.rotate) {
30002 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30003 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30005 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30006 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30008 var targetWidth = this.minWidth - 2 * x;
30009 var targetHeight = this.minHeight - 2 * y;
30013 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30014 scale = targetWidth / width;
30017 if(x > 0 && y == 0){
30018 scale = targetHeight / height;
30021 if(x > 0 && y > 0){
30022 scale = targetWidth / width;
30024 if(width < height){
30025 scale = targetHeight / height;
30029 context.scale(scale, scale);
30031 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30032 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30034 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30035 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30037 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30042 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30043 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30045 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30046 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30048 var targetWidth = this.minWidth - 2 * x;
30049 var targetHeight = this.minHeight - 2 * y;
30053 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30054 scale = targetWidth / width;
30057 if(x > 0 && y == 0){
30058 scale = targetHeight / height;
30061 if(x > 0 && y > 0){
30062 scale = targetWidth / width;
30064 if(width < height){
30065 scale = targetHeight / height;
30069 context.scale(scale, scale);
30071 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30072 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30074 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30075 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30077 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30079 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30084 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30085 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30087 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30088 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30090 var targetWidth = this.minWidth - 2 * x;
30091 var targetHeight = this.minHeight - 2 * y;
30095 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30096 scale = targetWidth / width;
30099 if(x > 0 && y == 0){
30100 scale = targetHeight / height;
30103 if(x > 0 && y > 0){
30104 scale = targetWidth / width;
30106 if(width < height){
30107 scale = targetHeight / height;
30111 context.scale(scale, scale);
30113 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30114 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30116 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30117 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30119 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30120 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30122 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30127 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30128 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30130 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30131 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30133 var targetWidth = this.minWidth - 2 * x;
30134 var targetHeight = this.minHeight - 2 * y;
30138 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30139 scale = targetWidth / width;
30142 if(x > 0 && y == 0){
30143 scale = targetHeight / height;
30146 if(x > 0 && y > 0){
30147 scale = targetWidth / width;
30149 if(width < height){
30150 scale = targetHeight / height;
30154 context.scale(scale, scale);
30156 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30157 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30159 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30160 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30162 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30164 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30171 this.cropData = canvas.toDataURL(this.cropType);
30173 if(this.fireEvent('crop', this, this.cropData) !== false){
30174 this.process(this.file, this.cropData);
30181 setThumbBoxSize : function()
30185 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30186 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30187 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30189 this.minWidth = width;
30190 this.minHeight = height;
30192 if(this.rotate == 90 || this.rotate == 270){
30193 this.minWidth = height;
30194 this.minHeight = width;
30199 width = Math.ceil(this.minWidth * height / this.minHeight);
30201 if(this.minWidth > this.minHeight){
30203 height = Math.ceil(this.minHeight * width / this.minWidth);
30206 this.thumbEl.setStyle({
30207 width : width + 'px',
30208 height : height + 'px'
30215 setThumbBoxPosition : function()
30217 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30218 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30220 this.thumbEl.setLeft(x);
30221 this.thumbEl.setTop(y);
30225 baseRotateLevel : function()
30227 this.baseRotate = 1;
30230 typeof(this.exif) != 'undefined' &&
30231 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30232 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30234 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30237 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30241 baseScaleLevel : function()
30245 if(this.isDocument){
30247 if(this.baseRotate == 6 || this.baseRotate == 8){
30249 height = this.thumbEl.getHeight();
30250 this.baseScale = height / this.imageEl.OriginWidth;
30252 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30253 width = this.thumbEl.getWidth();
30254 this.baseScale = width / this.imageEl.OriginHeight;
30260 height = this.thumbEl.getHeight();
30261 this.baseScale = height / this.imageEl.OriginHeight;
30263 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30264 width = this.thumbEl.getWidth();
30265 this.baseScale = width / this.imageEl.OriginWidth;
30271 if(this.baseRotate == 6 || this.baseRotate == 8){
30273 width = this.thumbEl.getHeight();
30274 this.baseScale = width / this.imageEl.OriginHeight;
30276 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30277 height = this.thumbEl.getWidth();
30278 this.baseScale = height / this.imageEl.OriginHeight;
30281 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30282 height = this.thumbEl.getWidth();
30283 this.baseScale = height / this.imageEl.OriginHeight;
30285 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30286 width = this.thumbEl.getHeight();
30287 this.baseScale = width / this.imageEl.OriginWidth;
30294 width = this.thumbEl.getWidth();
30295 this.baseScale = width / this.imageEl.OriginWidth;
30297 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30298 height = this.thumbEl.getHeight();
30299 this.baseScale = height / this.imageEl.OriginHeight;
30302 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30304 height = this.thumbEl.getHeight();
30305 this.baseScale = height / this.imageEl.OriginHeight;
30307 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30308 width = this.thumbEl.getWidth();
30309 this.baseScale = width / this.imageEl.OriginWidth;
30317 getScaleLevel : function()
30319 return this.baseScale * Math.pow(1.1, this.scale);
30322 onTouchStart : function(e)
30324 if(!this.canvasLoaded){
30325 this.beforeSelectFile(e);
30329 var touches = e.browserEvent.touches;
30335 if(touches.length == 1){
30336 this.onMouseDown(e);
30340 if(touches.length != 2){
30346 for(var i = 0, finger; finger = touches[i]; i++){
30347 coords.push(finger.pageX, finger.pageY);
30350 var x = Math.pow(coords[0] - coords[2], 2);
30351 var y = Math.pow(coords[1] - coords[3], 2);
30353 this.startDistance = Math.sqrt(x + y);
30355 this.startScale = this.scale;
30357 this.pinching = true;
30358 this.dragable = false;
30362 onTouchMove : function(e)
30364 if(!this.pinching && !this.dragable){
30368 var touches = e.browserEvent.touches;
30375 this.onMouseMove(e);
30381 for(var i = 0, finger; finger = touches[i]; i++){
30382 coords.push(finger.pageX, finger.pageY);
30385 var x = Math.pow(coords[0] - coords[2], 2);
30386 var y = Math.pow(coords[1] - coords[3], 2);
30388 this.endDistance = Math.sqrt(x + y);
30390 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30392 if(!this.zoomable()){
30393 this.scale = this.startScale;
30401 onTouchEnd : function(e)
30403 this.pinching = false;
30404 this.dragable = false;
30408 process : function(file, crop)
30411 this.maskEl.mask(this.loadingText);
30414 this.xhr = new XMLHttpRequest();
30416 file.xhr = this.xhr;
30418 this.xhr.open(this.method, this.url, true);
30421 "Accept": "application/json",
30422 "Cache-Control": "no-cache",
30423 "X-Requested-With": "XMLHttpRequest"
30426 for (var headerName in headers) {
30427 var headerValue = headers[headerName];
30429 this.xhr.setRequestHeader(headerName, headerValue);
30435 this.xhr.onload = function()
30437 _this.xhrOnLoad(_this.xhr);
30440 this.xhr.onerror = function()
30442 _this.xhrOnError(_this.xhr);
30445 var formData = new FormData();
30447 formData.append('returnHTML', 'NO');
30450 formData.append('crop', crop);
30453 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30454 formData.append(this.paramName, file, file.name);
30457 if(typeof(file.filename) != 'undefined'){
30458 formData.append('filename', file.filename);
30461 if(typeof(file.mimetype) != 'undefined'){
30462 formData.append('mimetype', file.mimetype);
30465 if(this.fireEvent('arrange', this, formData) != false){
30466 this.xhr.send(formData);
30470 xhrOnLoad : function(xhr)
30473 this.maskEl.unmask();
30476 if (xhr.readyState !== 4) {
30477 this.fireEvent('exception', this, xhr);
30481 var response = Roo.decode(xhr.responseText);
30483 if(!response.success){
30484 this.fireEvent('exception', this, xhr);
30488 var response = Roo.decode(xhr.responseText);
30490 this.fireEvent('upload', this, response);
30494 xhrOnError : function()
30497 this.maskEl.unmask();
30500 Roo.log('xhr on error');
30502 var response = Roo.decode(xhr.responseText);
30508 prepare : function(file)
30511 this.maskEl.mask(this.loadingText);
30517 if(typeof(file) === 'string'){
30518 this.loadCanvas(file);
30522 if(!file || !this.urlAPI){
30527 this.cropType = file.type;
30531 if(this.fireEvent('prepare', this, this.file) != false){
30533 var reader = new FileReader();
30535 reader.onload = function (e) {
30536 if (e.target.error) {
30537 Roo.log(e.target.error);
30541 var buffer = e.target.result,
30542 dataView = new DataView(buffer),
30544 maxOffset = dataView.byteLength - 4,
30548 if (dataView.getUint16(0) === 0xffd8) {
30549 while (offset < maxOffset) {
30550 markerBytes = dataView.getUint16(offset);
30552 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30553 markerLength = dataView.getUint16(offset + 2) + 2;
30554 if (offset + markerLength > dataView.byteLength) {
30555 Roo.log('Invalid meta data: Invalid segment size.');
30559 if(markerBytes == 0xffe1){
30560 _this.parseExifData(
30567 offset += markerLength;
30577 var url = _this.urlAPI.createObjectURL(_this.file);
30579 _this.loadCanvas(url);
30584 reader.readAsArrayBuffer(this.file);
30590 parseExifData : function(dataView, offset, length)
30592 var tiffOffset = offset + 10,
30596 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30597 // No Exif data, might be XMP data instead
30601 // Check for the ASCII code for "Exif" (0x45786966):
30602 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30603 // No Exif data, might be XMP data instead
30606 if (tiffOffset + 8 > dataView.byteLength) {
30607 Roo.log('Invalid Exif data: Invalid segment size.');
30610 // Check for the two null bytes:
30611 if (dataView.getUint16(offset + 8) !== 0x0000) {
30612 Roo.log('Invalid Exif data: Missing byte alignment offset.');
30615 // Check the byte alignment:
30616 switch (dataView.getUint16(tiffOffset)) {
30618 littleEndian = true;
30621 littleEndian = false;
30624 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30627 // Check for the TIFF tag marker (0x002A):
30628 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30629 Roo.log('Invalid Exif data: Missing TIFF marker.');
30632 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30633 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30635 this.parseExifTags(
30638 tiffOffset + dirOffset,
30643 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30648 if (dirOffset + 6 > dataView.byteLength) {
30649 Roo.log('Invalid Exif data: Invalid directory offset.');
30652 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30653 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30654 if (dirEndOffset + 4 > dataView.byteLength) {
30655 Roo.log('Invalid Exif data: Invalid directory size.');
30658 for (i = 0; i < tagsNumber; i += 1) {
30662 dirOffset + 2 + 12 * i, // tag offset
30666 // Return the offset to the next directory:
30667 return dataView.getUint32(dirEndOffset, littleEndian);
30670 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
30672 var tag = dataView.getUint16(offset, littleEndian);
30674 this.exif[tag] = this.getExifValue(
30678 dataView.getUint16(offset + 2, littleEndian), // tag type
30679 dataView.getUint32(offset + 4, littleEndian), // tag length
30684 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30686 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30695 Roo.log('Invalid Exif data: Invalid tag type.');
30699 tagSize = tagType.size * length;
30700 // Determine if the value is contained in the dataOffset bytes,
30701 // or if the value at the dataOffset is a pointer to the actual data:
30702 dataOffset = tagSize > 4 ?
30703 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30704 if (dataOffset + tagSize > dataView.byteLength) {
30705 Roo.log('Invalid Exif data: Invalid data offset.');
30708 if (length === 1) {
30709 return tagType.getValue(dataView, dataOffset, littleEndian);
30712 for (i = 0; i < length; i += 1) {
30713 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30716 if (tagType.ascii) {
30718 // Concatenate the chars:
30719 for (i = 0; i < values.length; i += 1) {
30721 // Ignore the terminating NULL byte(s):
30722 if (c === '\u0000') {
30734 Roo.apply(Roo.bootstrap.UploadCropbox, {
30736 'Orientation': 0x0112
30740 1: 0, //'top-left',
30742 3: 180, //'bottom-right',
30743 // 4: 'bottom-left',
30745 6: 90, //'right-top',
30746 // 7: 'right-bottom',
30747 8: 270 //'left-bottom'
30751 // byte, 8-bit unsigned int:
30753 getValue: function (dataView, dataOffset) {
30754 return dataView.getUint8(dataOffset);
30758 // ascii, 8-bit byte:
30760 getValue: function (dataView, dataOffset) {
30761 return String.fromCharCode(dataView.getUint8(dataOffset));
30766 // short, 16 bit int:
30768 getValue: function (dataView, dataOffset, littleEndian) {
30769 return dataView.getUint16(dataOffset, littleEndian);
30773 // long, 32 bit int:
30775 getValue: function (dataView, dataOffset, littleEndian) {
30776 return dataView.getUint32(dataOffset, littleEndian);
30780 // rational = two long values, first is numerator, second is denominator:
30782 getValue: function (dataView, dataOffset, littleEndian) {
30783 return dataView.getUint32(dataOffset, littleEndian) /
30784 dataView.getUint32(dataOffset + 4, littleEndian);
30788 // slong, 32 bit signed int:
30790 getValue: function (dataView, dataOffset, littleEndian) {
30791 return dataView.getInt32(dataOffset, littleEndian);
30795 // srational, two slongs, first is numerator, second is denominator:
30797 getValue: function (dataView, dataOffset, littleEndian) {
30798 return dataView.getInt32(dataOffset, littleEndian) /
30799 dataView.getInt32(dataOffset + 4, littleEndian);
30809 cls : 'btn-group roo-upload-cropbox-rotate-left',
30810 action : 'rotate-left',
30814 cls : 'btn btn-default',
30815 html : '<i class="fa fa-undo"></i>'
30821 cls : 'btn-group roo-upload-cropbox-picture',
30822 action : 'picture',
30826 cls : 'btn btn-default',
30827 html : '<i class="fa fa-picture-o"></i>'
30833 cls : 'btn-group roo-upload-cropbox-rotate-right',
30834 action : 'rotate-right',
30838 cls : 'btn btn-default',
30839 html : '<i class="fa fa-repeat"></i>'
30847 cls : 'btn-group roo-upload-cropbox-rotate-left',
30848 action : 'rotate-left',
30852 cls : 'btn btn-default',
30853 html : '<i class="fa fa-undo"></i>'
30859 cls : 'btn-group roo-upload-cropbox-download',
30860 action : 'download',
30864 cls : 'btn btn-default',
30865 html : '<i class="fa fa-download"></i>'
30871 cls : 'btn-group roo-upload-cropbox-crop',
30876 cls : 'btn btn-default',
30877 html : '<i class="fa fa-crop"></i>'
30883 cls : 'btn-group roo-upload-cropbox-trash',
30888 cls : 'btn btn-default',
30889 html : '<i class="fa fa-trash"></i>'
30895 cls : 'btn-group roo-upload-cropbox-rotate-right',
30896 action : 'rotate-right',
30900 cls : 'btn btn-default',
30901 html : '<i class="fa fa-repeat"></i>'
30909 cls : 'btn-group roo-upload-cropbox-rotate-left',
30910 action : 'rotate-left',
30914 cls : 'btn btn-default',
30915 html : '<i class="fa fa-undo"></i>'
30921 cls : 'btn-group roo-upload-cropbox-rotate-right',
30922 action : 'rotate-right',
30926 cls : 'btn btn-default',
30927 html : '<i class="fa fa-repeat"></i>'
30940 * @class Roo.bootstrap.DocumentManager
30941 * @extends Roo.bootstrap.Component
30942 * Bootstrap DocumentManager class
30943 * @cfg {String} paramName default 'imageUpload'
30944 * @cfg {String} toolTipName default 'filename'
30945 * @cfg {String} method default POST
30946 * @cfg {String} url action url
30947 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30948 * @cfg {Boolean} multiple multiple upload default true
30949 * @cfg {Number} thumbSize default 300
30950 * @cfg {String} fieldLabel
30951 * @cfg {Number} labelWidth default 4
30952 * @cfg {String} labelAlign (left|top) default left
30953 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30954 * @cfg {Number} labellg set the width of label (1-12)
30955 * @cfg {Number} labelmd set the width of label (1-12)
30956 * @cfg {Number} labelsm set the width of label (1-12)
30957 * @cfg {Number} labelxs set the width of label (1-12)
30960 * Create a new DocumentManager
30961 * @param {Object} config The config object
30964 Roo.bootstrap.DocumentManager = function(config){
30965 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30968 this.delegates = [];
30973 * Fire when initial the DocumentManager
30974 * @param {Roo.bootstrap.DocumentManager} this
30979 * inspect selected file
30980 * @param {Roo.bootstrap.DocumentManager} this
30981 * @param {File} file
30986 * Fire when xhr load exception
30987 * @param {Roo.bootstrap.DocumentManager} this
30988 * @param {XMLHttpRequest} xhr
30990 "exception" : true,
30992 * @event afterupload
30993 * Fire when xhr load exception
30994 * @param {Roo.bootstrap.DocumentManager} this
30995 * @param {XMLHttpRequest} xhr
30997 "afterupload" : true,
31000 * prepare the form data
31001 * @param {Roo.bootstrap.DocumentManager} this
31002 * @param {Object} formData
31007 * Fire when remove the file
31008 * @param {Roo.bootstrap.DocumentManager} this
31009 * @param {Object} file
31014 * Fire after refresh the file
31015 * @param {Roo.bootstrap.DocumentManager} this
31020 * Fire after click the image
31021 * @param {Roo.bootstrap.DocumentManager} this
31022 * @param {Object} file
31027 * Fire when upload a image and editable set to true
31028 * @param {Roo.bootstrap.DocumentManager} this
31029 * @param {Object} file
31033 * @event beforeselectfile
31034 * Fire before select file
31035 * @param {Roo.bootstrap.DocumentManager} this
31037 "beforeselectfile" : true,
31040 * Fire before process file
31041 * @param {Roo.bootstrap.DocumentManager} this
31042 * @param {Object} file
31046 * @event previewrendered
31047 * Fire when preview rendered
31048 * @param {Roo.bootstrap.DocumentManager} this
31049 * @param {Object} file
31051 "previewrendered" : true,
31054 "previewResize" : true
31059 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31068 paramName : 'imageUpload',
31069 toolTipName : 'filename',
31072 labelAlign : 'left',
31082 getAutoCreate : function()
31084 var managerWidget = {
31086 cls : 'roo-document-manager',
31090 cls : 'roo-document-manager-selector',
31095 cls : 'roo-document-manager-uploader',
31099 cls : 'roo-document-manager-upload-btn',
31100 html : '<i class="fa fa-plus"></i>'
31111 cls : 'column col-md-12',
31116 if(this.fieldLabel.length){
31121 cls : 'column col-md-12',
31122 html : this.fieldLabel
31126 cls : 'column col-md-12',
31131 if(this.labelAlign == 'left'){
31136 html : this.fieldLabel
31145 if(this.labelWidth > 12){
31146 content[0].style = "width: " + this.labelWidth + 'px';
31149 if(this.labelWidth < 13 && this.labelmd == 0){
31150 this.labelmd = this.labelWidth;
31153 if(this.labellg > 0){
31154 content[0].cls += ' col-lg-' + this.labellg;
31155 content[1].cls += ' col-lg-' + (12 - this.labellg);
31158 if(this.labelmd > 0){
31159 content[0].cls += ' col-md-' + this.labelmd;
31160 content[1].cls += ' col-md-' + (12 - this.labelmd);
31163 if(this.labelsm > 0){
31164 content[0].cls += ' col-sm-' + this.labelsm;
31165 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31168 if(this.labelxs > 0){
31169 content[0].cls += ' col-xs-' + this.labelxs;
31170 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31178 cls : 'row clearfix',
31186 initEvents : function()
31188 this.managerEl = this.el.select('.roo-document-manager', true).first();
31189 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31191 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31192 this.selectorEl.hide();
31195 this.selectorEl.attr('multiple', 'multiple');
31198 this.selectorEl.on('change', this.onFileSelected, this);
31200 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31201 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31203 this.uploader.on('click', this.onUploaderClick, this);
31205 this.renderProgressDialog();
31209 window.addEventListener("resize", function() { _this.refresh(); } );
31211 this.fireEvent('initial', this);
31214 renderProgressDialog : function()
31218 this.progressDialog = new Roo.bootstrap.Modal({
31219 cls : 'roo-document-manager-progress-dialog',
31220 allow_close : false,
31231 btnclick : function() {
31232 _this.uploadCancel();
31238 this.progressDialog.render(Roo.get(document.body));
31240 this.progress = new Roo.bootstrap.Progress({
31241 cls : 'roo-document-manager-progress',
31246 this.progress.render(this.progressDialog.getChildContainer());
31248 this.progressBar = new Roo.bootstrap.ProgressBar({
31249 cls : 'roo-document-manager-progress-bar',
31252 aria_valuemax : 12,
31256 this.progressBar.render(this.progress.getChildContainer());
31259 onUploaderClick : function(e)
31261 e.preventDefault();
31263 if(this.fireEvent('beforeselectfile', this) != false){
31264 this.selectorEl.dom.click();
31269 onFileSelected : function(e)
31271 e.preventDefault();
31273 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31277 Roo.each(this.selectorEl.dom.files, function(file){
31278 if(this.fireEvent('inspect', this, file) != false){
31279 this.files.push(file);
31289 this.selectorEl.dom.value = '';
31291 if(!this.files || !this.files.length){
31295 if(this.boxes > 0 && this.files.length > this.boxes){
31296 this.files = this.files.slice(0, this.boxes);
31299 this.uploader.show();
31301 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31302 this.uploader.hide();
31311 Roo.each(this.files, function(file){
31313 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31314 var f = this.renderPreview(file);
31319 if(file.type.indexOf('image') != -1){
31320 this.delegates.push(
31322 _this.process(file);
31323 }).createDelegate(this)
31331 _this.process(file);
31332 }).createDelegate(this)
31337 this.files = files;
31339 this.delegates = this.delegates.concat(docs);
31341 if(!this.delegates.length){
31346 this.progressBar.aria_valuemax = this.delegates.length;
31353 arrange : function()
31355 if(!this.delegates.length){
31356 this.progressDialog.hide();
31361 var delegate = this.delegates.shift();
31363 this.progressDialog.show();
31365 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31367 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31372 refresh : function()
31374 this.uploader.show();
31376 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31377 this.uploader.hide();
31380 Roo.isTouch ? this.closable(false) : this.closable(true);
31382 this.fireEvent('refresh', this);
31385 onRemove : function(e, el, o)
31387 e.preventDefault();
31389 this.fireEvent('remove', this, o);
31393 remove : function(o)
31397 Roo.each(this.files, function(file){
31398 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31407 this.files = files;
31414 Roo.each(this.files, function(file){
31419 file.target.remove();
31428 onClick : function(e, el, o)
31430 e.preventDefault();
31432 this.fireEvent('click', this, o);
31436 closable : function(closable)
31438 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31440 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31452 xhrOnLoad : function(xhr)
31454 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31458 if (xhr.readyState !== 4) {
31460 this.fireEvent('exception', this, xhr);
31464 var response = Roo.decode(xhr.responseText);
31466 if(!response.success){
31468 this.fireEvent('exception', this, xhr);
31472 var file = this.renderPreview(response.data);
31474 this.files.push(file);
31478 this.fireEvent('afterupload', this, xhr);
31482 xhrOnError : function(xhr)
31484 Roo.log('xhr on error');
31486 var response = Roo.decode(xhr.responseText);
31493 process : function(file)
31495 if(this.fireEvent('process', this, file) !== false){
31496 if(this.editable && file.type.indexOf('image') != -1){
31497 this.fireEvent('edit', this, file);
31501 this.uploadStart(file, false);
31508 uploadStart : function(file, crop)
31510 this.xhr = new XMLHttpRequest();
31512 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31517 file.xhr = this.xhr;
31519 this.managerEl.createChild({
31521 cls : 'roo-document-manager-loading',
31525 tooltip : file.name,
31526 cls : 'roo-document-manager-thumb',
31527 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31533 this.xhr.open(this.method, this.url, true);
31536 "Accept": "application/json",
31537 "Cache-Control": "no-cache",
31538 "X-Requested-With": "XMLHttpRequest"
31541 for (var headerName in headers) {
31542 var headerValue = headers[headerName];
31544 this.xhr.setRequestHeader(headerName, headerValue);
31550 this.xhr.onload = function()
31552 _this.xhrOnLoad(_this.xhr);
31555 this.xhr.onerror = function()
31557 _this.xhrOnError(_this.xhr);
31560 var formData = new FormData();
31562 formData.append('returnHTML', 'NO');
31565 formData.append('crop', crop);
31568 formData.append(this.paramName, file, file.name);
31575 if(this.fireEvent('prepare', this, formData, options) != false){
31577 if(options.manually){
31581 this.xhr.send(formData);
31585 this.uploadCancel();
31588 uploadCancel : function()
31594 this.delegates = [];
31596 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31603 renderPreview : function(file)
31605 if(typeof(file.target) != 'undefined' && file.target){
31609 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31611 var previewEl = this.managerEl.createChild({
31613 cls : 'roo-document-manager-preview',
31617 tooltip : file[this.toolTipName],
31618 cls : 'roo-document-manager-thumb',
31619 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31624 html : '<i class="fa fa-times-circle"></i>'
31629 var close = previewEl.select('button.close', true).first();
31631 close.on('click', this.onRemove, this, file);
31633 file.target = previewEl;
31635 var image = previewEl.select('img', true).first();
31639 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31641 image.on('click', this.onClick, this, file);
31643 this.fireEvent('previewrendered', this, file);
31649 onPreviewLoad : function(file, image)
31651 if(typeof(file.target) == 'undefined' || !file.target){
31655 var width = image.dom.naturalWidth || image.dom.width;
31656 var height = image.dom.naturalHeight || image.dom.height;
31658 if(!this.previewResize) {
31662 if(width > height){
31663 file.target.addClass('wide');
31667 file.target.addClass('tall');
31672 uploadFromSource : function(file, crop)
31674 this.xhr = new XMLHttpRequest();
31676 this.managerEl.createChild({
31678 cls : 'roo-document-manager-loading',
31682 tooltip : file.name,
31683 cls : 'roo-document-manager-thumb',
31684 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31690 this.xhr.open(this.method, this.url, true);
31693 "Accept": "application/json",
31694 "Cache-Control": "no-cache",
31695 "X-Requested-With": "XMLHttpRequest"
31698 for (var headerName in headers) {
31699 var headerValue = headers[headerName];
31701 this.xhr.setRequestHeader(headerName, headerValue);
31707 this.xhr.onload = function()
31709 _this.xhrOnLoad(_this.xhr);
31712 this.xhr.onerror = function()
31714 _this.xhrOnError(_this.xhr);
31717 var formData = new FormData();
31719 formData.append('returnHTML', 'NO');
31721 formData.append('crop', crop);
31723 if(typeof(file.filename) != 'undefined'){
31724 formData.append('filename', file.filename);
31727 if(typeof(file.mimetype) != 'undefined'){
31728 formData.append('mimetype', file.mimetype);
31733 if(this.fireEvent('prepare', this, formData) != false){
31734 this.xhr.send(formData);
31744 * @class Roo.bootstrap.DocumentViewer
31745 * @extends Roo.bootstrap.Component
31746 * Bootstrap DocumentViewer class
31747 * @cfg {Boolean} showDownload (true|false) show download button (default true)
31748 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31751 * Create a new DocumentViewer
31752 * @param {Object} config The config object
31755 Roo.bootstrap.DocumentViewer = function(config){
31756 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31761 * Fire after initEvent
31762 * @param {Roo.bootstrap.DocumentViewer} this
31768 * @param {Roo.bootstrap.DocumentViewer} this
31773 * Fire after download button
31774 * @param {Roo.bootstrap.DocumentViewer} this
31779 * Fire after trash button
31780 * @param {Roo.bootstrap.DocumentViewer} this
31787 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
31789 showDownload : true,
31793 getAutoCreate : function()
31797 cls : 'roo-document-viewer',
31801 cls : 'roo-document-viewer-body',
31805 cls : 'roo-document-viewer-thumb',
31809 cls : 'roo-document-viewer-image'
31817 cls : 'roo-document-viewer-footer',
31820 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31824 cls : 'btn-group roo-document-viewer-download',
31828 cls : 'btn btn-default',
31829 html : '<i class="fa fa-download"></i>'
31835 cls : 'btn-group roo-document-viewer-trash',
31839 cls : 'btn btn-default',
31840 html : '<i class="fa fa-trash"></i>'
31853 initEvents : function()
31855 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31856 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31858 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31859 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31861 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31862 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31864 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31865 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31867 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31868 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31870 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31871 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31873 this.bodyEl.on('click', this.onClick, this);
31874 this.downloadBtn.on('click', this.onDownload, this);
31875 this.trashBtn.on('click', this.onTrash, this);
31877 this.downloadBtn.hide();
31878 this.trashBtn.hide();
31880 if(this.showDownload){
31881 this.downloadBtn.show();
31884 if(this.showTrash){
31885 this.trashBtn.show();
31888 if(!this.showDownload && !this.showTrash) {
31889 this.footerEl.hide();
31894 initial : function()
31896 this.fireEvent('initial', this);
31900 onClick : function(e)
31902 e.preventDefault();
31904 this.fireEvent('click', this);
31907 onDownload : function(e)
31909 e.preventDefault();
31911 this.fireEvent('download', this);
31914 onTrash : function(e)
31916 e.preventDefault();
31918 this.fireEvent('trash', this);
31930 * @class Roo.bootstrap.NavProgressBar
31931 * @extends Roo.bootstrap.Component
31932 * Bootstrap NavProgressBar class
31935 * Create a new nav progress bar
31936 * @param {Object} config The config object
31939 Roo.bootstrap.NavProgressBar = function(config){
31940 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31942 this.bullets = this.bullets || [];
31944 // Roo.bootstrap.NavProgressBar.register(this);
31948 * Fires when the active item changes
31949 * @param {Roo.bootstrap.NavProgressBar} this
31950 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31951 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
31958 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
31963 getAutoCreate : function()
31965 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31969 cls : 'roo-navigation-bar-group',
31973 cls : 'roo-navigation-top-bar'
31977 cls : 'roo-navigation-bullets-bar',
31981 cls : 'roo-navigation-bar'
31988 cls : 'roo-navigation-bottom-bar'
31998 initEvents: function()
32003 onRender : function(ct, position)
32005 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32007 if(this.bullets.length){
32008 Roo.each(this.bullets, function(b){
32017 addItem : function(cfg)
32019 var item = new Roo.bootstrap.NavProgressItem(cfg);
32021 item.parentId = this.id;
32022 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32025 var top = new Roo.bootstrap.Element({
32027 cls : 'roo-navigation-bar-text'
32030 var bottom = new Roo.bootstrap.Element({
32032 cls : 'roo-navigation-bar-text'
32035 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32036 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32038 var topText = new Roo.bootstrap.Element({
32040 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32043 var bottomText = new Roo.bootstrap.Element({
32045 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32048 topText.onRender(top.el, null);
32049 bottomText.onRender(bottom.el, null);
32052 item.bottomEl = bottom;
32055 this.barItems.push(item);
32060 getActive : function()
32062 var active = false;
32064 Roo.each(this.barItems, function(v){
32066 if (!v.isActive()) {
32078 setActiveItem : function(item)
32082 Roo.each(this.barItems, function(v){
32083 if (v.rid == item.rid) {
32087 if (v.isActive()) {
32088 v.setActive(false);
32093 item.setActive(true);
32095 this.fireEvent('changed', this, item, prev);
32098 getBarItem: function(rid)
32102 Roo.each(this.barItems, function(e) {
32103 if (e.rid != rid) {
32114 indexOfItem : function(item)
32118 Roo.each(this.barItems, function(v, i){
32120 if (v.rid != item.rid) {
32131 setActiveNext : function()
32133 var i = this.indexOfItem(this.getActive());
32135 if (i > this.barItems.length) {
32139 this.setActiveItem(this.barItems[i+1]);
32142 setActivePrev : function()
32144 var i = this.indexOfItem(this.getActive());
32150 this.setActiveItem(this.barItems[i-1]);
32153 format : function()
32155 if(!this.barItems.length){
32159 var width = 100 / this.barItems.length;
32161 Roo.each(this.barItems, function(i){
32162 i.el.setStyle('width', width + '%');
32163 i.topEl.el.setStyle('width', width + '%');
32164 i.bottomEl.el.setStyle('width', width + '%');
32173 * Nav Progress Item
32178 * @class Roo.bootstrap.NavProgressItem
32179 * @extends Roo.bootstrap.Component
32180 * Bootstrap NavProgressItem class
32181 * @cfg {String} rid the reference id
32182 * @cfg {Boolean} active (true|false) Is item active default false
32183 * @cfg {Boolean} disabled (true|false) Is item active default false
32184 * @cfg {String} html
32185 * @cfg {String} position (top|bottom) text position default bottom
32186 * @cfg {String} icon show icon instead of number
32189 * Create a new NavProgressItem
32190 * @param {Object} config The config object
32192 Roo.bootstrap.NavProgressItem = function(config){
32193 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32198 * The raw click event for the entire grid.
32199 * @param {Roo.bootstrap.NavProgressItem} this
32200 * @param {Roo.EventObject} e
32207 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32213 position : 'bottom',
32216 getAutoCreate : function()
32218 var iconCls = 'roo-navigation-bar-item-icon';
32220 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32224 cls: 'roo-navigation-bar-item',
32234 cfg.cls += ' active';
32237 cfg.cls += ' disabled';
32243 disable : function()
32245 this.setDisabled(true);
32248 enable : function()
32250 this.setDisabled(false);
32253 initEvents: function()
32255 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32257 this.iconEl.on('click', this.onClick, this);
32260 onClick : function(e)
32262 e.preventDefault();
32268 if(this.fireEvent('click', this, e) === false){
32272 this.parent().setActiveItem(this);
32275 isActive: function ()
32277 return this.active;
32280 setActive : function(state)
32282 if(this.active == state){
32286 this.active = state;
32289 this.el.addClass('active');
32293 this.el.removeClass('active');
32298 setDisabled : function(state)
32300 if(this.disabled == state){
32304 this.disabled = state;
32307 this.el.addClass('disabled');
32311 this.el.removeClass('disabled');
32314 tooltipEl : function()
32316 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32329 * @class Roo.bootstrap.FieldLabel
32330 * @extends Roo.bootstrap.Component
32331 * Bootstrap FieldLabel class
32332 * @cfg {String} html contents of the element
32333 * @cfg {String} tag tag of the element default label
32334 * @cfg {String} cls class of the element
32335 * @cfg {String} target label target
32336 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32337 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32338 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32339 * @cfg {String} iconTooltip default "This field is required"
32340 * @cfg {String} indicatorpos (left|right) default left
32343 * Create a new FieldLabel
32344 * @param {Object} config The config object
32347 Roo.bootstrap.FieldLabel = function(config){
32348 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32353 * Fires after the field has been marked as invalid.
32354 * @param {Roo.form.FieldLabel} this
32355 * @param {String} msg The validation message
32360 * Fires after the field has been validated with no errors.
32361 * @param {Roo.form.FieldLabel} this
32367 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32374 invalidClass : 'has-warning',
32375 validClass : 'has-success',
32376 iconTooltip : 'This field is required',
32377 indicatorpos : 'left',
32379 getAutoCreate : function(){
32382 if (!this.allowBlank) {
32388 cls : 'roo-bootstrap-field-label ' + this.cls,
32393 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32394 tooltip : this.iconTooltip
32403 if(this.indicatorpos == 'right'){
32406 cls : 'roo-bootstrap-field-label ' + this.cls,
32415 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32416 tooltip : this.iconTooltip
32425 initEvents: function()
32427 Roo.bootstrap.Element.superclass.initEvents.call(this);
32429 this.indicator = this.indicatorEl();
32431 if(this.indicator){
32432 this.indicator.removeClass('visible');
32433 this.indicator.addClass('invisible');
32436 Roo.bootstrap.FieldLabel.register(this);
32439 indicatorEl : function()
32441 var indicator = this.el.select('i.roo-required-indicator',true).first();
32452 * Mark this field as valid
32454 markValid : function()
32456 if(this.indicator){
32457 this.indicator.removeClass('visible');
32458 this.indicator.addClass('invisible');
32460 if (Roo.bootstrap.version == 3) {
32461 this.el.removeClass(this.invalidClass);
32462 this.el.addClass(this.validClass);
32464 this.el.removeClass('is-invalid');
32465 this.el.addClass('is-valid');
32469 this.fireEvent('valid', this);
32473 * Mark this field as invalid
32474 * @param {String} msg The validation message
32476 markInvalid : function(msg)
32478 if(this.indicator){
32479 this.indicator.removeClass('invisible');
32480 this.indicator.addClass('visible');
32482 if (Roo.bootstrap.version == 3) {
32483 this.el.removeClass(this.validClass);
32484 this.el.addClass(this.invalidClass);
32486 this.el.removeClass('is-valid');
32487 this.el.addClass('is-invalid');
32491 this.fireEvent('invalid', this, msg);
32497 Roo.apply(Roo.bootstrap.FieldLabel, {
32502 * register a FieldLabel Group
32503 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32505 register : function(label)
32507 if(this.groups.hasOwnProperty(label.target)){
32511 this.groups[label.target] = label;
32515 * fetch a FieldLabel Group based on the target
32516 * @param {string} target
32517 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32519 get: function(target) {
32520 if (typeof(this.groups[target]) == 'undefined') {
32524 return this.groups[target] ;
32533 * page DateSplitField.
32539 * @class Roo.bootstrap.DateSplitField
32540 * @extends Roo.bootstrap.Component
32541 * Bootstrap DateSplitField class
32542 * @cfg {string} fieldLabel - the label associated
32543 * @cfg {Number} labelWidth set the width of label (0-12)
32544 * @cfg {String} labelAlign (top|left)
32545 * @cfg {Boolean} dayAllowBlank (true|false) default false
32546 * @cfg {Boolean} monthAllowBlank (true|false) default false
32547 * @cfg {Boolean} yearAllowBlank (true|false) default false
32548 * @cfg {string} dayPlaceholder
32549 * @cfg {string} monthPlaceholder
32550 * @cfg {string} yearPlaceholder
32551 * @cfg {string} dayFormat default 'd'
32552 * @cfg {string} monthFormat default 'm'
32553 * @cfg {string} yearFormat default 'Y'
32554 * @cfg {Number} labellg set the width of label (1-12)
32555 * @cfg {Number} labelmd set the width of label (1-12)
32556 * @cfg {Number} labelsm set the width of label (1-12)
32557 * @cfg {Number} labelxs set the width of label (1-12)
32561 * Create a new DateSplitField
32562 * @param {Object} config The config object
32565 Roo.bootstrap.DateSplitField = function(config){
32566 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32572 * getting the data of years
32573 * @param {Roo.bootstrap.DateSplitField} this
32574 * @param {Object} years
32579 * getting the data of days
32580 * @param {Roo.bootstrap.DateSplitField} this
32581 * @param {Object} days
32586 * Fires after the field has been marked as invalid.
32587 * @param {Roo.form.Field} this
32588 * @param {String} msg The validation message
32593 * Fires after the field has been validated with no errors.
32594 * @param {Roo.form.Field} this
32600 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
32603 labelAlign : 'top',
32605 dayAllowBlank : false,
32606 monthAllowBlank : false,
32607 yearAllowBlank : false,
32608 dayPlaceholder : '',
32609 monthPlaceholder : '',
32610 yearPlaceholder : '',
32614 isFormField : true,
32620 getAutoCreate : function()
32624 cls : 'row roo-date-split-field-group',
32629 cls : 'form-hidden-field roo-date-split-field-group-value',
32635 var labelCls = 'col-md-12';
32636 var contentCls = 'col-md-4';
32638 if(this.fieldLabel){
32642 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32646 html : this.fieldLabel
32651 if(this.labelAlign == 'left'){
32653 if(this.labelWidth > 12){
32654 label.style = "width: " + this.labelWidth + 'px';
32657 if(this.labelWidth < 13 && this.labelmd == 0){
32658 this.labelmd = this.labelWidth;
32661 if(this.labellg > 0){
32662 labelCls = ' col-lg-' + this.labellg;
32663 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32666 if(this.labelmd > 0){
32667 labelCls = ' col-md-' + this.labelmd;
32668 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32671 if(this.labelsm > 0){
32672 labelCls = ' col-sm-' + this.labelsm;
32673 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32676 if(this.labelxs > 0){
32677 labelCls = ' col-xs-' + this.labelxs;
32678 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32682 label.cls += ' ' + labelCls;
32684 cfg.cn.push(label);
32687 Roo.each(['day', 'month', 'year'], function(t){
32690 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32697 inputEl: function ()
32699 return this.el.select('.roo-date-split-field-group-value', true).first();
32702 onRender : function(ct, position)
32706 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32708 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32710 this.dayField = new Roo.bootstrap.ComboBox({
32711 allowBlank : this.dayAllowBlank,
32712 alwaysQuery : true,
32713 displayField : 'value',
32716 forceSelection : true,
32718 placeholder : this.dayPlaceholder,
32719 selectOnFocus : true,
32720 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32721 triggerAction : 'all',
32723 valueField : 'value',
32724 store : new Roo.data.SimpleStore({
32725 data : (function() {
32727 _this.fireEvent('days', _this, days);
32730 fields : [ 'value' ]
32733 select : function (_self, record, index)
32735 _this.setValue(_this.getValue());
32740 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32742 this.monthField = new Roo.bootstrap.MonthField({
32743 after : '<i class=\"fa fa-calendar\"></i>',
32744 allowBlank : this.monthAllowBlank,
32745 placeholder : this.monthPlaceholder,
32748 render : function (_self)
32750 this.el.select('span.input-group-addon', true).first().on('click', function(e){
32751 e.preventDefault();
32755 select : function (_self, oldvalue, newvalue)
32757 _this.setValue(_this.getValue());
32762 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32764 this.yearField = new Roo.bootstrap.ComboBox({
32765 allowBlank : this.yearAllowBlank,
32766 alwaysQuery : true,
32767 displayField : 'value',
32770 forceSelection : true,
32772 placeholder : this.yearPlaceholder,
32773 selectOnFocus : true,
32774 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32775 triggerAction : 'all',
32777 valueField : 'value',
32778 store : new Roo.data.SimpleStore({
32779 data : (function() {
32781 _this.fireEvent('years', _this, years);
32784 fields : [ 'value' ]
32787 select : function (_self, record, index)
32789 _this.setValue(_this.getValue());
32794 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32797 setValue : function(v, format)
32799 this.inputEl.dom.value = v;
32801 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32803 var d = Date.parseDate(v, f);
32810 this.setDay(d.format(this.dayFormat));
32811 this.setMonth(d.format(this.monthFormat));
32812 this.setYear(d.format(this.yearFormat));
32819 setDay : function(v)
32821 this.dayField.setValue(v);
32822 this.inputEl.dom.value = this.getValue();
32827 setMonth : function(v)
32829 this.monthField.setValue(v, true);
32830 this.inputEl.dom.value = this.getValue();
32835 setYear : function(v)
32837 this.yearField.setValue(v);
32838 this.inputEl.dom.value = this.getValue();
32843 getDay : function()
32845 return this.dayField.getValue();
32848 getMonth : function()
32850 return this.monthField.getValue();
32853 getYear : function()
32855 return this.yearField.getValue();
32858 getValue : function()
32860 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32862 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32872 this.inputEl.dom.value = '';
32877 validate : function()
32879 var d = this.dayField.validate();
32880 var m = this.monthField.validate();
32881 var y = this.yearField.validate();
32886 (!this.dayAllowBlank && !d) ||
32887 (!this.monthAllowBlank && !m) ||
32888 (!this.yearAllowBlank && !y)
32893 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32902 this.markInvalid();
32907 markValid : function()
32910 var label = this.el.select('label', true).first();
32911 var icon = this.el.select('i.fa-star', true).first();
32917 this.fireEvent('valid', this);
32921 * Mark this field as invalid
32922 * @param {String} msg The validation message
32924 markInvalid : function(msg)
32927 var label = this.el.select('label', true).first();
32928 var icon = this.el.select('i.fa-star', true).first();
32930 if(label && !icon){
32931 this.el.select('.roo-date-split-field-label', true).createChild({
32933 cls : 'text-danger fa fa-lg fa-star',
32934 tooltip : 'This field is required',
32935 style : 'margin-right:5px;'
32939 this.fireEvent('invalid', this, msg);
32942 clearInvalid : function()
32944 var label = this.el.select('label', true).first();
32945 var icon = this.el.select('i.fa-star', true).first();
32951 this.fireEvent('valid', this);
32954 getName: function()
32964 * http://masonry.desandro.com
32966 * The idea is to render all the bricks based on vertical width...
32968 * The original code extends 'outlayer' - we might need to use that....
32974 * @class Roo.bootstrap.LayoutMasonry
32975 * @extends Roo.bootstrap.Component
32976 * Bootstrap Layout Masonry class
32979 * Create a new Element
32980 * @param {Object} config The config object
32983 Roo.bootstrap.LayoutMasonry = function(config){
32985 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32989 Roo.bootstrap.LayoutMasonry.register(this);
32995 * Fire after layout the items
32996 * @param {Roo.bootstrap.LayoutMasonry} this
32997 * @param {Roo.EventObject} e
33004 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33007 * @cfg {Boolean} isLayoutInstant = no animation?
33009 isLayoutInstant : false, // needed?
33012 * @cfg {Number} boxWidth width of the columns
33017 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33022 * @cfg {Number} padWidth padding below box..
33027 * @cfg {Number} gutter gutter width..
33032 * @cfg {Number} maxCols maximum number of columns
33038 * @cfg {Boolean} isAutoInitial defalut true
33040 isAutoInitial : true,
33045 * @cfg {Boolean} isHorizontal defalut false
33047 isHorizontal : false,
33049 currentSize : null,
33055 bricks: null, //CompositeElement
33059 _isLayoutInited : false,
33061 // isAlternative : false, // only use for vertical layout...
33064 * @cfg {Number} alternativePadWidth padding below box..
33066 alternativePadWidth : 50,
33068 selectedBrick : [],
33070 getAutoCreate : function(){
33072 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33076 cls: 'blog-masonary-wrapper ' + this.cls,
33078 cls : 'mas-boxes masonary'
33085 getChildContainer: function( )
33087 if (this.boxesEl) {
33088 return this.boxesEl;
33091 this.boxesEl = this.el.select('.mas-boxes').first();
33093 return this.boxesEl;
33097 initEvents : function()
33101 if(this.isAutoInitial){
33102 Roo.log('hook children rendered');
33103 this.on('childrenrendered', function() {
33104 Roo.log('children rendered');
33110 initial : function()
33112 this.selectedBrick = [];
33114 this.currentSize = this.el.getBox(true);
33116 Roo.EventManager.onWindowResize(this.resize, this);
33118 if(!this.isAutoInitial){
33126 //this.layout.defer(500,this);
33130 resize : function()
33132 var cs = this.el.getBox(true);
33135 this.currentSize.width == cs.width &&
33136 this.currentSize.x == cs.x &&
33137 this.currentSize.height == cs.height &&
33138 this.currentSize.y == cs.y
33140 Roo.log("no change in with or X or Y");
33144 this.currentSize = cs;
33150 layout : function()
33152 this._resetLayout();
33154 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33156 this.layoutItems( isInstant );
33158 this._isLayoutInited = true;
33160 this.fireEvent('layout', this);
33164 _resetLayout : function()
33166 if(this.isHorizontal){
33167 this.horizontalMeasureColumns();
33171 this.verticalMeasureColumns();
33175 verticalMeasureColumns : function()
33177 this.getContainerWidth();
33179 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33180 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33184 var boxWidth = this.boxWidth + this.padWidth;
33186 if(this.containerWidth < this.boxWidth){
33187 boxWidth = this.containerWidth
33190 var containerWidth = this.containerWidth;
33192 var cols = Math.floor(containerWidth / boxWidth);
33194 this.cols = Math.max( cols, 1 );
33196 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33198 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33200 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33202 this.colWidth = boxWidth + avail - this.padWidth;
33204 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33205 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33208 horizontalMeasureColumns : function()
33210 this.getContainerWidth();
33212 var boxWidth = this.boxWidth;
33214 if(this.containerWidth < boxWidth){
33215 boxWidth = this.containerWidth;
33218 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33220 this.el.setHeight(boxWidth);
33224 getContainerWidth : function()
33226 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33229 layoutItems : function( isInstant )
33231 Roo.log(this.bricks);
33233 var items = Roo.apply([], this.bricks);
33235 if(this.isHorizontal){
33236 this._horizontalLayoutItems( items , isInstant );
33240 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33241 // this._verticalAlternativeLayoutItems( items , isInstant );
33245 this._verticalLayoutItems( items , isInstant );
33249 _verticalLayoutItems : function ( items , isInstant)
33251 if ( !items || !items.length ) {
33256 ['xs', 'xs', 'xs', 'tall'],
33257 ['xs', 'xs', 'tall'],
33258 ['xs', 'xs', 'sm'],
33259 ['xs', 'xs', 'xs'],
33265 ['sm', 'xs', 'xs'],
33269 ['tall', 'xs', 'xs', 'xs'],
33270 ['tall', 'xs', 'xs'],
33282 Roo.each(items, function(item, k){
33284 switch (item.size) {
33285 // these layouts take up a full box,
33296 boxes.push([item]);
33319 var filterPattern = function(box, length)
33327 var pattern = box.slice(0, length);
33331 Roo.each(pattern, function(i){
33332 format.push(i.size);
33335 Roo.each(standard, function(s){
33337 if(String(s) != String(format)){
33346 if(!match && length == 1){
33351 filterPattern(box, length - 1);
33355 queue.push(pattern);
33357 box = box.slice(length, box.length);
33359 filterPattern(box, 4);
33365 Roo.each(boxes, function(box, k){
33371 if(box.length == 1){
33376 filterPattern(box, 4);
33380 this._processVerticalLayoutQueue( queue, isInstant );
33384 // _verticalAlternativeLayoutItems : function( items , isInstant )
33386 // if ( !items || !items.length ) {
33390 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33394 _horizontalLayoutItems : function ( items , isInstant)
33396 if ( !items || !items.length || items.length < 3) {
33402 var eItems = items.slice(0, 3);
33404 items = items.slice(3, items.length);
33407 ['xs', 'xs', 'xs', 'wide'],
33408 ['xs', 'xs', 'wide'],
33409 ['xs', 'xs', 'sm'],
33410 ['xs', 'xs', 'xs'],
33416 ['sm', 'xs', 'xs'],
33420 ['wide', 'xs', 'xs', 'xs'],
33421 ['wide', 'xs', 'xs'],
33434 Roo.each(items, function(item, k){
33436 switch (item.size) {
33447 boxes.push([item]);
33471 var filterPattern = function(box, length)
33479 var pattern = box.slice(0, length);
33483 Roo.each(pattern, function(i){
33484 format.push(i.size);
33487 Roo.each(standard, function(s){
33489 if(String(s) != String(format)){
33498 if(!match && length == 1){
33503 filterPattern(box, length - 1);
33507 queue.push(pattern);
33509 box = box.slice(length, box.length);
33511 filterPattern(box, 4);
33517 Roo.each(boxes, function(box, k){
33523 if(box.length == 1){
33528 filterPattern(box, 4);
33535 var pos = this.el.getBox(true);
33539 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33541 var hit_end = false;
33543 Roo.each(queue, function(box){
33547 Roo.each(box, function(b){
33549 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33559 Roo.each(box, function(b){
33561 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33564 mx = Math.max(mx, b.x);
33568 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33572 Roo.each(box, function(b){
33574 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33588 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33591 /** Sets position of item in DOM
33592 * @param {Element} item
33593 * @param {Number} x - horizontal position
33594 * @param {Number} y - vertical position
33595 * @param {Boolean} isInstant - disables transitions
33597 _processVerticalLayoutQueue : function( queue, isInstant )
33599 var pos = this.el.getBox(true);
33604 for (var i = 0; i < this.cols; i++){
33608 Roo.each(queue, function(box, k){
33610 var col = k % this.cols;
33612 Roo.each(box, function(b,kk){
33614 b.el.position('absolute');
33616 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33617 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33619 if(b.size == 'md-left' || b.size == 'md-right'){
33620 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33621 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33624 b.el.setWidth(width);
33625 b.el.setHeight(height);
33627 b.el.select('iframe',true).setSize(width,height);
33631 for (var i = 0; i < this.cols; i++){
33633 if(maxY[i] < maxY[col]){
33638 col = Math.min(col, i);
33642 x = pos.x + col * (this.colWidth + this.padWidth);
33646 var positions = [];
33648 switch (box.length){
33650 positions = this.getVerticalOneBoxColPositions(x, y, box);
33653 positions = this.getVerticalTwoBoxColPositions(x, y, box);
33656 positions = this.getVerticalThreeBoxColPositions(x, y, box);
33659 positions = this.getVerticalFourBoxColPositions(x, y, box);
33665 Roo.each(box, function(b,kk){
33667 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33669 var sz = b.el.getSize();
33671 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33679 for (var i = 0; i < this.cols; i++){
33680 mY = Math.max(mY, maxY[i]);
33683 this.el.setHeight(mY - pos.y);
33687 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33689 // var pos = this.el.getBox(true);
33692 // var maxX = pos.right;
33694 // var maxHeight = 0;
33696 // Roo.each(items, function(item, k){
33700 // item.el.position('absolute');
33702 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33704 // item.el.setWidth(width);
33706 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33708 // item.el.setHeight(height);
33711 // item.el.setXY([x, y], isInstant ? false : true);
33713 // item.el.setXY([maxX - width, y], isInstant ? false : true);
33716 // y = y + height + this.alternativePadWidth;
33718 // maxHeight = maxHeight + height + this.alternativePadWidth;
33722 // this.el.setHeight(maxHeight);
33726 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33728 var pos = this.el.getBox(true);
33733 var maxX = pos.right;
33735 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33737 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33739 Roo.each(queue, function(box, k){
33741 Roo.each(box, function(b, kk){
33743 b.el.position('absolute');
33745 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33746 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33748 if(b.size == 'md-left' || b.size == 'md-right'){
33749 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33750 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33753 b.el.setWidth(width);
33754 b.el.setHeight(height);
33762 var positions = [];
33764 switch (box.length){
33766 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33769 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33772 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33775 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33781 Roo.each(box, function(b,kk){
33783 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33785 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33793 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33795 Roo.each(eItems, function(b,k){
33797 b.size = (k == 0) ? 'sm' : 'xs';
33798 b.x = (k == 0) ? 2 : 1;
33799 b.y = (k == 0) ? 2 : 1;
33801 b.el.position('absolute');
33803 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33805 b.el.setWidth(width);
33807 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33809 b.el.setHeight(height);
33813 var positions = [];
33816 x : maxX - this.unitWidth * 2 - this.gutter,
33821 x : maxX - this.unitWidth,
33822 y : minY + (this.unitWidth + this.gutter) * 2
33826 x : maxX - this.unitWidth * 3 - this.gutter * 2,
33830 Roo.each(eItems, function(b,k){
33832 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33838 getVerticalOneBoxColPositions : function(x, y, box)
33842 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33844 if(box[0].size == 'md-left'){
33848 if(box[0].size == 'md-right'){
33853 x : x + (this.unitWidth + this.gutter) * rand,
33860 getVerticalTwoBoxColPositions : function(x, y, box)
33864 if(box[0].size == 'xs'){
33868 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33872 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33886 x : x + (this.unitWidth + this.gutter) * 2,
33887 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33894 getVerticalThreeBoxColPositions : function(x, y, box)
33898 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33906 x : x + (this.unitWidth + this.gutter) * 1,
33911 x : x + (this.unitWidth + this.gutter) * 2,
33919 if(box[0].size == 'xs' && box[1].size == 'xs'){
33928 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33932 x : x + (this.unitWidth + this.gutter) * 1,
33946 x : x + (this.unitWidth + this.gutter) * 2,
33951 x : x + (this.unitWidth + this.gutter) * 2,
33952 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33959 getVerticalFourBoxColPositions : function(x, y, box)
33963 if(box[0].size == 'xs'){
33972 y : y + (this.unitHeight + this.gutter) * 1
33977 y : y + (this.unitHeight + this.gutter) * 2
33981 x : x + (this.unitWidth + this.gutter) * 1,
33995 x : x + (this.unitWidth + this.gutter) * 2,
34000 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34001 y : y + (this.unitHeight + this.gutter) * 1
34005 x : x + (this.unitWidth + this.gutter) * 2,
34006 y : y + (this.unitWidth + this.gutter) * 2
34013 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34017 if(box[0].size == 'md-left'){
34019 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34026 if(box[0].size == 'md-right'){
34028 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34029 y : minY + (this.unitWidth + this.gutter) * 1
34035 var rand = Math.floor(Math.random() * (4 - box[0].y));
34038 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34039 y : minY + (this.unitWidth + this.gutter) * rand
34046 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34050 if(box[0].size == 'xs'){
34053 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34058 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34059 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34067 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34072 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34073 y : minY + (this.unitWidth + this.gutter) * 2
34080 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34084 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34087 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34092 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34093 y : minY + (this.unitWidth + this.gutter) * 1
34097 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34098 y : minY + (this.unitWidth + this.gutter) * 2
34105 if(box[0].size == 'xs' && box[1].size == 'xs'){
34108 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34113 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34118 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34119 y : minY + (this.unitWidth + this.gutter) * 1
34127 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34132 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34133 y : minY + (this.unitWidth + this.gutter) * 2
34137 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34138 y : minY + (this.unitWidth + this.gutter) * 2
34145 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34149 if(box[0].size == 'xs'){
34152 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34157 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34162 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),
34167 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34168 y : minY + (this.unitWidth + this.gutter) * 1
34176 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34181 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34182 y : minY + (this.unitWidth + this.gutter) * 2
34186 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34187 y : minY + (this.unitWidth + this.gutter) * 2
34191 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),
34192 y : minY + (this.unitWidth + this.gutter) * 2
34200 * remove a Masonry Brick
34201 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34203 removeBrick : function(brick_id)
34209 for (var i = 0; i<this.bricks.length; i++) {
34210 if (this.bricks[i].id == brick_id) {
34211 this.bricks.splice(i,1);
34212 this.el.dom.removeChild(Roo.get(brick_id).dom);
34219 * adds a Masonry Brick
34220 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34222 addBrick : function(cfg)
34224 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34225 //this.register(cn);
34226 cn.parentId = this.id;
34227 cn.render(this.el);
34232 * register a Masonry Brick
34233 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34236 register : function(brick)
34238 this.bricks.push(brick);
34239 brick.masonryId = this.id;
34243 * clear all the Masonry Brick
34245 clearAll : function()
34248 //this.getChildContainer().dom.innerHTML = "";
34249 this.el.dom.innerHTML = '';
34252 getSelected : function()
34254 if (!this.selectedBrick) {
34258 return this.selectedBrick;
34262 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34266 * register a Masonry Layout
34267 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34270 register : function(layout)
34272 this.groups[layout.id] = layout;
34275 * fetch a Masonry Layout based on the masonry layout ID
34276 * @param {string} the masonry layout to add
34277 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34280 get: function(layout_id) {
34281 if (typeof(this.groups[layout_id]) == 'undefined') {
34284 return this.groups[layout_id] ;
34296 * http://masonry.desandro.com
34298 * The idea is to render all the bricks based on vertical width...
34300 * The original code extends 'outlayer' - we might need to use that....
34306 * @class Roo.bootstrap.LayoutMasonryAuto
34307 * @extends Roo.bootstrap.Component
34308 * Bootstrap Layout Masonry class
34311 * Create a new Element
34312 * @param {Object} config The config object
34315 Roo.bootstrap.LayoutMasonryAuto = function(config){
34316 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34319 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34322 * @cfg {Boolean} isFitWidth - resize the width..
34324 isFitWidth : false, // options..
34326 * @cfg {Boolean} isOriginLeft = left align?
34328 isOriginLeft : true,
34330 * @cfg {Boolean} isOriginTop = top align?
34332 isOriginTop : false,
34334 * @cfg {Boolean} isLayoutInstant = no animation?
34336 isLayoutInstant : false, // needed?
34338 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34340 isResizingContainer : true,
34342 * @cfg {Number} columnWidth width of the columns
34348 * @cfg {Number} maxCols maximum number of columns
34353 * @cfg {Number} padHeight padding below box..
34359 * @cfg {Boolean} isAutoInitial defalut true
34362 isAutoInitial : true,
34368 initialColumnWidth : 0,
34369 currentSize : null,
34371 colYs : null, // array.
34378 bricks: null, //CompositeElement
34379 cols : 0, // array?
34380 // element : null, // wrapped now this.el
34381 _isLayoutInited : null,
34384 getAutoCreate : function(){
34388 cls: 'blog-masonary-wrapper ' + this.cls,
34390 cls : 'mas-boxes masonary'
34397 getChildContainer: function( )
34399 if (this.boxesEl) {
34400 return this.boxesEl;
34403 this.boxesEl = this.el.select('.mas-boxes').first();
34405 return this.boxesEl;
34409 initEvents : function()
34413 if(this.isAutoInitial){
34414 Roo.log('hook children rendered');
34415 this.on('childrenrendered', function() {
34416 Roo.log('children rendered');
34423 initial : function()
34425 this.reloadItems();
34427 this.currentSize = this.el.getBox(true);
34429 /// was window resize... - let's see if this works..
34430 Roo.EventManager.onWindowResize(this.resize, this);
34432 if(!this.isAutoInitial){
34437 this.layout.defer(500,this);
34440 reloadItems: function()
34442 this.bricks = this.el.select('.masonry-brick', true);
34444 this.bricks.each(function(b) {
34445 //Roo.log(b.getSize());
34446 if (!b.attr('originalwidth')) {
34447 b.attr('originalwidth', b.getSize().width);
34452 Roo.log(this.bricks.elements.length);
34455 resize : function()
34458 var cs = this.el.getBox(true);
34460 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34461 Roo.log("no change in with or X");
34464 this.currentSize = cs;
34468 layout : function()
34471 this._resetLayout();
34472 //this._manageStamps();
34474 // don't animate first layout
34475 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34476 this.layoutItems( isInstant );
34478 // flag for initalized
34479 this._isLayoutInited = true;
34482 layoutItems : function( isInstant )
34484 //var items = this._getItemsForLayout( this.items );
34485 // original code supports filtering layout items.. we just ignore it..
34487 this._layoutItems( this.bricks , isInstant );
34489 this._postLayout();
34491 _layoutItems : function ( items , isInstant)
34493 //this.fireEvent( 'layout', this, items );
34496 if ( !items || !items.elements.length ) {
34497 // no items, emit event with empty array
34502 items.each(function(item) {
34503 Roo.log("layout item");
34505 // get x/y object from method
34506 var position = this._getItemLayoutPosition( item );
34508 position.item = item;
34509 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34510 queue.push( position );
34513 this._processLayoutQueue( queue );
34515 /** Sets position of item in DOM
34516 * @param {Element} item
34517 * @param {Number} x - horizontal position
34518 * @param {Number} y - vertical position
34519 * @param {Boolean} isInstant - disables transitions
34521 _processLayoutQueue : function( queue )
34523 for ( var i=0, len = queue.length; i < len; i++ ) {
34524 var obj = queue[i];
34525 obj.item.position('absolute');
34526 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34532 * Any logic you want to do after each layout,
34533 * i.e. size the container
34535 _postLayout : function()
34537 this.resizeContainer();
34540 resizeContainer : function()
34542 if ( !this.isResizingContainer ) {
34545 var size = this._getContainerSize();
34547 this.el.setSize(size.width,size.height);
34548 this.boxesEl.setSize(size.width,size.height);
34554 _resetLayout : function()
34556 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34557 this.colWidth = this.el.getWidth();
34558 //this.gutter = this.el.getWidth();
34560 this.measureColumns();
34566 this.colYs.push( 0 );
34572 measureColumns : function()
34574 this.getContainerWidth();
34575 // if columnWidth is 0, default to outerWidth of first item
34576 if ( !this.columnWidth ) {
34577 var firstItem = this.bricks.first();
34578 Roo.log(firstItem);
34579 this.columnWidth = this.containerWidth;
34580 if (firstItem && firstItem.attr('originalwidth') ) {
34581 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34583 // columnWidth fall back to item of first element
34584 Roo.log("set column width?");
34585 this.initialColumnWidth = this.columnWidth ;
34587 // if first elem has no width, default to size of container
34592 if (this.initialColumnWidth) {
34593 this.columnWidth = this.initialColumnWidth;
34598 // column width is fixed at the top - however if container width get's smaller we should
34601 // this bit calcs how man columns..
34603 var columnWidth = this.columnWidth += this.gutter;
34605 // calculate columns
34606 var containerWidth = this.containerWidth + this.gutter;
34608 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34609 // fix rounding errors, typically with gutters
34610 var excess = columnWidth - containerWidth % columnWidth;
34613 // if overshoot is less than a pixel, round up, otherwise floor it
34614 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34615 cols = Math[ mathMethod ]( cols );
34616 this.cols = Math.max( cols, 1 );
34617 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34619 // padding positioning..
34620 var totalColWidth = this.cols * this.columnWidth;
34621 var padavail = this.containerWidth - totalColWidth;
34622 // so for 2 columns - we need 3 'pads'
34624 var padNeeded = (1+this.cols) * this.padWidth;
34626 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34628 this.columnWidth += padExtra
34629 //this.padWidth = Math.floor(padavail / ( this.cols));
34631 // adjust colum width so that padding is fixed??
34633 // we have 3 columns ... total = width * 3
34634 // we have X left over... that should be used by
34636 //if (this.expandC) {
34644 getContainerWidth : function()
34646 /* // container is parent if fit width
34647 var container = this.isFitWidth ? this.element.parentNode : this.element;
34648 // check that this.size and size are there
34649 // IE8 triggers resize on body size change, so they might not be
34651 var size = getSize( container ); //FIXME
34652 this.containerWidth = size && size.innerWidth; //FIXME
34655 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34659 _getItemLayoutPosition : function( item ) // what is item?
34661 // we resize the item to our columnWidth..
34663 item.setWidth(this.columnWidth);
34664 item.autoBoxAdjust = false;
34666 var sz = item.getSize();
34668 // how many columns does this brick span
34669 var remainder = this.containerWidth % this.columnWidth;
34671 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34672 // round if off by 1 pixel, otherwise use ceil
34673 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
34674 colSpan = Math.min( colSpan, this.cols );
34676 // normally this should be '1' as we dont' currently allow multi width columns..
34678 var colGroup = this._getColGroup( colSpan );
34679 // get the minimum Y value from the columns
34680 var minimumY = Math.min.apply( Math, colGroup );
34681 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34683 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
34685 // position the brick
34687 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34688 y: this.currentSize.y + minimumY + this.padHeight
34692 // apply setHeight to necessary columns
34693 var setHeight = minimumY + sz.height + this.padHeight;
34694 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34696 var setSpan = this.cols + 1 - colGroup.length;
34697 for ( var i = 0; i < setSpan; i++ ) {
34698 this.colYs[ shortColIndex + i ] = setHeight ;
34705 * @param {Number} colSpan - number of columns the element spans
34706 * @returns {Array} colGroup
34708 _getColGroup : function( colSpan )
34710 if ( colSpan < 2 ) {
34711 // if brick spans only one column, use all the column Ys
34716 // how many different places could this brick fit horizontally
34717 var groupCount = this.cols + 1 - colSpan;
34718 // for each group potential horizontal position
34719 for ( var i = 0; i < groupCount; i++ ) {
34720 // make an array of colY values for that one group
34721 var groupColYs = this.colYs.slice( i, i + colSpan );
34722 // and get the max value of the array
34723 colGroup[i] = Math.max.apply( Math, groupColYs );
34728 _manageStamp : function( stamp )
34730 var stampSize = stamp.getSize();
34731 var offset = stamp.getBox();
34732 // get the columns that this stamp affects
34733 var firstX = this.isOriginLeft ? offset.x : offset.right;
34734 var lastX = firstX + stampSize.width;
34735 var firstCol = Math.floor( firstX / this.columnWidth );
34736 firstCol = Math.max( 0, firstCol );
34738 var lastCol = Math.floor( lastX / this.columnWidth );
34739 // lastCol should not go over if multiple of columnWidth #425
34740 lastCol -= lastX % this.columnWidth ? 0 : 1;
34741 lastCol = Math.min( this.cols - 1, lastCol );
34743 // set colYs to bottom of the stamp
34744 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34747 for ( var i = firstCol; i <= lastCol; i++ ) {
34748 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34753 _getContainerSize : function()
34755 this.maxY = Math.max.apply( Math, this.colYs );
34760 if ( this.isFitWidth ) {
34761 size.width = this._getContainerFitWidth();
34767 _getContainerFitWidth : function()
34769 var unusedCols = 0;
34770 // count unused columns
34773 if ( this.colYs[i] !== 0 ) {
34778 // fit container to columns that have been used
34779 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34782 needsResizeLayout : function()
34784 var previousWidth = this.containerWidth;
34785 this.getContainerWidth();
34786 return previousWidth !== this.containerWidth;
34801 * @class Roo.bootstrap.MasonryBrick
34802 * @extends Roo.bootstrap.Component
34803 * Bootstrap MasonryBrick class
34806 * Create a new MasonryBrick
34807 * @param {Object} config The config object
34810 Roo.bootstrap.MasonryBrick = function(config){
34812 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34814 Roo.bootstrap.MasonryBrick.register(this);
34820 * When a MasonryBrick is clcik
34821 * @param {Roo.bootstrap.MasonryBrick} this
34822 * @param {Roo.EventObject} e
34828 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
34831 * @cfg {String} title
34835 * @cfg {String} html
34839 * @cfg {String} bgimage
34843 * @cfg {String} videourl
34847 * @cfg {String} cls
34851 * @cfg {String} href
34855 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34860 * @cfg {String} placetitle (center|bottom)
34865 * @cfg {Boolean} isFitContainer defalut true
34867 isFitContainer : true,
34870 * @cfg {Boolean} preventDefault defalut false
34872 preventDefault : false,
34875 * @cfg {Boolean} inverse defalut false
34877 maskInverse : false,
34879 getAutoCreate : function()
34881 if(!this.isFitContainer){
34882 return this.getSplitAutoCreate();
34885 var cls = 'masonry-brick masonry-brick-full';
34887 if(this.href.length){
34888 cls += ' masonry-brick-link';
34891 if(this.bgimage.length){
34892 cls += ' masonry-brick-image';
34895 if(this.maskInverse){
34896 cls += ' mask-inverse';
34899 if(!this.html.length && !this.maskInverse && !this.videourl.length){
34900 cls += ' enable-mask';
34904 cls += ' masonry-' + this.size + '-brick';
34907 if(this.placetitle.length){
34909 switch (this.placetitle) {
34911 cls += ' masonry-center-title';
34914 cls += ' masonry-bottom-title';
34921 if(!this.html.length && !this.bgimage.length){
34922 cls += ' masonry-center-title';
34925 if(!this.html.length && this.bgimage.length){
34926 cls += ' masonry-bottom-title';
34931 cls += ' ' + this.cls;
34935 tag: (this.href.length) ? 'a' : 'div',
34940 cls: 'masonry-brick-mask'
34944 cls: 'masonry-brick-paragraph',
34950 if(this.href.length){
34951 cfg.href = this.href;
34954 var cn = cfg.cn[1].cn;
34956 if(this.title.length){
34959 cls: 'masonry-brick-title',
34964 if(this.html.length){
34967 cls: 'masonry-brick-text',
34972 if (!this.title.length && !this.html.length) {
34973 cfg.cn[1].cls += ' hide';
34976 if(this.bgimage.length){
34979 cls: 'masonry-brick-image-view',
34984 if(this.videourl.length){
34985 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34986 // youtube support only?
34989 cls: 'masonry-brick-image-view',
34992 allowfullscreen : true
35000 getSplitAutoCreate : function()
35002 var cls = 'masonry-brick masonry-brick-split';
35004 if(this.href.length){
35005 cls += ' masonry-brick-link';
35008 if(this.bgimage.length){
35009 cls += ' masonry-brick-image';
35013 cls += ' masonry-' + this.size + '-brick';
35016 switch (this.placetitle) {
35018 cls += ' masonry-center-title';
35021 cls += ' masonry-bottom-title';
35024 if(!this.bgimage.length){
35025 cls += ' masonry-center-title';
35028 if(this.bgimage.length){
35029 cls += ' masonry-bottom-title';
35035 cls += ' ' + this.cls;
35039 tag: (this.href.length) ? 'a' : 'div',
35044 cls: 'masonry-brick-split-head',
35048 cls: 'masonry-brick-paragraph',
35055 cls: 'masonry-brick-split-body',
35061 if(this.href.length){
35062 cfg.href = this.href;
35065 if(this.title.length){
35066 cfg.cn[0].cn[0].cn.push({
35068 cls: 'masonry-brick-title',
35073 if(this.html.length){
35074 cfg.cn[1].cn.push({
35076 cls: 'masonry-brick-text',
35081 if(this.bgimage.length){
35082 cfg.cn[0].cn.push({
35084 cls: 'masonry-brick-image-view',
35089 if(this.videourl.length){
35090 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35091 // youtube support only?
35092 cfg.cn[0].cn.cn.push({
35094 cls: 'masonry-brick-image-view',
35097 allowfullscreen : true
35104 initEvents: function()
35106 switch (this.size) {
35139 this.el.on('touchstart', this.onTouchStart, this);
35140 this.el.on('touchmove', this.onTouchMove, this);
35141 this.el.on('touchend', this.onTouchEnd, this);
35142 this.el.on('contextmenu', this.onContextMenu, this);
35144 this.el.on('mouseenter' ,this.enter, this);
35145 this.el.on('mouseleave', this.leave, this);
35146 this.el.on('click', this.onClick, this);
35149 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35150 this.parent().bricks.push(this);
35155 onClick: function(e, el)
35157 var time = this.endTimer - this.startTimer;
35158 // Roo.log(e.preventDefault());
35161 e.preventDefault();
35166 if(!this.preventDefault){
35170 e.preventDefault();
35172 if (this.activeClass != '') {
35173 this.selectBrick();
35176 this.fireEvent('click', this, e);
35179 enter: function(e, el)
35181 e.preventDefault();
35183 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35187 if(this.bgimage.length && this.html.length){
35188 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35192 leave: function(e, el)
35194 e.preventDefault();
35196 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35200 if(this.bgimage.length && this.html.length){
35201 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35205 onTouchStart: function(e, el)
35207 // e.preventDefault();
35209 this.touchmoved = false;
35211 if(!this.isFitContainer){
35215 if(!this.bgimage.length || !this.html.length){
35219 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35221 this.timer = new Date().getTime();
35225 onTouchMove: function(e, el)
35227 this.touchmoved = true;
35230 onContextMenu : function(e,el)
35232 e.preventDefault();
35233 e.stopPropagation();
35237 onTouchEnd: function(e, el)
35239 // e.preventDefault();
35241 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35248 if(!this.bgimage.length || !this.html.length){
35250 if(this.href.length){
35251 window.location.href = this.href;
35257 if(!this.isFitContainer){
35261 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35263 window.location.href = this.href;
35266 //selection on single brick only
35267 selectBrick : function() {
35269 if (!this.parentId) {
35273 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35274 var index = m.selectedBrick.indexOf(this.id);
35277 m.selectedBrick.splice(index,1);
35278 this.el.removeClass(this.activeClass);
35282 for(var i = 0; i < m.selectedBrick.length; i++) {
35283 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35284 b.el.removeClass(b.activeClass);
35287 m.selectedBrick = [];
35289 m.selectedBrick.push(this.id);
35290 this.el.addClass(this.activeClass);
35294 isSelected : function(){
35295 return this.el.hasClass(this.activeClass);
35300 Roo.apply(Roo.bootstrap.MasonryBrick, {
35303 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35305 * register a Masonry Brick
35306 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35309 register : function(brick)
35311 //this.groups[brick.id] = brick;
35312 this.groups.add(brick.id, brick);
35315 * fetch a masonry brick based on the masonry brick ID
35316 * @param {string} the masonry brick to add
35317 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35320 get: function(brick_id)
35322 // if (typeof(this.groups[brick_id]) == 'undefined') {
35325 // return this.groups[brick_id] ;
35327 if(this.groups.key(brick_id)) {
35328 return this.groups.key(brick_id);
35346 * @class Roo.bootstrap.Brick
35347 * @extends Roo.bootstrap.Component
35348 * Bootstrap Brick class
35351 * Create a new Brick
35352 * @param {Object} config The config object
35355 Roo.bootstrap.Brick = function(config){
35356 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35362 * When a Brick is click
35363 * @param {Roo.bootstrap.Brick} this
35364 * @param {Roo.EventObject} e
35370 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35373 * @cfg {String} title
35377 * @cfg {String} html
35381 * @cfg {String} bgimage
35385 * @cfg {String} cls
35389 * @cfg {String} href
35393 * @cfg {String} video
35397 * @cfg {Boolean} square
35401 getAutoCreate : function()
35403 var cls = 'roo-brick';
35405 if(this.href.length){
35406 cls += ' roo-brick-link';
35409 if(this.bgimage.length){
35410 cls += ' roo-brick-image';
35413 if(!this.html.length && !this.bgimage.length){
35414 cls += ' roo-brick-center-title';
35417 if(!this.html.length && this.bgimage.length){
35418 cls += ' roo-brick-bottom-title';
35422 cls += ' ' + this.cls;
35426 tag: (this.href.length) ? 'a' : 'div',
35431 cls: 'roo-brick-paragraph',
35437 if(this.href.length){
35438 cfg.href = this.href;
35441 var cn = cfg.cn[0].cn;
35443 if(this.title.length){
35446 cls: 'roo-brick-title',
35451 if(this.html.length){
35454 cls: 'roo-brick-text',
35461 if(this.bgimage.length){
35464 cls: 'roo-brick-image-view',
35472 initEvents: function()
35474 if(this.title.length || this.html.length){
35475 this.el.on('mouseenter' ,this.enter, this);
35476 this.el.on('mouseleave', this.leave, this);
35479 Roo.EventManager.onWindowResize(this.resize, this);
35481 if(this.bgimage.length){
35482 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35483 this.imageEl.on('load', this.onImageLoad, this);
35490 onImageLoad : function()
35495 resize : function()
35497 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35499 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35501 if(this.bgimage.length){
35502 var image = this.el.select('.roo-brick-image-view', true).first();
35504 image.setWidth(paragraph.getWidth());
35507 image.setHeight(paragraph.getWidth());
35510 this.el.setHeight(image.getHeight());
35511 paragraph.setHeight(image.getHeight());
35517 enter: function(e, el)
35519 e.preventDefault();
35521 if(this.bgimage.length){
35522 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35523 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35527 leave: function(e, el)
35529 e.preventDefault();
35531 if(this.bgimage.length){
35532 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35533 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35548 * @class Roo.bootstrap.NumberField
35549 * @extends Roo.bootstrap.Input
35550 * Bootstrap NumberField class
35556 * Create a new NumberField
35557 * @param {Object} config The config object
35560 Roo.bootstrap.NumberField = function(config){
35561 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35564 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35567 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35569 allowDecimals : true,
35571 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35573 decimalSeparator : ".",
35575 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35577 decimalPrecision : 2,
35579 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35581 allowNegative : true,
35584 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35588 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35590 minValue : Number.NEGATIVE_INFINITY,
35592 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35594 maxValue : Number.MAX_VALUE,
35596 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35598 minText : "The minimum value for this field is {0}",
35600 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35602 maxText : "The maximum value for this field is {0}",
35604 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
35605 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35607 nanText : "{0} is not a valid number",
35609 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35611 thousandsDelimiter : false,
35613 * @cfg {String} valueAlign alignment of value
35615 valueAlign : "left",
35617 getAutoCreate : function()
35619 var hiddenInput = {
35623 cls: 'hidden-number-input'
35627 hiddenInput.name = this.name;
35632 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35634 this.name = hiddenInput.name;
35636 if(cfg.cn.length > 0) {
35637 cfg.cn.push(hiddenInput);
35644 initEvents : function()
35646 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35648 var allowed = "0123456789";
35650 if(this.allowDecimals){
35651 allowed += this.decimalSeparator;
35654 if(this.allowNegative){
35658 if(this.thousandsDelimiter) {
35662 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35664 var keyPress = function(e){
35666 var k = e.getKey();
35668 var c = e.getCharCode();
35671 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35672 allowed.indexOf(String.fromCharCode(c)) === -1
35678 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35682 if(allowed.indexOf(String.fromCharCode(c)) === -1){
35687 this.el.on("keypress", keyPress, this);
35690 validateValue : function(value)
35693 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35697 var num = this.parseValue(value);
35700 this.markInvalid(String.format(this.nanText, value));
35704 if(num < this.minValue){
35705 this.markInvalid(String.format(this.minText, this.minValue));
35709 if(num > this.maxValue){
35710 this.markInvalid(String.format(this.maxText, this.maxValue));
35717 getValue : function()
35719 var v = this.hiddenEl().getValue();
35721 return this.fixPrecision(this.parseValue(v));
35724 parseValue : function(value)
35726 if(this.thousandsDelimiter) {
35728 r = new RegExp(",", "g");
35729 value = value.replace(r, "");
35732 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35733 return isNaN(value) ? '' : value;
35736 fixPrecision : function(value)
35738 if(this.thousandsDelimiter) {
35740 r = new RegExp(",", "g");
35741 value = value.replace(r, "");
35744 var nan = isNaN(value);
35746 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35747 return nan ? '' : value;
35749 return parseFloat(value).toFixed(this.decimalPrecision);
35752 setValue : function(v)
35754 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35760 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35762 this.inputEl().dom.value = (v == '') ? '' :
35763 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35765 if(!this.allowZero && v === '0') {
35766 this.hiddenEl().dom.value = '';
35767 this.inputEl().dom.value = '';
35774 decimalPrecisionFcn : function(v)
35776 return Math.floor(v);
35779 beforeBlur : function()
35781 var v = this.parseValue(this.getRawValue());
35783 if(v || v === 0 || v === ''){
35788 hiddenEl : function()
35790 return this.el.select('input.hidden-number-input',true).first();
35802 * @class Roo.bootstrap.DocumentSlider
35803 * @extends Roo.bootstrap.Component
35804 * Bootstrap DocumentSlider class
35807 * Create a new DocumentViewer
35808 * @param {Object} config The config object
35811 Roo.bootstrap.DocumentSlider = function(config){
35812 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35819 * Fire after initEvent
35820 * @param {Roo.bootstrap.DocumentSlider} this
35825 * Fire after update
35826 * @param {Roo.bootstrap.DocumentSlider} this
35832 * @param {Roo.bootstrap.DocumentSlider} this
35838 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
35844 getAutoCreate : function()
35848 cls : 'roo-document-slider',
35852 cls : 'roo-document-slider-header',
35856 cls : 'roo-document-slider-header-title'
35862 cls : 'roo-document-slider-body',
35866 cls : 'roo-document-slider-prev',
35870 cls : 'fa fa-chevron-left'
35876 cls : 'roo-document-slider-thumb',
35880 cls : 'roo-document-slider-image'
35886 cls : 'roo-document-slider-next',
35890 cls : 'fa fa-chevron-right'
35902 initEvents : function()
35904 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35905 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35907 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35908 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35910 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35911 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35913 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35914 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35916 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35917 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35919 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35920 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35922 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35923 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35925 this.thumbEl.on('click', this.onClick, this);
35927 this.prevIndicator.on('click', this.prev, this);
35929 this.nextIndicator.on('click', this.next, this);
35933 initial : function()
35935 if(this.files.length){
35936 this.indicator = 1;
35940 this.fireEvent('initial', this);
35943 update : function()
35945 this.imageEl.attr('src', this.files[this.indicator - 1]);
35947 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35949 this.prevIndicator.show();
35951 if(this.indicator == 1){
35952 this.prevIndicator.hide();
35955 this.nextIndicator.show();
35957 if(this.indicator == this.files.length){
35958 this.nextIndicator.hide();
35961 this.thumbEl.scrollTo('top');
35963 this.fireEvent('update', this);
35966 onClick : function(e)
35968 e.preventDefault();
35970 this.fireEvent('click', this);
35975 e.preventDefault();
35977 this.indicator = Math.max(1, this.indicator - 1);
35984 e.preventDefault();
35986 this.indicator = Math.min(this.files.length, this.indicator + 1);
36000 * @class Roo.bootstrap.RadioSet
36001 * @extends Roo.bootstrap.Input
36002 * Bootstrap RadioSet class
36003 * @cfg {String} indicatorpos (left|right) default left
36004 * @cfg {Boolean} inline (true|false) inline the element (default true)
36005 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36007 * Create a new RadioSet
36008 * @param {Object} config The config object
36011 Roo.bootstrap.RadioSet = function(config){
36013 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36017 Roo.bootstrap.RadioSet.register(this);
36022 * Fires when the element is checked or unchecked.
36023 * @param {Roo.bootstrap.RadioSet} this This radio
36024 * @param {Roo.bootstrap.Radio} item The checked item
36029 * Fires when the element is click.
36030 * @param {Roo.bootstrap.RadioSet} this This radio set
36031 * @param {Roo.bootstrap.Radio} item The checked item
36032 * @param {Roo.EventObject} e The event object
36039 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36047 indicatorpos : 'left',
36049 getAutoCreate : function()
36053 cls : 'roo-radio-set-label',
36057 html : this.fieldLabel
36061 if (Roo.bootstrap.version == 3) {
36064 if(this.indicatorpos == 'left'){
36067 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36068 tooltip : 'This field is required'
36073 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36074 tooltip : 'This field is required'
36080 cls : 'roo-radio-set-items'
36083 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36085 if (align === 'left' && this.fieldLabel.length) {
36088 cls : "roo-radio-set-right",
36094 if(this.labelWidth > 12){
36095 label.style = "width: " + this.labelWidth + 'px';
36098 if(this.labelWidth < 13 && this.labelmd == 0){
36099 this.labelmd = this.labelWidth;
36102 if(this.labellg > 0){
36103 label.cls += ' col-lg-' + this.labellg;
36104 items.cls += ' col-lg-' + (12 - this.labellg);
36107 if(this.labelmd > 0){
36108 label.cls += ' col-md-' + this.labelmd;
36109 items.cls += ' col-md-' + (12 - this.labelmd);
36112 if(this.labelsm > 0){
36113 label.cls += ' col-sm-' + this.labelsm;
36114 items.cls += ' col-sm-' + (12 - this.labelsm);
36117 if(this.labelxs > 0){
36118 label.cls += ' col-xs-' + this.labelxs;
36119 items.cls += ' col-xs-' + (12 - this.labelxs);
36125 cls : 'roo-radio-set',
36129 cls : 'roo-radio-set-input',
36132 value : this.value ? this.value : ''
36139 if(this.weight.length){
36140 cfg.cls += ' roo-radio-' + this.weight;
36144 cfg.cls += ' roo-radio-set-inline';
36148 ['xs','sm','md','lg'].map(function(size){
36149 if (settings[size]) {
36150 cfg.cls += ' col-' + size + '-' + settings[size];
36158 initEvents : function()
36160 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36161 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36163 if(!this.fieldLabel.length){
36164 this.labelEl.hide();
36167 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36168 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36170 this.indicator = this.indicatorEl();
36172 if(this.indicator){
36173 this.indicator.addClass('invisible');
36176 this.originalValue = this.getValue();
36180 inputEl: function ()
36182 return this.el.select('.roo-radio-set-input', true).first();
36185 getChildContainer : function()
36187 return this.itemsEl;
36190 register : function(item)
36192 this.radioes.push(item);
36196 validate : function()
36198 if(this.getVisibilityEl().hasClass('hidden')){
36204 Roo.each(this.radioes, function(i){
36213 if(this.allowBlank) {
36217 if(this.disabled || valid){
36222 this.markInvalid();
36227 markValid : function()
36229 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36230 this.indicatorEl().removeClass('visible');
36231 this.indicatorEl().addClass('invisible');
36235 if (Roo.bootstrap.version == 3) {
36236 this.el.removeClass([this.invalidClass, this.validClass]);
36237 this.el.addClass(this.validClass);
36239 this.el.removeClass(['is-invalid','is-valid']);
36240 this.el.addClass(['is-valid']);
36242 this.fireEvent('valid', this);
36245 markInvalid : function(msg)
36247 if(this.allowBlank || this.disabled){
36251 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36252 this.indicatorEl().removeClass('invisible');
36253 this.indicatorEl().addClass('visible');
36255 if (Roo.bootstrap.version == 3) {
36256 this.el.removeClass([this.invalidClass, this.validClass]);
36257 this.el.addClass(this.invalidClass);
36259 this.el.removeClass(['is-invalid','is-valid']);
36260 this.el.addClass(['is-invalid']);
36263 this.fireEvent('invalid', this, msg);
36267 setValue : function(v, suppressEvent)
36269 if(this.value === v){
36276 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36279 Roo.each(this.radioes, function(i){
36281 i.el.removeClass('checked');
36284 Roo.each(this.radioes, function(i){
36286 if(i.value === v || i.value.toString() === v.toString()){
36288 i.el.addClass('checked');
36290 if(suppressEvent !== true){
36291 this.fireEvent('check', this, i);
36302 clearInvalid : function(){
36304 if(!this.el || this.preventMark){
36308 this.el.removeClass([this.invalidClass]);
36310 this.fireEvent('valid', this);
36315 Roo.apply(Roo.bootstrap.RadioSet, {
36319 register : function(set)
36321 this.groups[set.name] = set;
36324 get: function(name)
36326 if (typeof(this.groups[name]) == 'undefined') {
36330 return this.groups[name] ;
36336 * Ext JS Library 1.1.1
36337 * Copyright(c) 2006-2007, Ext JS, LLC.
36339 * Originally Released Under LGPL - original licence link has changed is not relivant.
36342 * <script type="text/javascript">
36347 * @class Roo.bootstrap.SplitBar
36348 * @extends Roo.util.Observable
36349 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36353 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36354 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36355 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36356 split.minSize = 100;
36357 split.maxSize = 600;
36358 split.animate = true;
36359 split.on('moved', splitterMoved);
36362 * Create a new SplitBar
36363 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36364 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36365 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36366 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36367 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36368 position of the SplitBar).
36370 Roo.bootstrap.SplitBar = function(cfg){
36375 // dragElement : elm
36376 // resizingElement: el,
36378 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36379 // placement : Roo.bootstrap.SplitBar.LEFT ,
36380 // existingProxy ???
36383 this.el = Roo.get(cfg.dragElement, true);
36384 this.el.dom.unselectable = "on";
36386 this.resizingEl = Roo.get(cfg.resizingElement, true);
36390 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36391 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36394 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36397 * The minimum size of the resizing element. (Defaults to 0)
36403 * The maximum size of the resizing element. (Defaults to 2000)
36406 this.maxSize = 2000;
36409 * Whether to animate the transition to the new size
36412 this.animate = false;
36415 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36418 this.useShim = false;
36423 if(!cfg.existingProxy){
36425 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36427 this.proxy = Roo.get(cfg.existingProxy).dom;
36430 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36433 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36436 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36439 this.dragSpecs = {};
36442 * @private The adapter to use to positon and resize elements
36444 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36445 this.adapter.init(this);
36447 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36449 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36450 this.el.addClass("roo-splitbar-h");
36453 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36454 this.el.addClass("roo-splitbar-v");
36460 * Fires when the splitter is moved (alias for {@link #event-moved})
36461 * @param {Roo.bootstrap.SplitBar} this
36462 * @param {Number} newSize the new width or height
36467 * Fires when the splitter is moved
36468 * @param {Roo.bootstrap.SplitBar} this
36469 * @param {Number} newSize the new width or height
36473 * @event beforeresize
36474 * Fires before the splitter is dragged
36475 * @param {Roo.bootstrap.SplitBar} this
36477 "beforeresize" : true,
36479 "beforeapply" : true
36482 Roo.util.Observable.call(this);
36485 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36486 onStartProxyDrag : function(x, y){
36487 this.fireEvent("beforeresize", this);
36489 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36491 o.enableDisplayMode("block");
36492 // all splitbars share the same overlay
36493 Roo.bootstrap.SplitBar.prototype.overlay = o;
36495 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36496 this.overlay.show();
36497 Roo.get(this.proxy).setDisplayed("block");
36498 var size = this.adapter.getElementSize(this);
36499 this.activeMinSize = this.getMinimumSize();;
36500 this.activeMaxSize = this.getMaximumSize();;
36501 var c1 = size - this.activeMinSize;
36502 var c2 = Math.max(this.activeMaxSize - size, 0);
36503 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36504 this.dd.resetConstraints();
36505 this.dd.setXConstraint(
36506 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36507 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36509 this.dd.setYConstraint(0, 0);
36511 this.dd.resetConstraints();
36512 this.dd.setXConstraint(0, 0);
36513 this.dd.setYConstraint(
36514 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36515 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36518 this.dragSpecs.startSize = size;
36519 this.dragSpecs.startPoint = [x, y];
36520 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36524 * @private Called after the drag operation by the DDProxy
36526 onEndProxyDrag : function(e){
36527 Roo.get(this.proxy).setDisplayed(false);
36528 var endPoint = Roo.lib.Event.getXY(e);
36530 this.overlay.hide();
36533 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36534 newSize = this.dragSpecs.startSize +
36535 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36536 endPoint[0] - this.dragSpecs.startPoint[0] :
36537 this.dragSpecs.startPoint[0] - endPoint[0]
36540 newSize = this.dragSpecs.startSize +
36541 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36542 endPoint[1] - this.dragSpecs.startPoint[1] :
36543 this.dragSpecs.startPoint[1] - endPoint[1]
36546 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36547 if(newSize != this.dragSpecs.startSize){
36548 if(this.fireEvent('beforeapply', this, newSize) !== false){
36549 this.adapter.setElementSize(this, newSize);
36550 this.fireEvent("moved", this, newSize);
36551 this.fireEvent("resize", this, newSize);
36557 * Get the adapter this SplitBar uses
36558 * @return The adapter object
36560 getAdapter : function(){
36561 return this.adapter;
36565 * Set the adapter this SplitBar uses
36566 * @param {Object} adapter A SplitBar adapter object
36568 setAdapter : function(adapter){
36569 this.adapter = adapter;
36570 this.adapter.init(this);
36574 * Gets the minimum size for the resizing element
36575 * @return {Number} The minimum size
36577 getMinimumSize : function(){
36578 return this.minSize;
36582 * Sets the minimum size for the resizing element
36583 * @param {Number} minSize The minimum size
36585 setMinimumSize : function(minSize){
36586 this.minSize = minSize;
36590 * Gets the maximum size for the resizing element
36591 * @return {Number} The maximum size
36593 getMaximumSize : function(){
36594 return this.maxSize;
36598 * Sets the maximum size for the resizing element
36599 * @param {Number} maxSize The maximum size
36601 setMaximumSize : function(maxSize){
36602 this.maxSize = maxSize;
36606 * Sets the initialize size for the resizing element
36607 * @param {Number} size The initial size
36609 setCurrentSize : function(size){
36610 var oldAnimate = this.animate;
36611 this.animate = false;
36612 this.adapter.setElementSize(this, size);
36613 this.animate = oldAnimate;
36617 * Destroy this splitbar.
36618 * @param {Boolean} removeEl True to remove the element
36620 destroy : function(removeEl){
36622 this.shim.remove();
36625 this.proxy.parentNode.removeChild(this.proxy);
36633 * @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.
36635 Roo.bootstrap.SplitBar.createProxy = function(dir){
36636 var proxy = new Roo.Element(document.createElement("div"));
36637 proxy.unselectable();
36638 var cls = 'roo-splitbar-proxy';
36639 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36640 document.body.appendChild(proxy.dom);
36645 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36646 * Default Adapter. It assumes the splitter and resizing element are not positioned
36647 * elements and only gets/sets the width of the element. Generally used for table based layouts.
36649 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36652 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36653 // do nothing for now
36654 init : function(s){
36658 * Called before drag operations to get the current size of the resizing element.
36659 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36661 getElementSize : function(s){
36662 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36663 return s.resizingEl.getWidth();
36665 return s.resizingEl.getHeight();
36670 * Called after drag operations to set the size of the resizing element.
36671 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36672 * @param {Number} newSize The new size to set
36673 * @param {Function} onComplete A function to be invoked when resizing is complete
36675 setElementSize : function(s, newSize, onComplete){
36676 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36678 s.resizingEl.setWidth(newSize);
36680 onComplete(s, newSize);
36683 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36688 s.resizingEl.setHeight(newSize);
36690 onComplete(s, newSize);
36693 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36700 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36701 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36702 * Adapter that moves the splitter element to align with the resized sizing element.
36703 * Used with an absolute positioned SplitBar.
36704 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36705 * document.body, make sure you assign an id to the body element.
36707 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36708 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36709 this.container = Roo.get(container);
36712 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36713 init : function(s){
36714 this.basic.init(s);
36717 getElementSize : function(s){
36718 return this.basic.getElementSize(s);
36721 setElementSize : function(s, newSize, onComplete){
36722 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36725 moveSplitter : function(s){
36726 var yes = Roo.bootstrap.SplitBar;
36727 switch(s.placement){
36729 s.el.setX(s.resizingEl.getRight());
36732 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36735 s.el.setY(s.resizingEl.getBottom());
36738 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36745 * Orientation constant - Create a vertical SplitBar
36749 Roo.bootstrap.SplitBar.VERTICAL = 1;
36752 * Orientation constant - Create a horizontal SplitBar
36756 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36759 * Placement constant - The resizing element is to the left of the splitter element
36763 Roo.bootstrap.SplitBar.LEFT = 1;
36766 * Placement constant - The resizing element is to the right of the splitter element
36770 Roo.bootstrap.SplitBar.RIGHT = 2;
36773 * Placement constant - The resizing element is positioned above the splitter element
36777 Roo.bootstrap.SplitBar.TOP = 3;
36780 * Placement constant - The resizing element is positioned under splitter element
36784 Roo.bootstrap.SplitBar.BOTTOM = 4;
36785 Roo.namespace("Roo.bootstrap.layout");/*
36787 * Ext JS Library 1.1.1
36788 * Copyright(c) 2006-2007, Ext JS, LLC.
36790 * Originally Released Under LGPL - original licence link has changed is not relivant.
36793 * <script type="text/javascript">
36797 * @class Roo.bootstrap.layout.Manager
36798 * @extends Roo.bootstrap.Component
36799 * Base class for layout managers.
36801 Roo.bootstrap.layout.Manager = function(config)
36803 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36809 /** false to disable window resize monitoring @type Boolean */
36810 this.monitorWindowResize = true;
36815 * Fires when a layout is performed.
36816 * @param {Roo.LayoutManager} this
36820 * @event regionresized
36821 * Fires when the user resizes a region.
36822 * @param {Roo.LayoutRegion} region The resized region
36823 * @param {Number} newSize The new size (width for east/west, height for north/south)
36825 "regionresized" : true,
36827 * @event regioncollapsed
36828 * Fires when a region is collapsed.
36829 * @param {Roo.LayoutRegion} region The collapsed region
36831 "regioncollapsed" : true,
36833 * @event regionexpanded
36834 * Fires when a region is expanded.
36835 * @param {Roo.LayoutRegion} region The expanded region
36837 "regionexpanded" : true
36839 this.updating = false;
36842 this.el = Roo.get(config.el);
36848 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36853 monitorWindowResize : true,
36859 onRender : function(ct, position)
36862 this.el = Roo.get(ct);
36865 //this.fireEvent('render',this);
36869 initEvents: function()
36873 // ie scrollbar fix
36874 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36875 document.body.scroll = "no";
36876 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36877 this.el.position('relative');
36879 this.id = this.el.id;
36880 this.el.addClass("roo-layout-container");
36881 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36882 if(this.el.dom != document.body ) {
36883 this.el.on('resize', this.layout,this);
36884 this.el.on('show', this.layout,this);
36890 * Returns true if this layout is currently being updated
36891 * @return {Boolean}
36893 isUpdating : function(){
36894 return this.updating;
36898 * Suspend the LayoutManager from doing auto-layouts while
36899 * making multiple add or remove calls
36901 beginUpdate : function(){
36902 this.updating = true;
36906 * Restore auto-layouts and optionally disable the manager from performing a layout
36907 * @param {Boolean} noLayout true to disable a layout update
36909 endUpdate : function(noLayout){
36910 this.updating = false;
36916 layout: function(){
36920 onRegionResized : function(region, newSize){
36921 this.fireEvent("regionresized", region, newSize);
36925 onRegionCollapsed : function(region){
36926 this.fireEvent("regioncollapsed", region);
36929 onRegionExpanded : function(region){
36930 this.fireEvent("regionexpanded", region);
36934 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36935 * performs box-model adjustments.
36936 * @return {Object} The size as an object {width: (the width), height: (the height)}
36938 getViewSize : function()
36941 if(this.el.dom != document.body){
36942 size = this.el.getSize();
36944 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36946 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36947 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36952 * Returns the Element this layout is bound to.
36953 * @return {Roo.Element}
36955 getEl : function(){
36960 * Returns the specified region.
36961 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36962 * @return {Roo.LayoutRegion}
36964 getRegion : function(target){
36965 return this.regions[target.toLowerCase()];
36968 onWindowResize : function(){
36969 if(this.monitorWindowResize){
36976 * Ext JS Library 1.1.1
36977 * Copyright(c) 2006-2007, Ext JS, LLC.
36979 * Originally Released Under LGPL - original licence link has changed is not relivant.
36982 * <script type="text/javascript">
36985 * @class Roo.bootstrap.layout.Border
36986 * @extends Roo.bootstrap.layout.Manager
36987 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36988 * please see: examples/bootstrap/nested.html<br><br>
36990 <b>The container the layout is rendered into can be either the body element or any other element.
36991 If it is not the body element, the container needs to either be an absolute positioned element,
36992 or you will need to add "position:relative" to the css of the container. You will also need to specify
36993 the container size if it is not the body element.</b>
36996 * Create a new Border
36997 * @param {Object} config Configuration options
36999 Roo.bootstrap.layout.Border = function(config){
37000 config = config || {};
37001 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37005 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37006 if(config[region]){
37007 config[region].region = region;
37008 this.addRegion(config[region]);
37014 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
37016 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37018 parent : false, // this might point to a 'nest' or a ???
37021 * Creates and adds a new region if it doesn't already exist.
37022 * @param {String} target The target region key (north, south, east, west or center).
37023 * @param {Object} config The regions config object
37024 * @return {BorderLayoutRegion} The new region
37026 addRegion : function(config)
37028 if(!this.regions[config.region]){
37029 var r = this.factory(config);
37030 this.bindRegion(r);
37032 return this.regions[config.region];
37036 bindRegion : function(r){
37037 this.regions[r.config.region] = r;
37039 r.on("visibilitychange", this.layout, this);
37040 r.on("paneladded", this.layout, this);
37041 r.on("panelremoved", this.layout, this);
37042 r.on("invalidated", this.layout, this);
37043 r.on("resized", this.onRegionResized, this);
37044 r.on("collapsed", this.onRegionCollapsed, this);
37045 r.on("expanded", this.onRegionExpanded, this);
37049 * Performs a layout update.
37051 layout : function()
37053 if(this.updating) {
37057 // render all the rebions if they have not been done alreayd?
37058 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37059 if(this.regions[region] && !this.regions[region].bodyEl){
37060 this.regions[region].onRender(this.el)
37064 var size = this.getViewSize();
37065 var w = size.width;
37066 var h = size.height;
37071 //var x = 0, y = 0;
37073 var rs = this.regions;
37074 var north = rs["north"];
37075 var south = rs["south"];
37076 var west = rs["west"];
37077 var east = rs["east"];
37078 var center = rs["center"];
37079 //if(this.hideOnLayout){ // not supported anymore
37080 //c.el.setStyle("display", "none");
37082 if(north && north.isVisible()){
37083 var b = north.getBox();
37084 var m = north.getMargins();
37085 b.width = w - (m.left+m.right);
37088 centerY = b.height + b.y + m.bottom;
37089 centerH -= centerY;
37090 north.updateBox(this.safeBox(b));
37092 if(south && south.isVisible()){
37093 var b = south.getBox();
37094 var m = south.getMargins();
37095 b.width = w - (m.left+m.right);
37097 var totalHeight = (b.height + m.top + m.bottom);
37098 b.y = h - totalHeight + m.top;
37099 centerH -= totalHeight;
37100 south.updateBox(this.safeBox(b));
37102 if(west && west.isVisible()){
37103 var b = west.getBox();
37104 var m = west.getMargins();
37105 b.height = centerH - (m.top+m.bottom);
37107 b.y = centerY + m.top;
37108 var totalWidth = (b.width + m.left + m.right);
37109 centerX += totalWidth;
37110 centerW -= totalWidth;
37111 west.updateBox(this.safeBox(b));
37113 if(east && east.isVisible()){
37114 var b = east.getBox();
37115 var m = east.getMargins();
37116 b.height = centerH - (m.top+m.bottom);
37117 var totalWidth = (b.width + m.left + m.right);
37118 b.x = w - totalWidth + m.left;
37119 b.y = centerY + m.top;
37120 centerW -= totalWidth;
37121 east.updateBox(this.safeBox(b));
37124 var m = center.getMargins();
37126 x: centerX + m.left,
37127 y: centerY + m.top,
37128 width: centerW - (m.left+m.right),
37129 height: centerH - (m.top+m.bottom)
37131 //if(this.hideOnLayout){
37132 //center.el.setStyle("display", "block");
37134 center.updateBox(this.safeBox(centerBox));
37137 this.fireEvent("layout", this);
37141 safeBox : function(box){
37142 box.width = Math.max(0, box.width);
37143 box.height = Math.max(0, box.height);
37148 * Adds a ContentPanel (or subclass) to this layout.
37149 * @param {String} target The target region key (north, south, east, west or center).
37150 * @param {Roo.ContentPanel} panel The panel to add
37151 * @return {Roo.ContentPanel} The added panel
37153 add : function(target, panel){
37155 target = target.toLowerCase();
37156 return this.regions[target].add(panel);
37160 * Remove a ContentPanel (or subclass) to this layout.
37161 * @param {String} target The target region key (north, south, east, west or center).
37162 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37163 * @return {Roo.ContentPanel} The removed panel
37165 remove : function(target, panel){
37166 target = target.toLowerCase();
37167 return this.regions[target].remove(panel);
37171 * Searches all regions for a panel with the specified id
37172 * @param {String} panelId
37173 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37175 findPanel : function(panelId){
37176 var rs = this.regions;
37177 for(var target in rs){
37178 if(typeof rs[target] != "function"){
37179 var p = rs[target].getPanel(panelId);
37189 * Searches all regions for a panel with the specified id and activates (shows) it.
37190 * @param {String/ContentPanel} panelId The panels id or the panel itself
37191 * @return {Roo.ContentPanel} The shown panel or null
37193 showPanel : function(panelId) {
37194 var rs = this.regions;
37195 for(var target in rs){
37196 var r = rs[target];
37197 if(typeof r != "function"){
37198 if(r.hasPanel(panelId)){
37199 return r.showPanel(panelId);
37207 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37208 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37211 restoreState : function(provider){
37213 provider = Roo.state.Manager;
37215 var sm = new Roo.LayoutStateManager();
37216 sm.init(this, provider);
37222 * Adds a xtype elements to the layout.
37226 xtype : 'ContentPanel',
37233 xtype : 'NestedLayoutPanel',
37239 items : [ ... list of content panels or nested layout panels.. ]
37243 * @param {Object} cfg Xtype definition of item to add.
37245 addxtype : function(cfg)
37247 // basically accepts a pannel...
37248 // can accept a layout region..!?!?
37249 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37252 // theory? children can only be panels??
37254 //if (!cfg.xtype.match(/Panel$/)) {
37259 if (typeof(cfg.region) == 'undefined') {
37260 Roo.log("Failed to add Panel, region was not set");
37264 var region = cfg.region;
37270 xitems = cfg.items;
37275 if ( region == 'center') {
37276 Roo.log("Center: " + cfg.title);
37282 case 'Content': // ContentPanel (el, cfg)
37283 case 'Scroll': // ContentPanel (el, cfg)
37285 cfg.autoCreate = cfg.autoCreate || true;
37286 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37288 // var el = this.el.createChild();
37289 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37292 this.add(region, ret);
37296 case 'TreePanel': // our new panel!
37297 cfg.el = this.el.createChild();
37298 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37299 this.add(region, ret);
37304 // create a new Layout (which is a Border Layout...
37306 var clayout = cfg.layout;
37307 clayout.el = this.el.createChild();
37308 clayout.items = clayout.items || [];
37312 // replace this exitems with the clayout ones..
37313 xitems = clayout.items;
37315 // force background off if it's in center...
37316 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37317 cfg.background = false;
37319 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37322 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37323 //console.log('adding nested layout panel ' + cfg.toSource());
37324 this.add(region, ret);
37325 nb = {}; /// find first...
37330 // needs grid and region
37332 //var el = this.getRegion(region).el.createChild();
37334 *var el = this.el.createChild();
37335 // create the grid first...
37336 cfg.grid.container = el;
37337 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37340 if (region == 'center' && this.active ) {
37341 cfg.background = false;
37344 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37346 this.add(region, ret);
37348 if (cfg.background) {
37349 // render grid on panel activation (if panel background)
37350 ret.on('activate', function(gp) {
37351 if (!gp.grid.rendered) {
37352 // gp.grid.render(el);
37356 // cfg.grid.render(el);
37362 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37363 // it was the old xcomponent building that caused this before.
37364 // espeically if border is the top element in the tree.
37374 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37376 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37377 this.add(region, ret);
37381 throw "Can not add '" + cfg.xtype + "' to Border";
37387 this.beginUpdate();
37391 Roo.each(xitems, function(i) {
37392 region = nb && i.region ? i.region : false;
37394 var add = ret.addxtype(i);
37397 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37398 if (!i.background) {
37399 abn[region] = nb[region] ;
37406 // make the last non-background panel active..
37407 //if (nb) { Roo.log(abn); }
37410 for(var r in abn) {
37411 region = this.getRegion(r);
37413 // tried using nb[r], but it does not work..
37415 region.showPanel(abn[r]);
37426 factory : function(cfg)
37429 var validRegions = Roo.bootstrap.layout.Border.regions;
37431 var target = cfg.region;
37434 var r = Roo.bootstrap.layout;
37438 return new r.North(cfg);
37440 return new r.South(cfg);
37442 return new r.East(cfg);
37444 return new r.West(cfg);
37446 return new r.Center(cfg);
37448 throw 'Layout region "'+target+'" not supported.';
37455 * Ext JS Library 1.1.1
37456 * Copyright(c) 2006-2007, Ext JS, LLC.
37458 * Originally Released Under LGPL - original licence link has changed is not relivant.
37461 * <script type="text/javascript">
37465 * @class Roo.bootstrap.layout.Basic
37466 * @extends Roo.util.Observable
37467 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37468 * and does not have a titlebar, tabs or any other features. All it does is size and position
37469 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37470 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37471 * @cfg {string} region the region that it inhabits..
37472 * @cfg {bool} skipConfig skip config?
37476 Roo.bootstrap.layout.Basic = function(config){
37478 this.mgr = config.mgr;
37480 this.position = config.region;
37482 var skipConfig = config.skipConfig;
37486 * @scope Roo.BasicLayoutRegion
37490 * @event beforeremove
37491 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37492 * @param {Roo.LayoutRegion} this
37493 * @param {Roo.ContentPanel} panel The panel
37494 * @param {Object} e The cancel event object
37496 "beforeremove" : true,
37498 * @event invalidated
37499 * Fires when the layout for this region is changed.
37500 * @param {Roo.LayoutRegion} this
37502 "invalidated" : true,
37504 * @event visibilitychange
37505 * Fires when this region is shown or hidden
37506 * @param {Roo.LayoutRegion} this
37507 * @param {Boolean} visibility true or false
37509 "visibilitychange" : true,
37511 * @event paneladded
37512 * Fires when a panel is added.
37513 * @param {Roo.LayoutRegion} this
37514 * @param {Roo.ContentPanel} panel The panel
37516 "paneladded" : true,
37518 * @event panelremoved
37519 * Fires when a panel is removed.
37520 * @param {Roo.LayoutRegion} this
37521 * @param {Roo.ContentPanel} panel The panel
37523 "panelremoved" : true,
37525 * @event beforecollapse
37526 * Fires when this region before collapse.
37527 * @param {Roo.LayoutRegion} this
37529 "beforecollapse" : true,
37532 * Fires when this region is collapsed.
37533 * @param {Roo.LayoutRegion} this
37535 "collapsed" : true,
37538 * Fires when this region is expanded.
37539 * @param {Roo.LayoutRegion} this
37544 * Fires when this region is slid into view.
37545 * @param {Roo.LayoutRegion} this
37547 "slideshow" : true,
37550 * Fires when this region slides out of view.
37551 * @param {Roo.LayoutRegion} this
37553 "slidehide" : true,
37555 * @event panelactivated
37556 * Fires when a panel is activated.
37557 * @param {Roo.LayoutRegion} this
37558 * @param {Roo.ContentPanel} panel The activated panel
37560 "panelactivated" : true,
37563 * Fires when the user resizes this region.
37564 * @param {Roo.LayoutRegion} this
37565 * @param {Number} newSize The new size (width for east/west, height for north/south)
37569 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37570 this.panels = new Roo.util.MixedCollection();
37571 this.panels.getKey = this.getPanelId.createDelegate(this);
37573 this.activePanel = null;
37574 // ensure listeners are added...
37576 if (config.listeners || config.events) {
37577 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37578 listeners : config.listeners || {},
37579 events : config.events || {}
37583 if(skipConfig !== true){
37584 this.applyConfig(config);
37588 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37590 getPanelId : function(p){
37594 applyConfig : function(config){
37595 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37596 this.config = config;
37601 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
37602 * the width, for horizontal (north, south) the height.
37603 * @param {Number} newSize The new width or height
37605 resizeTo : function(newSize){
37606 var el = this.el ? this.el :
37607 (this.activePanel ? this.activePanel.getEl() : null);
37609 switch(this.position){
37612 el.setWidth(newSize);
37613 this.fireEvent("resized", this, newSize);
37617 el.setHeight(newSize);
37618 this.fireEvent("resized", this, newSize);
37624 getBox : function(){
37625 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37628 getMargins : function(){
37629 return this.margins;
37632 updateBox : function(box){
37634 var el = this.activePanel.getEl();
37635 el.dom.style.left = box.x + "px";
37636 el.dom.style.top = box.y + "px";
37637 this.activePanel.setSize(box.width, box.height);
37641 * Returns the container element for this region.
37642 * @return {Roo.Element}
37644 getEl : function(){
37645 return this.activePanel;
37649 * Returns true if this region is currently visible.
37650 * @return {Boolean}
37652 isVisible : function(){
37653 return this.activePanel ? true : false;
37656 setActivePanel : function(panel){
37657 panel = this.getPanel(panel);
37658 if(this.activePanel && this.activePanel != panel){
37659 this.activePanel.setActiveState(false);
37660 this.activePanel.getEl().setLeftTop(-10000,-10000);
37662 this.activePanel = panel;
37663 panel.setActiveState(true);
37665 panel.setSize(this.box.width, this.box.height);
37667 this.fireEvent("panelactivated", this, panel);
37668 this.fireEvent("invalidated");
37672 * Show the specified panel.
37673 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37674 * @return {Roo.ContentPanel} The shown panel or null
37676 showPanel : function(panel){
37677 panel = this.getPanel(panel);
37679 this.setActivePanel(panel);
37685 * Get the active panel for this region.
37686 * @return {Roo.ContentPanel} The active panel or null
37688 getActivePanel : function(){
37689 return this.activePanel;
37693 * Add the passed ContentPanel(s)
37694 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37695 * @return {Roo.ContentPanel} The panel added (if only one was added)
37697 add : function(panel){
37698 if(arguments.length > 1){
37699 for(var i = 0, len = arguments.length; i < len; i++) {
37700 this.add(arguments[i]);
37704 if(this.hasPanel(panel)){
37705 this.showPanel(panel);
37708 var el = panel.getEl();
37709 if(el.dom.parentNode != this.mgr.el.dom){
37710 this.mgr.el.dom.appendChild(el.dom);
37712 if(panel.setRegion){
37713 panel.setRegion(this);
37715 this.panels.add(panel);
37716 el.setStyle("position", "absolute");
37717 if(!panel.background){
37718 this.setActivePanel(panel);
37719 if(this.config.initialSize && this.panels.getCount()==1){
37720 this.resizeTo(this.config.initialSize);
37723 this.fireEvent("paneladded", this, panel);
37728 * Returns true if the panel is in this region.
37729 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37730 * @return {Boolean}
37732 hasPanel : function(panel){
37733 if(typeof panel == "object"){ // must be panel obj
37734 panel = panel.getId();
37736 return this.getPanel(panel) ? true : false;
37740 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37741 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37742 * @param {Boolean} preservePanel Overrides the config preservePanel option
37743 * @return {Roo.ContentPanel} The panel that was removed
37745 remove : function(panel, preservePanel){
37746 panel = this.getPanel(panel);
37751 this.fireEvent("beforeremove", this, panel, e);
37752 if(e.cancel === true){
37755 var panelId = panel.getId();
37756 this.panels.removeKey(panelId);
37761 * Returns the panel specified or null if it's not in this region.
37762 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37763 * @return {Roo.ContentPanel}
37765 getPanel : function(id){
37766 if(typeof id == "object"){ // must be panel obj
37769 return this.panels.get(id);
37773 * Returns this regions position (north/south/east/west/center).
37776 getPosition: function(){
37777 return this.position;
37781 * Ext JS Library 1.1.1
37782 * Copyright(c) 2006-2007, Ext JS, LLC.
37784 * Originally Released Under LGPL - original licence link has changed is not relivant.
37787 * <script type="text/javascript">
37791 * @class Roo.bootstrap.layout.Region
37792 * @extends Roo.bootstrap.layout.Basic
37793 * This class represents a region in a layout manager.
37795 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37796 * @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})
37797 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
37798 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
37799 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
37800 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
37801 * @cfg {String} title The title for the region (overrides panel titles)
37802 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
37803 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37804 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
37805 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37806 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
37807 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37808 * the space available, similar to FireFox 1.5 tabs (defaults to false)
37809 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
37810 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
37811 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
37813 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
37814 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
37815 * @cfg {Boolean} disableTabTips True to disable tab tooltips
37816 * @cfg {Number} width For East/West panels
37817 * @cfg {Number} height For North/South panels
37818 * @cfg {Boolean} split To show the splitter
37819 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
37821 * @cfg {string} cls Extra CSS classes to add to region
37823 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37824 * @cfg {string} region the region that it inhabits..
37827 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
37828 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
37830 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
37831 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
37832 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
37834 Roo.bootstrap.layout.Region = function(config)
37836 this.applyConfig(config);
37838 var mgr = config.mgr;
37839 var pos = config.region;
37840 config.skipConfig = true;
37841 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37844 this.onRender(mgr.el);
37847 this.visible = true;
37848 this.collapsed = false;
37849 this.unrendered_panels = [];
37852 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37854 position: '', // set by wrapper (eg. north/south etc..)
37855 unrendered_panels : null, // unrendered panels.
37857 tabPosition : false,
37859 mgr: false, // points to 'Border'
37862 createBody : function(){
37863 /** This region's body element
37864 * @type Roo.Element */
37865 this.bodyEl = this.el.createChild({
37867 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37871 onRender: function(ctr, pos)
37873 var dh = Roo.DomHelper;
37874 /** This region's container element
37875 * @type Roo.Element */
37876 this.el = dh.append(ctr.dom, {
37878 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37880 /** This region's title element
37881 * @type Roo.Element */
37883 this.titleEl = dh.append(this.el.dom, {
37885 unselectable: "on",
37886 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37888 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
37889 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37893 this.titleEl.enableDisplayMode();
37894 /** This region's title text element
37895 * @type HTMLElement */
37896 this.titleTextEl = this.titleEl.dom.firstChild;
37897 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37899 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37900 this.closeBtn.enableDisplayMode();
37901 this.closeBtn.on("click", this.closeClicked, this);
37902 this.closeBtn.hide();
37904 this.createBody(this.config);
37905 if(this.config.hideWhenEmpty){
37907 this.on("paneladded", this.validateVisibility, this);
37908 this.on("panelremoved", this.validateVisibility, this);
37910 if(this.autoScroll){
37911 this.bodyEl.setStyle("overflow", "auto");
37913 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37915 //if(c.titlebar !== false){
37916 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37917 this.titleEl.hide();
37919 this.titleEl.show();
37920 if(this.config.title){
37921 this.titleTextEl.innerHTML = this.config.title;
37925 if(this.config.collapsed){
37926 this.collapse(true);
37928 if(this.config.hidden){
37932 if (this.unrendered_panels && this.unrendered_panels.length) {
37933 for (var i =0;i< this.unrendered_panels.length; i++) {
37934 this.add(this.unrendered_panels[i]);
37936 this.unrendered_panels = null;
37942 applyConfig : function(c)
37945 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37946 var dh = Roo.DomHelper;
37947 if(c.titlebar !== false){
37948 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37949 this.collapseBtn.on("click", this.collapse, this);
37950 this.collapseBtn.enableDisplayMode();
37952 if(c.showPin === true || this.showPin){
37953 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37954 this.stickBtn.enableDisplayMode();
37955 this.stickBtn.on("click", this.expand, this);
37956 this.stickBtn.hide();
37961 /** This region's collapsed element
37962 * @type Roo.Element */
37965 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37966 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37969 if(c.floatable !== false){
37970 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37971 this.collapsedEl.on("click", this.collapseClick, this);
37974 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37975 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37976 id: "message", unselectable: "on", style:{"float":"left"}});
37977 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37979 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37980 this.expandBtn.on("click", this.expand, this);
37984 if(this.collapseBtn){
37985 this.collapseBtn.setVisible(c.collapsible == true);
37988 this.cmargins = c.cmargins || this.cmargins ||
37989 (this.position == "west" || this.position == "east" ?
37990 {top: 0, left: 2, right:2, bottom: 0} :
37991 {top: 2, left: 0, right:0, bottom: 2});
37993 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37996 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37998 this.autoScroll = c.autoScroll || false;
38003 this.duration = c.duration || .30;
38004 this.slideDuration = c.slideDuration || .45;
38009 * Returns true if this region is currently visible.
38010 * @return {Boolean}
38012 isVisible : function(){
38013 return this.visible;
38017 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38018 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38020 //setCollapsedTitle : function(title){
38021 // title = title || " ";
38022 // if(this.collapsedTitleTextEl){
38023 // this.collapsedTitleTextEl.innerHTML = title;
38027 getBox : function(){
38029 // if(!this.collapsed){
38030 b = this.el.getBox(false, true);
38032 // b = this.collapsedEl.getBox(false, true);
38037 getMargins : function(){
38038 return this.margins;
38039 //return this.collapsed ? this.cmargins : this.margins;
38042 highlight : function(){
38043 this.el.addClass("x-layout-panel-dragover");
38046 unhighlight : function(){
38047 this.el.removeClass("x-layout-panel-dragover");
38050 updateBox : function(box)
38052 if (!this.bodyEl) {
38053 return; // not rendered yet..
38057 if(!this.collapsed){
38058 this.el.dom.style.left = box.x + "px";
38059 this.el.dom.style.top = box.y + "px";
38060 this.updateBody(box.width, box.height);
38062 this.collapsedEl.dom.style.left = box.x + "px";
38063 this.collapsedEl.dom.style.top = box.y + "px";
38064 this.collapsedEl.setSize(box.width, box.height);
38067 this.tabs.autoSizeTabs();
38071 updateBody : function(w, h)
38074 this.el.setWidth(w);
38075 w -= this.el.getBorderWidth("rl");
38076 if(this.config.adjustments){
38077 w += this.config.adjustments[0];
38080 if(h !== null && h > 0){
38081 this.el.setHeight(h);
38082 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38083 h -= this.el.getBorderWidth("tb");
38084 if(this.config.adjustments){
38085 h += this.config.adjustments[1];
38087 this.bodyEl.setHeight(h);
38089 h = this.tabs.syncHeight(h);
38092 if(this.panelSize){
38093 w = w !== null ? w : this.panelSize.width;
38094 h = h !== null ? h : this.panelSize.height;
38096 if(this.activePanel){
38097 var el = this.activePanel.getEl();
38098 w = w !== null ? w : el.getWidth();
38099 h = h !== null ? h : el.getHeight();
38100 this.panelSize = {width: w, height: h};
38101 this.activePanel.setSize(w, h);
38103 if(Roo.isIE && this.tabs){
38104 this.tabs.el.repaint();
38109 * Returns the container element for this region.
38110 * @return {Roo.Element}
38112 getEl : function(){
38117 * Hides this region.
38120 //if(!this.collapsed){
38121 this.el.dom.style.left = "-2000px";
38124 // this.collapsedEl.dom.style.left = "-2000px";
38125 // this.collapsedEl.hide();
38127 this.visible = false;
38128 this.fireEvent("visibilitychange", this, false);
38132 * Shows this region if it was previously hidden.
38135 //if(!this.collapsed){
38138 // this.collapsedEl.show();
38140 this.visible = true;
38141 this.fireEvent("visibilitychange", this, true);
38144 closeClicked : function(){
38145 if(this.activePanel){
38146 this.remove(this.activePanel);
38150 collapseClick : function(e){
38152 e.stopPropagation();
38155 e.stopPropagation();
38161 * Collapses this region.
38162 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38165 collapse : function(skipAnim, skipCheck = false){
38166 if(this.collapsed) {
38170 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38172 this.collapsed = true;
38174 this.split.el.hide();
38176 if(this.config.animate && skipAnim !== true){
38177 this.fireEvent("invalidated", this);
38178 this.animateCollapse();
38180 this.el.setLocation(-20000,-20000);
38182 this.collapsedEl.show();
38183 this.fireEvent("collapsed", this);
38184 this.fireEvent("invalidated", this);
38190 animateCollapse : function(){
38195 * Expands this region if it was previously collapsed.
38196 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38197 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38200 expand : function(e, skipAnim){
38202 e.stopPropagation();
38204 if(!this.collapsed || this.el.hasActiveFx()) {
38208 this.afterSlideIn();
38211 this.collapsed = false;
38212 if(this.config.animate && skipAnim !== true){
38213 this.animateExpand();
38217 this.split.el.show();
38219 this.collapsedEl.setLocation(-2000,-2000);
38220 this.collapsedEl.hide();
38221 this.fireEvent("invalidated", this);
38222 this.fireEvent("expanded", this);
38226 animateExpand : function(){
38230 initTabs : function()
38232 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38234 var ts = new Roo.bootstrap.panel.Tabs({
38235 el: this.bodyEl.dom,
38237 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38238 disableTooltips: this.config.disableTabTips,
38239 toolbar : this.config.toolbar
38242 if(this.config.hideTabs){
38243 ts.stripWrap.setDisplayed(false);
38246 ts.resizeTabs = this.config.resizeTabs === true;
38247 ts.minTabWidth = this.config.minTabWidth || 40;
38248 ts.maxTabWidth = this.config.maxTabWidth || 250;
38249 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38250 ts.monitorResize = false;
38251 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38252 ts.bodyEl.addClass('roo-layout-tabs-body');
38253 this.panels.each(this.initPanelAsTab, this);
38256 initPanelAsTab : function(panel){
38257 var ti = this.tabs.addTab(
38261 this.config.closeOnTab && panel.isClosable(),
38264 if(panel.tabTip !== undefined){
38265 ti.setTooltip(panel.tabTip);
38267 ti.on("activate", function(){
38268 this.setActivePanel(panel);
38271 if(this.config.closeOnTab){
38272 ti.on("beforeclose", function(t, e){
38274 this.remove(panel);
38278 panel.tabItem = ti;
38283 updatePanelTitle : function(panel, title)
38285 if(this.activePanel == panel){
38286 this.updateTitle(title);
38289 var ti = this.tabs.getTab(panel.getEl().id);
38291 if(panel.tabTip !== undefined){
38292 ti.setTooltip(panel.tabTip);
38297 updateTitle : function(title){
38298 if(this.titleTextEl && !this.config.title){
38299 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38303 setActivePanel : function(panel)
38305 panel = this.getPanel(panel);
38306 if(this.activePanel && this.activePanel != panel){
38307 if(this.activePanel.setActiveState(false) === false){
38311 this.activePanel = panel;
38312 panel.setActiveState(true);
38313 if(this.panelSize){
38314 panel.setSize(this.panelSize.width, this.panelSize.height);
38317 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38319 this.updateTitle(panel.getTitle());
38321 this.fireEvent("invalidated", this);
38323 this.fireEvent("panelactivated", this, panel);
38327 * Shows the specified panel.
38328 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38329 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38331 showPanel : function(panel)
38333 panel = this.getPanel(panel);
38336 var tab = this.tabs.getTab(panel.getEl().id);
38337 if(tab.isHidden()){
38338 this.tabs.unhideTab(tab.id);
38342 this.setActivePanel(panel);
38349 * Get the active panel for this region.
38350 * @return {Roo.ContentPanel} The active panel or null
38352 getActivePanel : function(){
38353 return this.activePanel;
38356 validateVisibility : function(){
38357 if(this.panels.getCount() < 1){
38358 this.updateTitle(" ");
38359 this.closeBtn.hide();
38362 if(!this.isVisible()){
38369 * Adds the passed ContentPanel(s) to this region.
38370 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38371 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38373 add : function(panel)
38375 if(arguments.length > 1){
38376 for(var i = 0, len = arguments.length; i < len; i++) {
38377 this.add(arguments[i]);
38382 // if we have not been rendered yet, then we can not really do much of this..
38383 if (!this.bodyEl) {
38384 this.unrendered_panels.push(panel);
38391 if(this.hasPanel(panel)){
38392 this.showPanel(panel);
38395 panel.setRegion(this);
38396 this.panels.add(panel);
38397 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38398 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38399 // and hide them... ???
38400 this.bodyEl.dom.appendChild(panel.getEl().dom);
38401 if(panel.background !== true){
38402 this.setActivePanel(panel);
38404 this.fireEvent("paneladded", this, panel);
38411 this.initPanelAsTab(panel);
38415 if(panel.background !== true){
38416 this.tabs.activate(panel.getEl().id);
38418 this.fireEvent("paneladded", this, panel);
38423 * Hides the tab for the specified panel.
38424 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38426 hidePanel : function(panel){
38427 if(this.tabs && (panel = this.getPanel(panel))){
38428 this.tabs.hideTab(panel.getEl().id);
38433 * Unhides the tab for a previously hidden panel.
38434 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38436 unhidePanel : function(panel){
38437 if(this.tabs && (panel = this.getPanel(panel))){
38438 this.tabs.unhideTab(panel.getEl().id);
38442 clearPanels : function(){
38443 while(this.panels.getCount() > 0){
38444 this.remove(this.panels.first());
38449 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38450 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38451 * @param {Boolean} preservePanel Overrides the config preservePanel option
38452 * @return {Roo.ContentPanel} The panel that was removed
38454 remove : function(panel, preservePanel)
38456 panel = this.getPanel(panel);
38461 this.fireEvent("beforeremove", this, panel, e);
38462 if(e.cancel === true){
38465 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38466 var panelId = panel.getId();
38467 this.panels.removeKey(panelId);
38469 document.body.appendChild(panel.getEl().dom);
38472 this.tabs.removeTab(panel.getEl().id);
38473 }else if (!preservePanel){
38474 this.bodyEl.dom.removeChild(panel.getEl().dom);
38476 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38477 var p = this.panels.first();
38478 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38479 tempEl.appendChild(p.getEl().dom);
38480 this.bodyEl.update("");
38481 this.bodyEl.dom.appendChild(p.getEl().dom);
38483 this.updateTitle(p.getTitle());
38485 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38486 this.setActivePanel(p);
38488 panel.setRegion(null);
38489 if(this.activePanel == panel){
38490 this.activePanel = null;
38492 if(this.config.autoDestroy !== false && preservePanel !== true){
38493 try{panel.destroy();}catch(e){}
38495 this.fireEvent("panelremoved", this, panel);
38500 * Returns the TabPanel component used by this region
38501 * @return {Roo.TabPanel}
38503 getTabs : function(){
38507 createTool : function(parentEl, className){
38508 var btn = Roo.DomHelper.append(parentEl, {
38510 cls: "x-layout-tools-button",
38513 cls: "roo-layout-tools-button-inner " + className,
38517 btn.addClassOnOver("roo-layout-tools-button-over");
38522 * Ext JS Library 1.1.1
38523 * Copyright(c) 2006-2007, Ext JS, LLC.
38525 * Originally Released Under LGPL - original licence link has changed is not relivant.
38528 * <script type="text/javascript">
38534 * @class Roo.SplitLayoutRegion
38535 * @extends Roo.LayoutRegion
38536 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38538 Roo.bootstrap.layout.Split = function(config){
38539 this.cursor = config.cursor;
38540 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38543 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38545 splitTip : "Drag to resize.",
38546 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38547 useSplitTips : false,
38549 applyConfig : function(config){
38550 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38553 onRender : function(ctr,pos) {
38555 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38556 if(!this.config.split){
38561 var splitEl = Roo.DomHelper.append(ctr.dom, {
38563 id: this.el.id + "-split",
38564 cls: "roo-layout-split roo-layout-split-"+this.position,
38567 /** The SplitBar for this region
38568 * @type Roo.SplitBar */
38569 // does not exist yet...
38570 Roo.log([this.position, this.orientation]);
38572 this.split = new Roo.bootstrap.SplitBar({
38573 dragElement : splitEl,
38574 resizingElement: this.el,
38575 orientation : this.orientation
38578 this.split.on("moved", this.onSplitMove, this);
38579 this.split.useShim = this.config.useShim === true;
38580 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38581 if(this.useSplitTips){
38582 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38584 //if(config.collapsible){
38585 // this.split.el.on("dblclick", this.collapse, this);
38588 if(typeof this.config.minSize != "undefined"){
38589 this.split.minSize = this.config.minSize;
38591 if(typeof this.config.maxSize != "undefined"){
38592 this.split.maxSize = this.config.maxSize;
38594 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38595 this.hideSplitter();
38600 getHMaxSize : function(){
38601 var cmax = this.config.maxSize || 10000;
38602 var center = this.mgr.getRegion("center");
38603 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38606 getVMaxSize : function(){
38607 var cmax = this.config.maxSize || 10000;
38608 var center = this.mgr.getRegion("center");
38609 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38612 onSplitMove : function(split, newSize){
38613 this.fireEvent("resized", this, newSize);
38617 * Returns the {@link Roo.SplitBar} for this region.
38618 * @return {Roo.SplitBar}
38620 getSplitBar : function(){
38625 this.hideSplitter();
38626 Roo.bootstrap.layout.Split.superclass.hide.call(this);
38629 hideSplitter : function(){
38631 this.split.el.setLocation(-2000,-2000);
38632 this.split.el.hide();
38638 this.split.el.show();
38640 Roo.bootstrap.layout.Split.superclass.show.call(this);
38643 beforeSlide: function(){
38644 if(Roo.isGecko){// firefox overflow auto bug workaround
38645 this.bodyEl.clip();
38647 this.tabs.bodyEl.clip();
38649 if(this.activePanel){
38650 this.activePanel.getEl().clip();
38652 if(this.activePanel.beforeSlide){
38653 this.activePanel.beforeSlide();
38659 afterSlide : function(){
38660 if(Roo.isGecko){// firefox overflow auto bug workaround
38661 this.bodyEl.unclip();
38663 this.tabs.bodyEl.unclip();
38665 if(this.activePanel){
38666 this.activePanel.getEl().unclip();
38667 if(this.activePanel.afterSlide){
38668 this.activePanel.afterSlide();
38674 initAutoHide : function(){
38675 if(this.autoHide !== false){
38676 if(!this.autoHideHd){
38677 var st = new Roo.util.DelayedTask(this.slideIn, this);
38678 this.autoHideHd = {
38679 "mouseout": function(e){
38680 if(!e.within(this.el, true)){
38684 "mouseover" : function(e){
38690 this.el.on(this.autoHideHd);
38694 clearAutoHide : function(){
38695 if(this.autoHide !== false){
38696 this.el.un("mouseout", this.autoHideHd.mouseout);
38697 this.el.un("mouseover", this.autoHideHd.mouseover);
38701 clearMonitor : function(){
38702 Roo.get(document).un("click", this.slideInIf, this);
38705 // these names are backwards but not changed for compat
38706 slideOut : function(){
38707 if(this.isSlid || this.el.hasActiveFx()){
38710 this.isSlid = true;
38711 if(this.collapseBtn){
38712 this.collapseBtn.hide();
38714 this.closeBtnState = this.closeBtn.getStyle('display');
38715 this.closeBtn.hide();
38717 this.stickBtn.show();
38720 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38721 this.beforeSlide();
38722 this.el.setStyle("z-index", 10001);
38723 this.el.slideIn(this.getSlideAnchor(), {
38724 callback: function(){
38726 this.initAutoHide();
38727 Roo.get(document).on("click", this.slideInIf, this);
38728 this.fireEvent("slideshow", this);
38735 afterSlideIn : function(){
38736 this.clearAutoHide();
38737 this.isSlid = false;
38738 this.clearMonitor();
38739 this.el.setStyle("z-index", "");
38740 if(this.collapseBtn){
38741 this.collapseBtn.show();
38743 this.closeBtn.setStyle('display', this.closeBtnState);
38745 this.stickBtn.hide();
38747 this.fireEvent("slidehide", this);
38750 slideIn : function(cb){
38751 if(!this.isSlid || this.el.hasActiveFx()){
38755 this.isSlid = false;
38756 this.beforeSlide();
38757 this.el.slideOut(this.getSlideAnchor(), {
38758 callback: function(){
38759 this.el.setLeftTop(-10000, -10000);
38761 this.afterSlideIn();
38769 slideInIf : function(e){
38770 if(!e.within(this.el)){
38775 animateCollapse : function(){
38776 this.beforeSlide();
38777 this.el.setStyle("z-index", 20000);
38778 var anchor = this.getSlideAnchor();
38779 this.el.slideOut(anchor, {
38780 callback : function(){
38781 this.el.setStyle("z-index", "");
38782 this.collapsedEl.slideIn(anchor, {duration:.3});
38784 this.el.setLocation(-10000,-10000);
38786 this.fireEvent("collapsed", this);
38793 animateExpand : function(){
38794 this.beforeSlide();
38795 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38796 this.el.setStyle("z-index", 20000);
38797 this.collapsedEl.hide({
38800 this.el.slideIn(this.getSlideAnchor(), {
38801 callback : function(){
38802 this.el.setStyle("z-index", "");
38805 this.split.el.show();
38807 this.fireEvent("invalidated", this);
38808 this.fireEvent("expanded", this);
38836 getAnchor : function(){
38837 return this.anchors[this.position];
38840 getCollapseAnchor : function(){
38841 return this.canchors[this.position];
38844 getSlideAnchor : function(){
38845 return this.sanchors[this.position];
38848 getAlignAdj : function(){
38849 var cm = this.cmargins;
38850 switch(this.position){
38866 getExpandAdj : function(){
38867 var c = this.collapsedEl, cm = this.cmargins;
38868 switch(this.position){
38870 return [-(cm.right+c.getWidth()+cm.left), 0];
38873 return [cm.right+c.getWidth()+cm.left, 0];
38876 return [0, -(cm.top+cm.bottom+c.getHeight())];
38879 return [0, cm.top+cm.bottom+c.getHeight()];
38885 * Ext JS Library 1.1.1
38886 * Copyright(c) 2006-2007, Ext JS, LLC.
38888 * Originally Released Under LGPL - original licence link has changed is not relivant.
38891 * <script type="text/javascript">
38894 * These classes are private internal classes
38896 Roo.bootstrap.layout.Center = function(config){
38897 config.region = "center";
38898 Roo.bootstrap.layout.Region.call(this, config);
38899 this.visible = true;
38900 this.minWidth = config.minWidth || 20;
38901 this.minHeight = config.minHeight || 20;
38904 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38906 // center panel can't be hidden
38910 // center panel can't be hidden
38913 getMinWidth: function(){
38914 return this.minWidth;
38917 getMinHeight: function(){
38918 return this.minHeight;
38932 Roo.bootstrap.layout.North = function(config)
38934 config.region = 'north';
38935 config.cursor = 'n-resize';
38937 Roo.bootstrap.layout.Split.call(this, config);
38941 this.split.placement = Roo.bootstrap.SplitBar.TOP;
38942 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38943 this.split.el.addClass("roo-layout-split-v");
38945 var size = config.initialSize || config.height;
38946 if(typeof size != "undefined"){
38947 this.el.setHeight(size);
38950 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38952 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38956 getBox : function(){
38957 if(this.collapsed){
38958 return this.collapsedEl.getBox();
38960 var box = this.el.getBox();
38962 box.height += this.split.el.getHeight();
38967 updateBox : function(box){
38968 if(this.split && !this.collapsed){
38969 box.height -= this.split.el.getHeight();
38970 this.split.el.setLeft(box.x);
38971 this.split.el.setTop(box.y+box.height);
38972 this.split.el.setWidth(box.width);
38974 if(this.collapsed){
38975 this.updateBody(box.width, null);
38977 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38985 Roo.bootstrap.layout.South = function(config){
38986 config.region = 'south';
38987 config.cursor = 's-resize';
38988 Roo.bootstrap.layout.Split.call(this, config);
38990 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38991 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38992 this.split.el.addClass("roo-layout-split-v");
38994 var size = config.initialSize || config.height;
38995 if(typeof size != "undefined"){
38996 this.el.setHeight(size);
39000 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39001 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39002 getBox : function(){
39003 if(this.collapsed){
39004 return this.collapsedEl.getBox();
39006 var box = this.el.getBox();
39008 var sh = this.split.el.getHeight();
39015 updateBox : function(box){
39016 if(this.split && !this.collapsed){
39017 var sh = this.split.el.getHeight();
39020 this.split.el.setLeft(box.x);
39021 this.split.el.setTop(box.y-sh);
39022 this.split.el.setWidth(box.width);
39024 if(this.collapsed){
39025 this.updateBody(box.width, null);
39027 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39031 Roo.bootstrap.layout.East = function(config){
39032 config.region = "east";
39033 config.cursor = "e-resize";
39034 Roo.bootstrap.layout.Split.call(this, config);
39036 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39037 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39038 this.split.el.addClass("roo-layout-split-h");
39040 var size = config.initialSize || config.width;
39041 if(typeof size != "undefined"){
39042 this.el.setWidth(size);
39045 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39046 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39047 getBox : function(){
39048 if(this.collapsed){
39049 return this.collapsedEl.getBox();
39051 var box = this.el.getBox();
39053 var sw = this.split.el.getWidth();
39060 updateBox : function(box){
39061 if(this.split && !this.collapsed){
39062 var sw = this.split.el.getWidth();
39064 this.split.el.setLeft(box.x);
39065 this.split.el.setTop(box.y);
39066 this.split.el.setHeight(box.height);
39069 if(this.collapsed){
39070 this.updateBody(null, box.height);
39072 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39076 Roo.bootstrap.layout.West = function(config){
39077 config.region = "west";
39078 config.cursor = "w-resize";
39080 Roo.bootstrap.layout.Split.call(this, config);
39082 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39083 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39084 this.split.el.addClass("roo-layout-split-h");
39088 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39089 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39091 onRender: function(ctr, pos)
39093 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39094 var size = this.config.initialSize || this.config.width;
39095 if(typeof size != "undefined"){
39096 this.el.setWidth(size);
39100 getBox : function(){
39101 if(this.collapsed){
39102 return this.collapsedEl.getBox();
39104 var box = this.el.getBox();
39106 box.width += this.split.el.getWidth();
39111 updateBox : function(box){
39112 if(this.split && !this.collapsed){
39113 var sw = this.split.el.getWidth();
39115 this.split.el.setLeft(box.x+box.width);
39116 this.split.el.setTop(box.y);
39117 this.split.el.setHeight(box.height);
39119 if(this.collapsed){
39120 this.updateBody(null, box.height);
39122 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39124 });Roo.namespace("Roo.bootstrap.panel");/*
39126 * Ext JS Library 1.1.1
39127 * Copyright(c) 2006-2007, Ext JS, LLC.
39129 * Originally Released Under LGPL - original licence link has changed is not relivant.
39132 * <script type="text/javascript">
39135 * @class Roo.ContentPanel
39136 * @extends Roo.util.Observable
39137 * A basic ContentPanel element.
39138 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39139 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39140 * @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
39141 * @cfg {Boolean} closable True if the panel can be closed/removed
39142 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39143 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39144 * @cfg {Toolbar} toolbar A toolbar for this panel
39145 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39146 * @cfg {String} title The title for this panel
39147 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39148 * @cfg {String} url Calls {@link #setUrl} with this value
39149 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39150 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39151 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39152 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39153 * @cfg {Boolean} badges render the badges
39156 * Create a new ContentPanel.
39157 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39158 * @param {String/Object} config A string to set only the title or a config object
39159 * @param {String} content (optional) Set the HTML content for this panel
39160 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39162 Roo.bootstrap.panel.Content = function( config){
39164 this.tpl = config.tpl || false;
39166 var el = config.el;
39167 var content = config.content;
39169 if(config.autoCreate){ // xtype is available if this is called from factory
39172 this.el = Roo.get(el);
39173 if(!this.el && config && config.autoCreate){
39174 if(typeof config.autoCreate == "object"){
39175 if(!config.autoCreate.id){
39176 config.autoCreate.id = config.id||el;
39178 this.el = Roo.DomHelper.append(document.body,
39179 config.autoCreate, true);
39181 var elcfg = { tag: "div",
39182 cls: "roo-layout-inactive-content",
39186 elcfg.html = config.html;
39190 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39193 this.closable = false;
39194 this.loaded = false;
39195 this.active = false;
39198 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39200 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39202 this.wrapEl = this.el; //this.el.wrap();
39204 if (config.toolbar.items) {
39205 ti = config.toolbar.items ;
39206 delete config.toolbar.items ;
39210 this.toolbar.render(this.wrapEl, 'before');
39211 for(var i =0;i < ti.length;i++) {
39212 // Roo.log(['add child', items[i]]);
39213 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39215 this.toolbar.items = nitems;
39216 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39217 delete config.toolbar;
39221 // xtype created footer. - not sure if will work as we normally have to render first..
39222 if (this.footer && !this.footer.el && this.footer.xtype) {
39223 if (!this.wrapEl) {
39224 this.wrapEl = this.el.wrap();
39227 this.footer.container = this.wrapEl.createChild();
39229 this.footer = Roo.factory(this.footer, Roo);
39234 if(typeof config == "string"){
39235 this.title = config;
39237 Roo.apply(this, config);
39241 this.resizeEl = Roo.get(this.resizeEl, true);
39243 this.resizeEl = this.el;
39245 // handle view.xtype
39253 * Fires when this panel is activated.
39254 * @param {Roo.ContentPanel} this
39258 * @event deactivate
39259 * Fires when this panel is activated.
39260 * @param {Roo.ContentPanel} this
39262 "deactivate" : true,
39266 * Fires when this panel is resized if fitToFrame is true.
39267 * @param {Roo.ContentPanel} this
39268 * @param {Number} width The width after any component adjustments
39269 * @param {Number} height The height after any component adjustments
39275 * Fires when this tab is created
39276 * @param {Roo.ContentPanel} this
39287 if(this.autoScroll){
39288 this.resizeEl.setStyle("overflow", "auto");
39290 // fix randome scrolling
39291 //this.el.on('scroll', function() {
39292 // Roo.log('fix random scolling');
39293 // this.scrollTo('top',0);
39296 content = content || this.content;
39298 this.setContent(content);
39300 if(config && config.url){
39301 this.setUrl(this.url, this.params, this.loadOnce);
39306 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39308 if (this.view && typeof(this.view.xtype) != 'undefined') {
39309 this.view.el = this.el.appendChild(document.createElement("div"));
39310 this.view = Roo.factory(this.view);
39311 this.view.render && this.view.render(false, '');
39315 this.fireEvent('render', this);
39318 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39322 setRegion : function(region){
39323 this.region = region;
39324 this.setActiveClass(region && !this.background);
39328 setActiveClass: function(state)
39331 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39332 this.el.setStyle('position','relative');
39334 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39335 this.el.setStyle('position', 'absolute');
39340 * Returns the toolbar for this Panel if one was configured.
39341 * @return {Roo.Toolbar}
39343 getToolbar : function(){
39344 return this.toolbar;
39347 setActiveState : function(active)
39349 this.active = active;
39350 this.setActiveClass(active);
39352 if(this.fireEvent("deactivate", this) === false){
39357 this.fireEvent("activate", this);
39361 * Updates this panel's element
39362 * @param {String} content The new content
39363 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39365 setContent : function(content, loadScripts){
39366 this.el.update(content, loadScripts);
39369 ignoreResize : function(w, h){
39370 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39373 this.lastSize = {width: w, height: h};
39378 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39379 * @return {Roo.UpdateManager} The UpdateManager
39381 getUpdateManager : function(){
39382 return this.el.getUpdateManager();
39385 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39386 * @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:
39389 url: "your-url.php",
39390 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39391 callback: yourFunction,
39392 scope: yourObject, //(optional scope)
39395 text: "Loading...",
39400 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39401 * 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.
39402 * @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}
39403 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39404 * @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.
39405 * @return {Roo.ContentPanel} this
39408 var um = this.el.getUpdateManager();
39409 um.update.apply(um, arguments);
39415 * 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.
39416 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39417 * @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)
39418 * @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)
39419 * @return {Roo.UpdateManager} The UpdateManager
39421 setUrl : function(url, params, loadOnce){
39422 if(this.refreshDelegate){
39423 this.removeListener("activate", this.refreshDelegate);
39425 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39426 this.on("activate", this.refreshDelegate);
39427 return this.el.getUpdateManager();
39430 _handleRefresh : function(url, params, loadOnce){
39431 if(!loadOnce || !this.loaded){
39432 var updater = this.el.getUpdateManager();
39433 updater.update(url, params, this._setLoaded.createDelegate(this));
39437 _setLoaded : function(){
39438 this.loaded = true;
39442 * Returns this panel's id
39445 getId : function(){
39450 * Returns this panel's element - used by regiosn to add.
39451 * @return {Roo.Element}
39453 getEl : function(){
39454 return this.wrapEl || this.el;
39459 adjustForComponents : function(width, height)
39461 //Roo.log('adjustForComponents ');
39462 if(this.resizeEl != this.el){
39463 width -= this.el.getFrameWidth('lr');
39464 height -= this.el.getFrameWidth('tb');
39467 var te = this.toolbar.getEl();
39468 te.setWidth(width);
39469 height -= te.getHeight();
39472 var te = this.footer.getEl();
39473 te.setWidth(width);
39474 height -= te.getHeight();
39478 if(this.adjustments){
39479 width += this.adjustments[0];
39480 height += this.adjustments[1];
39482 return {"width": width, "height": height};
39485 setSize : function(width, height){
39486 if(this.fitToFrame && !this.ignoreResize(width, height)){
39487 if(this.fitContainer && this.resizeEl != this.el){
39488 this.el.setSize(width, height);
39490 var size = this.adjustForComponents(width, height);
39491 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39492 this.fireEvent('resize', this, size.width, size.height);
39497 * Returns this panel's title
39500 getTitle : function(){
39502 if (typeof(this.title) != 'object') {
39507 for (var k in this.title) {
39508 if (!this.title.hasOwnProperty(k)) {
39512 if (k.indexOf('-') >= 0) {
39513 var s = k.split('-');
39514 for (var i = 0; i<s.length; i++) {
39515 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39518 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39525 * Set this panel's title
39526 * @param {String} title
39528 setTitle : function(title){
39529 this.title = title;
39531 this.region.updatePanelTitle(this, title);
39536 * Returns true is this panel was configured to be closable
39537 * @return {Boolean}
39539 isClosable : function(){
39540 return this.closable;
39543 beforeSlide : function(){
39545 this.resizeEl.clip();
39548 afterSlide : function(){
39550 this.resizeEl.unclip();
39554 * Force a content refresh from the URL specified in the {@link #setUrl} method.
39555 * Will fail silently if the {@link #setUrl} method has not been called.
39556 * This does not activate the panel, just updates its content.
39558 refresh : function(){
39559 if(this.refreshDelegate){
39560 this.loaded = false;
39561 this.refreshDelegate();
39566 * Destroys this panel
39568 destroy : function(){
39569 this.el.removeAllListeners();
39570 var tempEl = document.createElement("span");
39571 tempEl.appendChild(this.el.dom);
39572 tempEl.innerHTML = "";
39578 * form - if the content panel contains a form - this is a reference to it.
39579 * @type {Roo.form.Form}
39583 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39584 * This contains a reference to it.
39590 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39600 * @param {Object} cfg Xtype definition of item to add.
39604 getChildContainer: function () {
39605 return this.getEl();
39610 var ret = new Roo.factory(cfg);
39615 if (cfg.xtype.match(/^Form$/)) {
39618 //if (this.footer) {
39619 // el = this.footer.container.insertSibling(false, 'before');
39621 el = this.el.createChild();
39624 this.form = new Roo.form.Form(cfg);
39627 if ( this.form.allItems.length) {
39628 this.form.render(el.dom);
39632 // should only have one of theses..
39633 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39634 // views.. should not be just added - used named prop 'view''
39636 cfg.el = this.el.appendChild(document.createElement("div"));
39639 var ret = new Roo.factory(cfg);
39641 ret.render && ret.render(false, ''); // render blank..
39651 * @class Roo.bootstrap.panel.Grid
39652 * @extends Roo.bootstrap.panel.Content
39654 * Create a new GridPanel.
39655 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39656 * @param {Object} config A the config object
39662 Roo.bootstrap.panel.Grid = function(config)
39666 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39667 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39669 config.el = this.wrapper;
39670 //this.el = this.wrapper;
39672 if (config.container) {
39673 // ctor'ed from a Border/panel.grid
39676 this.wrapper.setStyle("overflow", "hidden");
39677 this.wrapper.addClass('roo-grid-container');
39682 if(config.toolbar){
39683 var tool_el = this.wrapper.createChild();
39684 this.toolbar = Roo.factory(config.toolbar);
39686 if (config.toolbar.items) {
39687 ti = config.toolbar.items ;
39688 delete config.toolbar.items ;
39692 this.toolbar.render(tool_el);
39693 for(var i =0;i < ti.length;i++) {
39694 // Roo.log(['add child', items[i]]);
39695 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39697 this.toolbar.items = nitems;
39699 delete config.toolbar;
39702 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39703 config.grid.scrollBody = true;;
39704 config.grid.monitorWindowResize = false; // turn off autosizing
39705 config.grid.autoHeight = false;
39706 config.grid.autoWidth = false;
39708 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39710 if (config.background) {
39711 // render grid on panel activation (if panel background)
39712 this.on('activate', function(gp) {
39713 if (!gp.grid.rendered) {
39714 gp.grid.render(this.wrapper);
39715 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39720 this.grid.render(this.wrapper);
39721 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39724 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39725 // ??? needed ??? config.el = this.wrapper;
39730 // xtype created footer. - not sure if will work as we normally have to render first..
39731 if (this.footer && !this.footer.el && this.footer.xtype) {
39733 var ctr = this.grid.getView().getFooterPanel(true);
39734 this.footer.dataSource = this.grid.dataSource;
39735 this.footer = Roo.factory(this.footer, Roo);
39736 this.footer.render(ctr);
39746 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39747 getId : function(){
39748 return this.grid.id;
39752 * Returns the grid for this panel
39753 * @return {Roo.bootstrap.Table}
39755 getGrid : function(){
39759 setSize : function(width, height){
39760 if(!this.ignoreResize(width, height)){
39761 var grid = this.grid;
39762 var size = this.adjustForComponents(width, height);
39763 // tfoot is not a footer?
39766 var gridel = grid.getGridEl();
39767 gridel.setSize(size.width, size.height);
39769 var tbd = grid.getGridEl().select('tbody', true).first();
39770 var thd = grid.getGridEl().select('thead',true).first();
39771 var tbf= grid.getGridEl().select('tfoot', true).first();
39774 size.height -= thd.getHeight();
39777 size.height -= thd.getHeight();
39780 tbd.setSize(size.width, size.height );
39781 // this is for the account management tab -seems to work there.
39782 var thd = grid.getGridEl().select('thead',true).first();
39784 // tbd.setSize(size.width, size.height - thd.getHeight());
39793 beforeSlide : function(){
39794 this.grid.getView().scroller.clip();
39797 afterSlide : function(){
39798 this.grid.getView().scroller.unclip();
39801 destroy : function(){
39802 this.grid.destroy();
39804 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
39809 * @class Roo.bootstrap.panel.Nest
39810 * @extends Roo.bootstrap.panel.Content
39812 * Create a new Panel, that can contain a layout.Border.
39815 * @param {Roo.BorderLayout} layout The layout for this panel
39816 * @param {String/Object} config A string to set only the title or a config object
39818 Roo.bootstrap.panel.Nest = function(config)
39820 // construct with only one argument..
39821 /* FIXME - implement nicer consturctors
39822 if (layout.layout) {
39824 layout = config.layout;
39825 delete config.layout;
39827 if (layout.xtype && !layout.getEl) {
39828 // then layout needs constructing..
39829 layout = Roo.factory(layout, Roo);
39833 config.el = config.layout.getEl();
39835 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39837 config.layout.monitorWindowResize = false; // turn off autosizing
39838 this.layout = config.layout;
39839 this.layout.getEl().addClass("roo-layout-nested-layout");
39840 this.layout.parent = this;
39847 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39849 setSize : function(width, height){
39850 if(!this.ignoreResize(width, height)){
39851 var size = this.adjustForComponents(width, height);
39852 var el = this.layout.getEl();
39853 if (size.height < 1) {
39854 el.setWidth(size.width);
39856 el.setSize(size.width, size.height);
39858 var touch = el.dom.offsetWidth;
39859 this.layout.layout();
39860 // ie requires a double layout on the first pass
39861 if(Roo.isIE && !this.initialized){
39862 this.initialized = true;
39863 this.layout.layout();
39868 // activate all subpanels if not currently active..
39870 setActiveState : function(active){
39871 this.active = active;
39872 this.setActiveClass(active);
39875 this.fireEvent("deactivate", this);
39879 this.fireEvent("activate", this);
39880 // not sure if this should happen before or after..
39881 if (!this.layout) {
39882 return; // should not happen..
39885 for (var r in this.layout.regions) {
39886 reg = this.layout.getRegion(r);
39887 if (reg.getActivePanel()) {
39888 //reg.showPanel(reg.getActivePanel()); // force it to activate..
39889 reg.setActivePanel(reg.getActivePanel());
39892 if (!reg.panels.length) {
39895 reg.showPanel(reg.getPanel(0));
39904 * Returns the nested BorderLayout for this panel
39905 * @return {Roo.BorderLayout}
39907 getLayout : function(){
39908 return this.layout;
39912 * Adds a xtype elements to the layout of the nested panel
39916 xtype : 'ContentPanel',
39923 xtype : 'NestedLayoutPanel',
39929 items : [ ... list of content panels or nested layout panels.. ]
39933 * @param {Object} cfg Xtype definition of item to add.
39935 addxtype : function(cfg) {
39936 return this.layout.addxtype(cfg);
39941 * Ext JS Library 1.1.1
39942 * Copyright(c) 2006-2007, Ext JS, LLC.
39944 * Originally Released Under LGPL - original licence link has changed is not relivant.
39947 * <script type="text/javascript">
39950 * @class Roo.TabPanel
39951 * @extends Roo.util.Observable
39952 * A lightweight tab container.
39956 // basic tabs 1, built from existing content
39957 var tabs = new Roo.TabPanel("tabs1");
39958 tabs.addTab("script", "View Script");
39959 tabs.addTab("markup", "View Markup");
39960 tabs.activate("script");
39962 // more advanced tabs, built from javascript
39963 var jtabs = new Roo.TabPanel("jtabs");
39964 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39966 // set up the UpdateManager
39967 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39968 var updater = tab2.getUpdateManager();
39969 updater.setDefaultUrl("ajax1.htm");
39970 tab2.on('activate', updater.refresh, updater, true);
39972 // Use setUrl for Ajax loading
39973 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39974 tab3.setUrl("ajax2.htm", null, true);
39977 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39980 jtabs.activate("jtabs-1");
39983 * Create a new TabPanel.
39984 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39985 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39987 Roo.bootstrap.panel.Tabs = function(config){
39989 * The container element for this TabPanel.
39990 * @type Roo.Element
39992 this.el = Roo.get(config.el);
39995 if(typeof config == "boolean"){
39996 this.tabPosition = config ? "bottom" : "top";
39998 Roo.apply(this, config);
40002 if(this.tabPosition == "bottom"){
40003 // if tabs are at the bottom = create the body first.
40004 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40005 this.el.addClass("roo-tabs-bottom");
40007 // next create the tabs holders
40009 if (this.tabPosition == "west"){
40011 var reg = this.region; // fake it..
40013 if (!reg.mgr.parent) {
40016 reg = reg.mgr.parent.region;
40018 Roo.log("got nest?");
40020 if (reg.mgr.getRegion('west')) {
40021 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40022 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40023 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40024 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40025 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40033 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40034 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40035 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40036 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40041 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40044 // finally - if tabs are at the top, then create the body last..
40045 if(this.tabPosition != "bottom"){
40046 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40047 * @type Roo.Element
40049 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40050 this.el.addClass("roo-tabs-top");
40054 this.bodyEl.setStyle("position", "relative");
40056 this.active = null;
40057 this.activateDelegate = this.activate.createDelegate(this);
40062 * Fires when the active tab changes
40063 * @param {Roo.TabPanel} this
40064 * @param {Roo.TabPanelItem} activePanel The new active tab
40068 * @event beforetabchange
40069 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40070 * @param {Roo.TabPanel} this
40071 * @param {Object} e Set cancel to true on this object to cancel the tab change
40072 * @param {Roo.TabPanelItem} tab The tab being changed to
40074 "beforetabchange" : true
40077 Roo.EventManager.onWindowResize(this.onResize, this);
40078 this.cpad = this.el.getPadding("lr");
40079 this.hiddenCount = 0;
40082 // toolbar on the tabbar support...
40083 if (this.toolbar) {
40084 alert("no toolbar support yet");
40085 this.toolbar = false;
40087 var tcfg = this.toolbar;
40088 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40089 this.toolbar = new Roo.Toolbar(tcfg);
40090 if (Roo.isSafari) {
40091 var tbl = tcfg.container.child('table', true);
40092 tbl.setAttribute('width', '100%');
40100 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40103 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40105 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40107 tabPosition : "top",
40109 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40111 currentTabWidth : 0,
40113 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40117 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40121 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40123 preferredTabWidth : 175,
40125 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40127 resizeTabs : false,
40129 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40131 monitorResize : true,
40133 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40135 toolbar : false, // set by caller..
40137 region : false, /// set by caller
40139 disableTooltips : true, // not used yet...
40142 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40143 * @param {String} id The id of the div to use <b>or create</b>
40144 * @param {String} text The text for the tab
40145 * @param {String} content (optional) Content to put in the TabPanelItem body
40146 * @param {Boolean} closable (optional) True to create a close icon on the tab
40147 * @return {Roo.TabPanelItem} The created TabPanelItem
40149 addTab : function(id, text, content, closable, tpl)
40151 var item = new Roo.bootstrap.panel.TabItem({
40155 closable : closable,
40158 this.addTabItem(item);
40160 item.setContent(content);
40166 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40167 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40168 * @return {Roo.TabPanelItem}
40170 getTab : function(id){
40171 return this.items[id];
40175 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40176 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40178 hideTab : function(id){
40179 var t = this.items[id];
40182 this.hiddenCount++;
40183 this.autoSizeTabs();
40188 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40189 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40191 unhideTab : function(id){
40192 var t = this.items[id];
40194 t.setHidden(false);
40195 this.hiddenCount--;
40196 this.autoSizeTabs();
40201 * Adds an existing {@link Roo.TabPanelItem}.
40202 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40204 addTabItem : function(item)
40206 this.items[item.id] = item;
40207 this.items.push(item);
40208 this.autoSizeTabs();
40209 // if(this.resizeTabs){
40210 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40211 // this.autoSizeTabs();
40213 // item.autoSize();
40218 * Removes a {@link Roo.TabPanelItem}.
40219 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40221 removeTab : function(id){
40222 var items = this.items;
40223 var tab = items[id];
40224 if(!tab) { return; }
40225 var index = items.indexOf(tab);
40226 if(this.active == tab && items.length > 1){
40227 var newTab = this.getNextAvailable(index);
40232 this.stripEl.dom.removeChild(tab.pnode.dom);
40233 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40234 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40236 items.splice(index, 1);
40237 delete this.items[tab.id];
40238 tab.fireEvent("close", tab);
40239 tab.purgeListeners();
40240 this.autoSizeTabs();
40243 getNextAvailable : function(start){
40244 var items = this.items;
40246 // look for a next tab that will slide over to
40247 // replace the one being removed
40248 while(index < items.length){
40249 var item = items[++index];
40250 if(item && !item.isHidden()){
40254 // if one isn't found select the previous tab (on the left)
40257 var item = items[--index];
40258 if(item && !item.isHidden()){
40266 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40267 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40269 disableTab : function(id){
40270 var tab = this.items[id];
40271 if(tab && this.active != tab){
40277 * Enables a {@link Roo.TabPanelItem} that is disabled.
40278 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40280 enableTab : function(id){
40281 var tab = this.items[id];
40286 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40287 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40288 * @return {Roo.TabPanelItem} The TabPanelItem.
40290 activate : function(id)
40292 //Roo.log('activite:' + id);
40294 var tab = this.items[id];
40298 if(tab == this.active || tab.disabled){
40302 this.fireEvent("beforetabchange", this, e, tab);
40303 if(e.cancel !== true && !tab.disabled){
40305 this.active.hide();
40307 this.active = this.items[id];
40308 this.active.show();
40309 this.fireEvent("tabchange", this, this.active);
40315 * Gets the active {@link Roo.TabPanelItem}.
40316 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40318 getActiveTab : function(){
40319 return this.active;
40323 * Updates the tab body element to fit the height of the container element
40324 * for overflow scrolling
40325 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40327 syncHeight : function(targetHeight){
40328 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40329 var bm = this.bodyEl.getMargins();
40330 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40331 this.bodyEl.setHeight(newHeight);
40335 onResize : function(){
40336 if(this.monitorResize){
40337 this.autoSizeTabs();
40342 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40344 beginUpdate : function(){
40345 this.updating = true;
40349 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40351 endUpdate : function(){
40352 this.updating = false;
40353 this.autoSizeTabs();
40357 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40359 autoSizeTabs : function()
40361 var count = this.items.length;
40362 var vcount = count - this.hiddenCount;
40365 this.stripEl.hide();
40367 this.stripEl.show();
40370 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40375 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40376 var availWidth = Math.floor(w / vcount);
40377 var b = this.stripBody;
40378 if(b.getWidth() > w){
40379 var tabs = this.items;
40380 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40381 if(availWidth < this.minTabWidth){
40382 /*if(!this.sleft){ // incomplete scrolling code
40383 this.createScrollButtons();
40386 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40389 if(this.currentTabWidth < this.preferredTabWidth){
40390 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40396 * Returns the number of tabs in this TabPanel.
40399 getCount : function(){
40400 return this.items.length;
40404 * Resizes all the tabs to the passed width
40405 * @param {Number} The new width
40407 setTabWidth : function(width){
40408 this.currentTabWidth = width;
40409 for(var i = 0, len = this.items.length; i < len; i++) {
40410 if(!this.items[i].isHidden()) {
40411 this.items[i].setWidth(width);
40417 * Destroys this TabPanel
40418 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40420 destroy : function(removeEl){
40421 Roo.EventManager.removeResizeListener(this.onResize, this);
40422 for(var i = 0, len = this.items.length; i < len; i++){
40423 this.items[i].purgeListeners();
40425 if(removeEl === true){
40426 this.el.update("");
40431 createStrip : function(container)
40433 var strip = document.createElement("nav");
40434 strip.className = Roo.bootstrap.version == 4 ?
40435 "navbar-light bg-light" :
40436 "navbar navbar-default"; //"x-tabs-wrap";
40437 container.appendChild(strip);
40441 createStripList : function(strip)
40443 // div wrapper for retard IE
40444 // returns the "tr" element.
40445 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40446 //'<div class="x-tabs-strip-wrap">'+
40447 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40448 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40449 return strip.firstChild; //.firstChild.firstChild.firstChild;
40451 createBody : function(container)
40453 var body = document.createElement("div");
40454 Roo.id(body, "tab-body");
40455 //Roo.fly(body).addClass("x-tabs-body");
40456 Roo.fly(body).addClass("tab-content");
40457 container.appendChild(body);
40460 createItemBody :function(bodyEl, id){
40461 var body = Roo.getDom(id);
40463 body = document.createElement("div");
40466 //Roo.fly(body).addClass("x-tabs-item-body");
40467 Roo.fly(body).addClass("tab-pane");
40468 bodyEl.insertBefore(body, bodyEl.firstChild);
40472 createStripElements : function(stripEl, text, closable, tpl)
40474 var td = document.createElement("li"); // was td..
40475 td.className = 'nav-item';
40477 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40480 stripEl.appendChild(td);
40482 td.className = "x-tabs-closable";
40483 if(!this.closeTpl){
40484 this.closeTpl = new Roo.Template(
40485 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40486 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40487 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40490 var el = this.closeTpl.overwrite(td, {"text": text});
40491 var close = el.getElementsByTagName("div")[0];
40492 var inner = el.getElementsByTagName("em")[0];
40493 return {"el": el, "close": close, "inner": inner};
40496 // not sure what this is..
40497 // if(!this.tabTpl){
40498 //this.tabTpl = new Roo.Template(
40499 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40500 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40502 // this.tabTpl = new Roo.Template(
40503 // '<a href="#">' +
40504 // '<span unselectable="on"' +
40505 // (this.disableTooltips ? '' : ' title="{text}"') +
40506 // ' >{text}</span></a>'
40512 var template = tpl || this.tabTpl || false;
40515 template = new Roo.Template(
40516 Roo.bootstrap.version == 4 ?
40518 '<a class="nav-link" href="#" unselectable="on"' +
40519 (this.disableTooltips ? '' : ' title="{text}"') +
40522 '<a class="nav-link" href="#">' +
40523 '<span unselectable="on"' +
40524 (this.disableTooltips ? '' : ' title="{text}"') +
40525 ' >{text}</span></a>'
40530 switch (typeof(template)) {
40534 template = new Roo.Template(template);
40540 var el = template.overwrite(td, {"text": text});
40542 var inner = el.getElementsByTagName("span")[0];
40544 return {"el": el, "inner": inner};
40552 * @class Roo.TabPanelItem
40553 * @extends Roo.util.Observable
40554 * Represents an individual item (tab plus body) in a TabPanel.
40555 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40556 * @param {String} id The id of this TabPanelItem
40557 * @param {String} text The text for the tab of this TabPanelItem
40558 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40560 Roo.bootstrap.panel.TabItem = function(config){
40562 * The {@link Roo.TabPanel} this TabPanelItem belongs to
40563 * @type Roo.TabPanel
40565 this.tabPanel = config.panel;
40567 * The id for this TabPanelItem
40570 this.id = config.id;
40572 this.disabled = false;
40574 this.text = config.text;
40576 this.loaded = false;
40577 this.closable = config.closable;
40580 * The body element for this TabPanelItem.
40581 * @type Roo.Element
40583 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40584 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40585 this.bodyEl.setStyle("display", "block");
40586 this.bodyEl.setStyle("zoom", "1");
40587 //this.hideAction();
40589 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40591 this.el = Roo.get(els.el);
40592 this.inner = Roo.get(els.inner, true);
40593 this.textEl = Roo.bootstrap.version == 4 ?
40594 this.el : Roo.get(this.el.dom.firstChild, true);
40596 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40597 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40600 // this.el.on("mousedown", this.onTabMouseDown, this);
40601 this.el.on("click", this.onTabClick, this);
40603 if(config.closable){
40604 var c = Roo.get(els.close, true);
40605 c.dom.title = this.closeText;
40606 c.addClassOnOver("close-over");
40607 c.on("click", this.closeClick, this);
40613 * Fires when this tab becomes the active tab.
40614 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40615 * @param {Roo.TabPanelItem} this
40619 * @event beforeclose
40620 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40621 * @param {Roo.TabPanelItem} this
40622 * @param {Object} e Set cancel to true on this object to cancel the close.
40624 "beforeclose": true,
40627 * Fires when this tab is closed.
40628 * @param {Roo.TabPanelItem} this
40632 * @event deactivate
40633 * Fires when this tab is no longer the active tab.
40634 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40635 * @param {Roo.TabPanelItem} this
40637 "deactivate" : true
40639 this.hidden = false;
40641 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40644 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40646 purgeListeners : function(){
40647 Roo.util.Observable.prototype.purgeListeners.call(this);
40648 this.el.removeAllListeners();
40651 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40654 this.status_node.addClass("active");
40657 this.tabPanel.stripWrap.repaint();
40659 this.fireEvent("activate", this.tabPanel, this);
40663 * Returns true if this tab is the active tab.
40664 * @return {Boolean}
40666 isActive : function(){
40667 return this.tabPanel.getActiveTab() == this;
40671 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40674 this.status_node.removeClass("active");
40676 this.fireEvent("deactivate", this.tabPanel, this);
40679 hideAction : function(){
40680 this.bodyEl.hide();
40681 this.bodyEl.setStyle("position", "absolute");
40682 this.bodyEl.setLeft("-20000px");
40683 this.bodyEl.setTop("-20000px");
40686 showAction : function(){
40687 this.bodyEl.setStyle("position", "relative");
40688 this.bodyEl.setTop("");
40689 this.bodyEl.setLeft("");
40690 this.bodyEl.show();
40694 * Set the tooltip for the tab.
40695 * @param {String} tooltip The tab's tooltip
40697 setTooltip : function(text){
40698 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40699 this.textEl.dom.qtip = text;
40700 this.textEl.dom.removeAttribute('title');
40702 this.textEl.dom.title = text;
40706 onTabClick : function(e){
40707 e.preventDefault();
40708 this.tabPanel.activate(this.id);
40711 onTabMouseDown : function(e){
40712 e.preventDefault();
40713 this.tabPanel.activate(this.id);
40716 getWidth : function(){
40717 return this.inner.getWidth();
40720 setWidth : function(width){
40721 var iwidth = width - this.linode.getPadding("lr");
40722 this.inner.setWidth(iwidth);
40723 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40724 this.linode.setWidth(width);
40728 * Show or hide the tab
40729 * @param {Boolean} hidden True to hide or false to show.
40731 setHidden : function(hidden){
40732 this.hidden = hidden;
40733 this.linode.setStyle("display", hidden ? "none" : "");
40737 * Returns true if this tab is "hidden"
40738 * @return {Boolean}
40740 isHidden : function(){
40741 return this.hidden;
40745 * Returns the text for this tab
40748 getText : function(){
40752 autoSize : function(){
40753 //this.el.beginMeasure();
40754 this.textEl.setWidth(1);
40756 * #2804 [new] Tabs in Roojs
40757 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40759 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40760 //this.el.endMeasure();
40764 * Sets the text for the tab (Note: this also sets the tooltip text)
40765 * @param {String} text The tab's text and tooltip
40767 setText : function(text){
40769 this.textEl.update(text);
40770 this.setTooltip(text);
40771 //if(!this.tabPanel.resizeTabs){
40772 // this.autoSize();
40776 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40778 activate : function(){
40779 this.tabPanel.activate(this.id);
40783 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40785 disable : function(){
40786 if(this.tabPanel.active != this){
40787 this.disabled = true;
40788 this.status_node.addClass("disabled");
40793 * Enables this TabPanelItem if it was previously disabled.
40795 enable : function(){
40796 this.disabled = false;
40797 this.status_node.removeClass("disabled");
40801 * Sets the content for this TabPanelItem.
40802 * @param {String} content The content
40803 * @param {Boolean} loadScripts true to look for and load scripts
40805 setContent : function(content, loadScripts){
40806 this.bodyEl.update(content, loadScripts);
40810 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40811 * @return {Roo.UpdateManager} The UpdateManager
40813 getUpdateManager : function(){
40814 return this.bodyEl.getUpdateManager();
40818 * Set a URL to be used to load the content for this TabPanelItem.
40819 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40820 * @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)
40821 * @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)
40822 * @return {Roo.UpdateManager} The UpdateManager
40824 setUrl : function(url, params, loadOnce){
40825 if(this.refreshDelegate){
40826 this.un('activate', this.refreshDelegate);
40828 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40829 this.on("activate", this.refreshDelegate);
40830 return this.bodyEl.getUpdateManager();
40834 _handleRefresh : function(url, params, loadOnce){
40835 if(!loadOnce || !this.loaded){
40836 var updater = this.bodyEl.getUpdateManager();
40837 updater.update(url, params, this._setLoaded.createDelegate(this));
40842 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
40843 * Will fail silently if the setUrl method has not been called.
40844 * This does not activate the panel, just updates its content.
40846 refresh : function(){
40847 if(this.refreshDelegate){
40848 this.loaded = false;
40849 this.refreshDelegate();
40854 _setLoaded : function(){
40855 this.loaded = true;
40859 closeClick : function(e){
40862 this.fireEvent("beforeclose", this, o);
40863 if(o.cancel !== true){
40864 this.tabPanel.removeTab(this.id);
40868 * The text displayed in the tooltip for the close icon.
40871 closeText : "Close this tab"
40874 * This script refer to:
40875 * Title: International Telephone Input
40876 * Author: Jack O'Connor
40877 * Code version: v12.1.12
40878 * Availability: https://github.com/jackocnr/intl-tel-input.git
40881 Roo.bootstrap.PhoneInputData = function() {
40884 "Afghanistan (افغانستان)",
40889 "Albania (Shqipëri)",
40894 "Algeria (الجزائر)",
40919 "Antigua and Barbuda",
40929 "Armenia (Հայաստան)",
40945 "Austria (Österreich)",
40950 "Azerbaijan (Azərbaycan)",
40960 "Bahrain (البحرين)",
40965 "Bangladesh (বাংলাদেশ)",
40975 "Belarus (Беларусь)",
40980 "Belgium (België)",
41010 "Bosnia and Herzegovina (Босна и Херцеговина)",
41025 "British Indian Ocean Territory",
41030 "British Virgin Islands",
41040 "Bulgaria (България)",
41050 "Burundi (Uburundi)",
41055 "Cambodia (កម្ពុជា)",
41060 "Cameroon (Cameroun)",
41069 ["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"]
41072 "Cape Verde (Kabu Verdi)",
41077 "Caribbean Netherlands",
41088 "Central African Republic (République centrafricaine)",
41108 "Christmas Island",
41114 "Cocos (Keeling) Islands",
41125 "Comoros (جزر القمر)",
41130 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41135 "Congo (Republic) (Congo-Brazzaville)",
41155 "Croatia (Hrvatska)",
41176 "Czech Republic (Česká republika)",
41181 "Denmark (Danmark)",
41196 "Dominican Republic (República Dominicana)",
41200 ["809", "829", "849"]
41218 "Equatorial Guinea (Guinea Ecuatorial)",
41238 "Falkland Islands (Islas Malvinas)",
41243 "Faroe Islands (Føroyar)",
41264 "French Guiana (Guyane française)",
41269 "French Polynesia (Polynésie française)",
41284 "Georgia (საქართველო)",
41289 "Germany (Deutschland)",
41309 "Greenland (Kalaallit Nunaat)",
41346 "Guinea-Bissau (Guiné Bissau)",
41371 "Hungary (Magyarország)",
41376 "Iceland (Ísland)",
41396 "Iraq (العراق)",
41412 "Israel (ישראל)",
41439 "Jordan (الأردن)",
41444 "Kazakhstan (Казахстан)",
41465 "Kuwait (الكويت)",
41470 "Kyrgyzstan (Кыргызстан)",
41480 "Latvia (Latvija)",
41485 "Lebanon (لبنان)",
41500 "Libya (ليبيا)",
41510 "Lithuania (Lietuva)",
41525 "Macedonia (FYROM) (Македонија)",
41530 "Madagascar (Madagasikara)",
41560 "Marshall Islands",
41570 "Mauritania (موريتانيا)",
41575 "Mauritius (Moris)",
41596 "Moldova (Republica Moldova)",
41606 "Mongolia (Монгол)",
41611 "Montenegro (Crna Gora)",
41621 "Morocco (المغرب)",
41627 "Mozambique (Moçambique)",
41632 "Myanmar (Burma) (မြန်မာ)",
41637 "Namibia (Namibië)",
41652 "Netherlands (Nederland)",
41657 "New Caledonia (Nouvelle-Calédonie)",
41692 "North Korea (조선 민주주의 인민 공화국)",
41697 "Northern Mariana Islands",
41713 "Pakistan (پاکستان)",
41723 "Palestine (فلسطين)",
41733 "Papua New Guinea",
41775 "Réunion (La Réunion)",
41781 "Romania (România)",
41797 "Saint Barthélemy",
41808 "Saint Kitts and Nevis",
41818 "Saint Martin (Saint-Martin (partie française))",
41824 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41829 "Saint Vincent and the Grenadines",
41844 "São Tomé and Príncipe (São Tomé e Príncipe)",
41849 "Saudi Arabia (المملكة العربية السعودية)",
41854 "Senegal (Sénégal)",
41884 "Slovakia (Slovensko)",
41889 "Slovenia (Slovenija)",
41899 "Somalia (Soomaaliya)",
41909 "South Korea (대한민국)",
41914 "South Sudan (جنوب السودان)",
41924 "Sri Lanka (ශ්රී ලංකාව)",
41929 "Sudan (السودان)",
41939 "Svalbard and Jan Mayen",
41950 "Sweden (Sverige)",
41955 "Switzerland (Schweiz)",
41960 "Syria (سوريا)",
42005 "Trinidad and Tobago",
42010 "Tunisia (تونس)",
42015 "Turkey (Türkiye)",
42025 "Turks and Caicos Islands",
42035 "U.S. Virgin Islands",
42045 "Ukraine (Україна)",
42050 "United Arab Emirates (الإمارات العربية المتحدة)",
42072 "Uzbekistan (Oʻzbekiston)",
42082 "Vatican City (Città del Vaticano)",
42093 "Vietnam (Việt Nam)",
42098 "Wallis and Futuna (Wallis-et-Futuna)",
42103 "Western Sahara (الصحراء الغربية)",
42109 "Yemen (اليمن)",
42133 * This script refer to:
42134 * Title: International Telephone Input
42135 * Author: Jack O'Connor
42136 * Code version: v12.1.12
42137 * Availability: https://github.com/jackocnr/intl-tel-input.git
42141 * @class Roo.bootstrap.PhoneInput
42142 * @extends Roo.bootstrap.TriggerField
42143 * An input with International dial-code selection
42145 * @cfg {String} defaultDialCode default '+852'
42146 * @cfg {Array} preferedCountries default []
42149 * Create a new PhoneInput.
42150 * @param {Object} config Configuration options
42153 Roo.bootstrap.PhoneInput = function(config) {
42154 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42157 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42159 listWidth: undefined,
42161 selectedClass: 'active',
42163 invalidClass : "has-warning",
42165 validClass: 'has-success',
42167 allowed: '0123456789',
42172 * @cfg {String} defaultDialCode The default dial code when initializing the input
42174 defaultDialCode: '+852',
42177 * @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
42179 preferedCountries: false,
42181 getAutoCreate : function()
42183 var data = Roo.bootstrap.PhoneInputData();
42184 var align = this.labelAlign || this.parentLabelAlign();
42187 this.allCountries = [];
42188 this.dialCodeMapping = [];
42190 for (var i = 0; i < data.length; i++) {
42192 this.allCountries[i] = {
42196 priority: c[3] || 0,
42197 areaCodes: c[4] || null
42199 this.dialCodeMapping[c[2]] = {
42202 priority: c[3] || 0,
42203 areaCodes: c[4] || null
42215 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42216 maxlength: this.max_length,
42217 cls : 'form-control tel-input',
42218 autocomplete: 'new-password'
42221 var hiddenInput = {
42224 cls: 'hidden-tel-input'
42228 hiddenInput.name = this.name;
42231 if (this.disabled) {
42232 input.disabled = true;
42235 var flag_container = {
42252 cls: this.hasFeedback ? 'has-feedback' : '',
42258 cls: 'dial-code-holder',
42265 cls: 'roo-select2-container input-group',
42272 if (this.fieldLabel.length) {
42275 tooltip: 'This field is required'
42281 cls: 'control-label',
42287 html: this.fieldLabel
42290 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42296 if(this.indicatorpos == 'right') {
42297 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42304 if(align == 'left') {
42312 if(this.labelWidth > 12){
42313 label.style = "width: " + this.labelWidth + 'px';
42315 if(this.labelWidth < 13 && this.labelmd == 0){
42316 this.labelmd = this.labelWidth;
42318 if(this.labellg > 0){
42319 label.cls += ' col-lg-' + this.labellg;
42320 input.cls += ' col-lg-' + (12 - this.labellg);
42322 if(this.labelmd > 0){
42323 label.cls += ' col-md-' + this.labelmd;
42324 container.cls += ' col-md-' + (12 - this.labelmd);
42326 if(this.labelsm > 0){
42327 label.cls += ' col-sm-' + this.labelsm;
42328 container.cls += ' col-sm-' + (12 - this.labelsm);
42330 if(this.labelxs > 0){
42331 label.cls += ' col-xs-' + this.labelxs;
42332 container.cls += ' col-xs-' + (12 - this.labelxs);
42342 var settings = this;
42344 ['xs','sm','md','lg'].map(function(size){
42345 if (settings[size]) {
42346 cfg.cls += ' col-' + size + '-' + settings[size];
42350 this.store = new Roo.data.Store({
42351 proxy : new Roo.data.MemoryProxy({}),
42352 reader : new Roo.data.JsonReader({
42363 'name' : 'dialCode',
42367 'name' : 'priority',
42371 'name' : 'areaCodes',
42378 if(!this.preferedCountries) {
42379 this.preferedCountries = [
42386 var p = this.preferedCountries.reverse();
42389 for (var i = 0; i < p.length; i++) {
42390 for (var j = 0; j < this.allCountries.length; j++) {
42391 if(this.allCountries[j].iso2 == p[i]) {
42392 var t = this.allCountries[j];
42393 this.allCountries.splice(j,1);
42394 this.allCountries.unshift(t);
42400 this.store.proxy.data = {
42402 data: this.allCountries
42408 initEvents : function()
42411 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42413 this.indicator = this.indicatorEl();
42414 this.flag = this.flagEl();
42415 this.dialCodeHolder = this.dialCodeHolderEl();
42417 this.trigger = this.el.select('div.flag-box',true).first();
42418 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42423 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42424 _this.list.setWidth(lw);
42427 this.list.on('mouseover', this.onViewOver, this);
42428 this.list.on('mousemove', this.onViewMove, this);
42429 this.inputEl().on("keyup", this.onKeyUp, this);
42430 this.inputEl().on("keypress", this.onKeyPress, this);
42432 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42434 this.view = new Roo.View(this.list, this.tpl, {
42435 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42438 this.view.on('click', this.onViewClick, this);
42439 this.setValue(this.defaultDialCode);
42442 onTriggerClick : function(e)
42444 Roo.log('trigger click');
42449 if(this.isExpanded()){
42451 this.hasFocus = false;
42453 this.store.load({});
42454 this.hasFocus = true;
42459 isExpanded : function()
42461 return this.list.isVisible();
42464 collapse : function()
42466 if(!this.isExpanded()){
42470 Roo.get(document).un('mousedown', this.collapseIf, this);
42471 Roo.get(document).un('mousewheel', this.collapseIf, this);
42472 this.fireEvent('collapse', this);
42476 expand : function()
42480 if(this.isExpanded() || !this.hasFocus){
42484 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42485 this.list.setWidth(lw);
42488 this.restrictHeight();
42490 Roo.get(document).on('mousedown', this.collapseIf, this);
42491 Roo.get(document).on('mousewheel', this.collapseIf, this);
42493 this.fireEvent('expand', this);
42496 restrictHeight : function()
42498 this.list.alignTo(this.inputEl(), this.listAlign);
42499 this.list.alignTo(this.inputEl(), this.listAlign);
42502 onViewOver : function(e, t)
42504 if(this.inKeyMode){
42507 var item = this.view.findItemFromChild(t);
42510 var index = this.view.indexOf(item);
42511 this.select(index, false);
42516 onViewClick : function(view, doFocus, el, e)
42518 var index = this.view.getSelectedIndexes()[0];
42520 var r = this.store.getAt(index);
42523 this.onSelect(r, index);
42525 if(doFocus !== false && !this.blockFocus){
42526 this.inputEl().focus();
42530 onViewMove : function(e, t)
42532 this.inKeyMode = false;
42535 select : function(index, scrollIntoView)
42537 this.selectedIndex = index;
42538 this.view.select(index);
42539 if(scrollIntoView !== false){
42540 var el = this.view.getNode(index);
42542 this.list.scrollChildIntoView(el, false);
42547 createList : function()
42549 this.list = Roo.get(document.body).createChild({
42551 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42552 style: 'display:none'
42555 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42558 collapseIf : function(e)
42560 var in_combo = e.within(this.el);
42561 var in_list = e.within(this.list);
42562 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42564 if (in_combo || in_list || is_list) {
42570 onSelect : function(record, index)
42572 if(this.fireEvent('beforeselect', this, record, index) !== false){
42574 this.setFlagClass(record.data.iso2);
42575 this.setDialCode(record.data.dialCode);
42576 this.hasFocus = false;
42578 this.fireEvent('select', this, record, index);
42582 flagEl : function()
42584 var flag = this.el.select('div.flag',true).first();
42591 dialCodeHolderEl : function()
42593 var d = this.el.select('input.dial-code-holder',true).first();
42600 setDialCode : function(v)
42602 this.dialCodeHolder.dom.value = '+'+v;
42605 setFlagClass : function(n)
42607 this.flag.dom.className = 'flag '+n;
42610 getValue : function()
42612 var v = this.inputEl().getValue();
42613 if(this.dialCodeHolder) {
42614 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42619 setValue : function(v)
42621 var d = this.getDialCode(v);
42623 //invalid dial code
42624 if(v.length == 0 || !d || d.length == 0) {
42626 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42627 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42633 this.setFlagClass(this.dialCodeMapping[d].iso2);
42634 this.setDialCode(d);
42635 this.inputEl().dom.value = v.replace('+'+d,'');
42636 this.hiddenEl().dom.value = this.getValue();
42641 getDialCode : function(v)
42645 if (v.length == 0) {
42646 return this.dialCodeHolder.dom.value;
42650 if (v.charAt(0) != "+") {
42653 var numericChars = "";
42654 for (var i = 1; i < v.length; i++) {
42655 var c = v.charAt(i);
42658 if (this.dialCodeMapping[numericChars]) {
42659 dialCode = v.substr(1, i);
42661 if (numericChars.length == 4) {
42671 this.setValue(this.defaultDialCode);
42675 hiddenEl : function()
42677 return this.el.select('input.hidden-tel-input',true).first();
42680 // after setting val
42681 onKeyUp : function(e){
42682 this.setValue(this.getValue());
42685 onKeyPress : function(e){
42686 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42693 * @class Roo.bootstrap.MoneyField
42694 * @extends Roo.bootstrap.ComboBox
42695 * Bootstrap MoneyField class
42698 * Create a new MoneyField.
42699 * @param {Object} config Configuration options
42702 Roo.bootstrap.MoneyField = function(config) {
42704 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42708 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42711 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42713 allowDecimals : true,
42715 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42717 decimalSeparator : ".",
42719 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42721 decimalPrecision : 0,
42723 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42725 allowNegative : true,
42727 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42731 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42733 minValue : Number.NEGATIVE_INFINITY,
42735 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42737 maxValue : Number.MAX_VALUE,
42739 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42741 minText : "The minimum value for this field is {0}",
42743 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42745 maxText : "The maximum value for this field is {0}",
42747 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
42748 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42750 nanText : "{0} is not a valid number",
42752 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42756 * @cfg {String} defaults currency of the MoneyField
42757 * value should be in lkey
42759 defaultCurrency : false,
42761 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42763 thousandsDelimiter : false,
42765 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42776 getAutoCreate : function()
42778 var align = this.labelAlign || this.parentLabelAlign();
42790 cls : 'form-control roo-money-amount-input',
42791 autocomplete: 'new-password'
42794 var hiddenInput = {
42798 cls: 'hidden-number-input'
42801 if(this.max_length) {
42802 input.maxlength = this.max_length;
42806 hiddenInput.name = this.name;
42809 if (this.disabled) {
42810 input.disabled = true;
42813 var clg = 12 - this.inputlg;
42814 var cmd = 12 - this.inputmd;
42815 var csm = 12 - this.inputsm;
42816 var cxs = 12 - this.inputxs;
42820 cls : 'row roo-money-field',
42824 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42828 cls: 'roo-select2-container input-group',
42832 cls : 'form-control roo-money-currency-input',
42833 autocomplete: 'new-password',
42835 name : this.currencyName
42839 cls : 'input-group-addon',
42853 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42857 cls: this.hasFeedback ? 'has-feedback' : '',
42868 if (this.fieldLabel.length) {
42871 tooltip: 'This field is required'
42877 cls: 'control-label',
42883 html: this.fieldLabel
42886 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42892 if(this.indicatorpos == 'right') {
42893 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42900 if(align == 'left') {
42908 if(this.labelWidth > 12){
42909 label.style = "width: " + this.labelWidth + 'px';
42911 if(this.labelWidth < 13 && this.labelmd == 0){
42912 this.labelmd = this.labelWidth;
42914 if(this.labellg > 0){
42915 label.cls += ' col-lg-' + this.labellg;
42916 input.cls += ' col-lg-' + (12 - this.labellg);
42918 if(this.labelmd > 0){
42919 label.cls += ' col-md-' + this.labelmd;
42920 container.cls += ' col-md-' + (12 - this.labelmd);
42922 if(this.labelsm > 0){
42923 label.cls += ' col-sm-' + this.labelsm;
42924 container.cls += ' col-sm-' + (12 - this.labelsm);
42926 if(this.labelxs > 0){
42927 label.cls += ' col-xs-' + this.labelxs;
42928 container.cls += ' col-xs-' + (12 - this.labelxs);
42939 var settings = this;
42941 ['xs','sm','md','lg'].map(function(size){
42942 if (settings[size]) {
42943 cfg.cls += ' col-' + size + '-' + settings[size];
42950 initEvents : function()
42952 this.indicator = this.indicatorEl();
42954 this.initCurrencyEvent();
42956 this.initNumberEvent();
42959 initCurrencyEvent : function()
42962 throw "can not find store for combo";
42965 this.store = Roo.factory(this.store, Roo.data);
42966 this.store.parent = this;
42970 this.triggerEl = this.el.select('.input-group-addon', true).first();
42972 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42977 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42978 _this.list.setWidth(lw);
42981 this.list.on('mouseover', this.onViewOver, this);
42982 this.list.on('mousemove', this.onViewMove, this);
42983 this.list.on('scroll', this.onViewScroll, this);
42986 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42989 this.view = new Roo.View(this.list, this.tpl, {
42990 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42993 this.view.on('click', this.onViewClick, this);
42995 this.store.on('beforeload', this.onBeforeLoad, this);
42996 this.store.on('load', this.onLoad, this);
42997 this.store.on('loadexception', this.onLoadException, this);
42999 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43000 "up" : function(e){
43001 this.inKeyMode = true;
43005 "down" : function(e){
43006 if(!this.isExpanded()){
43007 this.onTriggerClick();
43009 this.inKeyMode = true;
43014 "enter" : function(e){
43017 if(this.fireEvent("specialkey", this, e)){
43018 this.onViewClick(false);
43024 "esc" : function(e){
43028 "tab" : function(e){
43031 if(this.fireEvent("specialkey", this, e)){
43032 this.onViewClick(false);
43040 doRelay : function(foo, bar, hname){
43041 if(hname == 'down' || this.scope.isExpanded()){
43042 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43050 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43054 initNumberEvent : function(e)
43056 this.inputEl().on("keydown" , this.fireKey, this);
43057 this.inputEl().on("focus", this.onFocus, this);
43058 this.inputEl().on("blur", this.onBlur, this);
43060 this.inputEl().relayEvent('keyup', this);
43062 if(this.indicator){
43063 this.indicator.addClass('invisible');
43066 this.originalValue = this.getValue();
43068 if(this.validationEvent == 'keyup'){
43069 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43070 this.inputEl().on('keyup', this.filterValidation, this);
43072 else if(this.validationEvent !== false){
43073 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43076 if(this.selectOnFocus){
43077 this.on("focus", this.preFocus, this);
43080 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43081 this.inputEl().on("keypress", this.filterKeys, this);
43083 this.inputEl().relayEvent('keypress', this);
43086 var allowed = "0123456789";
43088 if(this.allowDecimals){
43089 allowed += this.decimalSeparator;
43092 if(this.allowNegative){
43096 if(this.thousandsDelimiter) {
43100 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43102 var keyPress = function(e){
43104 var k = e.getKey();
43106 var c = e.getCharCode();
43109 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43110 allowed.indexOf(String.fromCharCode(c)) === -1
43116 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43120 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43125 this.inputEl().on("keypress", keyPress, this);
43129 onTriggerClick : function(e)
43136 this.loadNext = false;
43138 if(this.isExpanded()){
43143 this.hasFocus = true;
43145 if(this.triggerAction == 'all') {
43146 this.doQuery(this.allQuery, true);
43150 this.doQuery(this.getRawValue());
43153 getCurrency : function()
43155 var v = this.currencyEl().getValue();
43160 restrictHeight : function()
43162 this.list.alignTo(this.currencyEl(), this.listAlign);
43163 this.list.alignTo(this.currencyEl(), this.listAlign);
43166 onViewClick : function(view, doFocus, el, e)
43168 var index = this.view.getSelectedIndexes()[0];
43170 var r = this.store.getAt(index);
43173 this.onSelect(r, index);
43177 onSelect : function(record, index){
43179 if(this.fireEvent('beforeselect', this, record, index) !== false){
43181 this.setFromCurrencyData(index > -1 ? record.data : false);
43185 this.fireEvent('select', this, record, index);
43189 setFromCurrencyData : function(o)
43193 this.lastCurrency = o;
43195 if (this.currencyField) {
43196 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43198 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43201 this.lastSelectionText = currency;
43203 //setting default currency
43204 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43205 this.setCurrency(this.defaultCurrency);
43209 this.setCurrency(currency);
43212 setFromData : function(o)
43216 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43218 this.setFromCurrencyData(c);
43223 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43225 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43228 this.setValue(value);
43232 setCurrency : function(v)
43234 this.currencyValue = v;
43237 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43242 setValue : function(v)
43244 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43250 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43252 this.inputEl().dom.value = (v == '') ? '' :
43253 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43255 if(!this.allowZero && v === '0') {
43256 this.hiddenEl().dom.value = '';
43257 this.inputEl().dom.value = '';
43264 getRawValue : function()
43266 var v = this.inputEl().getValue();
43271 getValue : function()
43273 return this.fixPrecision(this.parseValue(this.getRawValue()));
43276 parseValue : function(value)
43278 if(this.thousandsDelimiter) {
43280 r = new RegExp(",", "g");
43281 value = value.replace(r, "");
43284 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43285 return isNaN(value) ? '' : value;
43289 fixPrecision : function(value)
43291 if(this.thousandsDelimiter) {
43293 r = new RegExp(",", "g");
43294 value = value.replace(r, "");
43297 var nan = isNaN(value);
43299 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43300 return nan ? '' : value;
43302 return parseFloat(value).toFixed(this.decimalPrecision);
43305 decimalPrecisionFcn : function(v)
43307 return Math.floor(v);
43310 validateValue : function(value)
43312 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43316 var num = this.parseValue(value);
43319 this.markInvalid(String.format(this.nanText, value));
43323 if(num < this.minValue){
43324 this.markInvalid(String.format(this.minText, this.minValue));
43328 if(num > this.maxValue){
43329 this.markInvalid(String.format(this.maxText, this.maxValue));
43336 validate : function()
43338 if(this.disabled || this.allowBlank){
43343 var currency = this.getCurrency();
43345 if(this.validateValue(this.getRawValue()) && currency.length){
43350 this.markInvalid();
43354 getName: function()
43359 beforeBlur : function()
43365 var v = this.parseValue(this.getRawValue());
43372 onBlur : function()
43376 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43377 //this.el.removeClass(this.focusClass);
43380 this.hasFocus = false;
43382 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43386 var v = this.getValue();
43388 if(String(v) !== String(this.startValue)){
43389 this.fireEvent('change', this, v, this.startValue);
43392 this.fireEvent("blur", this);
43395 inputEl : function()
43397 return this.el.select('.roo-money-amount-input', true).first();
43400 currencyEl : function()
43402 return this.el.select('.roo-money-currency-input', true).first();
43405 hiddenEl : function()
43407 return this.el.select('input.hidden-number-input',true).first();
43411 * @class Roo.bootstrap.BezierSignature
43412 * @extends Roo.bootstrap.Component
43413 * Bootstrap BezierSignature class
43414 * This script refer to:
43415 * Title: Signature Pad
43417 * Availability: https://github.com/szimek/signature_pad
43420 * Create a new BezierSignature
43421 * @param {Object} config The config object
43424 Roo.bootstrap.BezierSignature = function(config){
43425 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43431 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43438 mouse_btn_down: true,
43441 * @cfg {int} canvas height
43443 canvas_height: '200px',
43446 * @cfg {float|function} Radius of a single dot.
43451 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43456 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43461 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43466 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43471 * @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.
43473 bg_color: 'rgba(0, 0, 0, 0)',
43476 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43478 dot_color: 'black',
43481 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43483 velocity_filter_weight: 0.7,
43486 * @cfg {function} Callback when stroke begin.
43491 * @cfg {function} Callback when stroke end.
43495 getAutoCreate : function()
43497 var cls = 'roo-signature column';
43500 cls += ' ' + this.cls;
43510 for(var i = 0; i < col_sizes.length; i++) {
43511 if(this[col_sizes[i]]) {
43512 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43522 cls: 'roo-signature-body',
43526 cls: 'roo-signature-body-canvas',
43527 height: this.canvas_height,
43528 width: this.canvas_width
43535 style: 'display: none'
43543 initEvents: function()
43545 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43547 var canvas = this.canvasEl();
43549 // mouse && touch event swapping...
43550 canvas.dom.style.touchAction = 'none';
43551 canvas.dom.style.msTouchAction = 'none';
43553 this.mouse_btn_down = false;
43554 canvas.on('mousedown', this._handleMouseDown, this);
43555 canvas.on('mousemove', this._handleMouseMove, this);
43556 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43558 if (window.PointerEvent) {
43559 canvas.on('pointerdown', this._handleMouseDown, this);
43560 canvas.on('pointermove', this._handleMouseMove, this);
43561 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43564 if ('ontouchstart' in window) {
43565 canvas.on('touchstart', this._handleTouchStart, this);
43566 canvas.on('touchmove', this._handleTouchMove, this);
43567 canvas.on('touchend', this._handleTouchEnd, this);
43570 Roo.EventManager.onWindowResize(this.resize, this, true);
43572 // file input event
43573 this.fileEl().on('change', this.uploadImage, this);
43580 resize: function(){
43582 var canvas = this.canvasEl().dom;
43583 var ctx = this.canvasElCtx();
43584 var img_data = false;
43586 if(canvas.width > 0) {
43587 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43589 // setting canvas width will clean img data
43592 var style = window.getComputedStyle ?
43593 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43595 var padding_left = parseInt(style.paddingLeft) || 0;
43596 var padding_right = parseInt(style.paddingRight) || 0;
43598 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43601 ctx.putImageData(img_data, 0, 0);
43605 _handleMouseDown: function(e)
43607 if (e.browserEvent.which === 1) {
43608 this.mouse_btn_down = true;
43609 this.strokeBegin(e);
43613 _handleMouseMove: function (e)
43615 if (this.mouse_btn_down) {
43616 this.strokeMoveUpdate(e);
43620 _handleMouseUp: function (e)
43622 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43623 this.mouse_btn_down = false;
43628 _handleTouchStart: function (e) {
43630 e.preventDefault();
43631 if (e.browserEvent.targetTouches.length === 1) {
43632 // var touch = e.browserEvent.changedTouches[0];
43633 // this.strokeBegin(touch);
43635 this.strokeBegin(e); // assume e catching the correct xy...
43639 _handleTouchMove: function (e) {
43640 e.preventDefault();
43641 // var touch = event.targetTouches[0];
43642 // _this._strokeMoveUpdate(touch);
43643 this.strokeMoveUpdate(e);
43646 _handleTouchEnd: function (e) {
43647 var wasCanvasTouched = e.target === this.canvasEl().dom;
43648 if (wasCanvasTouched) {
43649 e.preventDefault();
43650 // var touch = event.changedTouches[0];
43651 // _this._strokeEnd(touch);
43656 reset: function () {
43657 this._lastPoints = [];
43658 this._lastVelocity = 0;
43659 this._lastWidth = (this.min_width + this.max_width) / 2;
43660 this.canvasElCtx().fillStyle = this.dot_color;
43663 strokeMoveUpdate: function(e)
43665 this.strokeUpdate(e);
43667 if (this.throttle) {
43668 this.throttleStroke(this.strokeUpdate, this.throttle);
43671 this.strokeUpdate(e);
43675 strokeBegin: function(e)
43677 var newPointGroup = {
43678 color: this.dot_color,
43682 if (typeof this.onBegin === 'function') {
43686 this.curve_data.push(newPointGroup);
43688 this.strokeUpdate(e);
43691 strokeUpdate: function(e)
43693 var rect = this.canvasEl().dom.getBoundingClientRect();
43694 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43695 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43696 var lastPoints = lastPointGroup.points;
43697 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43698 var isLastPointTooClose = lastPoint
43699 ? point.distanceTo(lastPoint) <= this.min_distance
43701 var color = lastPointGroup.color;
43702 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43703 var curve = this.addPoint(point);
43705 this.drawDot({color: color, point: point});
43708 this.drawCurve({color: color, curve: curve});
43718 strokeEnd: function(e)
43720 this.strokeUpdate(e);
43721 if (typeof this.onEnd === 'function') {
43726 addPoint: function (point) {
43727 var _lastPoints = this._lastPoints;
43728 _lastPoints.push(point);
43729 if (_lastPoints.length > 2) {
43730 if (_lastPoints.length === 3) {
43731 _lastPoints.unshift(_lastPoints[0]);
43733 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43734 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43735 _lastPoints.shift();
43741 calculateCurveWidths: function (startPoint, endPoint) {
43742 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43743 (1 - this.velocity_filter_weight) * this._lastVelocity;
43745 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43748 start: this._lastWidth
43751 this._lastVelocity = velocity;
43752 this._lastWidth = newWidth;
43756 drawDot: function (_a) {
43757 var color = _a.color, point = _a.point;
43758 var ctx = this.canvasElCtx();
43759 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43761 this.drawCurveSegment(point.x, point.y, width);
43763 ctx.fillStyle = color;
43767 drawCurve: function (_a) {
43768 var color = _a.color, curve = _a.curve;
43769 var ctx = this.canvasElCtx();
43770 var widthDelta = curve.endWidth - curve.startWidth;
43771 var drawSteps = Math.floor(curve.length()) * 2;
43773 ctx.fillStyle = color;
43774 for (var i = 0; i < drawSteps; i += 1) {
43775 var t = i / drawSteps;
43781 var x = uuu * curve.startPoint.x;
43782 x += 3 * uu * t * curve.control1.x;
43783 x += 3 * u * tt * curve.control2.x;
43784 x += ttt * curve.endPoint.x;
43785 var y = uuu * curve.startPoint.y;
43786 y += 3 * uu * t * curve.control1.y;
43787 y += 3 * u * tt * curve.control2.y;
43788 y += ttt * curve.endPoint.y;
43789 var width = curve.startWidth + ttt * widthDelta;
43790 this.drawCurveSegment(x, y, width);
43796 drawCurveSegment: function (x, y, width) {
43797 var ctx = this.canvasElCtx();
43799 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43800 this.is_empty = false;
43805 var ctx = this.canvasElCtx();
43806 var canvas = this.canvasEl().dom;
43807 ctx.fillStyle = this.bg_color;
43808 ctx.clearRect(0, 0, canvas.width, canvas.height);
43809 ctx.fillRect(0, 0, canvas.width, canvas.height);
43810 this.curve_data = [];
43812 this.is_empty = true;
43817 return this.el.select('input',true).first();
43820 canvasEl: function()
43822 return this.el.select('canvas',true).first();
43825 canvasElCtx: function()
43827 return this.el.select('canvas',true).first().dom.getContext('2d');
43830 getImage: function(type)
43832 if(this.is_empty) {
43837 return this.canvasEl().dom.toDataURL('image/'+type, 1);
43840 drawFromImage: function(img_src)
43842 var img = new Image();
43844 img.onload = function(){
43845 this.canvasElCtx().drawImage(img, 0, 0);
43850 this.is_empty = false;
43853 selectImage: function()
43855 this.fileEl().dom.click();
43858 uploadImage: function(e)
43860 var reader = new FileReader();
43862 reader.onload = function(e){
43863 var img = new Image();
43864 img.onload = function(){
43866 this.canvasElCtx().drawImage(img, 0, 0);
43868 img.src = e.target.result;
43871 reader.readAsDataURL(e.target.files[0]);
43874 // Bezier Point Constructor
43875 Point: (function () {
43876 function Point(x, y, time) {
43879 this.time = time || Date.now();
43881 Point.prototype.distanceTo = function (start) {
43882 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43884 Point.prototype.equals = function (other) {
43885 return this.x === other.x && this.y === other.y && this.time === other.time;
43887 Point.prototype.velocityFrom = function (start) {
43888 return this.time !== start.time
43889 ? this.distanceTo(start) / (this.time - start.time)
43896 // Bezier Constructor
43897 Bezier: (function () {
43898 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43899 this.startPoint = startPoint;
43900 this.control2 = control2;
43901 this.control1 = control1;
43902 this.endPoint = endPoint;
43903 this.startWidth = startWidth;
43904 this.endWidth = endWidth;
43906 Bezier.fromPoints = function (points, widths, scope) {
43907 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43908 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43909 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43911 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43912 var dx1 = s1.x - s2.x;
43913 var dy1 = s1.y - s2.y;
43914 var dx2 = s2.x - s3.x;
43915 var dy2 = s2.y - s3.y;
43916 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43917 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43918 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43919 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43920 var dxm = m1.x - m2.x;
43921 var dym = m1.y - m2.y;
43922 var k = l2 / (l1 + l2);
43923 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43924 var tx = s2.x - cm.x;
43925 var ty = s2.y - cm.y;
43927 c1: new scope.Point(m1.x + tx, m1.y + ty),
43928 c2: new scope.Point(m2.x + tx, m2.y + ty)
43931 Bezier.prototype.length = function () {
43936 for (var i = 0; i <= steps; i += 1) {
43938 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43939 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43941 var xdiff = cx - px;
43942 var ydiff = cy - py;
43943 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43950 Bezier.prototype.point = function (t, start, c1, c2, end) {
43951 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43952 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43953 + (3.0 * c2 * (1.0 - t) * t * t)
43954 + (end * t * t * t);
43959 throttleStroke: function(fn, wait) {
43960 if (wait === void 0) { wait = 250; }
43962 var timeout = null;
43966 var later = function () {
43967 previous = Date.now();
43969 result = fn.apply(storedContext, storedArgs);
43971 storedContext = null;
43975 return function wrapper() {
43977 for (var _i = 0; _i < arguments.length; _i++) {
43978 args[_i] = arguments[_i];
43980 var now = Date.now();
43981 var remaining = wait - (now - previous);
43982 storedContext = this;
43984 if (remaining <= 0 || remaining > wait) {
43986 clearTimeout(timeout);
43990 result = fn.apply(storedContext, storedArgs);
43992 storedContext = null;
43996 else if (!timeout) {
43997 timeout = window.setTimeout(later, remaining);