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
1952 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1954 * @cfg {String} title
1955 * @cfg {String} subtitle
1956 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957 * @cfg {String} footer
1959 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1961 * @cfg {String} margin (0|1|2|3|4|5|auto)
1962 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1969 * @cfg {String} padding (0|1|2|3|4|5)
1970 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972 * @cfg {String} padding_left (0|1|2|3|4|5)
1973 * @cfg {String} padding_right (0|1|2|3|4|5)
1974 * @cfg {String} padding_x (0|1|2|3|4|5)
1975 * @cfg {String} padding_y (0|1|2|3|4|5)
1977 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1983 * @config {Boolean} dragable if this card can be dragged.
1984 * @config {String} drag_group group for drag
1985 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1986 * @config {String} drop_group group for drag
1988 * @config {Boolean} collapsable can the body be collapsed.
1989 * @config {Boolean} collapsed is the body collapsed when rendered...
1990 * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991 * @config {Boolean} rotated is the body rotated when rendered...
1994 * Create a new Container
1995 * @param {Object} config The config object
1998 Roo.bootstrap.Card = function(config){
1999 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2005 * When a element a card is dropped
2006 * @param {Roo.bootstrap.Card} this
2009 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010 * @param {String} position 'above' or 'below'
2011 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2017 * When a element a card is rotate
2018 * @param {Roo.bootstrap.Element} this
2019 * @param {Roo.Element} n the node being dropped?
2020 * @param {Boolean} rotate status
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2033 margin: '', /// may be better in component?
2063 collapsable : false,
2072 childContainer : false,
2073 dropEl : false, /// the dom placeholde element that indicates drop location.
2074 containerEl: false, // body container
2075 bodyEl: false, // card-body
2076 headerContainerEl : false, //
2079 layoutCls : function()
2083 Roo.log(this.margin_bottom.length);
2084 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2087 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2090 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2095 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2101 // more generic support?
2109 // Roo.log("Call onRender: " + this.xtype);
2110 /* We are looking at something like this.
2112 <img src="..." class="card-img-top" alt="...">
2113 <div class="card-body">
2114 <h5 class="card-title">Card title</h5>
2115 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2117 >> this bit is really the body...
2118 <div> << we will ad dthis in hopefully it will not break shit.
2120 ** card text does not actually have any styling...
2122 <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>
2125 <a href="#" class="card-link">Card link</a>
2128 <div class="card-footer">
2129 <small class="text-muted">Last updated 3 mins ago</small>
2133 getAutoCreate : function(){
2141 if (this.weight.length && this.weight != 'light') {
2142 cfg.cls += ' text-white';
2144 cfg.cls += ' text-dark'; // need as it's nested..
2146 if (this.weight.length) {
2147 cfg.cls += ' bg-' + this.weight;
2150 cfg.cls += this.layoutCls();
2153 var hdr_ctr = false;
2154 if (this.header.length) {
2156 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2165 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2171 if (this.collapsable) {
2174 cls : 'd-block user-select-none',
2178 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2183 hdr.cn.push(hdr_ctr);
2188 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2193 if (this.header_image.length) {
2196 cls : 'card-img-top',
2197 src: this.header_image // escape?
2202 cls : 'card-img-top d-none'
2208 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2212 if (this.collapsable || this.rotateable) {
2215 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2222 if (this.title.length) {
2226 src: this.title // escape?
2230 if (this.subtitle.length) {
2234 src: this.subtitle // escape?
2240 cls : 'roo-card-body-ctr'
2243 if (this.html.length) {
2249 // fixme ? handle objects?
2251 if (this.footer.length) {
2254 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2259 cfg.cn.push({cls : 'card-footer d-none'});
2268 getCardHeader : function()
2270 var ret = this.el.select('.card-header',true).first();
2271 if (ret.hasClass('d-none')) {
2272 ret.removeClass('d-none');
2277 getCardFooter : function()
2279 var ret = this.el.select('.card-footer',true).first();
2280 if (ret.hasClass('d-none')) {
2281 ret.removeClass('d-none');
2286 getCardImageTop : function()
2288 var ret = this.el.select('.card-img-top',true).first();
2289 if (ret.hasClass('d-none')) {
2290 ret.removeClass('d-none');
2296 getChildContainer : function()
2302 return this.el.select('.roo-card-body-ctr',true).first();
2305 initEvents: function()
2307 this.bodyEl = this.el.select('.card-body',true).first();
2308 this.containerEl = this.getChildContainer();
2310 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311 containerScroll: true,
2312 ddGroup: this.drag_group || 'default_card_drag_group'
2314 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2316 if (this.dropable) {
2317 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318 containerScroll: true,
2319 ddGroup: this.drop_group || 'default_card_drag_group'
2321 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2328 if (this.collapsable) {
2329 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2331 if (this.rotateable) {
2332 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2334 this.collapsableEl = this.el.select('.roo-collapsable').first();
2336 this.footerEl = this.el.select('.card-footer').first();
2337 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338 this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339 this.headerEl = this.el.select('.card-header',true).first();
2342 this.el.addClass('roo-card-rotated');
2343 this.fireEvent('rotate', this, true);
2347 getDragData : function(e)
2349 var target = this.getEl();
2351 //this.handleSelection(e);
2356 nodes: this.getEl(),
2361 dragData.ddel = target.dom ; // the div element
2362 Roo.log(target.getWidth( ));
2363 dragData.ddel.style.width = target.getWidth() + 'px';
2370 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2371 * whole Element becomes the target, and this causes the drop gesture to append.
2373 getTargetFromEvent : function(e, dragged_card_el)
2375 var target = e.getTarget();
2376 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377 target = target.parentNode;
2388 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389 // see if target is one of the 'cards'...
2392 //Roo.log(this.items.length);
2395 var last_card_n = 0;
2397 for (var i = 0;i< this.items.length;i++) {
2399 if (!this.items[i].el.hasClass('card')) {
2402 pos = this.getDropPoint(e, this.items[i].el.dom);
2404 cards_len = ret.cards.length;
2405 //Roo.log(this.items[i].el.dom.id);
2406 ret.cards.push(this.items[i]);
2408 if (ret.card_n < 0 && pos == 'above') {
2409 ret.position = cards_len > 0 ? 'below' : pos;
2410 ret.items_n = i > 0 ? i - 1 : 0;
2411 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2412 ret.card = ret.cards[ret.card_n];
2415 if (!ret.cards.length) {
2417 ret.position = 'below';
2421 // could not find a card.. stick it at the end..
2422 if (ret.card_n < 0) {
2423 ret.card_n = last_card_n;
2424 ret.card = ret.cards[last_card_n];
2425 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426 ret.position = 'below';
2429 if (this.items[ret.items_n].el == dragged_card_el) {
2433 if (ret.position == 'below') {
2434 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2436 if (card_after && card_after.el == dragged_card_el) {
2443 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2445 if (card_before && card_before.el == dragged_card_el) {
2452 onNodeEnter : function(n, dd, e, data){
2455 onNodeOver : function(n, dd, e, data)
2458 var target_info = this.getTargetFromEvent(e,data.source.el);
2459 if (target_info === false) {
2460 this.dropPlaceHolder('hide');
2463 Roo.log(['getTargetFromEvent', target_info ]);
2466 this.dropPlaceHolder('show', target_info,data);
2470 onNodeOut : function(n, dd, e, data){
2471 this.dropPlaceHolder('hide');
2474 onNodeDrop : function(n, dd, e, data)
2477 // call drop - return false if
2479 // this could actually fail - if the Network drops..
2480 // we will ignore this at present..- client should probably reload
2481 // the whole set of cards if stuff like that fails.
2484 var info = this.getTargetFromEvent(e,data.source.el);
2485 if (info === false) {
2488 this.dropPlaceHolder('hide');
2494 this.acceptCard(data.source, info.position, info.card, info.items_n);
2498 firstChildCard : function()
2500 for (var i = 0;i< this.items.length;i++) {
2502 if (!this.items[i].el.hasClass('card')) {
2505 return this.items[i];
2507 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2512 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2514 acceptCard : function(move_card, position, next_to_card )
2516 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2520 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2523 var dom = move_card.el.dom;
2524 dom.parentNode.removeChild(dom);
2525 dom.style.width = ''; // clear with - which is set by drag.
2527 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2528 var cardel = next_to_card.el.dom;
2530 if (position == 'above' ) {
2531 cardel.parentNode.insertBefore(dom, cardel);
2532 } else if (cardel.nextSibling) {
2533 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2535 cardel.parentNode.append(dom);
2538 // card container???
2539 this.containerEl.dom.append(dom);
2542 //FIXME HANDLE card = true
2544 // add this to the correct place in items.
2548 // remove Card from items.
2550 var old_parent = move_card.parent();
2552 old_parent.items = old_parent.items.filter(function(e) { return e != move_card });
2554 if (this.items.length) {
2556 //Roo.log([info.items_n, info.position, this.items.length]);
2557 for (var i =0; i < this.items.length; i++) {
2558 if (i == to_items_n && position == 'above') {
2559 nitems.push(move_card);
2561 nitems.push(this.items[i]);
2562 if (i == to_items_n && position == 'below') {
2563 nitems.push(move_card);
2566 this.items = nitems;
2567 Roo.log(this.items);
2569 this.items.push(move_card);
2572 move_card.parentId = this.id;
2580 /** Decide whether to drop above or below a View node. */
2581 getDropPoint : function(e, n, dd)
2586 if (n == this.containerEl.dom) {
2589 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2590 var c = t + (b - t) / 2;
2591 var y = Roo.lib.Event.getPageY(e);
2598 onToggleCollapse : function(e)
2600 if (this.collapsed) {
2601 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2602 this.collapsableEl.addClass('show');
2603 this.collapsed = false;
2606 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2607 this.collapsableEl.removeClass('show');
2608 this.collapsed = true;
2613 onToggleRotate : function(e)
2615 this.collapsableEl.removeClass('show');
2616 this.footerEl.removeClass('d-none');
2617 this.el.removeClass('roo-card-rotated');
2618 this.el.removeClass('d-none');
2621 this.collapsableEl.addClass('show');
2622 this.rotated = false;
2623 this.fireEvent('rotate', this, this.rotated);
2626 this.el.addClass('roo-card-rotated');
2627 this.footerEl.addClass('d-none');
2628 this.el.select('.roo-collapsable').removeClass('show');
2630 this.rotated = true;
2631 this.fireEvent('rotate', this, this.rotated);
2635 dropPlaceHolder: function (action, info, data)
2637 if (this.dropEl === false) {
2638 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2642 this.dropEl.removeClass(['d-none', 'd-block']);
2643 if (action == 'hide') {
2645 this.dropEl.addClass('d-none');
2648 // FIXME - info.card == true!!!
2649 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2651 if (info.card !== true) {
2652 var cardel = info.card.el.dom;
2654 if (info.position == 'above') {
2655 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2656 } else if (cardel.nextSibling) {
2657 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2659 cardel.parentNode.append(this.dropEl.dom);
2662 // card container???
2663 this.containerEl.dom.append(this.dropEl.dom);
2666 this.dropEl.addClass('d-block roo-card-dropzone');
2668 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2675 setHeaderText: function(html)
2677 this.headerContainerEl.dom.innerHTML = html;
2686 * Card header - holder for the card header elements.
2691 * @class Roo.bootstrap.CardHeader
2692 * @extends Roo.bootstrap.Element
2693 * Bootstrap CardHeader class
2695 * Create a new Card Header - that you can embed children into
2696 * @param {Object} config The config object
2699 Roo.bootstrap.CardHeader = function(config){
2700 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2703 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2706 container_method : 'getCardHeader'
2719 * Card footer - holder for the card footer elements.
2724 * @class Roo.bootstrap.CardFooter
2725 * @extends Roo.bootstrap.Element
2726 * Bootstrap CardFooter class
2728 * Create a new Card Footer - that you can embed children into
2729 * @param {Object} config The config object
2732 Roo.bootstrap.CardFooter = function(config){
2733 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2736 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2739 container_method : 'getCardFooter'
2752 * Card header - holder for the card header elements.
2757 * @class Roo.bootstrap.CardImageTop
2758 * @extends Roo.bootstrap.Element
2759 * Bootstrap CardImageTop class
2761 * Create a new Card Image Top container
2762 * @param {Object} config The config object
2765 Roo.bootstrap.CardImageTop = function(config){
2766 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2769 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2772 container_method : 'getCardImageTop'
2790 * @class Roo.bootstrap.Img
2791 * @extends Roo.bootstrap.Component
2792 * Bootstrap Img class
2793 * @cfg {Boolean} imgResponsive false | true
2794 * @cfg {String} border rounded | circle | thumbnail
2795 * @cfg {String} src image source
2796 * @cfg {String} alt image alternative text
2797 * @cfg {String} href a tag href
2798 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2799 * @cfg {String} xsUrl xs image source
2800 * @cfg {String} smUrl sm image source
2801 * @cfg {String} mdUrl md image source
2802 * @cfg {String} lgUrl lg image source
2805 * Create a new Input
2806 * @param {Object} config The config object
2809 Roo.bootstrap.Img = function(config){
2810 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2816 * The img click event for the img.
2817 * @param {Roo.EventObject} e
2823 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2825 imgResponsive: true,
2835 getAutoCreate : function()
2837 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2838 return this.createSingleImg();
2843 cls: 'roo-image-responsive-group',
2848 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2850 if(!_this[size + 'Url']){
2856 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2857 html: _this.html || cfg.html,
2858 src: _this[size + 'Url']
2861 img.cls += ' roo-image-responsive-' + size;
2863 var s = ['xs', 'sm', 'md', 'lg'];
2865 s.splice(s.indexOf(size), 1);
2867 Roo.each(s, function(ss){
2868 img.cls += ' hidden-' + ss;
2871 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2872 cfg.cls += ' img-' + _this.border;
2876 cfg.alt = _this.alt;
2889 a.target = _this.target;
2893 cfg.cn.push((_this.href) ? a : img);
2900 createSingleImg : function()
2904 cls: (this.imgResponsive) ? 'img-responsive' : '',
2906 src : 'about:blank' // just incase src get's set to undefined?!?
2909 cfg.html = this.html || cfg.html;
2911 cfg.src = this.src || cfg.src;
2913 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2914 cfg.cls += ' img-' + this.border;
2931 a.target = this.target;
2936 return (this.href) ? a : cfg;
2939 initEvents: function()
2942 this.el.on('click', this.onClick, this);
2947 onClick : function(e)
2949 Roo.log('img onclick');
2950 this.fireEvent('click', this, e);
2953 * Sets the url of the image - used to update it
2954 * @param {String} url the url of the image
2957 setSrc : function(url)
2961 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2962 this.el.dom.src = url;
2966 this.el.select('img', true).first().dom.src = url;
2982 * @class Roo.bootstrap.Link
2983 * @extends Roo.bootstrap.Component
2984 * Bootstrap Link Class
2985 * @cfg {String} alt image alternative text
2986 * @cfg {String} href a tag href
2987 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2988 * @cfg {String} html the content of the link.
2989 * @cfg {String} anchor name for the anchor link
2990 * @cfg {String} fa - favicon
2992 * @cfg {Boolean} preventDefault (true | false) default false
2996 * Create a new Input
2997 * @param {Object} config The config object
3000 Roo.bootstrap.Link = function(config){
3001 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3007 * The img click event for the img.
3008 * @param {Roo.EventObject} e
3014 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3018 preventDefault: false,
3024 getAutoCreate : function()
3026 var html = this.html || '';
3028 if (this.fa !== false) {
3029 html = '<i class="fa fa-' + this.fa + '"></i>';
3034 // anchor's do not require html/href...
3035 if (this.anchor === false) {
3037 cfg.href = this.href || '#';
3039 cfg.name = this.anchor;
3040 if (this.html !== false || this.fa !== false) {
3043 if (this.href !== false) {
3044 cfg.href = this.href;
3048 if(this.alt !== false){
3053 if(this.target !== false) {
3054 cfg.target = this.target;
3060 initEvents: function() {
3062 if(!this.href || this.preventDefault){
3063 this.el.on('click', this.onClick, this);
3067 onClick : function(e)
3069 if(this.preventDefault){
3072 //Roo.log('img onclick');
3073 this.fireEvent('click', this, e);
3086 * @class Roo.bootstrap.Header
3087 * @extends Roo.bootstrap.Component
3088 * Bootstrap Header class
3089 * @cfg {String} html content of header
3090 * @cfg {Number} level (1|2|3|4|5|6) default 1
3093 * Create a new Header
3094 * @param {Object} config The config object
3098 Roo.bootstrap.Header = function(config){
3099 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3102 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3110 getAutoCreate : function(){
3115 tag: 'h' + (1 *this.level),
3116 html: this.html || ''
3128 * Ext JS Library 1.1.1
3129 * Copyright(c) 2006-2007, Ext JS, LLC.
3131 * Originally Released Under LGPL - original licence link has changed is not relivant.
3134 * <script type="text/javascript">
3138 * @class Roo.bootstrap.MenuMgr
3139 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3142 Roo.bootstrap.MenuMgr = function(){
3143 var menus, active, groups = {}, attached = false, lastShow = new Date();
3145 // private - called when first menu is created
3148 active = new Roo.util.MixedCollection();
3149 Roo.get(document).addKeyListener(27, function(){
3150 if(active.length > 0){
3158 if(active && active.length > 0){
3159 var c = active.clone();
3169 if(active.length < 1){
3170 Roo.get(document).un("mouseup", onMouseDown);
3178 var last = active.last();
3179 lastShow = new Date();
3182 Roo.get(document).on("mouseup", onMouseDown);
3187 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3188 m.parentMenu.activeChild = m;
3189 }else if(last && last.isVisible()){
3190 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3195 function onBeforeHide(m){
3197 m.activeChild.hide();
3199 if(m.autoHideTimer){
3200 clearTimeout(m.autoHideTimer);
3201 delete m.autoHideTimer;
3206 function onBeforeShow(m){
3207 var pm = m.parentMenu;
3208 if(!pm && !m.allowOtherMenus){
3210 }else if(pm && pm.activeChild && active != m){
3211 pm.activeChild.hide();
3215 // private this should really trigger on mouseup..
3216 function onMouseDown(e){
3217 Roo.log("on Mouse Up");
3219 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3220 Roo.log("MenuManager hideAll");
3229 function onBeforeCheck(mi, state){
3231 var g = groups[mi.group];
3232 for(var i = 0, l = g.length; i < l; i++){
3234 g[i].setChecked(false);
3243 * Hides all menus that are currently visible
3245 hideAll : function(){
3250 register : function(menu){
3254 menus[menu.id] = menu;
3255 menu.on("beforehide", onBeforeHide);
3256 menu.on("hide", onHide);
3257 menu.on("beforeshow", onBeforeShow);
3258 menu.on("show", onShow);
3260 if(g && menu.events["checkchange"]){
3264 groups[g].push(menu);
3265 menu.on("checkchange", onCheck);
3270 * Returns a {@link Roo.menu.Menu} object
3271 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3272 * be used to generate and return a new Menu instance.
3274 get : function(menu){
3275 if(typeof menu == "string"){ // menu id
3277 }else if(menu.events){ // menu instance
3280 /*else if(typeof menu.length == 'number'){ // array of menu items?
3281 return new Roo.bootstrap.Menu({items:menu});
3282 }else{ // otherwise, must be a config
3283 return new Roo.bootstrap.Menu(menu);
3290 unregister : function(menu){
3291 delete menus[menu.id];
3292 menu.un("beforehide", onBeforeHide);
3293 menu.un("hide", onHide);
3294 menu.un("beforeshow", onBeforeShow);
3295 menu.un("show", onShow);
3297 if(g && menu.events["checkchange"]){
3298 groups[g].remove(menu);
3299 menu.un("checkchange", onCheck);
3304 registerCheckable : function(menuItem){
3305 var g = menuItem.group;
3310 groups[g].push(menuItem);
3311 menuItem.on("beforecheckchange", onBeforeCheck);
3316 unregisterCheckable : function(menuItem){
3317 var g = menuItem.group;
3319 groups[g].remove(menuItem);
3320 menuItem.un("beforecheckchange", onBeforeCheck);
3332 * @class Roo.bootstrap.Menu
3333 * @extends Roo.bootstrap.Component
3334 * Bootstrap Menu class - container for MenuItems
3335 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3336 * @cfg {bool} hidden if the menu should be hidden when rendered.
3337 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3338 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3342 * @param {Object} config The config object
3346 Roo.bootstrap.Menu = function(config){
3347 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3348 if (this.registerMenu && this.type != 'treeview') {
3349 Roo.bootstrap.MenuMgr.register(this);
3356 * Fires before this menu is displayed (return false to block)
3357 * @param {Roo.menu.Menu} this
3362 * Fires before this menu is hidden (return false to block)
3363 * @param {Roo.menu.Menu} this
3368 * Fires after this menu is displayed
3369 * @param {Roo.menu.Menu} this
3374 * Fires after this menu is hidden
3375 * @param {Roo.menu.Menu} this
3380 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3381 * @param {Roo.menu.Menu} this
3382 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3383 * @param {Roo.EventObject} e
3388 * Fires when the mouse is hovering over this menu
3389 * @param {Roo.menu.Menu} this
3390 * @param {Roo.EventObject} e
3391 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3396 * Fires when the mouse exits this menu
3397 * @param {Roo.menu.Menu} this
3398 * @param {Roo.EventObject} e
3399 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3404 * Fires when a menu item contained in this menu is clicked
3405 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3406 * @param {Roo.EventObject} e
3410 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3413 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3417 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3420 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3422 registerMenu : true,
3424 menuItems :false, // stores the menu items..
3434 getChildContainer : function() {
3438 getAutoCreate : function(){
3440 //if (['right'].indexOf(this.align)!==-1) {
3441 // cfg.cn[1].cls += ' pull-right'
3447 cls : 'dropdown-menu' ,
3448 style : 'z-index:1000'
3452 if (this.type === 'submenu') {
3453 cfg.cls = 'submenu active';
3455 if (this.type === 'treeview') {
3456 cfg.cls = 'treeview-menu';
3461 initEvents : function() {
3463 // Roo.log("ADD event");
3464 // Roo.log(this.triggerEl.dom);
3466 this.triggerEl.on('click', this.onTriggerClick, this);
3468 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3471 if (this.triggerEl.hasClass('nav-item')) {
3472 // dropdown toggle on the 'a' in BS4?
3473 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3475 this.triggerEl.addClass('dropdown-toggle');
3478 this.el.on('touchstart' , this.onTouch, this);
3480 this.el.on('click' , this.onClick, this);
3482 this.el.on("mouseover", this.onMouseOver, this);
3483 this.el.on("mouseout", this.onMouseOut, this);
3487 findTargetItem : function(e)
3489 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3493 //Roo.log(t); Roo.log(t.id);
3495 //Roo.log(this.menuitems);
3496 return this.menuitems.get(t.id);
3498 //return this.items.get(t.menuItemId);
3504 onTouch : function(e)
3506 Roo.log("menu.onTouch");
3507 //e.stopEvent(); this make the user popdown broken
3511 onClick : function(e)
3513 Roo.log("menu.onClick");
3515 var t = this.findTargetItem(e);
3516 if(!t || t.isContainer){
3521 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3522 if(t == this.activeItem && t.shouldDeactivate(e)){
3523 this.activeItem.deactivate();
3524 delete this.activeItem;
3528 this.setActiveItem(t, true);
3536 Roo.log('pass click event');
3540 this.fireEvent("click", this, t, e);
3544 if(!t.href.length || t.href == '#'){
3545 (function() { _this.hide(); }).defer(100);
3550 onMouseOver : function(e){
3551 var t = this.findTargetItem(e);
3554 // if(t.canActivate && !t.disabled){
3555 // this.setActiveItem(t, true);
3559 this.fireEvent("mouseover", this, e, t);
3561 isVisible : function(){
3562 return !this.hidden;
3564 onMouseOut : function(e){
3565 var t = this.findTargetItem(e);
3568 // if(t == this.activeItem && t.shouldDeactivate(e)){
3569 // this.activeItem.deactivate();
3570 // delete this.activeItem;
3573 this.fireEvent("mouseout", this, e, t);
3578 * Displays this menu relative to another element
3579 * @param {String/HTMLElement/Roo.Element} element The element to align to
3580 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3581 * the element (defaults to this.defaultAlign)
3582 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3584 show : function(el, pos, parentMenu)
3586 if (false === this.fireEvent("beforeshow", this)) {
3587 Roo.log("show canceled");
3590 this.parentMenu = parentMenu;
3595 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3598 * Displays this menu at a specific xy position
3599 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3600 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3602 showAt : function(xy, parentMenu, /* private: */_e){
3603 this.parentMenu = parentMenu;
3608 this.fireEvent("beforeshow", this);
3609 //xy = this.el.adjustForConstraints(xy);
3613 this.hideMenuItems();
3614 this.hidden = false;
3615 this.triggerEl.addClass('open');
3616 this.el.addClass('show');
3618 // reassign x when hitting right
3619 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3620 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3623 // reassign y when hitting bottom
3624 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3625 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3628 // but the list may align on trigger left or trigger top... should it be a properity?
3630 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3635 this.fireEvent("show", this);
3641 this.doFocus.defer(50, this);
3645 doFocus : function(){
3647 this.focusEl.focus();
3652 * Hides this menu and optionally all parent menus
3653 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3655 hide : function(deep)
3657 if (false === this.fireEvent("beforehide", this)) {
3658 Roo.log("hide canceled");
3661 this.hideMenuItems();
3662 if(this.el && this.isVisible()){
3664 if(this.activeItem){
3665 this.activeItem.deactivate();
3666 this.activeItem = null;
3668 this.triggerEl.removeClass('open');;
3669 this.el.removeClass('show');
3671 this.fireEvent("hide", this);
3673 if(deep === true && this.parentMenu){
3674 this.parentMenu.hide(true);
3678 onTriggerClick : function(e)
3680 Roo.log('trigger click');
3682 var target = e.getTarget();
3684 Roo.log(target.nodeName.toLowerCase());
3686 if(target.nodeName.toLowerCase() === 'i'){
3692 onTriggerPress : function(e)
3694 Roo.log('trigger press');
3695 //Roo.log(e.getTarget());
3696 // Roo.log(this.triggerEl.dom);
3698 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3699 var pel = Roo.get(e.getTarget());
3700 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3701 Roo.log('is treeview or dropdown?');
3705 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3709 if (this.isVisible()) {
3714 this.show(this.triggerEl, '?', false);
3717 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3724 hideMenuItems : function()
3726 Roo.log("hide Menu Items");
3731 this.el.select('.open',true).each(function(aa) {
3733 aa.removeClass('open');
3737 addxtypeChild : function (tree, cntr) {
3738 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3740 this.menuitems.add(comp);
3752 this.getEl().dom.innerHTML = '';
3753 this.menuitems.clear();
3767 * @class Roo.bootstrap.MenuItem
3768 * @extends Roo.bootstrap.Component
3769 * Bootstrap MenuItem class
3770 * @cfg {String} html the menu label
3771 * @cfg {String} href the link
3772 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3773 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3774 * @cfg {Boolean} active used on sidebars to highlight active itesm
3775 * @cfg {String} fa favicon to show on left of menu item.
3776 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3780 * Create a new MenuItem
3781 * @param {Object} config The config object
3785 Roo.bootstrap.MenuItem = function(config){
3786 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3791 * The raw click event for the entire grid.
3792 * @param {Roo.bootstrap.MenuItem} this
3793 * @param {Roo.EventObject} e
3799 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3803 preventDefault: false,
3804 isContainer : false,
3808 getAutoCreate : function(){
3810 if(this.isContainer){
3813 cls: 'dropdown-menu-item '
3823 cls : 'dropdown-item',
3828 if (this.fa !== false) {
3831 cls : 'fa fa-' + this.fa
3840 cls: 'dropdown-menu-item',
3843 if (this.parent().type == 'treeview') {
3844 cfg.cls = 'treeview-menu';
3847 cfg.cls += ' active';
3852 anc.href = this.href || cfg.cn[0].href ;
3853 ctag.html = this.html || cfg.cn[0].html ;
3857 initEvents: function()
3859 if (this.parent().type == 'treeview') {
3860 this.el.select('a').on('click', this.onClick, this);
3864 this.menu.parentType = this.xtype;
3865 this.menu.triggerEl = this.el;
3866 this.menu = this.addxtype(Roo.apply({}, this.menu));
3870 onClick : function(e)
3872 Roo.log('item on click ');
3874 if(this.preventDefault){
3877 //this.parent().hideMenuItems();
3879 this.fireEvent('click', this, e);
3898 * @class Roo.bootstrap.MenuSeparator
3899 * @extends Roo.bootstrap.Component
3900 * Bootstrap MenuSeparator class
3903 * Create a new MenuItem
3904 * @param {Object} config The config object
3908 Roo.bootstrap.MenuSeparator = function(config){
3909 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3912 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3914 getAutoCreate : function(){
3933 * @class Roo.bootstrap.Modal
3934 * @extends Roo.bootstrap.Component
3935 * Bootstrap Modal class
3936 * @cfg {String} title Title of dialog
3937 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3938 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3939 * @cfg {Boolean} specificTitle default false
3940 * @cfg {Array} buttons Array of buttons or standard button set..
3941 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3942 * @cfg {Boolean} animate default true
3943 * @cfg {Boolean} allow_close default true
3944 * @cfg {Boolean} fitwindow default false
3945 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3946 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3947 * @cfg {String} size (sm|lg|xl) default empty
3948 * @cfg {Number} max_width set the max width of modal
3949 * @cfg {Boolean} editableTitle can the title be edited
3954 * Create a new Modal Dialog
3955 * @param {Object} config The config object
3958 Roo.bootstrap.Modal = function(config){
3959 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3964 * The raw btnclick event for the button
3965 * @param {Roo.EventObject} e
3970 * Fire when dialog resize
3971 * @param {Roo.bootstrap.Modal} this
3972 * @param {Roo.EventObject} e
3976 * @event titlechanged
3977 * Fire when the editable title has been changed
3978 * @param {Roo.bootstrap.Modal} this
3979 * @param {Roo.EventObject} value
3981 "titlechanged" : true
3984 this.buttons = this.buttons || [];
3987 this.tmpl = Roo.factory(this.tmpl);
3992 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
3994 title : 'test dialog',
4004 specificTitle: false,
4006 buttonPosition: 'right',
4028 editableTitle : false,
4030 onRender : function(ct, position)
4032 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4035 var cfg = Roo.apply({}, this.getAutoCreate());
4038 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4040 //if (!cfg.name.length) {
4044 cfg.cls += ' ' + this.cls;
4047 cfg.style = this.style;
4049 this.el = Roo.get(document.body).createChild(cfg, position);
4051 //var type = this.el.dom.type;
4054 if(this.tabIndex !== undefined){
4055 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4058 this.dialogEl = this.el.select('.modal-dialog',true).first();
4059 this.bodyEl = this.el.select('.modal-body',true).first();
4060 this.closeEl = this.el.select('.modal-header .close', true).first();
4061 this.headerEl = this.el.select('.modal-header',true).first();
4062 this.titleEl = this.el.select('.modal-title',true).first();
4063 this.footerEl = this.el.select('.modal-footer',true).first();
4065 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4067 //this.el.addClass("x-dlg-modal");
4069 if (this.buttons.length) {
4070 Roo.each(this.buttons, function(bb) {
4071 var b = Roo.apply({}, bb);
4072 b.xns = b.xns || Roo.bootstrap;
4073 b.xtype = b.xtype || 'Button';
4074 if (typeof(b.listeners) == 'undefined') {
4075 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4078 var btn = Roo.factory(b);
4080 btn.render(this.getButtonContainer());
4084 // render the children.
4087 if(typeof(this.items) != 'undefined'){
4088 var items = this.items;
4091 for(var i =0;i < items.length;i++) {
4092 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4096 this.items = nitems;
4098 // where are these used - they used to be body/close/footer
4102 //this.el.addClass([this.fieldClass, this.cls]);
4106 getAutoCreate : function()
4108 // we will default to modal-body-overflow - might need to remove or make optional later.
4110 cls : 'modal-body enable-modal-body-overflow ',
4111 html : this.html || ''
4116 cls : 'modal-title',
4120 if(this.specificTitle){ // WTF is this?
4125 if (this.allow_close && Roo.bootstrap.version == 3) {
4135 if (this.editableTitle) {
4137 cls: 'form-control roo-editable-title d-none',
4143 if (this.allow_close && Roo.bootstrap.version == 4) {
4153 if(this.size.length){
4154 size = 'modal-' + this.size;
4157 var footer = Roo.bootstrap.version == 3 ?
4159 cls : 'modal-footer',
4163 cls: 'btn-' + this.buttonPosition
4168 { // BS4 uses mr-auto on left buttons....
4169 cls : 'modal-footer'
4180 cls: "modal-dialog " + size,
4183 cls : "modal-content",
4186 cls : 'modal-header',
4201 modal.cls += ' fade';
4207 getChildContainer : function() {
4212 getButtonContainer : function() {
4214 return Roo.bootstrap.version == 4 ?
4215 this.el.select('.modal-footer',true).first()
4216 : this.el.select('.modal-footer div',true).first();
4219 initEvents : function()
4221 if (this.allow_close) {
4222 this.closeEl.on('click', this.hide, this);
4224 Roo.EventManager.onWindowResize(this.resize, this, true);
4225 if (this.editableTitle) {
4226 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4227 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4228 this.headerEditEl.on('keyup', function(e) {
4229 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4230 this.toggleHeaderInput(false)
4233 this.headerEditEl.on('blur', function(e) {
4234 this.toggleHeaderInput(false)
4243 this.maskEl.setSize(
4244 Roo.lib.Dom.getViewWidth(true),
4245 Roo.lib.Dom.getViewHeight(true)
4248 if (this.fitwindow) {
4252 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4253 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4258 if(this.max_width !== 0) {
4260 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4263 this.setSize(w, this.height);
4267 if(this.max_height) {
4268 this.setSize(w,Math.min(
4270 Roo.lib.Dom.getViewportHeight(true) - 60
4276 if(!this.fit_content) {
4277 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4281 this.setSize(w, Math.min(
4283 this.headerEl.getHeight() +
4284 this.footerEl.getHeight() +
4285 this.getChildHeight(this.bodyEl.dom.childNodes),
4286 Roo.lib.Dom.getViewportHeight(true) - 60)
4292 setSize : function(w,h)
4303 if (!this.rendered) {
4306 this.toggleHeaderInput(false);
4307 //this.el.setStyle('display', 'block');
4308 this.el.removeClass('hideing');
4309 this.el.dom.style.display='block';
4311 Roo.get(document.body).addClass('modal-open');
4313 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4316 this.el.addClass('show');
4317 this.el.addClass('in');
4320 this.el.addClass('show');
4321 this.el.addClass('in');
4324 // not sure how we can show data in here..
4326 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4329 Roo.get(document.body).addClass("x-body-masked");
4331 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4332 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4333 this.maskEl.dom.style.display = 'block';
4334 this.maskEl.addClass('show');
4339 this.fireEvent('show', this);
4341 // set zindex here - otherwise it appears to be ignored...
4342 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4345 this.items.forEach( function(e) {
4346 e.layout ? e.layout() : false;
4354 if(this.fireEvent("beforehide", this) !== false){
4356 this.maskEl.removeClass('show');
4358 this.maskEl.dom.style.display = '';
4359 Roo.get(document.body).removeClass("x-body-masked");
4360 this.el.removeClass('in');
4361 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4363 if(this.animate){ // why
4364 this.el.addClass('hideing');
4365 this.el.removeClass('show');
4367 if (!this.el.hasClass('hideing')) {
4368 return; // it's been shown again...
4371 this.el.dom.style.display='';
4373 Roo.get(document.body).removeClass('modal-open');
4374 this.el.removeClass('hideing');
4378 this.el.removeClass('show');
4379 this.el.dom.style.display='';
4380 Roo.get(document.body).removeClass('modal-open');
4383 this.fireEvent('hide', this);
4386 isVisible : function()
4389 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4393 addButton : function(str, cb)
4397 var b = Roo.apply({}, { html : str } );
4398 b.xns = b.xns || Roo.bootstrap;
4399 b.xtype = b.xtype || 'Button';
4400 if (typeof(b.listeners) == 'undefined') {
4401 b.listeners = { click : cb.createDelegate(this) };
4404 var btn = Roo.factory(b);
4406 btn.render(this.getButtonContainer());
4412 setDefaultButton : function(btn)
4414 //this.el.select('.modal-footer').()
4417 resizeTo: function(w,h)
4419 this.dialogEl.setWidth(w);
4421 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4423 this.bodyEl.setHeight(h - diff);
4425 this.fireEvent('resize', this);
4428 setContentSize : function(w, h)
4432 onButtonClick: function(btn,e)
4435 this.fireEvent('btnclick', btn.name, e);
4438 * Set the title of the Dialog
4439 * @param {String} str new Title
4441 setTitle: function(str) {
4442 this.titleEl.dom.innerHTML = str;
4446 * Set the body of the Dialog
4447 * @param {String} str new Title
4449 setBody: function(str) {
4450 this.bodyEl.dom.innerHTML = str;
4453 * Set the body of the Dialog using the template
4454 * @param {Obj} data - apply this data to the template and replace the body contents.
4456 applyBody: function(obj)
4459 Roo.log("Error - using apply Body without a template");
4462 this.tmpl.overwrite(this.bodyEl, obj);
4465 getChildHeight : function(child_nodes)
4469 child_nodes.length == 0
4474 var child_height = 0;
4476 for(var i = 0; i < child_nodes.length; i++) {
4479 * for modal with tabs...
4480 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4482 var layout_childs = child_nodes[i].childNodes;
4484 for(var j = 0; j < layout_childs.length; j++) {
4486 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4488 var layout_body_childs = layout_childs[j].childNodes;
4490 for(var k = 0; k < layout_body_childs.length; k++) {
4492 if(layout_body_childs[k].classList.contains('navbar')) {
4493 child_height += layout_body_childs[k].offsetHeight;
4497 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4499 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4501 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4503 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4504 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4519 child_height += child_nodes[i].offsetHeight;
4520 // Roo.log(child_nodes[i].offsetHeight);
4523 return child_height;
4525 toggleHeaderInput : function(is_edit)
4527 if (!this.editableTitle) {
4528 return; // not editable.
4530 if (is_edit && this.is_header_editing) {
4531 return; // already editing..
4535 this.headerEditEl.dom.value = this.title;
4536 this.headerEditEl.removeClass('d-none');
4537 this.headerEditEl.dom.focus();
4538 this.titleEl.addClass('d-none');
4540 this.is_header_editing = true;
4543 // flip back to not editing.
4544 this.title = this.headerEditEl.dom.value;
4545 this.headerEditEl.addClass('d-none');
4546 this.titleEl.removeClass('d-none');
4547 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4548 this.is_header_editing = false;
4549 this.fireEvent('titlechanged', this, this.title);
4558 Roo.apply(Roo.bootstrap.Modal, {
4560 * Button config that displays a single OK button
4569 * Button config that displays Yes and No buttons
4585 * Button config that displays OK and Cancel buttons
4600 * Button config that displays Yes, No and Cancel buttons
4625 * messagebox - can be used as a replace
4629 * @class Roo.MessageBox
4630 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4634 Roo.Msg.alert('Status', 'Changes saved successfully.');
4636 // Prompt for user data:
4637 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4639 // process text value...
4643 // Show a dialog using config options:
4645 title:'Save Changes?',
4646 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4647 buttons: Roo.Msg.YESNOCANCEL,
4654 Roo.bootstrap.MessageBox = function(){
4655 var dlg, opt, mask, waitTimer;
4656 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4657 var buttons, activeTextEl, bwidth;
4661 var handleButton = function(button){
4663 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4667 var handleHide = function(){
4669 dlg.el.removeClass(opt.cls);
4672 // Roo.TaskMgr.stop(waitTimer);
4673 // waitTimer = null;
4678 var updateButtons = function(b){
4681 buttons["ok"].hide();
4682 buttons["cancel"].hide();
4683 buttons["yes"].hide();
4684 buttons["no"].hide();
4685 dlg.footerEl.hide();
4689 dlg.footerEl.show();
4690 for(var k in buttons){
4691 if(typeof buttons[k] != "function"){
4694 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4695 width += buttons[k].el.getWidth()+15;
4705 var handleEsc = function(d, k, e){
4706 if(opt && opt.closable !== false){
4716 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4717 * @return {Roo.BasicDialog} The BasicDialog element
4719 getDialog : function(){
4721 dlg = new Roo.bootstrap.Modal( {
4724 //constraintoviewport:false,
4726 //collapsible : false,
4731 //buttonAlign:"center",
4732 closeClick : function(){
4733 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4736 handleButton("cancel");
4741 dlg.on("hide", handleHide);
4743 //dlg.addKeyListener(27, handleEsc);
4745 this.buttons = buttons;
4746 var bt = this.buttonText;
4747 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4748 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4749 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4750 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4752 bodyEl = dlg.bodyEl.createChild({
4754 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4755 '<textarea class="roo-mb-textarea"></textarea>' +
4756 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4758 msgEl = bodyEl.dom.firstChild;
4759 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4760 textboxEl.enableDisplayMode();
4761 textboxEl.addKeyListener([10,13], function(){
4762 if(dlg.isVisible() && opt && opt.buttons){
4765 }else if(opt.buttons.yes){
4766 handleButton("yes");
4770 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4771 textareaEl.enableDisplayMode();
4772 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4773 progressEl.enableDisplayMode();
4775 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4776 var pf = progressEl.dom.firstChild;
4778 pp = Roo.get(pf.firstChild);
4779 pp.setHeight(pf.offsetHeight);
4787 * Updates the message box body text
4788 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4789 * the XHTML-compliant non-breaking space character '&#160;')
4790 * @return {Roo.MessageBox} This message box
4792 updateText : function(text)
4794 if(!dlg.isVisible() && !opt.width){
4795 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4796 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4798 msgEl.innerHTML = text || ' ';
4800 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4801 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4803 Math.min(opt.width || cw , this.maxWidth),
4804 Math.max(opt.minWidth || this.minWidth, bwidth)
4807 activeTextEl.setWidth(w);
4809 if(dlg.isVisible()){
4810 dlg.fixedcenter = false;
4812 // to big, make it scroll. = But as usual stupid IE does not support
4815 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4816 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4817 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4819 bodyEl.dom.style.height = '';
4820 bodyEl.dom.style.overflowY = '';
4823 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4825 bodyEl.dom.style.overflowX = '';
4828 dlg.setContentSize(w, bodyEl.getHeight());
4829 if(dlg.isVisible()){
4830 dlg.fixedcenter = true;
4836 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4837 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4838 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4839 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4840 * @return {Roo.MessageBox} This message box
4842 updateProgress : function(value, text){
4844 this.updateText(text);
4847 if (pp) { // weird bug on my firefox - for some reason this is not defined
4848 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4849 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4855 * Returns true if the message box is currently displayed
4856 * @return {Boolean} True if the message box is visible, else false
4858 isVisible : function(){
4859 return dlg && dlg.isVisible();
4863 * Hides the message box if it is displayed
4866 if(this.isVisible()){
4872 * Displays a new message box, or reinitializes an existing message box, based on the config options
4873 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4874 * The following config object properties are supported:
4876 Property Type Description
4877 ---------- --------------- ------------------------------------------------------------------------------------
4878 animEl String/Element An id or Element from which the message box should animate as it opens and
4879 closes (defaults to undefined)
4880 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4881 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4882 closable Boolean False to hide the top-right close button (defaults to true). Note that
4883 progress and wait dialogs will ignore this property and always hide the
4884 close button as they can only be closed programmatically.
4885 cls String A custom CSS class to apply to the message box element
4886 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4887 displayed (defaults to 75)
4888 fn Function A callback function to execute after closing the dialog. The arguments to the
4889 function will be btn (the name of the button that was clicked, if applicable,
4890 e.g. "ok"), and text (the value of the active text field, if applicable).
4891 Progress and wait dialogs will ignore this option since they do not respond to
4892 user actions and can only be closed programmatically, so any required function
4893 should be called by the same code after it closes the dialog.
4894 icon String A CSS class that provides a background image to be used as an icon for
4895 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4896 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4897 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4898 modal Boolean False to allow user interaction with the page while the message box is
4899 displayed (defaults to true)
4900 msg String A string that will replace the existing message box body text (defaults
4901 to the XHTML-compliant non-breaking space character ' ')
4902 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4903 progress Boolean True to display a progress bar (defaults to false)
4904 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4905 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4906 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4907 title String The title text
4908 value String The string value to set into the active textbox element if displayed
4909 wait Boolean True to display a progress bar (defaults to false)
4910 width Number The width of the dialog in pixels
4917 msg: 'Please enter your address:',
4919 buttons: Roo.MessageBox.OKCANCEL,
4922 animEl: 'addAddressBtn'
4925 * @param {Object} config Configuration options
4926 * @return {Roo.MessageBox} This message box
4928 show : function(options)
4931 // this causes nightmares if you show one dialog after another
4932 // especially on callbacks..
4934 if(this.isVisible()){
4937 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4938 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4939 Roo.log("New Dialog Message:" + options.msg )
4940 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4941 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4944 var d = this.getDialog();
4946 d.setTitle(opt.title || " ");
4947 d.closeEl.setDisplayed(opt.closable !== false);
4948 activeTextEl = textboxEl;
4949 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4954 textareaEl.setHeight(typeof opt.multiline == "number" ?
4955 opt.multiline : this.defaultTextHeight);
4956 activeTextEl = textareaEl;
4965 progressEl.setDisplayed(opt.progress === true);
4967 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4969 this.updateProgress(0);
4970 activeTextEl.dom.value = opt.value || "";
4972 dlg.setDefaultButton(activeTextEl);
4974 var bs = opt.buttons;
4978 }else if(bs && bs.yes){
4979 db = buttons["yes"];
4981 dlg.setDefaultButton(db);
4983 bwidth = updateButtons(opt.buttons);
4984 this.updateText(opt.msg);
4986 d.el.addClass(opt.cls);
4988 d.proxyDrag = opt.proxyDrag === true;
4989 d.modal = opt.modal !== false;
4990 d.mask = opt.modal !== false ? mask : false;
4992 // force it to the end of the z-index stack so it gets a cursor in FF
4993 document.body.appendChild(dlg.el.dom);
4994 d.animateTarget = null;
4995 d.show(options.animEl);
5001 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5002 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5003 * and closing the message box when the process is complete.
5004 * @param {String} title The title bar text
5005 * @param {String} msg The message box body text
5006 * @return {Roo.MessageBox} This message box
5008 progress : function(title, msg){
5015 minWidth: this.minProgressWidth,
5022 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5023 * If a callback function is passed it will be called after the user clicks the button, and the
5024 * id of the button that was clicked will be passed as the only parameter to the callback
5025 * (could also be the top-right close button).
5026 * @param {String} title The title bar text
5027 * @param {String} msg The message box body text
5028 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5029 * @param {Object} scope (optional) The scope of the callback function
5030 * @return {Roo.MessageBox} This message box
5032 alert : function(title, msg, fn, scope)
5047 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5048 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5049 * You are responsible for closing the message box when the process is complete.
5050 * @param {String} msg The message box body text
5051 * @param {String} title (optional) The title bar text
5052 * @return {Roo.MessageBox} This message box
5054 wait : function(msg, title){
5065 waitTimer = Roo.TaskMgr.start({
5067 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5075 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5076 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5077 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5078 * @param {String} title The title bar text
5079 * @param {String} msg The message box body text
5080 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5081 * @param {Object} scope (optional) The scope of the callback function
5082 * @return {Roo.MessageBox} This message box
5084 confirm : function(title, msg, fn, scope){
5088 buttons: this.YESNO,
5097 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5098 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5099 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5100 * (could also be the top-right close button) and the text that was entered will be passed as the two
5101 * parameters to the callback.
5102 * @param {String} title The title bar text
5103 * @param {String} msg The message box body text
5104 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5105 * @param {Object} scope (optional) The scope of the callback function
5106 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5107 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5108 * @return {Roo.MessageBox} This message box
5110 prompt : function(title, msg, fn, scope, multiline){
5114 buttons: this.OKCANCEL,
5119 multiline: multiline,
5126 * Button config that displays a single OK button
5131 * Button config that displays Yes and No buttons
5134 YESNO : {yes:true, no:true},
5136 * Button config that displays OK and Cancel buttons
5139 OKCANCEL : {ok:true, cancel:true},
5141 * Button config that displays Yes, No and Cancel buttons
5144 YESNOCANCEL : {yes:true, no:true, cancel:true},
5147 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5150 defaultTextHeight : 75,
5152 * The maximum width in pixels of the message box (defaults to 600)
5157 * The minimum width in pixels of the message box (defaults to 100)
5162 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5163 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5166 minProgressWidth : 250,
5168 * An object containing the default button text strings that can be overriden for localized language support.
5169 * Supported properties are: ok, cancel, yes and no.
5170 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5183 * Shorthand for {@link Roo.MessageBox}
5185 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5186 Roo.Msg = Roo.Msg || Roo.MessageBox;
5195 * @class Roo.bootstrap.Navbar
5196 * @extends Roo.bootstrap.Component
5197 * Bootstrap Navbar class
5200 * Create a new Navbar
5201 * @param {Object} config The config object
5205 Roo.bootstrap.Navbar = function(config){
5206 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5210 * @event beforetoggle
5211 * Fire before toggle the menu
5212 * @param {Roo.EventObject} e
5214 "beforetoggle" : true
5218 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5227 getAutoCreate : function(){
5230 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5234 initEvents :function ()
5236 //Roo.log(this.el.select('.navbar-toggle',true));
5237 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5244 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5246 var size = this.el.getSize();
5247 this.maskEl.setSize(size.width, size.height);
5248 this.maskEl.enableDisplayMode("block");
5257 getChildContainer : function()
5259 if (this.el && this.el.select('.collapse').getCount()) {
5260 return this.el.select('.collapse',true).first();
5275 onToggle : function()
5278 if(this.fireEvent('beforetoggle', this) === false){
5281 var ce = this.el.select('.navbar-collapse',true).first();
5283 if (!ce.hasClass('show')) {
5293 * Expand the navbar pulldown
5295 expand : function ()
5298 var ce = this.el.select('.navbar-collapse',true).first();
5299 if (ce.hasClass('collapsing')) {
5302 ce.dom.style.height = '';
5304 ce.addClass('in'); // old...
5305 ce.removeClass('collapse');
5306 ce.addClass('show');
5307 var h = ce.getHeight();
5309 ce.removeClass('show');
5310 // at this point we should be able to see it..
5311 ce.addClass('collapsing');
5313 ce.setHeight(0); // resize it ...
5314 ce.on('transitionend', function() {
5315 //Roo.log('done transition');
5316 ce.removeClass('collapsing');
5317 ce.addClass('show');
5318 ce.removeClass('collapse');
5320 ce.dom.style.height = '';
5321 }, this, { single: true} );
5323 ce.dom.scrollTop = 0;
5326 * Collapse the navbar pulldown
5328 collapse : function()
5330 var ce = this.el.select('.navbar-collapse',true).first();
5332 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5333 // it's collapsed or collapsing..
5336 ce.removeClass('in'); // old...
5337 ce.setHeight(ce.getHeight());
5338 ce.removeClass('show');
5339 ce.addClass('collapsing');
5341 ce.on('transitionend', function() {
5342 ce.dom.style.height = '';
5343 ce.removeClass('collapsing');
5344 ce.addClass('collapse');
5345 }, this, { single: true} );
5365 * @class Roo.bootstrap.NavSimplebar
5366 * @extends Roo.bootstrap.Navbar
5367 * Bootstrap Sidebar class
5369 * @cfg {Boolean} inverse is inverted color
5371 * @cfg {String} type (nav | pills | tabs)
5372 * @cfg {Boolean} arrangement stacked | justified
5373 * @cfg {String} align (left | right) alignment
5375 * @cfg {Boolean} main (true|false) main nav bar? default false
5376 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5378 * @cfg {String} tag (header|footer|nav|div) default is nav
5380 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5384 * Create a new Sidebar
5385 * @param {Object} config The config object
5389 Roo.bootstrap.NavSimplebar = function(config){
5390 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5393 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5409 getAutoCreate : function(){
5413 tag : this.tag || 'div',
5414 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5416 if (['light','white'].indexOf(this.weight) > -1) {
5417 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5419 cfg.cls += ' bg-' + this.weight;
5422 cfg.cls += ' navbar-inverse';
5426 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5428 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5437 cls: 'nav nav-' + this.xtype,
5443 this.type = this.type || 'nav';
5444 if (['tabs','pills'].indexOf(this.type) != -1) {
5445 cfg.cn[0].cls += ' nav-' + this.type
5449 if (this.type!=='nav') {
5450 Roo.log('nav type must be nav/tabs/pills')
5452 cfg.cn[0].cls += ' navbar-nav'
5458 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5459 cfg.cn[0].cls += ' nav-' + this.arrangement;
5463 if (this.align === 'right') {
5464 cfg.cn[0].cls += ' navbar-right';
5489 * navbar-expand-md fixed-top
5493 * @class Roo.bootstrap.NavHeaderbar
5494 * @extends Roo.bootstrap.NavSimplebar
5495 * Bootstrap Sidebar class
5497 * @cfg {String} brand what is brand
5498 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5499 * @cfg {String} brand_href href of the brand
5500 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5501 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5502 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5503 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5506 * Create a new Sidebar
5507 * @param {Object} config The config object
5511 Roo.bootstrap.NavHeaderbar = function(config){
5512 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5516 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5523 desktopCenter : false,
5526 getAutoCreate : function(){
5529 tag: this.nav || 'nav',
5530 cls: 'navbar navbar-expand-md',
5536 if (this.desktopCenter) {
5537 cn.push({cls : 'container', cn : []});
5545 cls: 'navbar-toggle navbar-toggler',
5546 'data-toggle': 'collapse',
5551 html: 'Toggle navigation'
5555 cls: 'icon-bar navbar-toggler-icon'
5568 cn.push( Roo.bootstrap.version == 4 ? btn : {
5570 cls: 'navbar-header',
5579 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5583 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5585 if (['light','white'].indexOf(this.weight) > -1) {
5586 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5588 cfg.cls += ' bg-' + this.weight;
5591 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5592 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5594 // tag can override this..
5596 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5599 if (this.brand !== '') {
5600 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5601 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5603 href: this.brand_href ? this.brand_href : '#',
5604 cls: 'navbar-brand',
5612 cfg.cls += ' main-nav';
5620 getHeaderChildContainer : function()
5622 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5623 return this.el.select('.navbar-header',true).first();
5626 return this.getChildContainer();
5629 getChildContainer : function()
5632 return this.el.select('.roo-navbar-collapse',true).first();
5637 initEvents : function()
5639 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5641 if (this.autohide) {
5646 Roo.get(document).on('scroll',function(e) {
5647 var ns = Roo.get(document).getScroll().top;
5648 var os = prevScroll;
5652 ft.removeClass('slideDown');
5653 ft.addClass('slideUp');
5656 ft.removeClass('slideUp');
5657 ft.addClass('slideDown');
5678 * @class Roo.bootstrap.NavSidebar
5679 * @extends Roo.bootstrap.Navbar
5680 * Bootstrap Sidebar class
5683 * Create a new Sidebar
5684 * @param {Object} config The config object
5688 Roo.bootstrap.NavSidebar = function(config){
5689 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5692 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5694 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5696 getAutoCreate : function(){
5701 cls: 'sidebar sidebar-nav'
5723 * @class Roo.bootstrap.NavGroup
5724 * @extends Roo.bootstrap.Component
5725 * Bootstrap NavGroup class
5726 * @cfg {String} align (left|right)
5727 * @cfg {Boolean} inverse
5728 * @cfg {String} type (nav|pills|tab) default nav
5729 * @cfg {String} navId - reference Id for navbar.
5733 * Create a new nav group
5734 * @param {Object} config The config object
5737 Roo.bootstrap.NavGroup = function(config){
5738 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5741 Roo.bootstrap.NavGroup.register(this);
5745 * Fires when the active item changes
5746 * @param {Roo.bootstrap.NavGroup} this
5747 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5748 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5755 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5766 getAutoCreate : function()
5768 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5774 if (Roo.bootstrap.version == 4) {
5775 if (['tabs','pills'].indexOf(this.type) != -1) {
5776 cfg.cls += ' nav-' + this.type;
5778 // trying to remove so header bar can right align top?
5779 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5780 // do not use on header bar...
5781 cfg.cls += ' navbar-nav';
5786 if (['tabs','pills'].indexOf(this.type) != -1) {
5787 cfg.cls += ' nav-' + this.type
5789 if (this.type !== 'nav') {
5790 Roo.log('nav type must be nav/tabs/pills')
5792 cfg.cls += ' navbar-nav'
5796 if (this.parent() && this.parent().sidebar) {
5799 cls: 'dashboard-menu sidebar-menu'
5805 if (this.form === true) {
5808 cls: 'navbar-form form-inline'
5810 //nav navbar-right ml-md-auto
5811 if (this.align === 'right') {
5812 cfg.cls += ' navbar-right ml-md-auto';
5814 cfg.cls += ' navbar-left';
5818 if (this.align === 'right') {
5819 cfg.cls += ' navbar-right ml-md-auto';
5821 cfg.cls += ' mr-auto';
5825 cfg.cls += ' navbar-inverse';
5833 * sets the active Navigation item
5834 * @param {Roo.bootstrap.NavItem} the new current navitem
5836 setActiveItem : function(item)
5839 Roo.each(this.navItems, function(v){
5844 v.setActive(false, true);
5851 item.setActive(true, true);
5852 this.fireEvent('changed', this, item, prev);
5857 * gets the active Navigation item
5858 * @return {Roo.bootstrap.NavItem} the current navitem
5860 getActive : function()
5864 Roo.each(this.navItems, function(v){
5875 indexOfNav : function()
5879 Roo.each(this.navItems, function(v,i){
5890 * adds a Navigation item
5891 * @param {Roo.bootstrap.NavItem} the navitem to add
5893 addItem : function(cfg)
5895 if (this.form && Roo.bootstrap.version == 4) {
5898 var cn = new Roo.bootstrap.NavItem(cfg);
5900 cn.parentId = this.id;
5901 cn.onRender(this.el, null);
5905 * register a Navigation item
5906 * @param {Roo.bootstrap.NavItem} the navitem to add
5908 register : function(item)
5910 this.navItems.push( item);
5911 item.navId = this.navId;
5916 * clear all the Navigation item
5919 clearAll : function()
5922 this.el.dom.innerHTML = '';
5925 getNavItem: function(tabId)
5928 Roo.each(this.navItems, function(e) {
5929 if (e.tabId == tabId) {
5939 setActiveNext : function()
5941 var i = this.indexOfNav(this.getActive());
5942 if (i > this.navItems.length) {
5945 this.setActiveItem(this.navItems[i+1]);
5947 setActivePrev : function()
5949 var i = this.indexOfNav(this.getActive());
5953 this.setActiveItem(this.navItems[i-1]);
5955 clearWasActive : function(except) {
5956 Roo.each(this.navItems, function(e) {
5957 if (e.tabId != except.tabId && e.was_active) {
5958 e.was_active = false;
5965 getWasActive : function ()
5968 Roo.each(this.navItems, function(e) {
5983 Roo.apply(Roo.bootstrap.NavGroup, {
5987 * register a Navigation Group
5988 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5990 register : function(navgrp)
5992 this.groups[navgrp.navId] = navgrp;
5996 * fetch a Navigation Group based on the navigation ID
5997 * @param {string} the navgroup to add
5998 * @returns {Roo.bootstrap.NavGroup} the navgroup
6000 get: function(navId) {
6001 if (typeof(this.groups[navId]) == 'undefined') {
6003 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6005 return this.groups[navId] ;
6020 * @class Roo.bootstrap.NavItem
6021 * @extends Roo.bootstrap.Component
6022 * Bootstrap Navbar.NavItem class
6023 * @cfg {String} href link to
6024 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6026 * @cfg {String} html content of button
6027 * @cfg {String} badge text inside badge
6028 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6029 * @cfg {String} glyphicon DEPRICATED - use fa
6030 * @cfg {String} icon DEPRICATED - use fa
6031 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6032 * @cfg {Boolean} active Is item active
6033 * @cfg {Boolean} disabled Is item disabled
6035 * @cfg {Boolean} preventDefault (true | false) default false
6036 * @cfg {String} tabId the tab that this item activates.
6037 * @cfg {String} tagtype (a|span) render as a href or span?
6038 * @cfg {Boolean} animateRef (true|false) link to element default false
6041 * Create a new Navbar Item
6042 * @param {Object} config The config object
6044 Roo.bootstrap.NavItem = function(config){
6045 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6050 * The raw click event for the entire grid.
6051 * @param {Roo.EventObject} e
6056 * Fires when the active item active state changes
6057 * @param {Roo.bootstrap.NavItem} this
6058 * @param {boolean} state the new state
6064 * Fires when scroll to element
6065 * @param {Roo.bootstrap.NavItem} this
6066 * @param {Object} options
6067 * @param {Roo.EventObject} e
6075 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6084 preventDefault : false,
6092 button_outline : false,
6096 getAutoCreate : function(){
6104 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6106 if (this.disabled) {
6107 cfg.cls += ' disabled';
6111 if (this.button_weight.length) {
6112 cfg.tag = this.href ? 'a' : 'button';
6113 cfg.html = this.html || '';
6114 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6116 cfg.href = this.href;
6119 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6122 // menu .. should add dropdown-menu class - so no need for carat..
6124 if (this.badge !== '') {
6126 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6131 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6135 href : this.href || "#",
6136 html: this.html || ''
6139 if (this.tagtype == 'a') {
6140 cfg.cn[0].cls = 'nav-link';
6143 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6146 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6148 if(this.glyphicon) {
6149 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6154 cfg.cn[0].html += " <span class='caret'></span>";
6158 if (this.badge !== '') {
6160 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6168 onRender : function(ct, position)
6170 // Roo.log("Call onRender: " + this.xtype);
6171 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6175 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6176 this.navLink = this.el.select('.nav-link',true).first();
6181 initEvents: function()
6183 if (typeof (this.menu) != 'undefined') {
6184 this.menu.parentType = this.xtype;
6185 this.menu.triggerEl = this.el;
6186 this.menu = this.addxtype(Roo.apply({}, this.menu));
6189 this.el.select('a',true).on('click', this.onClick, this);
6191 if(this.tagtype == 'span'){
6192 this.el.select('span',true).on('click', this.onClick, this);
6195 // at this point parent should be available..
6196 this.parent().register(this);
6199 onClick : function(e)
6201 if (e.getTarget('.dropdown-menu-item')) {
6202 // did you click on a menu itemm.... - then don't trigger onclick..
6207 this.preventDefault ||
6210 Roo.log("NavItem - prevent Default?");
6214 if (this.disabled) {
6218 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6219 if (tg && tg.transition) {
6220 Roo.log("waiting for the transitionend");
6226 //Roo.log("fire event clicked");
6227 if(this.fireEvent('click', this, e) === false){
6231 if(this.tagtype == 'span'){
6235 //Roo.log(this.href);
6236 var ael = this.el.select('a',true).first();
6239 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6240 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6241 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6242 return; // ignore... - it's a 'hash' to another page.
6244 Roo.log("NavItem - prevent Default?");
6246 this.scrollToElement(e);
6250 var p = this.parent();
6252 if (['tabs','pills'].indexOf(p.type)!==-1) {
6253 if (typeof(p.setActiveItem) !== 'undefined') {
6254 p.setActiveItem(this);
6258 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6259 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6260 // remove the collapsed menu expand...
6261 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6265 isActive: function () {
6268 setActive : function(state, fire, is_was_active)
6270 if (this.active && !state && this.navId) {
6271 this.was_active = true;
6272 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6274 nv.clearWasActive(this);
6278 this.active = state;
6281 this.el.removeClass('active');
6282 this.navLink ? this.navLink.removeClass('active') : false;
6283 } else if (!this.el.hasClass('active')) {
6285 this.el.addClass('active');
6286 if (Roo.bootstrap.version == 4 && this.navLink ) {
6287 this.navLink.addClass('active');
6292 this.fireEvent('changed', this, state);
6295 // show a panel if it's registered and related..
6297 if (!this.navId || !this.tabId || !state || is_was_active) {
6301 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6305 var pan = tg.getPanelByName(this.tabId);
6309 // if we can not flip to new panel - go back to old nav highlight..
6310 if (false == tg.showPanel(pan)) {
6311 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6313 var onav = nv.getWasActive();
6315 onav.setActive(true, false, true);
6324 // this should not be here...
6325 setDisabled : function(state)
6327 this.disabled = state;
6329 this.el.removeClass('disabled');
6330 } else if (!this.el.hasClass('disabled')) {
6331 this.el.addClass('disabled');
6337 * Fetch the element to display the tooltip on.
6338 * @return {Roo.Element} defaults to this.el
6340 tooltipEl : function()
6342 return this.el.select('' + this.tagtype + '', true).first();
6345 scrollToElement : function(e)
6347 var c = document.body;
6350 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6352 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6353 c = document.documentElement;
6356 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6362 var o = target.calcOffsetsTo(c);
6369 this.fireEvent('scrollto', this, options, e);
6371 Roo.get(c).scrollTo('top', options.value, true);
6384 * <span> icon </span>
6385 * <span> text </span>
6386 * <span>badge </span>
6390 * @class Roo.bootstrap.NavSidebarItem
6391 * @extends Roo.bootstrap.NavItem
6392 * Bootstrap Navbar.NavSidebarItem class
6393 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6394 * {Boolean} open is the menu open
6395 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6396 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6397 * {String} buttonSize (sm|md|lg)the extra classes for the button
6398 * {Boolean} showArrow show arrow next to the text (default true)
6400 * Create a new Navbar Button
6401 * @param {Object} config The config object
6403 Roo.bootstrap.NavSidebarItem = function(config){
6404 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6409 * The raw click event for the entire grid.
6410 * @param {Roo.EventObject} e
6415 * Fires when the active item active state changes
6416 * @param {Roo.bootstrap.NavSidebarItem} this
6417 * @param {boolean} state the new state
6425 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6427 badgeWeight : 'default',
6433 buttonWeight : 'default',
6439 getAutoCreate : function(){
6444 href : this.href || '#',
6450 if(this.buttonView){
6453 href : this.href || '#',
6454 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6467 cfg.cls += ' active';
6470 if (this.disabled) {
6471 cfg.cls += ' disabled';
6474 cfg.cls += ' open x-open';
6477 if (this.glyphicon || this.icon) {
6478 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6479 a.cn.push({ tag : 'i', cls : c }) ;
6482 if(!this.buttonView){
6485 html : this.html || ''
6492 if (this.badge !== '') {
6493 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6499 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6502 a.cls += ' dropdown-toggle treeview' ;
6508 initEvents : function()
6510 if (typeof (this.menu) != 'undefined') {
6511 this.menu.parentType = this.xtype;
6512 this.menu.triggerEl = this.el;
6513 this.menu = this.addxtype(Roo.apply({}, this.menu));
6516 this.el.on('click', this.onClick, this);
6518 if(this.badge !== ''){
6519 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6524 onClick : function(e)
6531 if(this.preventDefault){
6535 this.fireEvent('click', this, e);
6538 disable : function()
6540 this.setDisabled(true);
6545 this.setDisabled(false);
6548 setDisabled : function(state)
6550 if(this.disabled == state){
6554 this.disabled = state;
6557 this.el.addClass('disabled');
6561 this.el.removeClass('disabled');
6566 setActive : function(state)
6568 if(this.active == state){
6572 this.active = state;
6575 this.el.addClass('active');
6579 this.el.removeClass('active');
6584 isActive: function ()
6589 setBadge : function(str)
6595 this.badgeEl.dom.innerHTML = str;
6612 * @class Roo.bootstrap.Row
6613 * @extends Roo.bootstrap.Component
6614 * Bootstrap Row class (contains columns...)
6618 * @param {Object} config The config object
6621 Roo.bootstrap.Row = function(config){
6622 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6625 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6627 getAutoCreate : function(){
6646 * @class Roo.bootstrap.Pagination
6647 * @extends Roo.bootstrap.Component
6648 * Bootstrap Pagination class
6649 * @cfg {String} size xs | sm | md | lg
6650 * @cfg {Boolean} inverse false | true
6653 * Create a new Pagination
6654 * @param {Object} config The config object
6657 Roo.bootstrap.Pagination = function(config){
6658 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6661 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6667 getAutoCreate : function(){
6673 cfg.cls += ' inverse';
6679 cfg.cls += " " + this.cls;
6697 * @class Roo.bootstrap.PaginationItem
6698 * @extends Roo.bootstrap.Component
6699 * Bootstrap PaginationItem class
6700 * @cfg {String} html text
6701 * @cfg {String} href the link
6702 * @cfg {Boolean} preventDefault (true | false) default true
6703 * @cfg {Boolean} active (true | false) default false
6704 * @cfg {Boolean} disabled default false
6708 * Create a new PaginationItem
6709 * @param {Object} config The config object
6713 Roo.bootstrap.PaginationItem = function(config){
6714 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6719 * The raw click event for the entire grid.
6720 * @param {Roo.EventObject} e
6726 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6730 preventDefault: true,
6735 getAutoCreate : function(){
6741 href : this.href ? this.href : '#',
6742 html : this.html ? this.html : ''
6752 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6756 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6762 initEvents: function() {
6764 this.el.on('click', this.onClick, this);
6767 onClick : function(e)
6769 Roo.log('PaginationItem on click ');
6770 if(this.preventDefault){
6778 this.fireEvent('click', this, e);
6794 * @class Roo.bootstrap.Slider
6795 * @extends Roo.bootstrap.Component
6796 * Bootstrap Slider class
6799 * Create a new Slider
6800 * @param {Object} config The config object
6803 Roo.bootstrap.Slider = function(config){
6804 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6807 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6809 getAutoCreate : function(){
6813 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6817 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6829 * Ext JS Library 1.1.1
6830 * Copyright(c) 2006-2007, Ext JS, LLC.
6832 * Originally Released Under LGPL - original licence link has changed is not relivant.
6835 * <script type="text/javascript">
6840 * @class Roo.grid.ColumnModel
6841 * @extends Roo.util.Observable
6842 * This is the default implementation of a ColumnModel used by the Grid. It defines
6843 * the columns in the grid.
6846 var colModel = new Roo.grid.ColumnModel([
6847 {header: "Ticker", width: 60, sortable: true, locked: true},
6848 {header: "Company Name", width: 150, sortable: true},
6849 {header: "Market Cap.", width: 100, sortable: true},
6850 {header: "$ Sales", width: 100, sortable: true, renderer: money},
6851 {header: "Employees", width: 100, sortable: true, resizable: false}
6856 * The config options listed for this class are options which may appear in each
6857 * individual column definition.
6858 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6860 * @param {Object} config An Array of column config objects. See this class's
6861 * config objects for details.
6863 Roo.grid.ColumnModel = function(config){
6865 * The config passed into the constructor
6867 this.config = config;
6870 // if no id, create one
6871 // if the column does not have a dataIndex mapping,
6872 // map it to the order it is in the config
6873 for(var i = 0, len = config.length; i < len; i++){
6875 if(typeof c.dataIndex == "undefined"){
6878 if(typeof c.renderer == "string"){
6879 c.renderer = Roo.util.Format[c.renderer];
6881 if(typeof c.id == "undefined"){
6884 if(c.editor && c.editor.xtype){
6885 c.editor = Roo.factory(c.editor, Roo.grid);
6887 if(c.editor && c.editor.isFormField){
6888 c.editor = new Roo.grid.GridEditor(c.editor);
6890 this.lookup[c.id] = c;
6894 * The width of columns which have no width specified (defaults to 100)
6897 this.defaultWidth = 100;
6900 * Default sortable of columns which have no sortable specified (defaults to false)
6903 this.defaultSortable = false;
6907 * @event widthchange
6908 * Fires when the width of a column changes.
6909 * @param {ColumnModel} this
6910 * @param {Number} columnIndex The column index
6911 * @param {Number} newWidth The new width
6913 "widthchange": true,
6915 * @event headerchange
6916 * Fires when the text of a header changes.
6917 * @param {ColumnModel} this
6918 * @param {Number} columnIndex The column index
6919 * @param {Number} newText The new header text
6921 "headerchange": true,
6923 * @event hiddenchange
6924 * Fires when a column is hidden or "unhidden".
6925 * @param {ColumnModel} this
6926 * @param {Number} columnIndex The column index
6927 * @param {Boolean} hidden true if hidden, false otherwise
6929 "hiddenchange": true,
6931 * @event columnmoved
6932 * Fires when a column is moved.
6933 * @param {ColumnModel} this
6934 * @param {Number} oldIndex
6935 * @param {Number} newIndex
6937 "columnmoved" : true,
6939 * @event columlockchange
6940 * Fires when a column's locked state is changed
6941 * @param {ColumnModel} this
6942 * @param {Number} colIndex
6943 * @param {Boolean} locked true if locked
6945 "columnlockchange" : true
6947 Roo.grid.ColumnModel.superclass.constructor.call(this);
6949 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6951 * @cfg {String} header The header text to display in the Grid view.
6954 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6955 * {@link Roo.data.Record} definition from which to draw the column's value. If not
6956 * specified, the column's index is used as an index into the Record's data Array.
6959 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6960 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6963 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6964 * Defaults to the value of the {@link #defaultSortable} property.
6965 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6968 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
6971 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
6974 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6977 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6980 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6981 * given the cell's data value. See {@link #setRenderer}. If not specified, the
6982 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6983 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6986 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
6989 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
6992 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
6995 * @cfg {String} cursor (Optional)
6998 * @cfg {String} tooltip (Optional)
7001 * @cfg {Number} xs (Optional)
7004 * @cfg {Number} sm (Optional)
7007 * @cfg {Number} md (Optional)
7010 * @cfg {Number} lg (Optional)
7013 * Returns the id of the column at the specified index.
7014 * @param {Number} index The column index
7015 * @return {String} the id
7017 getColumnId : function(index){
7018 return this.config[index].id;
7022 * Returns the column for a specified id.
7023 * @param {String} id The column id
7024 * @return {Object} the column
7026 getColumnById : function(id){
7027 return this.lookup[id];
7032 * Returns the column for a specified dataIndex.
7033 * @param {String} dataIndex The column dataIndex
7034 * @return {Object|Boolean} the column or false if not found
7036 getColumnByDataIndex: function(dataIndex){
7037 var index = this.findColumnIndex(dataIndex);
7038 return index > -1 ? this.config[index] : false;
7042 * Returns the index for a specified column id.
7043 * @param {String} id The column id
7044 * @return {Number} the index, or -1 if not found
7046 getIndexById : function(id){
7047 for(var i = 0, len = this.config.length; i < len; i++){
7048 if(this.config[i].id == id){
7056 * Returns the index for a specified column dataIndex.
7057 * @param {String} dataIndex The column dataIndex
7058 * @return {Number} the index, or -1 if not found
7061 findColumnIndex : function(dataIndex){
7062 for(var i = 0, len = this.config.length; i < len; i++){
7063 if(this.config[i].dataIndex == dataIndex){
7071 moveColumn : function(oldIndex, newIndex){
7072 var c = this.config[oldIndex];
7073 this.config.splice(oldIndex, 1);
7074 this.config.splice(newIndex, 0, c);
7075 this.dataMap = null;
7076 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7079 isLocked : function(colIndex){
7080 return this.config[colIndex].locked === true;
7083 setLocked : function(colIndex, value, suppressEvent){
7084 if(this.isLocked(colIndex) == value){
7087 this.config[colIndex].locked = value;
7089 this.fireEvent("columnlockchange", this, colIndex, value);
7093 getTotalLockedWidth : function(){
7095 for(var i = 0; i < this.config.length; i++){
7096 if(this.isLocked(i) && !this.isHidden(i)){
7097 this.totalWidth += this.getColumnWidth(i);
7103 getLockedCount : function(){
7104 for(var i = 0, len = this.config.length; i < len; i++){
7105 if(!this.isLocked(i)){
7110 return this.config.length;
7114 * Returns the number of columns.
7117 getColumnCount : function(visibleOnly){
7118 if(visibleOnly === true){
7120 for(var i = 0, len = this.config.length; i < len; i++){
7121 if(!this.isHidden(i)){
7127 return this.config.length;
7131 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7132 * @param {Function} fn
7133 * @param {Object} scope (optional)
7134 * @return {Array} result
7136 getColumnsBy : function(fn, scope){
7138 for(var i = 0, len = this.config.length; i < len; i++){
7139 var c = this.config[i];
7140 if(fn.call(scope||this, c, i) === true){
7148 * Returns true if the specified column is sortable.
7149 * @param {Number} col The column index
7152 isSortable : function(col){
7153 if(typeof this.config[col].sortable == "undefined"){
7154 return this.defaultSortable;
7156 return this.config[col].sortable;
7160 * Returns the rendering (formatting) function defined for the column.
7161 * @param {Number} col The column index.
7162 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7164 getRenderer : function(col){
7165 if(!this.config[col].renderer){
7166 return Roo.grid.ColumnModel.defaultRenderer;
7168 return this.config[col].renderer;
7172 * Sets the rendering (formatting) function for a column.
7173 * @param {Number} col The column index
7174 * @param {Function} fn The function to use to process the cell's raw data
7175 * to return HTML markup for the grid view. The render function is called with
7176 * the following parameters:<ul>
7177 * <li>Data value.</li>
7178 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7179 * <li>css A CSS style string to apply to the table cell.</li>
7180 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7181 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7182 * <li>Row index</li>
7183 * <li>Column index</li>
7184 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7186 setRenderer : function(col, fn){
7187 this.config[col].renderer = fn;
7191 * Returns the width for the specified column.
7192 * @param {Number} col The column index
7195 getColumnWidth : function(col){
7196 return this.config[col].width * 1 || this.defaultWidth;
7200 * Sets the width for a column.
7201 * @param {Number} col The column index
7202 * @param {Number} width The new width
7204 setColumnWidth : function(col, width, suppressEvent){
7205 this.config[col].width = width;
7206 this.totalWidth = null;
7208 this.fireEvent("widthchange", this, col, width);
7213 * Returns the total width of all columns.
7214 * @param {Boolean} includeHidden True to include hidden column widths
7217 getTotalWidth : function(includeHidden){
7218 if(!this.totalWidth){
7219 this.totalWidth = 0;
7220 for(var i = 0, len = this.config.length; i < len; i++){
7221 if(includeHidden || !this.isHidden(i)){
7222 this.totalWidth += this.getColumnWidth(i);
7226 return this.totalWidth;
7230 * Returns the header for the specified column.
7231 * @param {Number} col The column index
7234 getColumnHeader : function(col){
7235 return this.config[col].header;
7239 * Sets the header for a column.
7240 * @param {Number} col The column index
7241 * @param {String} header The new header
7243 setColumnHeader : function(col, header){
7244 this.config[col].header = header;
7245 this.fireEvent("headerchange", this, col, header);
7249 * Returns the tooltip for the specified column.
7250 * @param {Number} col The column index
7253 getColumnTooltip : function(col){
7254 return this.config[col].tooltip;
7257 * Sets the tooltip for a column.
7258 * @param {Number} col The column index
7259 * @param {String} tooltip The new tooltip
7261 setColumnTooltip : function(col, tooltip){
7262 this.config[col].tooltip = tooltip;
7266 * Returns the dataIndex for the specified column.
7267 * @param {Number} col The column index
7270 getDataIndex : function(col){
7271 return this.config[col].dataIndex;
7275 * Sets the dataIndex for a column.
7276 * @param {Number} col The column index
7277 * @param {Number} dataIndex The new dataIndex
7279 setDataIndex : function(col, dataIndex){
7280 this.config[col].dataIndex = dataIndex;
7286 * Returns true if the cell is editable.
7287 * @param {Number} colIndex The column index
7288 * @param {Number} rowIndex The row index - this is nto actually used..?
7291 isCellEditable : function(colIndex, rowIndex){
7292 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7296 * Returns the editor defined for the cell/column.
7297 * return false or null to disable editing.
7298 * @param {Number} colIndex The column index
7299 * @param {Number} rowIndex The row index
7302 getCellEditor : function(colIndex, rowIndex){
7303 return this.config[colIndex].editor;
7307 * Sets if a column is editable.
7308 * @param {Number} col The column index
7309 * @param {Boolean} editable True if the column is editable
7311 setEditable : function(col, editable){
7312 this.config[col].editable = editable;
7317 * Returns true if the column is hidden.
7318 * @param {Number} colIndex The column index
7321 isHidden : function(colIndex){
7322 return this.config[colIndex].hidden;
7327 * Returns true if the column width cannot be changed
7329 isFixed : function(colIndex){
7330 return this.config[colIndex].fixed;
7334 * Returns true if the column can be resized
7337 isResizable : function(colIndex){
7338 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7341 * Sets if a column is hidden.
7342 * @param {Number} colIndex The column index
7343 * @param {Boolean} hidden True if the column is hidden
7345 setHidden : function(colIndex, hidden){
7346 this.config[colIndex].hidden = hidden;
7347 this.totalWidth = null;
7348 this.fireEvent("hiddenchange", this, colIndex, hidden);
7352 * Sets the editor for a column.
7353 * @param {Number} col The column index
7354 * @param {Object} editor The editor object
7356 setEditor : function(col, editor){
7357 this.config[col].editor = editor;
7361 Roo.grid.ColumnModel.defaultRenderer = function(value)
7363 if(typeof value == "object") {
7366 if(typeof value == "string" && value.length < 1){
7370 return String.format("{0}", value);
7373 // Alias for backwards compatibility
7374 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7377 * Ext JS Library 1.1.1
7378 * Copyright(c) 2006-2007, Ext JS, LLC.
7380 * Originally Released Under LGPL - original licence link has changed is not relivant.
7383 * <script type="text/javascript">
7387 * @class Roo.LoadMask
7388 * A simple utility class for generically masking elements while loading data. If the element being masked has
7389 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7390 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7391 * element's UpdateManager load indicator and will be destroyed after the initial load.
7393 * Create a new LoadMask
7394 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7395 * @param {Object} config The config object
7397 Roo.LoadMask = function(el, config){
7398 this.el = Roo.get(el);
7399 Roo.apply(this, config);
7401 this.store.on('beforeload', this.onBeforeLoad, this);
7402 this.store.on('load', this.onLoad, this);
7403 this.store.on('loadexception', this.onLoadException, this);
7404 this.removeMask = false;
7406 var um = this.el.getUpdateManager();
7407 um.showLoadIndicator = false; // disable the default indicator
7408 um.on('beforeupdate', this.onBeforeLoad, this);
7409 um.on('update', this.onLoad, this);
7410 um.on('failure', this.onLoad, this);
7411 this.removeMask = true;
7415 Roo.LoadMask.prototype = {
7417 * @cfg {Boolean} removeMask
7418 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7419 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7423 * The text to display in a centered loading message box (defaults to 'Loading...')
7427 * @cfg {String} msgCls
7428 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7430 msgCls : 'x-mask-loading',
7433 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7439 * Disables the mask to prevent it from being displayed
7441 disable : function(){
7442 this.disabled = true;
7446 * Enables the mask so that it can be displayed
7448 enable : function(){
7449 this.disabled = false;
7452 onLoadException : function()
7456 if (typeof(arguments[3]) != 'undefined') {
7457 Roo.MessageBox.alert("Error loading",arguments[3]);
7461 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7462 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7469 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7474 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7478 onBeforeLoad : function(){
7480 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7485 destroy : function(){
7487 this.store.un('beforeload', this.onBeforeLoad, this);
7488 this.store.un('load', this.onLoad, this);
7489 this.store.un('loadexception', this.onLoadException, this);
7491 var um = this.el.getUpdateManager();
7492 um.un('beforeupdate', this.onBeforeLoad, this);
7493 um.un('update', this.onLoad, this);
7494 um.un('failure', this.onLoad, this);
7505 * @class Roo.bootstrap.Table
7506 * @extends Roo.bootstrap.Component
7507 * Bootstrap Table class
7508 * @cfg {String} cls table class
7509 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7510 * @cfg {String} bgcolor Specifies the background color for a table
7511 * @cfg {Number} border Specifies whether the table cells should have borders or not
7512 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7513 * @cfg {Number} cellspacing Specifies the space between cells
7514 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7515 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7516 * @cfg {String} sortable Specifies that the table should be sortable
7517 * @cfg {String} summary Specifies a summary of the content of a table
7518 * @cfg {Number} width Specifies the width of a table
7519 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7521 * @cfg {boolean} striped Should the rows be alternative striped
7522 * @cfg {boolean} bordered Add borders to the table
7523 * @cfg {boolean} hover Add hover highlighting
7524 * @cfg {boolean} condensed Format condensed
7525 * @cfg {boolean} responsive Format condensed
7526 * @cfg {Boolean} loadMask (true|false) default false
7527 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7528 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7529 * @cfg {Boolean} rowSelection (true|false) default false
7530 * @cfg {Boolean} cellSelection (true|false) default false
7531 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7532 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7533 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7534 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7538 * Create a new Table
7539 * @param {Object} config The config object
7542 Roo.bootstrap.Table = function(config){
7543 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7548 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7549 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7550 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7551 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7553 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7555 this.sm.grid = this;
7556 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7557 this.sm = this.selModel;
7558 this.sm.xmodule = this.xmodule || false;
7561 if (this.cm && typeof(this.cm.config) == 'undefined') {
7562 this.colModel = new Roo.grid.ColumnModel(this.cm);
7563 this.cm = this.colModel;
7564 this.cm.xmodule = this.xmodule || false;
7567 this.store= Roo.factory(this.store, Roo.data);
7568 this.ds = this.store;
7569 this.ds.xmodule = this.xmodule || false;
7572 if (this.footer && this.store) {
7573 this.footer.dataSource = this.ds;
7574 this.footer = Roo.factory(this.footer);
7581 * Fires when a cell is clicked
7582 * @param {Roo.bootstrap.Table} this
7583 * @param {Roo.Element} el
7584 * @param {Number} rowIndex
7585 * @param {Number} columnIndex
7586 * @param {Roo.EventObject} e
7590 * @event celldblclick
7591 * Fires when a cell is double clicked
7592 * @param {Roo.bootstrap.Table} this
7593 * @param {Roo.Element} el
7594 * @param {Number} rowIndex
7595 * @param {Number} columnIndex
7596 * @param {Roo.EventObject} e
7598 "celldblclick" : true,
7601 * Fires when a row is clicked
7602 * @param {Roo.bootstrap.Table} this
7603 * @param {Roo.Element} el
7604 * @param {Number} rowIndex
7605 * @param {Roo.EventObject} e
7609 * @event rowdblclick
7610 * Fires when a row is double clicked
7611 * @param {Roo.bootstrap.Table} this
7612 * @param {Roo.Element} el
7613 * @param {Number} rowIndex
7614 * @param {Roo.EventObject} e
7616 "rowdblclick" : true,
7619 * Fires when a mouseover occur
7620 * @param {Roo.bootstrap.Table} this
7621 * @param {Roo.Element} el
7622 * @param {Number} rowIndex
7623 * @param {Number} columnIndex
7624 * @param {Roo.EventObject} e
7629 * Fires when a mouseout occur
7630 * @param {Roo.bootstrap.Table} this
7631 * @param {Roo.Element} el
7632 * @param {Number} rowIndex
7633 * @param {Number} columnIndex
7634 * @param {Roo.EventObject} e
7639 * Fires when a row is rendered, so you can change add a style to it.
7640 * @param {Roo.bootstrap.Table} this
7641 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7645 * @event rowsrendered
7646 * Fires when all the rows have been rendered
7647 * @param {Roo.bootstrap.Table} this
7649 'rowsrendered' : true,
7651 * @event contextmenu
7652 * The raw contextmenu event for the entire grid.
7653 * @param {Roo.EventObject} e
7655 "contextmenu" : true,
7657 * @event rowcontextmenu
7658 * Fires when a row is right clicked
7659 * @param {Roo.bootstrap.Table} this
7660 * @param {Number} rowIndex
7661 * @param {Roo.EventObject} e
7663 "rowcontextmenu" : true,
7665 * @event cellcontextmenu
7666 * Fires when a cell is right clicked
7667 * @param {Roo.bootstrap.Table} this
7668 * @param {Number} rowIndex
7669 * @param {Number} cellIndex
7670 * @param {Roo.EventObject} e
7672 "cellcontextmenu" : true,
7674 * @event headercontextmenu
7675 * Fires when a header is right clicked
7676 * @param {Roo.bootstrap.Table} this
7677 * @param {Number} columnIndex
7678 * @param {Roo.EventObject} e
7680 "headercontextmenu" : true
7684 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7710 rowSelection : false,
7711 cellSelection : false,
7714 // Roo.Element - the tbody
7716 // Roo.Element - thead element
7719 container: false, // used by gridpanel...
7725 auto_hide_footer : false,
7727 getAutoCreate : function()
7729 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7736 if (this.scrollBody) {
7737 cfg.cls += ' table-body-fixed';
7740 cfg.cls += ' table-striped';
7744 cfg.cls += ' table-hover';
7746 if (this.bordered) {
7747 cfg.cls += ' table-bordered';
7749 if (this.condensed) {
7750 cfg.cls += ' table-condensed';
7752 if (this.responsive) {
7753 cfg.cls += ' table-responsive';
7757 cfg.cls+= ' ' +this.cls;
7760 // this lot should be simplifed...
7773 ].forEach(function(k) {
7781 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7784 if(this.store || this.cm){
7785 if(this.headerShow){
7786 cfg.cn.push(this.renderHeader());
7789 cfg.cn.push(this.renderBody());
7791 if(this.footerShow){
7792 cfg.cn.push(this.renderFooter());
7794 // where does this come from?
7795 //cfg.cls+= ' TableGrid';
7798 return { cn : [ cfg ] };
7801 initEvents : function()
7803 if(!this.store || !this.cm){
7806 if (this.selModel) {
7807 this.selModel.initEvents();
7811 //Roo.log('initEvents with ds!!!!');
7813 this.mainBody = this.el.select('tbody', true).first();
7814 this.mainHead = this.el.select('thead', true).first();
7815 this.mainFoot = this.el.select('tfoot', true).first();
7821 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7822 e.on('click', _this.sort, _this);
7825 this.mainBody.on("click", this.onClick, this);
7826 this.mainBody.on("dblclick", this.onDblClick, this);
7828 // why is this done????? = it breaks dialogs??
7829 //this.parent().el.setStyle('position', 'relative');
7833 this.footer.parentId = this.id;
7834 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7837 this.el.select('tfoot tr td').first().addClass('hide');
7842 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7845 this.store.on('load', this.onLoad, this);
7846 this.store.on('beforeload', this.onBeforeLoad, this);
7847 this.store.on('update', this.onUpdate, this);
7848 this.store.on('add', this.onAdd, this);
7849 this.store.on("clear", this.clear, this);
7851 this.el.on("contextmenu", this.onContextMenu, this);
7853 this.mainBody.on('scroll', this.onBodyScroll, this);
7855 this.cm.on("headerchange", this.onHeaderChange, this);
7857 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7861 onContextMenu : function(e, t)
7863 this.processEvent("contextmenu", e);
7866 processEvent : function(name, e)
7868 if (name != 'touchstart' ) {
7869 this.fireEvent(name, e);
7872 var t = e.getTarget();
7874 var cell = Roo.get(t);
7880 if(cell.findParent('tfoot', false, true)){
7884 if(cell.findParent('thead', false, true)){
7886 if(e.getTarget().nodeName.toLowerCase() != 'th'){
7887 cell = Roo.get(t).findParent('th', false, true);
7889 Roo.log("failed to find th in thead?");
7890 Roo.log(e.getTarget());
7895 var cellIndex = cell.dom.cellIndex;
7897 var ename = name == 'touchstart' ? 'click' : name;
7898 this.fireEvent("header" + ename, this, cellIndex, e);
7903 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7904 cell = Roo.get(t).findParent('td', false, true);
7906 Roo.log("failed to find th in tbody?");
7907 Roo.log(e.getTarget());
7912 var row = cell.findParent('tr', false, true);
7913 var cellIndex = cell.dom.cellIndex;
7914 var rowIndex = row.dom.rowIndex - 1;
7918 this.fireEvent("row" + name, this, rowIndex, e);
7922 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7928 onMouseover : function(e, el)
7930 var cell = Roo.get(el);
7936 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7937 cell = cell.findParent('td', false, true);
7940 var row = cell.findParent('tr', false, true);
7941 var cellIndex = cell.dom.cellIndex;
7942 var rowIndex = row.dom.rowIndex - 1; // start from 0
7944 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7948 onMouseout : function(e, el)
7950 var cell = Roo.get(el);
7956 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7957 cell = cell.findParent('td', false, true);
7960 var row = cell.findParent('tr', false, true);
7961 var cellIndex = cell.dom.cellIndex;
7962 var rowIndex = row.dom.rowIndex - 1; // start from 0
7964 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7968 onClick : function(e, el)
7970 var cell = Roo.get(el);
7972 if(!cell || (!this.cellSelection && !this.rowSelection)){
7976 if(e.getTarget().nodeName.toLowerCase() != 'td'){
7977 cell = cell.findParent('td', false, true);
7980 if(!cell || typeof(cell) == 'undefined'){
7984 var row = cell.findParent('tr', false, true);
7986 if(!row || typeof(row) == 'undefined'){
7990 var cellIndex = cell.dom.cellIndex;
7991 var rowIndex = this.getRowIndex(row);
7993 // why??? - should these not be based on SelectionModel?
7994 if(this.cellSelection){
7995 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7998 if(this.rowSelection){
7999 this.fireEvent('rowclick', this, row, rowIndex, e);
8005 onDblClick : function(e,el)
8007 var cell = Roo.get(el);
8009 if(!cell || (!this.cellSelection && !this.rowSelection)){
8013 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8014 cell = cell.findParent('td', false, true);
8017 if(!cell || typeof(cell) == 'undefined'){
8021 var row = cell.findParent('tr', false, true);
8023 if(!row || typeof(row) == 'undefined'){
8027 var cellIndex = cell.dom.cellIndex;
8028 var rowIndex = this.getRowIndex(row);
8030 if(this.cellSelection){
8031 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8034 if(this.rowSelection){
8035 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8039 sort : function(e,el)
8041 var col = Roo.get(el);
8043 if(!col.hasClass('sortable')){
8047 var sort = col.attr('sort');
8050 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8054 this.store.sortInfo = {field : sort, direction : dir};
8057 Roo.log("calling footer first");
8058 this.footer.onClick('first');
8061 this.store.load({ params : { start : 0 } });
8065 renderHeader : function()
8073 this.totalWidth = 0;
8075 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8077 var config = cm.config[i];
8081 cls : 'x-hcol-' + i,
8083 html: cm.getColumnHeader(i)
8088 if(typeof(config.sortable) != 'undefined' && config.sortable){
8090 c.html = '<i class="glyphicon"></i>' + c.html;
8093 // could use BS4 hidden-..-down
8095 if(typeof(config.lgHeader) != 'undefined'){
8096 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8099 if(typeof(config.mdHeader) != 'undefined'){
8100 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8103 if(typeof(config.smHeader) != 'undefined'){
8104 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8107 if(typeof(config.xsHeader) != 'undefined'){
8108 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8115 if(typeof(config.tooltip) != 'undefined'){
8116 c.tooltip = config.tooltip;
8119 if(typeof(config.colspan) != 'undefined'){
8120 c.colspan = config.colspan;
8123 if(typeof(config.hidden) != 'undefined' && config.hidden){
8124 c.style += ' display:none;';
8127 if(typeof(config.dataIndex) != 'undefined'){
8128 c.sort = config.dataIndex;
8133 if(typeof(config.align) != 'undefined' && config.align.length){
8134 c.style += ' text-align:' + config.align + ';';
8137 if(typeof(config.width) != 'undefined'){
8138 c.style += ' width:' + config.width + 'px;';
8139 this.totalWidth += config.width;
8141 this.totalWidth += 100; // assume minimum of 100 per column?
8144 if(typeof(config.cls) != 'undefined'){
8145 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8148 ['xs','sm','md','lg'].map(function(size){
8150 if(typeof(config[size]) == 'undefined'){
8154 if (!config[size]) { // 0 = hidden
8155 // BS 4 '0' is treated as hide that column and below.
8156 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8160 c.cls += ' col-' + size + '-' + config[size] + (
8161 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8173 renderBody : function()
8183 colspan : this.cm.getColumnCount()
8193 renderFooter : function()
8203 colspan : this.cm.getColumnCount()
8217 // Roo.log('ds onload');
8222 var ds = this.store;
8224 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8225 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8226 if (_this.store.sortInfo) {
8228 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8229 e.select('i', true).addClass(['glyphicon-arrow-up']);
8232 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8233 e.select('i', true).addClass(['glyphicon-arrow-down']);
8238 var tbody = this.mainBody;
8240 if(ds.getCount() > 0){
8241 ds.data.each(function(d,rowIndex){
8242 var row = this.renderRow(cm, ds, rowIndex);
8244 tbody.createChild(row);
8248 if(row.cellObjects.length){
8249 Roo.each(row.cellObjects, function(r){
8250 _this.renderCellObject(r);
8257 var tfoot = this.el.select('tfoot', true).first();
8259 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8261 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8263 var total = this.ds.getTotalCount();
8265 if(this.footer.pageSize < total){
8266 this.mainFoot.show();
8270 Roo.each(this.el.select('tbody td', true).elements, function(e){
8271 e.on('mouseover', _this.onMouseover, _this);
8274 Roo.each(this.el.select('tbody td', true).elements, function(e){
8275 e.on('mouseout', _this.onMouseout, _this);
8277 this.fireEvent('rowsrendered', this);
8283 onUpdate : function(ds,record)
8285 this.refreshRow(record);
8289 onRemove : function(ds, record, index, isUpdate){
8290 if(isUpdate !== true){
8291 this.fireEvent("beforerowremoved", this, index, record);
8293 var bt = this.mainBody.dom;
8295 var rows = this.el.select('tbody > tr', true).elements;
8297 if(typeof(rows[index]) != 'undefined'){
8298 bt.removeChild(rows[index].dom);
8301 // if(bt.rows[index]){
8302 // bt.removeChild(bt.rows[index]);
8305 if(isUpdate !== true){
8306 //this.stripeRows(index);
8307 //this.syncRowHeights(index, index);
8309 this.fireEvent("rowremoved", this, index, record);
8313 onAdd : function(ds, records, rowIndex)
8315 //Roo.log('on Add called');
8316 // - note this does not handle multiple adding very well..
8317 var bt = this.mainBody.dom;
8318 for (var i =0 ; i < records.length;i++) {
8319 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8320 //Roo.log(records[i]);
8321 //Roo.log(this.store.getAt(rowIndex+i));
8322 this.insertRow(this.store, rowIndex + i, false);
8329 refreshRow : function(record){
8330 var ds = this.store, index;
8331 if(typeof record == 'number'){
8333 record = ds.getAt(index);
8335 index = ds.indexOf(record);
8337 return; // should not happen - but seems to
8340 this.insertRow(ds, index, true);
8342 this.onRemove(ds, record, index+1, true);
8344 //this.syncRowHeights(index, index);
8346 this.fireEvent("rowupdated", this, index, record);
8349 insertRow : function(dm, rowIndex, isUpdate){
8352 this.fireEvent("beforerowsinserted", this, rowIndex);
8354 //var s = this.getScrollState();
8355 var row = this.renderRow(this.cm, this.store, rowIndex);
8356 // insert before rowIndex..
8357 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8361 if(row.cellObjects.length){
8362 Roo.each(row.cellObjects, function(r){
8363 _this.renderCellObject(r);
8368 this.fireEvent("rowsinserted", this, rowIndex);
8369 //this.syncRowHeights(firstRow, lastRow);
8370 //this.stripeRows(firstRow);
8377 getRowDom : function(rowIndex)
8379 var rows = this.el.select('tbody > tr', true).elements;
8381 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8384 // returns the object tree for a tr..
8387 renderRow : function(cm, ds, rowIndex)
8389 var d = ds.getAt(rowIndex);
8393 cls : 'x-row-' + rowIndex,
8397 var cellObjects = [];
8399 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8400 var config = cm.config[i];
8402 var renderer = cm.getRenderer(i);
8406 if(typeof(renderer) !== 'undefined'){
8407 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8409 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8410 // and are rendered into the cells after the row is rendered - using the id for the element.
8412 if(typeof(value) === 'object'){
8422 rowIndex : rowIndex,
8427 this.fireEvent('rowclass', this, rowcfg);
8431 cls : rowcfg.rowClass + ' x-col-' + i,
8433 html: (typeof(value) === 'object') ? '' : value
8440 if(typeof(config.colspan) != 'undefined'){
8441 td.colspan = config.colspan;
8444 if(typeof(config.hidden) != 'undefined' && config.hidden){
8445 td.style += ' display:none;';
8448 if(typeof(config.align) != 'undefined' && config.align.length){
8449 td.style += ' text-align:' + config.align + ';';
8451 if(typeof(config.valign) != 'undefined' && config.valign.length){
8452 td.style += ' vertical-align:' + config.valign + ';';
8455 if(typeof(config.width) != 'undefined'){
8456 td.style += ' width:' + config.width + 'px;';
8459 if(typeof(config.cursor) != 'undefined'){
8460 td.style += ' cursor:' + config.cursor + ';';
8463 if(typeof(config.cls) != 'undefined'){
8464 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8467 ['xs','sm','md','lg'].map(function(size){
8469 if(typeof(config[size]) == 'undefined'){
8475 if (!config[size]) { // 0 = hidden
8476 // BS 4 '0' is treated as hide that column and below.
8477 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8481 td.cls += ' col-' + size + '-' + config[size] + (
8482 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8492 row.cellObjects = cellObjects;
8500 onBeforeLoad : function()
8509 this.el.select('tbody', true).first().dom.innerHTML = '';
8512 * Show or hide a row.
8513 * @param {Number} rowIndex to show or hide
8514 * @param {Boolean} state hide
8516 setRowVisibility : function(rowIndex, state)
8518 var bt = this.mainBody.dom;
8520 var rows = this.el.select('tbody > tr', true).elements;
8522 if(typeof(rows[rowIndex]) == 'undefined'){
8525 rows[rowIndex].dom.style.display = state ? '' : 'none';
8529 getSelectionModel : function(){
8531 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8533 return this.selModel;
8536 * Render the Roo.bootstrap object from renderder
8538 renderCellObject : function(r)
8542 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8544 var t = r.cfg.render(r.container);
8547 Roo.each(r.cfg.cn, function(c){
8549 container: t.getChildContainer(),
8552 _this.renderCellObject(child);
8557 getRowIndex : function(row)
8561 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8572 * Returns the grid's underlying element = used by panel.Grid
8573 * @return {Element} The element
8575 getGridEl : function(){
8579 * Forces a resize - used by panel.Grid
8580 * @return {Element} The element
8582 autoSize : function()
8584 //var ctr = Roo.get(this.container.dom.parentElement);
8585 var ctr = Roo.get(this.el.dom);
8587 var thd = this.getGridEl().select('thead',true).first();
8588 var tbd = this.getGridEl().select('tbody', true).first();
8589 var tfd = this.getGridEl().select('tfoot', true).first();
8591 var cw = ctr.getWidth();
8595 tbd.setWidth(ctr.getWidth());
8596 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8597 // this needs fixing for various usage - currently only hydra job advers I think..
8599 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8601 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8604 cw = Math.max(cw, this.totalWidth);
8605 this.getGridEl().select('tr',true).setWidth(cw);
8606 // resize 'expandable coloumn?
8608 return; // we doe not have a view in this design..
8611 onBodyScroll: function()
8613 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8615 this.mainHead.setStyle({
8616 'position' : 'relative',
8617 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8623 var scrollHeight = this.mainBody.dom.scrollHeight;
8625 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8627 var height = this.mainBody.getHeight();
8629 if(scrollHeight - height == scrollTop) {
8631 var total = this.ds.getTotalCount();
8633 if(this.footer.cursor + this.footer.pageSize < total){
8635 this.footer.ds.load({
8637 start : this.footer.cursor + this.footer.pageSize,
8638 limit : this.footer.pageSize
8648 onHeaderChange : function()
8650 var header = this.renderHeader();
8651 var table = this.el.select('table', true).first();
8653 this.mainHead.remove();
8654 this.mainHead = table.createChild(header, this.mainBody, false);
8657 onHiddenChange : function(colModel, colIndex, hidden)
8659 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8660 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8662 this.CSS.updateRule(thSelector, "display", "");
8663 this.CSS.updateRule(tdSelector, "display", "");
8666 this.CSS.updateRule(thSelector, "display", "none");
8667 this.CSS.updateRule(tdSelector, "display", "none");
8670 this.onHeaderChange();
8674 setColumnWidth: function(col_index, width)
8676 // width = "md-2 xs-2..."
8677 if(!this.colModel.config[col_index]) {
8681 var w = width.split(" ");
8683 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8685 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8688 for(var j = 0; j < w.length; j++) {
8694 var size_cls = w[j].split("-");
8696 if(!Number.isInteger(size_cls[1] * 1)) {
8700 if(!this.colModel.config[col_index][size_cls[0]]) {
8704 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8708 h_row[0].classList.replace(
8709 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8710 "col-"+size_cls[0]+"-"+size_cls[1]
8713 for(var i = 0; i < rows.length; i++) {
8715 var size_cls = w[j].split("-");
8717 if(!Number.isInteger(size_cls[1] * 1)) {
8721 if(!this.colModel.config[col_index][size_cls[0]]) {
8725 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8729 rows[i].classList.replace(
8730 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8731 "col-"+size_cls[0]+"-"+size_cls[1]
8735 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8750 * @class Roo.bootstrap.TableCell
8751 * @extends Roo.bootstrap.Component
8752 * Bootstrap TableCell class
8753 * @cfg {String} html cell contain text
8754 * @cfg {String} cls cell class
8755 * @cfg {String} tag cell tag (td|th) default td
8756 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8757 * @cfg {String} align Aligns the content in a cell
8758 * @cfg {String} axis Categorizes cells
8759 * @cfg {String} bgcolor Specifies the background color of a cell
8760 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8761 * @cfg {Number} colspan Specifies the number of columns a cell should span
8762 * @cfg {String} headers Specifies one or more header cells a cell is related to
8763 * @cfg {Number} height Sets the height of a cell
8764 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8765 * @cfg {Number} rowspan Sets the number of rows a cell should span
8766 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8767 * @cfg {String} valign Vertical aligns the content in a cell
8768 * @cfg {Number} width Specifies the width of a cell
8771 * Create a new TableCell
8772 * @param {Object} config The config object
8775 Roo.bootstrap.TableCell = function(config){
8776 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8779 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8799 getAutoCreate : function(){
8800 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8820 cfg.align=this.align
8826 cfg.bgcolor=this.bgcolor
8829 cfg.charoff=this.charoff
8832 cfg.colspan=this.colspan
8835 cfg.headers=this.headers
8838 cfg.height=this.height
8841 cfg.nowrap=this.nowrap
8844 cfg.rowspan=this.rowspan
8847 cfg.scope=this.scope
8850 cfg.valign=this.valign
8853 cfg.width=this.width
8872 * @class Roo.bootstrap.TableRow
8873 * @extends Roo.bootstrap.Component
8874 * Bootstrap TableRow class
8875 * @cfg {String} cls row class
8876 * @cfg {String} align Aligns the content in a table row
8877 * @cfg {String} bgcolor Specifies a background color for a table row
8878 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8879 * @cfg {String} valign Vertical aligns the content in a table row
8882 * Create a new TableRow
8883 * @param {Object} config The config object
8886 Roo.bootstrap.TableRow = function(config){
8887 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8890 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
8898 getAutoCreate : function(){
8899 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8909 cfg.align = this.align;
8912 cfg.bgcolor = this.bgcolor;
8915 cfg.charoff = this.charoff;
8918 cfg.valign = this.valign;
8936 * @class Roo.bootstrap.TableBody
8937 * @extends Roo.bootstrap.Component
8938 * Bootstrap TableBody class
8939 * @cfg {String} cls element class
8940 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8941 * @cfg {String} align Aligns the content inside the element
8942 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8943 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8946 * Create a new TableBody
8947 * @param {Object} config The config object
8950 Roo.bootstrap.TableBody = function(config){
8951 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8954 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
8962 getAutoCreate : function(){
8963 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8977 cfg.align = this.align;
8980 cfg.charoff = this.charoff;
8983 cfg.valign = this.valign;
8990 // initEvents : function()
8997 // this.store = Roo.factory(this.store, Roo.data);
8998 // this.store.on('load', this.onLoad, this);
9000 // this.store.load();
9004 // onLoad: function ()
9006 // this.fireEvent('load', this);
9016 * Ext JS Library 1.1.1
9017 * Copyright(c) 2006-2007, Ext JS, LLC.
9019 * Originally Released Under LGPL - original licence link has changed is not relivant.
9022 * <script type="text/javascript">
9025 // as we use this in bootstrap.
9026 Roo.namespace('Roo.form');
9028 * @class Roo.form.Action
9029 * Internal Class used to handle form actions
9031 * @param {Roo.form.BasicForm} el The form element or its id
9032 * @param {Object} config Configuration options
9037 // define the action interface
9038 Roo.form.Action = function(form, options){
9040 this.options = options || {};
9043 * Client Validation Failed
9046 Roo.form.Action.CLIENT_INVALID = 'client';
9048 * Server Validation Failed
9051 Roo.form.Action.SERVER_INVALID = 'server';
9053 * Connect to Server Failed
9056 Roo.form.Action.CONNECT_FAILURE = 'connect';
9058 * Reading Data from Server Failed
9061 Roo.form.Action.LOAD_FAILURE = 'load';
9063 Roo.form.Action.prototype = {
9065 failureType : undefined,
9066 response : undefined,
9070 run : function(options){
9075 success : function(response){
9080 handleResponse : function(response){
9084 // default connection failure
9085 failure : function(response){
9087 this.response = response;
9088 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9089 this.form.afterAction(this, false);
9092 processResponse : function(response){
9093 this.response = response;
9094 if(!response.responseText){
9097 this.result = this.handleResponse(response);
9101 // utility functions used internally
9102 getUrl : function(appendParams){
9103 var url = this.options.url || this.form.url || this.form.el.dom.action;
9105 var p = this.getParams();
9107 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9113 getMethod : function(){
9114 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9117 getParams : function(){
9118 var bp = this.form.baseParams;
9119 var p = this.options.params;
9121 if(typeof p == "object"){
9122 p = Roo.urlEncode(Roo.applyIf(p, bp));
9123 }else if(typeof p == 'string' && bp){
9124 p += '&' + Roo.urlEncode(bp);
9127 p = Roo.urlEncode(bp);
9132 createCallback : function(){
9134 success: this.success,
9135 failure: this.failure,
9137 timeout: (this.form.timeout*1000),
9138 upload: this.form.fileUpload ? this.success : undefined
9143 Roo.form.Action.Submit = function(form, options){
9144 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9147 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9150 haveProgress : false,
9151 uploadComplete : false,
9153 // uploadProgress indicator.
9154 uploadProgress : function()
9156 if (!this.form.progressUrl) {
9160 if (!this.haveProgress) {
9161 Roo.MessageBox.progress("Uploading", "Uploading");
9163 if (this.uploadComplete) {
9164 Roo.MessageBox.hide();
9168 this.haveProgress = true;
9170 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9172 var c = new Roo.data.Connection();
9174 url : this.form.progressUrl,
9179 success : function(req){
9180 //console.log(data);
9184 rdata = Roo.decode(req.responseText)
9186 Roo.log("Invalid data from server..");
9190 if (!rdata || !rdata.success) {
9192 Roo.MessageBox.alert(Roo.encode(rdata));
9195 var data = rdata.data;
9197 if (this.uploadComplete) {
9198 Roo.MessageBox.hide();
9203 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9204 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9207 this.uploadProgress.defer(2000,this);
9210 failure: function(data) {
9211 Roo.log('progress url failed ');
9222 // run get Values on the form, so it syncs any secondary forms.
9223 this.form.getValues();
9225 var o = this.options;
9226 var method = this.getMethod();
9227 var isPost = method == 'POST';
9228 if(o.clientValidation === false || this.form.isValid()){
9230 if (this.form.progressUrl) {
9231 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9232 (new Date() * 1) + '' + Math.random());
9237 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9238 form:this.form.el.dom,
9239 url:this.getUrl(!isPost),
9241 params:isPost ? this.getParams() : null,
9242 isUpload: this.form.fileUpload,
9243 formData : this.form.formData
9246 this.uploadProgress();
9248 }else if (o.clientValidation !== false){ // client validation failed
9249 this.failureType = Roo.form.Action.CLIENT_INVALID;
9250 this.form.afterAction(this, false);
9254 success : function(response)
9256 this.uploadComplete= true;
9257 if (this.haveProgress) {
9258 Roo.MessageBox.hide();
9262 var result = this.processResponse(response);
9263 if(result === true || result.success){
9264 this.form.afterAction(this, true);
9268 this.form.markInvalid(result.errors);
9269 this.failureType = Roo.form.Action.SERVER_INVALID;
9271 this.form.afterAction(this, false);
9273 failure : function(response)
9275 this.uploadComplete= true;
9276 if (this.haveProgress) {
9277 Roo.MessageBox.hide();
9280 this.response = response;
9281 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9282 this.form.afterAction(this, false);
9285 handleResponse : function(response){
9286 if(this.form.errorReader){
9287 var rs = this.form.errorReader.read(response);
9290 for(var i = 0, len = rs.records.length; i < len; i++) {
9291 var r = rs.records[i];
9295 if(errors.length < 1){
9299 success : rs.success,
9305 ret = Roo.decode(response.responseText);
9309 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9319 Roo.form.Action.Load = function(form, options){
9320 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9321 this.reader = this.form.reader;
9324 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9329 Roo.Ajax.request(Roo.apply(
9330 this.createCallback(), {
9331 method:this.getMethod(),
9332 url:this.getUrl(false),
9333 params:this.getParams()
9337 success : function(response){
9339 var result = this.processResponse(response);
9340 if(result === true || !result.success || !result.data){
9341 this.failureType = Roo.form.Action.LOAD_FAILURE;
9342 this.form.afterAction(this, false);
9345 this.form.clearInvalid();
9346 this.form.setValues(result.data);
9347 this.form.afterAction(this, true);
9350 handleResponse : function(response){
9351 if(this.form.reader){
9352 var rs = this.form.reader.read(response);
9353 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9355 success : rs.success,
9359 return Roo.decode(response.responseText);
9363 Roo.form.Action.ACTION_TYPES = {
9364 'load' : Roo.form.Action.Load,
9365 'submit' : Roo.form.Action.Submit
9374 * @class Roo.bootstrap.Form
9375 * @extends Roo.bootstrap.Component
9376 * Bootstrap Form class
9377 * @cfg {String} method GET | POST (default POST)
9378 * @cfg {String} labelAlign top | left (default top)
9379 * @cfg {String} align left | right - for navbars
9380 * @cfg {Boolean} loadMask load mask when submit (default true)
9385 * @param {Object} config The config object
9389 Roo.bootstrap.Form = function(config){
9391 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9393 Roo.bootstrap.Form.popover.apply();
9397 * @event clientvalidation
9398 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9399 * @param {Form} this
9400 * @param {Boolean} valid true if the form has passed client-side validation
9402 clientvalidation: true,
9404 * @event beforeaction
9405 * Fires before any action is performed. Return false to cancel the action.
9406 * @param {Form} this
9407 * @param {Action} action The action to be performed
9411 * @event actionfailed
9412 * Fires when an action fails.
9413 * @param {Form} this
9414 * @param {Action} action The action that failed
9416 actionfailed : true,
9418 * @event actioncomplete
9419 * Fires when an action is completed.
9420 * @param {Form} this
9421 * @param {Action} action The action that completed
9423 actioncomplete : true
9427 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9430 * @cfg {String} method
9431 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9436 * The URL to use for form actions if one isn't supplied in the action options.
9439 * @cfg {Boolean} fileUpload
9440 * Set to true if this form is a file upload.
9444 * @cfg {Object} baseParams
9445 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9449 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9453 * @cfg {Sting} align (left|right) for navbar forms
9458 activeAction : null,
9461 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9462 * element by passing it or its id or mask the form itself by passing in true.
9465 waitMsgTarget : false,
9470 * @cfg {Boolean} errorMask (true|false) default false
9475 * @cfg {Number} maskOffset Default 100
9480 * @cfg {Boolean} maskBody
9484 getAutoCreate : function(){
9488 method : this.method || 'POST',
9489 id : this.id || Roo.id(),
9492 if (this.parent().xtype.match(/^Nav/)) {
9493 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9497 if (this.labelAlign == 'left' ) {
9498 cfg.cls += ' form-horizontal';
9504 initEvents : function()
9506 this.el.on('submit', this.onSubmit, this);
9507 // this was added as random key presses on the form where triggering form submit.
9508 this.el.on('keypress', function(e) {
9509 if (e.getCharCode() != 13) {
9512 // we might need to allow it for textareas.. and some other items.
9513 // check e.getTarget().
9515 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9519 Roo.log("keypress blocked");
9527 onSubmit : function(e){
9532 * Returns true if client-side validation on the form is successful.
9535 isValid : function(){
9536 var items = this.getItems();
9540 items.each(function(f){
9546 Roo.log('invalid field: ' + f.name);
9550 if(!target && f.el.isVisible(true)){
9556 if(this.errorMask && !valid){
9557 Roo.bootstrap.Form.popover.mask(this, target);
9564 * Returns true if any fields in this form have changed since their original load.
9567 isDirty : function(){
9569 var items = this.getItems();
9570 items.each(function(f){
9580 * Performs a predefined action (submit or load) or custom actions you define on this form.
9581 * @param {String} actionName The name of the action type
9582 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9583 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9584 * accept other config options):
9586 Property Type Description
9587 ---------------- --------------- ----------------------------------------------------------------------------------
9588 url String The url for the action (defaults to the form's url)
9589 method String The form method to use (defaults to the form's method, or POST if not defined)
9590 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9591 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9592 validate the form on the client (defaults to false)
9594 * @return {BasicForm} this
9596 doAction : function(action, options){
9597 if(typeof action == 'string'){
9598 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9600 if(this.fireEvent('beforeaction', this, action) !== false){
9601 this.beforeAction(action);
9602 action.run.defer(100, action);
9608 beforeAction : function(action){
9609 var o = action.options;
9614 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9616 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9619 // not really supported yet.. ??
9621 //if(this.waitMsgTarget === true){
9622 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9623 //}else if(this.waitMsgTarget){
9624 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9625 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9627 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9633 afterAction : function(action, success){
9634 this.activeAction = null;
9635 var o = action.options;
9640 Roo.get(document.body).unmask();
9646 //if(this.waitMsgTarget === true){
9647 // this.el.unmask();
9648 //}else if(this.waitMsgTarget){
9649 // this.waitMsgTarget.unmask();
9651 // Roo.MessageBox.updateProgress(1);
9652 // Roo.MessageBox.hide();
9659 Roo.callback(o.success, o.scope, [this, action]);
9660 this.fireEvent('actioncomplete', this, action);
9664 // failure condition..
9665 // we have a scenario where updates need confirming.
9666 // eg. if a locking scenario exists..
9667 // we look for { errors : { needs_confirm : true }} in the response.
9669 (typeof(action.result) != 'undefined') &&
9670 (typeof(action.result.errors) != 'undefined') &&
9671 (typeof(action.result.errors.needs_confirm) != 'undefined')
9674 Roo.log("not supported yet");
9677 Roo.MessageBox.confirm(
9678 "Change requires confirmation",
9679 action.result.errorMsg,
9684 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9694 Roo.callback(o.failure, o.scope, [this, action]);
9695 // show an error message if no failed handler is set..
9696 if (!this.hasListener('actionfailed')) {
9697 Roo.log("need to add dialog support");
9699 Roo.MessageBox.alert("Error",
9700 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9701 action.result.errorMsg :
9702 "Saving Failed, please check your entries or try again"
9707 this.fireEvent('actionfailed', this, action);
9712 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9713 * @param {String} id The value to search for
9716 findField : function(id){
9717 var items = this.getItems();
9718 var field = items.get(id);
9720 items.each(function(f){
9721 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9728 return field || null;
9731 * Mark fields in this form invalid in bulk.
9732 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9733 * @return {BasicForm} this
9735 markInvalid : function(errors){
9736 if(errors instanceof Array){
9737 for(var i = 0, len = errors.length; i < len; i++){
9738 var fieldError = errors[i];
9739 var f = this.findField(fieldError.id);
9741 f.markInvalid(fieldError.msg);
9747 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9748 field.markInvalid(errors[id]);
9752 //Roo.each(this.childForms || [], function (f) {
9753 // f.markInvalid(errors);
9760 * Set values for fields in this form in bulk.
9761 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9762 * @return {BasicForm} this
9764 setValues : function(values){
9765 if(values instanceof Array){ // array of objects
9766 for(var i = 0, len = values.length; i < len; i++){
9768 var f = this.findField(v.id);
9770 f.setValue(v.value);
9771 if(this.trackResetOnLoad){
9772 f.originalValue = f.getValue();
9776 }else{ // object hash
9779 if(typeof values[id] != 'function' && (field = this.findField(id))){
9781 if (field.setFromData &&
9783 field.displayField &&
9784 // combos' with local stores can
9785 // be queried via setValue()
9786 // to set their value..
9787 (field.store && !field.store.isLocal)
9791 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9792 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9793 field.setFromData(sd);
9795 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9797 field.setFromData(values);
9800 field.setValue(values[id]);
9804 if(this.trackResetOnLoad){
9805 field.originalValue = field.getValue();
9811 //Roo.each(this.childForms || [], function (f) {
9812 // f.setValues(values);
9819 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9820 * they are returned as an array.
9821 * @param {Boolean} asString
9824 getValues : function(asString){
9825 //if (this.childForms) {
9826 // copy values from the child forms
9827 // Roo.each(this.childForms, function (f) {
9828 // this.setValues(f.getValues());
9834 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9835 if(asString === true){
9838 return Roo.urlDecode(fs);
9842 * Returns the fields in this form as an object with key/value pairs.
9843 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9846 getFieldValues : function(with_hidden)
9848 var items = this.getItems();
9850 items.each(function(f){
9856 var v = f.getValue();
9858 if (f.inputType =='radio') {
9859 if (typeof(ret[f.getName()]) == 'undefined') {
9860 ret[f.getName()] = ''; // empty..
9863 if (!f.el.dom.checked) {
9871 if(f.xtype == 'MoneyField'){
9872 ret[f.currencyName] = f.getCurrency();
9875 // not sure if this supported any more..
9876 if ((typeof(v) == 'object') && f.getRawValue) {
9877 v = f.getRawValue() ; // dates..
9879 // combo boxes where name != hiddenName...
9880 if (f.name !== false && f.name != '' && f.name != f.getName()) {
9881 ret[f.name] = f.getRawValue();
9883 ret[f.getName()] = v;
9890 * Clears all invalid messages in this form.
9891 * @return {BasicForm} this
9893 clearInvalid : function(){
9894 var items = this.getItems();
9896 items.each(function(f){
9905 * @return {BasicForm} this
9908 var items = this.getItems();
9909 items.each(function(f){
9913 Roo.each(this.childForms || [], function (f) {
9921 getItems : function()
9923 var r=new Roo.util.MixedCollection(false, function(o){
9924 return o.id || (o.id = Roo.id());
9926 var iter = function(el) {
9933 Roo.each(el.items,function(e) {
9942 hideFields : function(items)
9944 Roo.each(items, function(i){
9946 var f = this.findField(i);
9957 showFields : function(items)
9959 Roo.each(items, function(i){
9961 var f = this.findField(i);
9974 Roo.apply(Roo.bootstrap.Form, {
10001 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10002 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10003 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10004 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10007 this.maskEl.top.enableDisplayMode("block");
10008 this.maskEl.left.enableDisplayMode("block");
10009 this.maskEl.bottom.enableDisplayMode("block");
10010 this.maskEl.right.enableDisplayMode("block");
10012 this.toolTip = new Roo.bootstrap.Tooltip({
10013 cls : 'roo-form-error-popover',
10015 'left' : ['r-l', [-2,0], 'right'],
10016 'right' : ['l-r', [2,0], 'left'],
10017 'bottom' : ['tl-bl', [0,2], 'top'],
10018 'top' : [ 'bl-tl', [0,-2], 'bottom']
10022 this.toolTip.render(Roo.get(document.body));
10024 this.toolTip.el.enableDisplayMode("block");
10026 Roo.get(document.body).on('click', function(){
10030 Roo.get(document.body).on('touchstart', function(){
10034 this.isApplied = true
10037 mask : function(form, target)
10041 this.target = target;
10043 if(!this.form.errorMask || !target.el){
10047 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10049 Roo.log(scrollable);
10051 var ot = this.target.el.calcOffsetsTo(scrollable);
10053 var scrollTo = ot[1] - this.form.maskOffset;
10055 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10057 scrollable.scrollTo('top', scrollTo);
10059 var box = this.target.el.getBox();
10061 var zIndex = Roo.bootstrap.Modal.zIndex++;
10064 this.maskEl.top.setStyle('position', 'absolute');
10065 this.maskEl.top.setStyle('z-index', zIndex);
10066 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10067 this.maskEl.top.setLeft(0);
10068 this.maskEl.top.setTop(0);
10069 this.maskEl.top.show();
10071 this.maskEl.left.setStyle('position', 'absolute');
10072 this.maskEl.left.setStyle('z-index', zIndex);
10073 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10074 this.maskEl.left.setLeft(0);
10075 this.maskEl.left.setTop(box.y - this.padding);
10076 this.maskEl.left.show();
10078 this.maskEl.bottom.setStyle('position', 'absolute');
10079 this.maskEl.bottom.setStyle('z-index', zIndex);
10080 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10081 this.maskEl.bottom.setLeft(0);
10082 this.maskEl.bottom.setTop(box.bottom + this.padding);
10083 this.maskEl.bottom.show();
10085 this.maskEl.right.setStyle('position', 'absolute');
10086 this.maskEl.right.setStyle('z-index', zIndex);
10087 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10088 this.maskEl.right.setLeft(box.right + this.padding);
10089 this.maskEl.right.setTop(box.y - this.padding);
10090 this.maskEl.right.show();
10092 this.toolTip.bindEl = this.target.el;
10094 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10096 var tip = this.target.blankText;
10098 if(this.target.getValue() !== '' ) {
10100 if (this.target.invalidText.length) {
10101 tip = this.target.invalidText;
10102 } else if (this.target.regexText.length){
10103 tip = this.target.regexText;
10107 this.toolTip.show(tip);
10109 this.intervalID = window.setInterval(function() {
10110 Roo.bootstrap.Form.popover.unmask();
10113 window.onwheel = function(){ return false;};
10115 (function(){ this.isMasked = true; }).defer(500, this);
10119 unmask : function()
10121 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10125 this.maskEl.top.setStyle('position', 'absolute');
10126 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10127 this.maskEl.top.hide();
10129 this.maskEl.left.setStyle('position', 'absolute');
10130 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10131 this.maskEl.left.hide();
10133 this.maskEl.bottom.setStyle('position', 'absolute');
10134 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10135 this.maskEl.bottom.hide();
10137 this.maskEl.right.setStyle('position', 'absolute');
10138 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10139 this.maskEl.right.hide();
10141 this.toolTip.hide();
10143 this.toolTip.el.hide();
10145 window.onwheel = function(){ return true;};
10147 if(this.intervalID){
10148 window.clearInterval(this.intervalID);
10149 this.intervalID = false;
10152 this.isMasked = false;
10162 * Ext JS Library 1.1.1
10163 * Copyright(c) 2006-2007, Ext JS, LLC.
10165 * Originally Released Under LGPL - original licence link has changed is not relivant.
10168 * <script type="text/javascript">
10171 * @class Roo.form.VTypes
10172 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10175 Roo.form.VTypes = function(){
10176 // closure these in so they are only created once.
10177 var alpha = /^[a-zA-Z_]+$/;
10178 var alphanum = /^[a-zA-Z0-9_]+$/;
10179 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10180 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10182 // All these messages and functions are configurable
10185 * The function used to validate email addresses
10186 * @param {String} value The email address
10188 'email' : function(v){
10189 return email.test(v);
10192 * The error text to display when the email validation function returns false
10195 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10197 * The keystroke filter mask to be applied on email input
10200 'emailMask' : /[a-z0-9_\.\-@]/i,
10203 * The function used to validate URLs
10204 * @param {String} value The URL
10206 'url' : function(v){
10207 return url.test(v);
10210 * The error text to display when the url validation function returns false
10213 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10216 * The function used to validate alpha values
10217 * @param {String} value The value
10219 'alpha' : function(v){
10220 return alpha.test(v);
10223 * The error text to display when the alpha validation function returns false
10226 'alphaText' : 'This field should only contain letters and _',
10228 * The keystroke filter mask to be applied on alpha input
10231 'alphaMask' : /[a-z_]/i,
10234 * The function used to validate alphanumeric values
10235 * @param {String} value The value
10237 'alphanum' : function(v){
10238 return alphanum.test(v);
10241 * The error text to display when the alphanumeric validation function returns false
10244 'alphanumText' : 'This field should only contain letters, numbers and _',
10246 * The keystroke filter mask to be applied on alphanumeric input
10249 'alphanumMask' : /[a-z0-9_]/i
10259 * @class Roo.bootstrap.Input
10260 * @extends Roo.bootstrap.Component
10261 * Bootstrap Input class
10262 * @cfg {Boolean} disabled is it disabled
10263 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10264 * @cfg {String} name name of the input
10265 * @cfg {string} fieldLabel - the label associated
10266 * @cfg {string} placeholder - placeholder to put in text.
10267 * @cfg {string} before - input group add on before
10268 * @cfg {string} after - input group add on after
10269 * @cfg {string} size - (lg|sm) or leave empty..
10270 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10271 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10272 * @cfg {Number} md colspan out of 12 for computer-sized screens
10273 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10274 * @cfg {string} value default value of the input
10275 * @cfg {Number} labelWidth set the width of label
10276 * @cfg {Number} labellg set the width of label (1-12)
10277 * @cfg {Number} labelmd set the width of label (1-12)
10278 * @cfg {Number} labelsm set the width of label (1-12)
10279 * @cfg {Number} labelxs set the width of label (1-12)
10280 * @cfg {String} labelAlign (top|left)
10281 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10282 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10283 * @cfg {String} indicatorpos (left|right) default left
10284 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10285 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10286 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10288 * @cfg {String} align (left|center|right) Default left
10289 * @cfg {Boolean} forceFeedback (true|false) Default false
10292 * Create a new Input
10293 * @param {Object} config The config object
10296 Roo.bootstrap.Input = function(config){
10298 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10303 * Fires when this field receives input focus.
10304 * @param {Roo.form.Field} this
10309 * Fires when this field loses input focus.
10310 * @param {Roo.form.Field} this
10314 * @event specialkey
10315 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10316 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10317 * @param {Roo.form.Field} this
10318 * @param {Roo.EventObject} e The event object
10323 * Fires just before the field blurs if the field value has changed.
10324 * @param {Roo.form.Field} this
10325 * @param {Mixed} newValue The new value
10326 * @param {Mixed} oldValue The original value
10331 * Fires after the field has been marked as invalid.
10332 * @param {Roo.form.Field} this
10333 * @param {String} msg The validation message
10338 * Fires after the field has been validated with no errors.
10339 * @param {Roo.form.Field} this
10344 * Fires after the key up
10345 * @param {Roo.form.Field} this
10346 * @param {Roo.EventObject} e The event Object
10352 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10354 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10355 automatic validation (defaults to "keyup").
10357 validationEvent : "keyup",
10359 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10361 validateOnBlur : true,
10363 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10365 validationDelay : 250,
10367 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10369 focusClass : "x-form-focus", // not needed???
10373 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10375 invalidClass : "has-warning",
10378 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10380 validClass : "has-success",
10383 * @cfg {Boolean} hasFeedback (true|false) default true
10385 hasFeedback : true,
10388 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10390 invalidFeedbackClass : "glyphicon-warning-sign",
10393 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10395 validFeedbackClass : "glyphicon-ok",
10398 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10400 selectOnFocus : false,
10403 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10407 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10412 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10414 disableKeyFilter : false,
10417 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10421 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10425 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10427 blankText : "Please complete this mandatory field",
10430 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10434 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10436 maxLength : Number.MAX_VALUE,
10438 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10440 minLengthText : "The minimum length for this field is {0}",
10442 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10444 maxLengthText : "The maximum length for this field is {0}",
10448 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10449 * If available, this function will be called only after the basic validators all return true, and will be passed the
10450 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10454 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10455 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10456 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10460 * @cfg {String} regexText -- Depricated - use Invalid Text
10465 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10471 autocomplete: false,
10475 inputType : 'text',
10478 placeholder: false,
10483 preventMark: false,
10484 isFormField : true,
10487 labelAlign : false,
10490 formatedValue : false,
10491 forceFeedback : false,
10493 indicatorpos : 'left',
10503 parentLabelAlign : function()
10506 while (parent.parent()) {
10507 parent = parent.parent();
10508 if (typeof(parent.labelAlign) !='undefined') {
10509 return parent.labelAlign;
10516 getAutoCreate : function()
10518 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10524 if(this.inputType != 'hidden'){
10525 cfg.cls = 'form-group' //input-group
10531 type : this.inputType,
10532 value : this.value,
10533 cls : 'form-control',
10534 placeholder : this.placeholder || '',
10535 autocomplete : this.autocomplete || 'new-password'
10537 if (this.inputType == 'file') {
10538 input.style = 'overflow:hidden'; // why not in CSS?
10541 if(this.capture.length){
10542 input.capture = this.capture;
10545 if(this.accept.length){
10546 input.accept = this.accept + "/*";
10550 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10553 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10554 input.maxLength = this.maxLength;
10557 if (this.disabled) {
10558 input.disabled=true;
10561 if (this.readOnly) {
10562 input.readonly=true;
10566 input.name = this.name;
10570 input.cls += ' input-' + this.size;
10574 ['xs','sm','md','lg'].map(function(size){
10575 if (settings[size]) {
10576 cfg.cls += ' col-' + size + '-' + settings[size];
10580 var inputblock = input;
10584 cls: 'glyphicon form-control-feedback'
10587 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10590 cls : 'has-feedback',
10598 if (this.before || this.after) {
10601 cls : 'input-group',
10605 if (this.before && typeof(this.before) == 'string') {
10607 inputblock.cn.push({
10609 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10613 if (this.before && typeof(this.before) == 'object') {
10614 this.before = Roo.factory(this.before);
10616 inputblock.cn.push({
10618 cls : 'roo-input-before input-group-prepend input-group-' +
10619 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10623 inputblock.cn.push(input);
10625 if (this.after && typeof(this.after) == 'string') {
10626 inputblock.cn.push({
10628 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10632 if (this.after && typeof(this.after) == 'object') {
10633 this.after = Roo.factory(this.after);
10635 inputblock.cn.push({
10637 cls : 'roo-input-after input-group-append input-group-' +
10638 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10642 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10643 inputblock.cls += ' has-feedback';
10644 inputblock.cn.push(feedback);
10649 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10650 tooltip : 'This field is required'
10652 if (this.allowBlank ) {
10653 indicator.style = this.allowBlank ? ' display:none' : '';
10655 if (align ==='left' && this.fieldLabel.length) {
10657 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10664 cls : 'control-label col-form-label',
10665 html : this.fieldLabel
10676 var labelCfg = cfg.cn[1];
10677 var contentCfg = cfg.cn[2];
10679 if(this.indicatorpos == 'right'){
10684 cls : 'control-label col-form-label',
10688 html : this.fieldLabel
10702 labelCfg = cfg.cn[0];
10703 contentCfg = cfg.cn[1];
10707 if(this.labelWidth > 12){
10708 labelCfg.style = "width: " + this.labelWidth + 'px';
10711 if(this.labelWidth < 13 && this.labelmd == 0){
10712 this.labelmd = this.labelWidth;
10715 if(this.labellg > 0){
10716 labelCfg.cls += ' col-lg-' + this.labellg;
10717 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10720 if(this.labelmd > 0){
10721 labelCfg.cls += ' col-md-' + this.labelmd;
10722 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10725 if(this.labelsm > 0){
10726 labelCfg.cls += ' col-sm-' + this.labelsm;
10727 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10730 if(this.labelxs > 0){
10731 labelCfg.cls += ' col-xs-' + this.labelxs;
10732 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10736 } else if ( this.fieldLabel.length) {
10743 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10744 tooltip : 'This field is required',
10745 style : this.allowBlank ? ' display:none' : ''
10749 //cls : 'input-group-addon',
10750 html : this.fieldLabel
10758 if(this.indicatorpos == 'right'){
10763 //cls : 'input-group-addon',
10764 html : this.fieldLabel
10769 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10770 tooltip : 'This field is required',
10771 style : this.allowBlank ? ' display:none' : ''
10791 if (this.parentType === 'Navbar' && this.parent().bar) {
10792 cfg.cls += ' navbar-form';
10795 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10796 // on BS4 we do this only if not form
10797 cfg.cls += ' navbar-form';
10805 * return the real input element.
10807 inputEl: function ()
10809 return this.el.select('input.form-control',true).first();
10812 tooltipEl : function()
10814 return this.inputEl();
10817 indicatorEl : function()
10819 if (Roo.bootstrap.version == 4) {
10820 return false; // not enabled in v4 yet.
10823 var indicator = this.el.select('i.roo-required-indicator',true).first();
10833 setDisabled : function(v)
10835 var i = this.inputEl().dom;
10837 i.removeAttribute('disabled');
10841 i.setAttribute('disabled','true');
10843 initEvents : function()
10846 this.inputEl().on("keydown" , this.fireKey, this);
10847 this.inputEl().on("focus", this.onFocus, this);
10848 this.inputEl().on("blur", this.onBlur, this);
10850 this.inputEl().relayEvent('keyup', this);
10852 this.indicator = this.indicatorEl();
10854 if(this.indicator){
10855 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
10858 // reference to original value for reset
10859 this.originalValue = this.getValue();
10860 //Roo.form.TextField.superclass.initEvents.call(this);
10861 if(this.validationEvent == 'keyup'){
10862 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10863 this.inputEl().on('keyup', this.filterValidation, this);
10865 else if(this.validationEvent !== false){
10866 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10869 if(this.selectOnFocus){
10870 this.on("focus", this.preFocus, this);
10873 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10874 this.inputEl().on("keypress", this.filterKeys, this);
10876 this.inputEl().relayEvent('keypress', this);
10879 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
10880 this.el.on("click", this.autoSize, this);
10883 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10884 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10887 if (typeof(this.before) == 'object') {
10888 this.before.render(this.el.select('.roo-input-before',true).first());
10890 if (typeof(this.after) == 'object') {
10891 this.after.render(this.el.select('.roo-input-after',true).first());
10894 this.inputEl().on('change', this.onChange, this);
10897 filterValidation : function(e){
10898 if(!e.isNavKeyPress()){
10899 this.validationTask.delay(this.validationDelay);
10903 * Validates the field value
10904 * @return {Boolean} True if the value is valid, else false
10906 validate : function(){
10907 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10908 if(this.disabled || this.validateValue(this.getRawValue())){
10913 this.markInvalid();
10919 * Validates a value according to the field's validation rules and marks the field as invalid
10920 * if the validation fails
10921 * @param {Mixed} value The value to validate
10922 * @return {Boolean} True if the value is valid, else false
10924 validateValue : function(value)
10926 if(this.getVisibilityEl().hasClass('hidden')){
10930 if(value.length < 1) { // if it's blank
10931 if(this.allowBlank){
10937 if(value.length < this.minLength){
10940 if(value.length > this.maxLength){
10944 var vt = Roo.form.VTypes;
10945 if(!vt[this.vtype](value, this)){
10949 if(typeof this.validator == "function"){
10950 var msg = this.validator(value);
10954 if (typeof(msg) == 'string') {
10955 this.invalidText = msg;
10959 if(this.regex && !this.regex.test(value)){
10967 fireKey : function(e){
10968 //Roo.log('field ' + e.getKey());
10969 if(e.isNavKeyPress()){
10970 this.fireEvent("specialkey", this, e);
10973 focus : function (selectText){
10975 this.inputEl().focus();
10976 if(selectText === true){
10977 this.inputEl().dom.select();
10983 onFocus : function(){
10984 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10985 // this.el.addClass(this.focusClass);
10987 if(!this.hasFocus){
10988 this.hasFocus = true;
10989 this.startValue = this.getValue();
10990 this.fireEvent("focus", this);
10994 beforeBlur : Roo.emptyFn,
10998 onBlur : function(){
11000 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11001 //this.el.removeClass(this.focusClass);
11003 this.hasFocus = false;
11004 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11007 var v = this.getValue();
11008 if(String(v) !== String(this.startValue)){
11009 this.fireEvent('change', this, v, this.startValue);
11011 this.fireEvent("blur", this);
11014 onChange : function(e)
11016 var v = this.getValue();
11017 if(String(v) !== String(this.startValue)){
11018 this.fireEvent('change', this, v, this.startValue);
11024 * Resets the current field value to the originally loaded value and clears any validation messages
11026 reset : function(){
11027 this.setValue(this.originalValue);
11031 * Returns the name of the field
11032 * @return {Mixed} name The name field
11034 getName: function(){
11038 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11039 * @return {Mixed} value The field value
11041 getValue : function(){
11043 var v = this.inputEl().getValue();
11048 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11049 * @return {Mixed} value The field value
11051 getRawValue : function(){
11052 var v = this.inputEl().getValue();
11058 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11059 * @param {Mixed} value The value to set
11061 setRawValue : function(v){
11062 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11065 selectText : function(start, end){
11066 var v = this.getRawValue();
11068 start = start === undefined ? 0 : start;
11069 end = end === undefined ? v.length : end;
11070 var d = this.inputEl().dom;
11071 if(d.setSelectionRange){
11072 d.setSelectionRange(start, end);
11073 }else if(d.createTextRange){
11074 var range = d.createTextRange();
11075 range.moveStart("character", start);
11076 range.moveEnd("character", v.length-end);
11083 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11084 * @param {Mixed} value The value to set
11086 setValue : function(v){
11089 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11095 processValue : function(value){
11096 if(this.stripCharsRe){
11097 var newValue = value.replace(this.stripCharsRe, '');
11098 if(newValue !== value){
11099 this.setRawValue(newValue);
11106 preFocus : function(){
11108 if(this.selectOnFocus){
11109 this.inputEl().dom.select();
11112 filterKeys : function(e){
11113 var k = e.getKey();
11114 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11117 var c = e.getCharCode(), cc = String.fromCharCode(c);
11118 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11121 if(!this.maskRe.test(cc)){
11126 * Clear any invalid styles/messages for this field
11128 clearInvalid : function(){
11130 if(!this.el || this.preventMark){ // not rendered
11135 this.el.removeClass([this.invalidClass, 'is-invalid']);
11137 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11139 var feedback = this.el.select('.form-control-feedback', true).first();
11142 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11147 if(this.indicator){
11148 this.indicator.removeClass('visible');
11149 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11152 this.fireEvent('valid', this);
11156 * Mark this field as valid
11158 markValid : function()
11160 if(!this.el || this.preventMark){ // not rendered...
11164 this.el.removeClass([this.invalidClass, this.validClass]);
11165 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11167 var feedback = this.el.select('.form-control-feedback', true).first();
11170 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11173 if(this.indicator){
11174 this.indicator.removeClass('visible');
11175 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11183 if(this.allowBlank && !this.getRawValue().length){
11186 if (Roo.bootstrap.version == 3) {
11187 this.el.addClass(this.validClass);
11189 this.inputEl().addClass('is-valid');
11192 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11194 var feedback = this.el.select('.form-control-feedback', true).first();
11197 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11198 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11203 this.fireEvent('valid', this);
11207 * Mark this field as invalid
11208 * @param {String} msg The validation message
11210 markInvalid : function(msg)
11212 if(!this.el || this.preventMark){ // not rendered
11216 this.el.removeClass([this.invalidClass, this.validClass]);
11217 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11219 var feedback = this.el.select('.form-control-feedback', true).first();
11222 this.el.select('.form-control-feedback', true).first().removeClass(
11223 [this.invalidFeedbackClass, this.validFeedbackClass]);
11230 if(this.allowBlank && !this.getRawValue().length){
11234 if(this.indicator){
11235 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11236 this.indicator.addClass('visible');
11238 if (Roo.bootstrap.version == 3) {
11239 this.el.addClass(this.invalidClass);
11241 this.inputEl().addClass('is-invalid');
11246 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11248 var feedback = this.el.select('.form-control-feedback', true).first();
11251 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11253 if(this.getValue().length || this.forceFeedback){
11254 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11261 this.fireEvent('invalid', this, msg);
11264 SafariOnKeyDown : function(event)
11266 // this is a workaround for a password hang bug on chrome/ webkit.
11267 if (this.inputEl().dom.type != 'password') {
11271 var isSelectAll = false;
11273 if(this.inputEl().dom.selectionEnd > 0){
11274 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11276 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11277 event.preventDefault();
11282 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11284 event.preventDefault();
11285 // this is very hacky as keydown always get's upper case.
11287 var cc = String.fromCharCode(event.getCharCode());
11288 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11292 adjustWidth : function(tag, w){
11293 tag = tag.toLowerCase();
11294 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11295 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11296 if(tag == 'input'){
11299 if(tag == 'textarea'){
11302 }else if(Roo.isOpera){
11303 if(tag == 'input'){
11306 if(tag == 'textarea'){
11314 setFieldLabel : function(v)
11316 if(!this.rendered){
11320 if(this.indicatorEl()){
11321 var ar = this.el.select('label > span',true);
11323 if (ar.elements.length) {
11324 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11325 this.fieldLabel = v;
11329 var br = this.el.select('label',true);
11331 if(br.elements.length) {
11332 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11333 this.fieldLabel = v;
11337 Roo.log('Cannot Found any of label > span || label in input');
11341 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11342 this.fieldLabel = v;
11357 * @class Roo.bootstrap.TextArea
11358 * @extends Roo.bootstrap.Input
11359 * Bootstrap TextArea class
11360 * @cfg {Number} cols Specifies the visible width of a text area
11361 * @cfg {Number} rows Specifies the visible number of lines in a text area
11362 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11363 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11364 * @cfg {string} html text
11367 * Create a new TextArea
11368 * @param {Object} config The config object
11371 Roo.bootstrap.TextArea = function(config){
11372 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11376 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11386 getAutoCreate : function(){
11388 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11394 if(this.inputType != 'hidden'){
11395 cfg.cls = 'form-group' //input-group
11403 value : this.value || '',
11404 html: this.html || '',
11405 cls : 'form-control',
11406 placeholder : this.placeholder || ''
11410 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11411 input.maxLength = this.maxLength;
11415 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11419 input.cols = this.cols;
11422 if (this.readOnly) {
11423 input.readonly = true;
11427 input.name = this.name;
11431 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11435 ['xs','sm','md','lg'].map(function(size){
11436 if (settings[size]) {
11437 cfg.cls += ' col-' + size + '-' + settings[size];
11441 var inputblock = input;
11443 if(this.hasFeedback && !this.allowBlank){
11447 cls: 'glyphicon form-control-feedback'
11451 cls : 'has-feedback',
11460 if (this.before || this.after) {
11463 cls : 'input-group',
11467 inputblock.cn.push({
11469 cls : 'input-group-addon',
11474 inputblock.cn.push(input);
11476 if(this.hasFeedback && !this.allowBlank){
11477 inputblock.cls += ' has-feedback';
11478 inputblock.cn.push(feedback);
11482 inputblock.cn.push({
11484 cls : 'input-group-addon',
11491 if (align ==='left' && this.fieldLabel.length) {
11496 cls : 'control-label',
11497 html : this.fieldLabel
11508 if(this.labelWidth > 12){
11509 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11512 if(this.labelWidth < 13 && this.labelmd == 0){
11513 this.labelmd = this.labelWidth;
11516 if(this.labellg > 0){
11517 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11518 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11521 if(this.labelmd > 0){
11522 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11523 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11526 if(this.labelsm > 0){
11527 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11528 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11531 if(this.labelxs > 0){
11532 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11533 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11536 } else if ( this.fieldLabel.length) {
11541 //cls : 'input-group-addon',
11542 html : this.fieldLabel
11560 if (this.disabled) {
11561 input.disabled=true;
11568 * return the real textarea element.
11570 inputEl: function ()
11572 return this.el.select('textarea.form-control',true).first();
11576 * Clear any invalid styles/messages for this field
11578 clearInvalid : function()
11581 if(!this.el || this.preventMark){ // not rendered
11585 var label = this.el.select('label', true).first();
11586 var icon = this.el.select('i.fa-star', true).first();
11591 this.el.removeClass( this.validClass);
11592 this.inputEl().removeClass('is-invalid');
11594 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11596 var feedback = this.el.select('.form-control-feedback', true).first();
11599 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11604 this.fireEvent('valid', this);
11608 * Mark this field as valid
11610 markValid : function()
11612 if(!this.el || this.preventMark){ // not rendered
11616 this.el.removeClass([this.invalidClass, this.validClass]);
11617 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11619 var feedback = this.el.select('.form-control-feedback', true).first();
11622 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11625 if(this.disabled || this.allowBlank){
11629 var label = this.el.select('label', true).first();
11630 var icon = this.el.select('i.fa-star', true).first();
11635 if (Roo.bootstrap.version == 3) {
11636 this.el.addClass(this.validClass);
11638 this.inputEl().addClass('is-valid');
11642 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11644 var feedback = this.el.select('.form-control-feedback', true).first();
11647 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11648 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11653 this.fireEvent('valid', this);
11657 * Mark this field as invalid
11658 * @param {String} msg The validation message
11660 markInvalid : function(msg)
11662 if(!this.el || this.preventMark){ // not rendered
11666 this.el.removeClass([this.invalidClass, this.validClass]);
11667 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11669 var feedback = this.el.select('.form-control-feedback', true).first();
11672 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11675 if(this.disabled || this.allowBlank){
11679 var label = this.el.select('label', true).first();
11680 var icon = this.el.select('i.fa-star', true).first();
11682 if(!this.getValue().length && label && !icon){
11683 this.el.createChild({
11685 cls : 'text-danger fa fa-lg fa-star',
11686 tooltip : 'This field is required',
11687 style : 'margin-right:5px;'
11691 if (Roo.bootstrap.version == 3) {
11692 this.el.addClass(this.invalidClass);
11694 this.inputEl().addClass('is-invalid');
11697 // fixme ... this may be depricated need to test..
11698 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11700 var feedback = this.el.select('.form-control-feedback', true).first();
11703 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11705 if(this.getValue().length || this.forceFeedback){
11706 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11713 this.fireEvent('invalid', this, msg);
11721 * trigger field - base class for combo..
11726 * @class Roo.bootstrap.TriggerField
11727 * @extends Roo.bootstrap.Input
11728 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11729 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11730 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11731 * for which you can provide a custom implementation. For example:
11733 var trigger = new Roo.bootstrap.TriggerField();
11734 trigger.onTriggerClick = myTriggerFn;
11735 trigger.applyTo('my-field');
11738 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11739 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11740 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11741 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11742 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11745 * Create a new TriggerField.
11746 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11747 * to the base TextField)
11749 Roo.bootstrap.TriggerField = function(config){
11750 this.mimicing = false;
11751 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11754 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11756 * @cfg {String} triggerClass A CSS class to apply to the trigger
11759 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11764 * @cfg {Boolean} removable (true|false) special filter default false
11768 /** @cfg {Boolean} grow @hide */
11769 /** @cfg {Number} growMin @hide */
11770 /** @cfg {Number} growMax @hide */
11776 autoSize: Roo.emptyFn,
11780 deferHeight : true,
11783 actionMode : 'wrap',
11788 getAutoCreate : function(){
11790 var align = this.labelAlign || this.parentLabelAlign();
11795 cls: 'form-group' //input-group
11802 type : this.inputType,
11803 cls : 'form-control',
11804 autocomplete: 'new-password',
11805 placeholder : this.placeholder || ''
11809 input.name = this.name;
11812 input.cls += ' input-' + this.size;
11815 if (this.disabled) {
11816 input.disabled=true;
11819 var inputblock = input;
11821 if(this.hasFeedback && !this.allowBlank){
11825 cls: 'glyphicon form-control-feedback'
11828 if(this.removable && !this.editable ){
11830 cls : 'has-feedback',
11836 cls : 'roo-combo-removable-btn close'
11843 cls : 'has-feedback',
11852 if(this.removable && !this.editable ){
11854 cls : 'roo-removable',
11860 cls : 'roo-combo-removable-btn close'
11867 if (this.before || this.after) {
11870 cls : 'input-group',
11874 inputblock.cn.push({
11876 cls : 'input-group-addon input-group-prepend input-group-text',
11881 inputblock.cn.push(input);
11883 if(this.hasFeedback && !this.allowBlank){
11884 inputblock.cls += ' has-feedback';
11885 inputblock.cn.push(feedback);
11889 inputblock.cn.push({
11891 cls : 'input-group-addon input-group-append input-group-text',
11900 var ibwrap = inputblock;
11905 cls: 'roo-select2-choices',
11909 cls: 'roo-select2-search-field',
11921 cls: 'roo-select2-container input-group',
11926 cls: 'form-hidden-field'
11932 if(!this.multiple && this.showToggleBtn){
11938 if (this.caret != false) {
11941 cls: 'fa fa-' + this.caret
11948 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11950 Roo.bootstrap.version == 3 ? caret : '',
11953 cls: 'combobox-clear',
11967 combobox.cls += ' roo-select2-container-multi';
11971 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11972 tooltip : 'This field is required'
11974 if (Roo.bootstrap.version == 4) {
11977 style : 'display:none'
11982 if (align ==='left' && this.fieldLabel.length) {
11984 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11991 cls : 'control-label',
11992 html : this.fieldLabel
12004 var labelCfg = cfg.cn[1];
12005 var contentCfg = cfg.cn[2];
12007 if(this.indicatorpos == 'right'){
12012 cls : 'control-label',
12016 html : this.fieldLabel
12030 labelCfg = cfg.cn[0];
12031 contentCfg = cfg.cn[1];
12034 if(this.labelWidth > 12){
12035 labelCfg.style = "width: " + this.labelWidth + 'px';
12038 if(this.labelWidth < 13 && this.labelmd == 0){
12039 this.labelmd = this.labelWidth;
12042 if(this.labellg > 0){
12043 labelCfg.cls += ' col-lg-' + this.labellg;
12044 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12047 if(this.labelmd > 0){
12048 labelCfg.cls += ' col-md-' + this.labelmd;
12049 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12052 if(this.labelsm > 0){
12053 labelCfg.cls += ' col-sm-' + this.labelsm;
12054 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12057 if(this.labelxs > 0){
12058 labelCfg.cls += ' col-xs-' + this.labelxs;
12059 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12062 } else if ( this.fieldLabel.length) {
12063 // Roo.log(" label");
12068 //cls : 'input-group-addon',
12069 html : this.fieldLabel
12077 if(this.indicatorpos == 'right'){
12085 html : this.fieldLabel
12099 // Roo.log(" no label && no align");
12106 ['xs','sm','md','lg'].map(function(size){
12107 if (settings[size]) {
12108 cfg.cls += ' col-' + size + '-' + settings[size];
12119 onResize : function(w, h){
12120 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12121 // if(typeof w == 'number'){
12122 // var x = w - this.trigger.getWidth();
12123 // this.inputEl().setWidth(this.adjustWidth('input', x));
12124 // this.trigger.setStyle('left', x+'px');
12129 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12132 getResizeEl : function(){
12133 return this.inputEl();
12137 getPositionEl : function(){
12138 return this.inputEl();
12142 alignErrorIcon : function(){
12143 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12147 initEvents : function(){
12151 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12152 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12153 if(!this.multiple && this.showToggleBtn){
12154 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12155 if(this.hideTrigger){
12156 this.trigger.setDisplayed(false);
12158 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12162 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12165 if(this.removable && !this.editable && !this.tickable){
12166 var close = this.closeTriggerEl();
12169 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12170 close.on('click', this.removeBtnClick, this, close);
12174 //this.trigger.addClassOnOver('x-form-trigger-over');
12175 //this.trigger.addClassOnClick('x-form-trigger-click');
12178 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12182 closeTriggerEl : function()
12184 var close = this.el.select('.roo-combo-removable-btn', true).first();
12185 return close ? close : false;
12188 removeBtnClick : function(e, h, el)
12190 e.preventDefault();
12192 if(this.fireEvent("remove", this) !== false){
12194 this.fireEvent("afterremove", this)
12198 createList : function()
12200 this.list = Roo.get(document.body).createChild({
12201 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12202 cls: 'typeahead typeahead-long dropdown-menu',
12203 style: 'display:none'
12206 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12211 initTrigger : function(){
12216 onDestroy : function(){
12218 this.trigger.removeAllListeners();
12219 // this.trigger.remove();
12222 // this.wrap.remove();
12224 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12228 onFocus : function(){
12229 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12231 if(!this.mimicing){
12232 this.wrap.addClass('x-trigger-wrap-focus');
12233 this.mimicing = true;
12234 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12235 if(this.monitorTab){
12236 this.el.on("keydown", this.checkTab, this);
12243 checkTab : function(e){
12244 if(e.getKey() == e.TAB){
12245 this.triggerBlur();
12250 onBlur : function(){
12255 mimicBlur : function(e, t){
12257 if(!this.wrap.contains(t) && this.validateBlur()){
12258 this.triggerBlur();
12264 triggerBlur : function(){
12265 this.mimicing = false;
12266 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12267 if(this.monitorTab){
12268 this.el.un("keydown", this.checkTab, this);
12270 //this.wrap.removeClass('x-trigger-wrap-focus');
12271 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12275 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12276 validateBlur : function(e, t){
12281 onDisable : function(){
12282 this.inputEl().dom.disabled = true;
12283 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12285 // this.wrap.addClass('x-item-disabled');
12290 onEnable : function(){
12291 this.inputEl().dom.disabled = false;
12292 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12294 // this.el.removeClass('x-item-disabled');
12299 onShow : function(){
12300 var ae = this.getActionEl();
12303 ae.dom.style.display = '';
12304 ae.dom.style.visibility = 'visible';
12310 onHide : function(){
12311 var ae = this.getActionEl();
12312 ae.dom.style.display = 'none';
12316 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12317 * by an implementing function.
12319 * @param {EventObject} e
12321 onTriggerClick : Roo.emptyFn
12329 * @class Roo.bootstrap.CardUploader
12330 * @extends Roo.bootstrap.Button
12331 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12332 * @cfg {Number} errorTimeout default 3000
12333 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12334 * @cfg {Array} html The button text.
12338 * Create a new CardUploader
12339 * @param {Object} config The config object
12342 Roo.bootstrap.CardUploader = function(config){
12346 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12349 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12356 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12359 errorTimeout : 3000,
12363 fileCollection : false,
12366 getAutoCreate : function()
12370 cls :'form-group' ,
12375 //cls : 'input-group-addon',
12376 html : this.fieldLabel
12383 value : this.value,
12384 cls : 'd-none form-control'
12389 multiple : 'multiple',
12391 cls : 'd-none roo-card-upload-selector'
12395 cls : 'roo-card-uploader-button-container w-100 mb-2'
12398 cls : 'card-columns roo-card-uploader-container'
12408 getChildContainer : function() /// what children are added to.
12410 return this.containerEl;
12413 getButtonContainer : function() /// what children are added to.
12415 return this.el.select(".roo-card-uploader-button-container").first();
12418 initEvents : function()
12421 Roo.bootstrap.Input.prototype.initEvents.call(this);
12425 xns: Roo.bootstrap,
12428 container_method : 'getButtonContainer' ,
12429 html : this.html, // fix changable?
12432 'click' : function(btn, e) {
12441 this.urlAPI = (window.createObjectURL && window) ||
12442 (window.URL && URL.revokeObjectURL && URL) ||
12443 (window.webkitURL && webkitURL);
12448 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12450 this.selectorEl.on('change', this.onFileSelected, this);
12453 this.images.forEach(function(img) {
12456 this.images = false;
12458 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12464 onClick : function(e)
12466 e.preventDefault();
12468 this.selectorEl.dom.click();
12472 onFileSelected : function(e)
12474 e.preventDefault();
12476 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12480 Roo.each(this.selectorEl.dom.files, function(file){
12481 this.addFile(file);
12490 addFile : function(file)
12493 if(typeof(file) === 'string'){
12494 throw "Add file by name?"; // should not happen
12498 if(!file || !this.urlAPI){
12508 var url = _this.urlAPI.createObjectURL( file);
12511 id : Roo.bootstrap.CardUploader.ID--,
12512 is_uploaded : false,
12515 mimetype : file.type,
12522 addCard : function (data)
12524 // hidden input element?
12525 // if the file is not an image...
12526 //then we need to use something other that and header_image
12531 xns : Roo.bootstrap,
12532 xtype : 'CardFooter',
12535 xns : Roo.bootstrap,
12541 xns : Roo.bootstrap,
12543 html : String.format("<small>{0}</small>", data.title),
12544 cls : 'col-11 text-left',
12549 click : function() {
12550 this.downloadCard(data.id)
12556 xns : Roo.bootstrap,
12564 click : function() {
12565 t.removeCard(data.id)
12577 var cn = this.addxtype(
12580 xns : Roo.bootstrap,
12583 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12584 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12585 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12590 initEvents : function() {
12591 Roo.bootstrap.Card.prototype.initEvents.call(this);
12592 this.imgEl = this.el.select('.card-img-top').first();
12594 this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12595 this.imgEl.set({ 'pointer' : 'cursor' });
12604 // dont' really need ot update items.
12605 // this.items.push(cn);
12606 this.fileCollection.add(cn);
12607 this.updateInput();
12610 removeCard : function(id)
12613 var card = this.fileCollection.get(id);
12614 card.data.is_deleted = 1;
12615 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12616 this.fileCollection.remove(card);
12617 //this.items = this.items.filter(function(e) { return e != card });
12618 // dont' really need ot update items.
12619 card.el.dom.parentNode.removeChild(card.el.dom);
12624 this.fileCollection.each(function(card) {
12625 card.el.dom.parentNode.removeChild(card.el.dom);
12627 this.fileCollection.clear();
12628 this.updateInput();
12631 updateInput : function()
12634 this.fileCollection.each(function(e) {
12638 this.inputEl().dom.value = JSON.stringify(data);
12645 Roo.bootstrap.CardUploader.ID = -1;/*
12647 * Ext JS Library 1.1.1
12648 * Copyright(c) 2006-2007, Ext JS, LLC.
12650 * Originally Released Under LGPL - original licence link has changed is not relivant.
12653 * <script type="text/javascript">
12658 * @class Roo.data.SortTypes
12660 * Defines the default sorting (casting?) comparison functions used when sorting data.
12662 Roo.data.SortTypes = {
12664 * Default sort that does nothing
12665 * @param {Mixed} s The value being converted
12666 * @return {Mixed} The comparison value
12668 none : function(s){
12673 * The regular expression used to strip tags
12677 stripTagsRE : /<\/?[^>]+>/gi,
12680 * Strips all HTML tags to sort on text only
12681 * @param {Mixed} s The value being converted
12682 * @return {String} The comparison value
12684 asText : function(s){
12685 return String(s).replace(this.stripTagsRE, "");
12689 * Strips all HTML tags to sort on text only - Case insensitive
12690 * @param {Mixed} s The value being converted
12691 * @return {String} The comparison value
12693 asUCText : function(s){
12694 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12698 * Case insensitive string
12699 * @param {Mixed} s The value being converted
12700 * @return {String} The comparison value
12702 asUCString : function(s) {
12703 return String(s).toUpperCase();
12708 * @param {Mixed} s The value being converted
12709 * @return {Number} The comparison value
12711 asDate : function(s) {
12715 if(s instanceof Date){
12716 return s.getTime();
12718 return Date.parse(String(s));
12723 * @param {Mixed} s The value being converted
12724 * @return {Float} The comparison value
12726 asFloat : function(s) {
12727 var val = parseFloat(String(s).replace(/,/g, ""));
12736 * @param {Mixed} s The value being converted
12737 * @return {Number} The comparison value
12739 asInt : function(s) {
12740 var val = parseInt(String(s).replace(/,/g, ""));
12748 * Ext JS Library 1.1.1
12749 * Copyright(c) 2006-2007, Ext JS, LLC.
12751 * Originally Released Under LGPL - original licence link has changed is not relivant.
12754 * <script type="text/javascript">
12758 * @class Roo.data.Record
12759 * Instances of this class encapsulate both record <em>definition</em> information, and record
12760 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12761 * to access Records cached in an {@link Roo.data.Store} object.<br>
12763 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12764 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12767 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12769 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12770 * {@link #create}. The parameters are the same.
12771 * @param {Array} data An associative Array of data values keyed by the field name.
12772 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12773 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12774 * not specified an integer id is generated.
12776 Roo.data.Record = function(data, id){
12777 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12782 * Generate a constructor for a specific record layout.
12783 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12784 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12785 * Each field definition object may contain the following properties: <ul>
12786 * <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,
12787 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12788 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12789 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12790 * is being used, then this is a string containing the javascript expression to reference the data relative to
12791 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12792 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12793 * this may be omitted.</p></li>
12794 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12795 * <ul><li>auto (Default, implies no conversion)</li>
12800 * <li>date</li></ul></p></li>
12801 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12802 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12803 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12804 * by the Reader into an object that will be stored in the Record. It is passed the
12805 * following parameters:<ul>
12806 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12808 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12810 * <br>usage:<br><pre><code>
12811 var TopicRecord = Roo.data.Record.create(
12812 {name: 'title', mapping: 'topic_title'},
12813 {name: 'author', mapping: 'username'},
12814 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12815 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12816 {name: 'lastPoster', mapping: 'user2'},
12817 {name: 'excerpt', mapping: 'post_text'}
12820 var myNewRecord = new TopicRecord({
12821 title: 'Do my job please',
12824 lastPost: new Date(),
12825 lastPoster: 'Animal',
12826 excerpt: 'No way dude!'
12828 myStore.add(myNewRecord);
12833 Roo.data.Record.create = function(o){
12834 var f = function(){
12835 f.superclass.constructor.apply(this, arguments);
12837 Roo.extend(f, Roo.data.Record);
12838 var p = f.prototype;
12839 p.fields = new Roo.util.MixedCollection(false, function(field){
12842 for(var i = 0, len = o.length; i < len; i++){
12843 p.fields.add(new Roo.data.Field(o[i]));
12845 f.getField = function(name){
12846 return p.fields.get(name);
12851 Roo.data.Record.AUTO_ID = 1000;
12852 Roo.data.Record.EDIT = 'edit';
12853 Roo.data.Record.REJECT = 'reject';
12854 Roo.data.Record.COMMIT = 'commit';
12856 Roo.data.Record.prototype = {
12858 * Readonly flag - true if this record has been modified.
12867 join : function(store){
12868 this.store = store;
12872 * Set the named field to the specified value.
12873 * @param {String} name The name of the field to set.
12874 * @param {Object} value The value to set the field to.
12876 set : function(name, value){
12877 if(this.data[name] == value){
12881 if(!this.modified){
12882 this.modified = {};
12884 if(typeof this.modified[name] == 'undefined'){
12885 this.modified[name] = this.data[name];
12887 this.data[name] = value;
12888 if(!this.editing && this.store){
12889 this.store.afterEdit(this);
12894 * Get the value of the named field.
12895 * @param {String} name The name of the field to get the value of.
12896 * @return {Object} The value of the field.
12898 get : function(name){
12899 return this.data[name];
12903 beginEdit : function(){
12904 this.editing = true;
12905 this.modified = {};
12909 cancelEdit : function(){
12910 this.editing = false;
12911 delete this.modified;
12915 endEdit : function(){
12916 this.editing = false;
12917 if(this.dirty && this.store){
12918 this.store.afterEdit(this);
12923 * Usually called by the {@link Roo.data.Store} which owns the Record.
12924 * Rejects all changes made to the Record since either creation, or the last commit operation.
12925 * Modified fields are reverted to their original values.
12927 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12928 * of reject operations.
12930 reject : function(){
12931 var m = this.modified;
12933 if(typeof m[n] != "function"){
12934 this.data[n] = m[n];
12937 this.dirty = false;
12938 delete this.modified;
12939 this.editing = false;
12941 this.store.afterReject(this);
12946 * Usually called by the {@link Roo.data.Store} which owns the Record.
12947 * Commits all changes made to the Record since either creation, or the last commit operation.
12949 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12950 * of commit operations.
12952 commit : function(){
12953 this.dirty = false;
12954 delete this.modified;
12955 this.editing = false;
12957 this.store.afterCommit(this);
12962 hasError : function(){
12963 return this.error != null;
12967 clearError : function(){
12972 * Creates a copy of this record.
12973 * @param {String} id (optional) A new record id if you don't want to use this record's id
12976 copy : function(newId) {
12977 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12981 * Ext JS Library 1.1.1
12982 * Copyright(c) 2006-2007, Ext JS, LLC.
12984 * Originally Released Under LGPL - original licence link has changed is not relivant.
12987 * <script type="text/javascript">
12993 * @class Roo.data.Store
12994 * @extends Roo.util.Observable
12995 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12996 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12998 * 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
12999 * has no knowledge of the format of the data returned by the Proxy.<br>
13001 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13002 * instances from the data object. These records are cached and made available through accessor functions.
13004 * Creates a new Store.
13005 * @param {Object} config A config object containing the objects needed for the Store to access data,
13006 * and read the data into Records.
13008 Roo.data.Store = function(config){
13009 this.data = new Roo.util.MixedCollection(false);
13010 this.data.getKey = function(o){
13013 this.baseParams = {};
13015 this.paramNames = {
13020 "multisort" : "_multisort"
13023 if(config && config.data){
13024 this.inlineData = config.data;
13025 delete config.data;
13028 Roo.apply(this, config);
13030 if(this.reader){ // reader passed
13031 this.reader = Roo.factory(this.reader, Roo.data);
13032 this.reader.xmodule = this.xmodule || false;
13033 if(!this.recordType){
13034 this.recordType = this.reader.recordType;
13036 if(this.reader.onMetaChange){
13037 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13041 if(this.recordType){
13042 this.fields = this.recordType.prototype.fields;
13044 this.modified = [];
13048 * @event datachanged
13049 * Fires when the data cache has changed, and a widget which is using this Store
13050 * as a Record cache should refresh its view.
13051 * @param {Store} this
13053 datachanged : true,
13055 * @event metachange
13056 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13057 * @param {Store} this
13058 * @param {Object} meta The JSON metadata
13063 * Fires when Records have been added to the Store
13064 * @param {Store} this
13065 * @param {Roo.data.Record[]} records The array of Records added
13066 * @param {Number} index The index at which the record(s) were added
13071 * Fires when a Record has been removed from the Store
13072 * @param {Store} this
13073 * @param {Roo.data.Record} record The Record that was removed
13074 * @param {Number} index The index at which the record was removed
13079 * Fires when a Record has been updated
13080 * @param {Store} this
13081 * @param {Roo.data.Record} record The Record that was updated
13082 * @param {String} operation The update operation being performed. Value may be one of:
13084 Roo.data.Record.EDIT
13085 Roo.data.Record.REJECT
13086 Roo.data.Record.COMMIT
13092 * Fires when the data cache has been cleared.
13093 * @param {Store} this
13097 * @event beforeload
13098 * Fires before a request is made for a new data object. If the beforeload handler returns false
13099 * the load action will be canceled.
13100 * @param {Store} this
13101 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13105 * @event beforeloadadd
13106 * Fires after a new set of Records has been loaded.
13107 * @param {Store} this
13108 * @param {Roo.data.Record[]} records The Records that were loaded
13109 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13111 beforeloadadd : true,
13114 * Fires after a new set of Records has been loaded, before they are added to the store.
13115 * @param {Store} this
13116 * @param {Roo.data.Record[]} records The Records that were loaded
13117 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13118 * @params {Object} return from reader
13122 * @event loadexception
13123 * Fires if an exception occurs in the Proxy during loading.
13124 * Called with the signature of the Proxy's "loadexception" event.
13125 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13128 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13129 * @param {Object} load options
13130 * @param {Object} jsonData from your request (normally this contains the Exception)
13132 loadexception : true
13136 this.proxy = Roo.factory(this.proxy, Roo.data);
13137 this.proxy.xmodule = this.xmodule || false;
13138 this.relayEvents(this.proxy, ["loadexception"]);
13140 this.sortToggle = {};
13141 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13143 Roo.data.Store.superclass.constructor.call(this);
13145 if(this.inlineData){
13146 this.loadData(this.inlineData);
13147 delete this.inlineData;
13151 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13153 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13154 * without a remote query - used by combo/forms at present.
13158 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13161 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13164 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13165 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13168 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13169 * on any HTTP request
13172 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13175 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13179 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13180 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13182 remoteSort : false,
13185 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13186 * loaded or when a record is removed. (defaults to false).
13188 pruneModifiedRecords : false,
13191 lastOptions : null,
13194 * Add Records to the Store and fires the add event.
13195 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13197 add : function(records){
13198 records = [].concat(records);
13199 for(var i = 0, len = records.length; i < len; i++){
13200 records[i].join(this);
13202 var index = this.data.length;
13203 this.data.addAll(records);
13204 this.fireEvent("add", this, records, index);
13208 * Remove a Record from the Store and fires the remove event.
13209 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13211 remove : function(record){
13212 var index = this.data.indexOf(record);
13213 this.data.removeAt(index);
13215 if(this.pruneModifiedRecords){
13216 this.modified.remove(record);
13218 this.fireEvent("remove", this, record, index);
13222 * Remove all Records from the Store and fires the clear event.
13224 removeAll : function(){
13226 if(this.pruneModifiedRecords){
13227 this.modified = [];
13229 this.fireEvent("clear", this);
13233 * Inserts Records to the Store at the given index and fires the add event.
13234 * @param {Number} index The start index at which to insert the passed Records.
13235 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13237 insert : function(index, records){
13238 records = [].concat(records);
13239 for(var i = 0, len = records.length; i < len; i++){
13240 this.data.insert(index, records[i]);
13241 records[i].join(this);
13243 this.fireEvent("add", this, records, index);
13247 * Get the index within the cache of the passed Record.
13248 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13249 * @return {Number} The index of the passed Record. Returns -1 if not found.
13251 indexOf : function(record){
13252 return this.data.indexOf(record);
13256 * Get the index within the cache of the Record with the passed id.
13257 * @param {String} id The id of the Record to find.
13258 * @return {Number} The index of the Record. Returns -1 if not found.
13260 indexOfId : function(id){
13261 return this.data.indexOfKey(id);
13265 * Get the Record with the specified id.
13266 * @param {String} id The id of the Record to find.
13267 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13269 getById : function(id){
13270 return this.data.key(id);
13274 * Get the Record at the specified index.
13275 * @param {Number} index The index of the Record to find.
13276 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13278 getAt : function(index){
13279 return this.data.itemAt(index);
13283 * Returns a range of Records between specified indices.
13284 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13285 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13286 * @return {Roo.data.Record[]} An array of Records
13288 getRange : function(start, end){
13289 return this.data.getRange(start, end);
13293 storeOptions : function(o){
13294 o = Roo.apply({}, o);
13297 this.lastOptions = o;
13301 * Loads the Record cache from the configured Proxy using the configured Reader.
13303 * If using remote paging, then the first load call must specify the <em>start</em>
13304 * and <em>limit</em> properties in the options.params property to establish the initial
13305 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13307 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13308 * and this call will return before the new data has been loaded. Perform any post-processing
13309 * in a callback function, or in a "load" event handler.</strong>
13311 * @param {Object} options An object containing properties which control loading options:<ul>
13312 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13313 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13314 * passed the following arguments:<ul>
13315 * <li>r : Roo.data.Record[]</li>
13316 * <li>options: Options object from the load call</li>
13317 * <li>success: Boolean success indicator</li></ul></li>
13318 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13319 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13322 load : function(options){
13323 options = options || {};
13324 if(this.fireEvent("beforeload", this, options) !== false){
13325 this.storeOptions(options);
13326 var p = Roo.apply(options.params || {}, this.baseParams);
13327 // if meta was not loaded from remote source.. try requesting it.
13328 if (!this.reader.metaFromRemote) {
13329 p._requestMeta = 1;
13331 if(this.sortInfo && this.remoteSort){
13332 var pn = this.paramNames;
13333 p[pn["sort"]] = this.sortInfo.field;
13334 p[pn["dir"]] = this.sortInfo.direction;
13336 if (this.multiSort) {
13337 var pn = this.paramNames;
13338 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13341 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13346 * Reloads the Record cache from the configured Proxy using the configured Reader and
13347 * the options from the last load operation performed.
13348 * @param {Object} options (optional) An object containing properties which may override the options
13349 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13350 * the most recently used options are reused).
13352 reload : function(options){
13353 this.load(Roo.applyIf(options||{}, this.lastOptions));
13357 // Called as a callback by the Reader during a load operation.
13358 loadRecords : function(o, options, success){
13359 if(!o || success === false){
13360 if(success !== false){
13361 this.fireEvent("load", this, [], options, o);
13363 if(options.callback){
13364 options.callback.call(options.scope || this, [], options, false);
13368 // if data returned failure - throw an exception.
13369 if (o.success === false) {
13370 // show a message if no listener is registered.
13371 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13372 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13374 // loadmask wil be hooked into this..
13375 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13378 var r = o.records, t = o.totalRecords || r.length;
13380 this.fireEvent("beforeloadadd", this, r, options, o);
13382 if(!options || options.add !== true){
13383 if(this.pruneModifiedRecords){
13384 this.modified = [];
13386 for(var i = 0, len = r.length; i < len; i++){
13390 this.data = this.snapshot;
13391 delete this.snapshot;
13394 this.data.addAll(r);
13395 this.totalLength = t;
13397 this.fireEvent("datachanged", this);
13399 this.totalLength = Math.max(t, this.data.length+r.length);
13403 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13405 var e = new Roo.data.Record({});
13407 e.set(this.parent.displayField, this.parent.emptyTitle);
13408 e.set(this.parent.valueField, '');
13413 this.fireEvent("load", this, r, options, o);
13414 if(options.callback){
13415 options.callback.call(options.scope || this, r, options, true);
13421 * Loads data from a passed data block. A Reader which understands the format of the data
13422 * must have been configured in the constructor.
13423 * @param {Object} data The data block from which to read the Records. The format of the data expected
13424 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13425 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13427 loadData : function(o, append){
13428 var r = this.reader.readRecords(o);
13429 this.loadRecords(r, {add: append}, true);
13433 * using 'cn' the nested child reader read the child array into it's child stores.
13434 * @param {Object} rec The record with a 'children array
13436 loadDataFromChildren : function(rec)
13438 this.loadData(this.reader.toLoadData(rec));
13443 * Gets the number of cached records.
13445 * <em>If using paging, this may not be the total size of the dataset. If the data object
13446 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13447 * the data set size</em>
13449 getCount : function(){
13450 return this.data.length || 0;
13454 * Gets the total number of records in the dataset as returned by the server.
13456 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13457 * the dataset size</em>
13459 getTotalCount : function(){
13460 return this.totalLength || 0;
13464 * Returns the sort state of the Store as an object with two properties:
13466 field {String} The name of the field by which the Records are sorted
13467 direction {String} The sort order, "ASC" or "DESC"
13470 getSortState : function(){
13471 return this.sortInfo;
13475 applySort : function(){
13476 if(this.sortInfo && !this.remoteSort){
13477 var s = this.sortInfo, f = s.field;
13478 var st = this.fields.get(f).sortType;
13479 var fn = function(r1, r2){
13480 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13481 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13483 this.data.sort(s.direction, fn);
13484 if(this.snapshot && this.snapshot != this.data){
13485 this.snapshot.sort(s.direction, fn);
13491 * Sets the default sort column and order to be used by the next load operation.
13492 * @param {String} fieldName The name of the field to sort by.
13493 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13495 setDefaultSort : function(field, dir){
13496 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13500 * Sort the Records.
13501 * If remote sorting is used, the sort is performed on the server, and the cache is
13502 * reloaded. If local sorting is used, the cache is sorted internally.
13503 * @param {String} fieldName The name of the field to sort by.
13504 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13506 sort : function(fieldName, dir){
13507 var f = this.fields.get(fieldName);
13509 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13511 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13512 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13517 this.sortToggle[f.name] = dir;
13518 this.sortInfo = {field: f.name, direction: dir};
13519 if(!this.remoteSort){
13521 this.fireEvent("datachanged", this);
13523 this.load(this.lastOptions);
13528 * Calls the specified function for each of the Records in the cache.
13529 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13530 * Returning <em>false</em> aborts and exits the iteration.
13531 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13533 each : function(fn, scope){
13534 this.data.each(fn, scope);
13538 * Gets all records modified since the last commit. Modified records are persisted across load operations
13539 * (e.g., during paging).
13540 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13542 getModifiedRecords : function(){
13543 return this.modified;
13547 createFilterFn : function(property, value, anyMatch){
13548 if(!value.exec){ // not a regex
13549 value = String(value);
13550 if(value.length == 0){
13553 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13555 return function(r){
13556 return value.test(r.data[property]);
13561 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13562 * @param {String} property A field on your records
13563 * @param {Number} start The record index to start at (defaults to 0)
13564 * @param {Number} end The last record index to include (defaults to length - 1)
13565 * @return {Number} The sum
13567 sum : function(property, start, end){
13568 var rs = this.data.items, v = 0;
13569 start = start || 0;
13570 end = (end || end === 0) ? end : rs.length-1;
13572 for(var i = start; i <= end; i++){
13573 v += (rs[i].data[property] || 0);
13579 * Filter the records by a specified property.
13580 * @param {String} field A field on your records
13581 * @param {String/RegExp} value Either a string that the field
13582 * should start with or a RegExp to test against the field
13583 * @param {Boolean} anyMatch True to match any part not just the beginning
13585 filter : function(property, value, anyMatch){
13586 var fn = this.createFilterFn(property, value, anyMatch);
13587 return fn ? this.filterBy(fn) : this.clearFilter();
13591 * Filter by a function. The specified function will be called with each
13592 * record in this data source. If the function returns true the record is included,
13593 * otherwise it is filtered.
13594 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13595 * @param {Object} scope (optional) The scope of the function (defaults to this)
13597 filterBy : function(fn, scope){
13598 this.snapshot = this.snapshot || this.data;
13599 this.data = this.queryBy(fn, scope||this);
13600 this.fireEvent("datachanged", this);
13604 * Query the records by a specified property.
13605 * @param {String} field A field on your records
13606 * @param {String/RegExp} value Either a string that the field
13607 * should start with or a RegExp to test against the field
13608 * @param {Boolean} anyMatch True to match any part not just the beginning
13609 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13611 query : function(property, value, anyMatch){
13612 var fn = this.createFilterFn(property, value, anyMatch);
13613 return fn ? this.queryBy(fn) : this.data.clone();
13617 * Query by a function. The specified function will be called with each
13618 * record in this data source. If the function returns true the record is included
13620 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13621 * @param {Object} scope (optional) The scope of the function (defaults to this)
13622 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13624 queryBy : function(fn, scope){
13625 var data = this.snapshot || this.data;
13626 return data.filterBy(fn, scope||this);
13630 * Collects unique values for a particular dataIndex from this store.
13631 * @param {String} dataIndex The property to collect
13632 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13633 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13634 * @return {Array} An array of the unique values
13636 collect : function(dataIndex, allowNull, bypassFilter){
13637 var d = (bypassFilter === true && this.snapshot) ?
13638 this.snapshot.items : this.data.items;
13639 var v, sv, r = [], l = {};
13640 for(var i = 0, len = d.length; i < len; i++){
13641 v = d[i].data[dataIndex];
13643 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13652 * Revert to a view of the Record cache with no filtering applied.
13653 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13655 clearFilter : function(suppressEvent){
13656 if(this.snapshot && this.snapshot != this.data){
13657 this.data = this.snapshot;
13658 delete this.snapshot;
13659 if(suppressEvent !== true){
13660 this.fireEvent("datachanged", this);
13666 afterEdit : function(record){
13667 if(this.modified.indexOf(record) == -1){
13668 this.modified.push(record);
13670 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13674 afterReject : function(record){
13675 this.modified.remove(record);
13676 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13680 afterCommit : function(record){
13681 this.modified.remove(record);
13682 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13686 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13687 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13689 commitChanges : function(){
13690 var m = this.modified.slice(0);
13691 this.modified = [];
13692 for(var i = 0, len = m.length; i < len; i++){
13698 * Cancel outstanding changes on all changed records.
13700 rejectChanges : function(){
13701 var m = this.modified.slice(0);
13702 this.modified = [];
13703 for(var i = 0, len = m.length; i < len; i++){
13708 onMetaChange : function(meta, rtype, o){
13709 this.recordType = rtype;
13710 this.fields = rtype.prototype.fields;
13711 delete this.snapshot;
13712 this.sortInfo = meta.sortInfo || this.sortInfo;
13713 this.modified = [];
13714 this.fireEvent('metachange', this, this.reader.meta);
13717 moveIndex : function(data, type)
13719 var index = this.indexOf(data);
13721 var newIndex = index + type;
13725 this.insert(newIndex, data);
13730 * Ext JS Library 1.1.1
13731 * Copyright(c) 2006-2007, Ext JS, LLC.
13733 * Originally Released Under LGPL - original licence link has changed is not relivant.
13736 * <script type="text/javascript">
13740 * @class Roo.data.SimpleStore
13741 * @extends Roo.data.Store
13742 * Small helper class to make creating Stores from Array data easier.
13743 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13744 * @cfg {Array} fields An array of field definition objects, or field name strings.
13745 * @cfg {Object} an existing reader (eg. copied from another store)
13746 * @cfg {Array} data The multi-dimensional array of data
13748 * @param {Object} config
13750 Roo.data.SimpleStore = function(config)
13752 Roo.data.SimpleStore.superclass.constructor.call(this, {
13754 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13757 Roo.data.Record.create(config.fields)
13759 proxy : new Roo.data.MemoryProxy(config.data)
13763 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13765 * Ext JS Library 1.1.1
13766 * Copyright(c) 2006-2007, Ext JS, LLC.
13768 * Originally Released Under LGPL - original licence link has changed is not relivant.
13771 * <script type="text/javascript">
13776 * @extends Roo.data.Store
13777 * @class Roo.data.JsonStore
13778 * Small helper class to make creating Stores for JSON data easier. <br/>
13780 var store = new Roo.data.JsonStore({
13781 url: 'get-images.php',
13783 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13786 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13787 * JsonReader and HttpProxy (unless inline data is provided).</b>
13788 * @cfg {Array} fields An array of field definition objects, or field name strings.
13790 * @param {Object} config
13792 Roo.data.JsonStore = function(c){
13793 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13794 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13795 reader: new Roo.data.JsonReader(c, c.fields)
13798 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13800 * Ext JS Library 1.1.1
13801 * Copyright(c) 2006-2007, Ext JS, LLC.
13803 * Originally Released Under LGPL - original licence link has changed is not relivant.
13806 * <script type="text/javascript">
13810 Roo.data.Field = function(config){
13811 if(typeof config == "string"){
13812 config = {name: config};
13814 Roo.apply(this, config);
13817 this.type = "auto";
13820 var st = Roo.data.SortTypes;
13821 // named sortTypes are supported, here we look them up
13822 if(typeof this.sortType == "string"){
13823 this.sortType = st[this.sortType];
13826 // set default sortType for strings and dates
13827 if(!this.sortType){
13830 this.sortType = st.asUCString;
13833 this.sortType = st.asDate;
13836 this.sortType = st.none;
13841 var stripRe = /[\$,%]/g;
13843 // prebuilt conversion function for this field, instead of
13844 // switching every time we're reading a value
13846 var cv, dateFormat = this.dateFormat;
13851 cv = function(v){ return v; };
13854 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13858 return v !== undefined && v !== null && v !== '' ?
13859 parseInt(String(v).replace(stripRe, ""), 10) : '';
13864 return v !== undefined && v !== null && v !== '' ?
13865 parseFloat(String(v).replace(stripRe, ""), 10) : '';
13870 cv = function(v){ return v === true || v === "true" || v == 1; };
13877 if(v instanceof Date){
13881 if(dateFormat == "timestamp"){
13882 return new Date(v*1000);
13884 return Date.parseDate(v, dateFormat);
13886 var parsed = Date.parse(v);
13887 return parsed ? new Date(parsed) : null;
13896 Roo.data.Field.prototype = {
13904 * Ext JS Library 1.1.1
13905 * Copyright(c) 2006-2007, Ext JS, LLC.
13907 * Originally Released Under LGPL - original licence link has changed is not relivant.
13910 * <script type="text/javascript">
13913 // Base class for reading structured data from a data source. This class is intended to be
13914 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13917 * @class Roo.data.DataReader
13918 * Base class for reading structured data from a data source. This class is intended to be
13919 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13922 Roo.data.DataReader = function(meta, recordType){
13926 this.recordType = recordType instanceof Array ?
13927 Roo.data.Record.create(recordType) : recordType;
13930 Roo.data.DataReader.prototype = {
13933 readerType : 'Data',
13935 * Create an empty record
13936 * @param {Object} data (optional) - overlay some values
13937 * @return {Roo.data.Record} record created.
13939 newRow : function(d) {
13941 this.recordType.prototype.fields.each(function(c) {
13943 case 'int' : da[c.name] = 0; break;
13944 case 'date' : da[c.name] = new Date(); break;
13945 case 'float' : da[c.name] = 0.0; break;
13946 case 'boolean' : da[c.name] = false; break;
13947 default : da[c.name] = ""; break;
13951 return new this.recordType(Roo.apply(da, d));
13957 * Ext JS Library 1.1.1
13958 * Copyright(c) 2006-2007, Ext JS, LLC.
13960 * Originally Released Under LGPL - original licence link has changed is not relivant.
13963 * <script type="text/javascript">
13967 * @class Roo.data.DataProxy
13968 * @extends Roo.data.Observable
13969 * This class is an abstract base class for implementations which provide retrieval of
13970 * unformatted data objects.<br>
13972 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13973 * (of the appropriate type which knows how to parse the data object) to provide a block of
13974 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13976 * Custom implementations must implement the load method as described in
13977 * {@link Roo.data.HttpProxy#load}.
13979 Roo.data.DataProxy = function(){
13982 * @event beforeload
13983 * Fires before a network request is made to retrieve a data object.
13984 * @param {Object} This DataProxy object.
13985 * @param {Object} params The params parameter to the load function.
13990 * Fires before the load method's callback is called.
13991 * @param {Object} This DataProxy object.
13992 * @param {Object} o The data object.
13993 * @param {Object} arg The callback argument object passed to the load function.
13997 * @event loadexception
13998 * Fires if an Exception occurs during data retrieval.
13999 * @param {Object} This DataProxy object.
14000 * @param {Object} o The data object.
14001 * @param {Object} arg The callback argument object passed to the load function.
14002 * @param {Object} e The Exception.
14004 loadexception : true
14006 Roo.data.DataProxy.superclass.constructor.call(this);
14009 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14012 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14016 * Ext JS Library 1.1.1
14017 * Copyright(c) 2006-2007, Ext JS, LLC.
14019 * Originally Released Under LGPL - original licence link has changed is not relivant.
14022 * <script type="text/javascript">
14025 * @class Roo.data.MemoryProxy
14026 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14027 * to the Reader when its load method is called.
14029 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14031 Roo.data.MemoryProxy = function(data){
14035 Roo.data.MemoryProxy.superclass.constructor.call(this);
14039 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14042 * Load data from the requested source (in this case an in-memory
14043 * data object passed to the constructor), read the data object into
14044 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14045 * process that block using the passed callback.
14046 * @param {Object} params This parameter is not used by the MemoryProxy class.
14047 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14048 * object into a block of Roo.data.Records.
14049 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14050 * The function must be passed <ul>
14051 * <li>The Record block object</li>
14052 * <li>The "arg" argument from the load function</li>
14053 * <li>A boolean success indicator</li>
14055 * @param {Object} scope The scope in which to call the callback
14056 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14058 load : function(params, reader, callback, scope, arg){
14059 params = params || {};
14062 result = reader.readRecords(params.data ? params.data :this.data);
14064 this.fireEvent("loadexception", this, arg, null, e);
14065 callback.call(scope, null, arg, false);
14068 callback.call(scope, result, arg, true);
14072 update : function(params, records){
14077 * Ext JS Library 1.1.1
14078 * Copyright(c) 2006-2007, Ext JS, LLC.
14080 * Originally Released Under LGPL - original licence link has changed is not relivant.
14083 * <script type="text/javascript">
14086 * @class Roo.data.HttpProxy
14087 * @extends Roo.data.DataProxy
14088 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14089 * configured to reference a certain URL.<br><br>
14091 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14092 * from which the running page was served.<br><br>
14094 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14096 * Be aware that to enable the browser to parse an XML document, the server must set
14097 * the Content-Type header in the HTTP response to "text/xml".
14099 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14100 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14101 * will be used to make the request.
14103 Roo.data.HttpProxy = function(conn){
14104 Roo.data.HttpProxy.superclass.constructor.call(this);
14105 // is conn a conn config or a real conn?
14107 this.useAjax = !conn || !conn.events;
14111 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14112 // thse are take from connection...
14115 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14118 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14119 * extra parameters to each request made by this object. (defaults to undefined)
14122 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14123 * to each request made by this object. (defaults to undefined)
14126 * @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)
14129 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14132 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14138 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14142 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14143 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14144 * a finer-grained basis than the DataProxy events.
14146 getConnection : function(){
14147 return this.useAjax ? Roo.Ajax : this.conn;
14151 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14152 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14153 * process that block using the passed callback.
14154 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14155 * for the request to the remote server.
14156 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14157 * object into a block of Roo.data.Records.
14158 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14159 * The function must be passed <ul>
14160 * <li>The Record block object</li>
14161 * <li>The "arg" argument from the load function</li>
14162 * <li>A boolean success indicator</li>
14164 * @param {Object} scope The scope in which to call the callback
14165 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14167 load : function(params, reader, callback, scope, arg){
14168 if(this.fireEvent("beforeload", this, params) !== false){
14170 params : params || {},
14172 callback : callback,
14177 callback : this.loadResponse,
14181 Roo.applyIf(o, this.conn);
14182 if(this.activeRequest){
14183 Roo.Ajax.abort(this.activeRequest);
14185 this.activeRequest = Roo.Ajax.request(o);
14187 this.conn.request(o);
14190 callback.call(scope||this, null, arg, false);
14195 loadResponse : function(o, success, response){
14196 delete this.activeRequest;
14198 this.fireEvent("loadexception", this, o, response);
14199 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14204 result = o.reader.read(response);
14206 this.fireEvent("loadexception", this, o, response, e);
14207 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14211 this.fireEvent("load", this, o, o.request.arg);
14212 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14216 update : function(dataSet){
14221 updateResponse : function(dataSet){
14226 * Ext JS Library 1.1.1
14227 * Copyright(c) 2006-2007, Ext JS, LLC.
14229 * Originally Released Under LGPL - original licence link has changed is not relivant.
14232 * <script type="text/javascript">
14236 * @class Roo.data.ScriptTagProxy
14237 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14238 * other than the originating domain of the running page.<br><br>
14240 * <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
14241 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14243 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14244 * source code that is used as the source inside a <script> tag.<br><br>
14246 * In order for the browser to process the returned data, the server must wrap the data object
14247 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14248 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14249 * depending on whether the callback name was passed:
14252 boolean scriptTag = false;
14253 String cb = request.getParameter("callback");
14256 response.setContentType("text/javascript");
14258 response.setContentType("application/x-json");
14260 Writer out = response.getWriter();
14262 out.write(cb + "(");
14264 out.print(dataBlock.toJsonString());
14271 * @param {Object} config A configuration object.
14273 Roo.data.ScriptTagProxy = function(config){
14274 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14275 Roo.apply(this, config);
14276 this.head = document.getElementsByTagName("head")[0];
14279 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14281 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14283 * @cfg {String} url The URL from which to request the data object.
14286 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14290 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14291 * the server the name of the callback function set up by the load call to process the returned data object.
14292 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14293 * javascript output which calls this named function passing the data object as its only parameter.
14295 callbackParam : "callback",
14297 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14298 * name to the request.
14303 * Load data from the configured URL, read the data object into
14304 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14305 * process that block using the passed callback.
14306 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14307 * for the request to the remote server.
14308 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14309 * object into a block of Roo.data.Records.
14310 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14311 * The function must be passed <ul>
14312 * <li>The Record block object</li>
14313 * <li>The "arg" argument from the load function</li>
14314 * <li>A boolean success indicator</li>
14316 * @param {Object} scope The scope in which to call the callback
14317 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14319 load : function(params, reader, callback, scope, arg){
14320 if(this.fireEvent("beforeload", this, params) !== false){
14322 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14324 var url = this.url;
14325 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14327 url += "&_dc=" + (new Date().getTime());
14329 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14332 cb : "stcCallback"+transId,
14333 scriptId : "stcScript"+transId,
14337 callback : callback,
14343 window[trans.cb] = function(o){
14344 conn.handleResponse(o, trans);
14347 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14349 if(this.autoAbort !== false){
14353 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14355 var script = document.createElement("script");
14356 script.setAttribute("src", url);
14357 script.setAttribute("type", "text/javascript");
14358 script.setAttribute("id", trans.scriptId);
14359 this.head.appendChild(script);
14361 this.trans = trans;
14363 callback.call(scope||this, null, arg, false);
14368 isLoading : function(){
14369 return this.trans ? true : false;
14373 * Abort the current server request.
14375 abort : function(){
14376 if(this.isLoading()){
14377 this.destroyTrans(this.trans);
14382 destroyTrans : function(trans, isLoaded){
14383 this.head.removeChild(document.getElementById(trans.scriptId));
14384 clearTimeout(trans.timeoutId);
14386 window[trans.cb] = undefined;
14388 delete window[trans.cb];
14391 // if hasn't been loaded, wait for load to remove it to prevent script error
14392 window[trans.cb] = function(){
14393 window[trans.cb] = undefined;
14395 delete window[trans.cb];
14402 handleResponse : function(o, trans){
14403 this.trans = false;
14404 this.destroyTrans(trans, true);
14407 result = trans.reader.readRecords(o);
14409 this.fireEvent("loadexception", this, o, trans.arg, e);
14410 trans.callback.call(trans.scope||window, null, trans.arg, false);
14413 this.fireEvent("load", this, o, trans.arg);
14414 trans.callback.call(trans.scope||window, result, trans.arg, true);
14418 handleFailure : function(trans){
14419 this.trans = false;
14420 this.destroyTrans(trans, false);
14421 this.fireEvent("loadexception", this, null, trans.arg);
14422 trans.callback.call(trans.scope||window, null, trans.arg, false);
14426 * Ext JS Library 1.1.1
14427 * Copyright(c) 2006-2007, Ext JS, LLC.
14429 * Originally Released Under LGPL - original licence link has changed is not relivant.
14432 * <script type="text/javascript">
14436 * @class Roo.data.JsonReader
14437 * @extends Roo.data.DataReader
14438 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14439 * based on mappings in a provided Roo.data.Record constructor.
14441 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14442 * in the reply previously.
14447 var RecordDef = Roo.data.Record.create([
14448 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14449 {name: 'occupation'} // This field will use "occupation" as the mapping.
14451 var myReader = new Roo.data.JsonReader({
14452 totalProperty: "results", // The property which contains the total dataset size (optional)
14453 root: "rows", // The property which contains an Array of row objects
14454 id: "id" // The property within each row object that provides an ID for the record (optional)
14458 * This would consume a JSON file like this:
14460 { 'results': 2, 'rows': [
14461 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14462 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14465 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14466 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14467 * paged from the remote server.
14468 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14469 * @cfg {String} root name of the property which contains the Array of row objects.
14470 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14471 * @cfg {Array} fields Array of field definition objects
14473 * Create a new JsonReader
14474 * @param {Object} meta Metadata configuration options
14475 * @param {Object} recordType Either an Array of field definition objects,
14476 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14478 Roo.data.JsonReader = function(meta, recordType){
14481 // set some defaults:
14482 Roo.applyIf(meta, {
14483 totalProperty: 'total',
14484 successProperty : 'success',
14489 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14491 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14493 readerType : 'Json',
14496 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14497 * Used by Store query builder to append _requestMeta to params.
14500 metaFromRemote : false,
14502 * This method is only used by a DataProxy which has retrieved data from a remote server.
14503 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14504 * @return {Object} data A data block which is used by an Roo.data.Store object as
14505 * a cache of Roo.data.Records.
14507 read : function(response){
14508 var json = response.responseText;
14510 var o = /* eval:var:o */ eval("("+json+")");
14512 throw {message: "JsonReader.read: Json object not found"};
14518 this.metaFromRemote = true;
14519 this.meta = o.metaData;
14520 this.recordType = Roo.data.Record.create(o.metaData.fields);
14521 this.onMetaChange(this.meta, this.recordType, o);
14523 return this.readRecords(o);
14526 // private function a store will implement
14527 onMetaChange : function(meta, recordType, o){
14534 simpleAccess: function(obj, subsc) {
14541 getJsonAccessor: function(){
14543 return function(expr) {
14545 return(re.test(expr))
14546 ? new Function("obj", "return obj." + expr)
14551 return Roo.emptyFn;
14556 * Create a data block containing Roo.data.Records from an XML document.
14557 * @param {Object} o An object which contains an Array of row objects in the property specified
14558 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14559 * which contains the total size of the dataset.
14560 * @return {Object} data A data block which is used by an Roo.data.Store object as
14561 * a cache of Roo.data.Records.
14563 readRecords : function(o){
14565 * After any data loads, the raw JSON data is available for further custom processing.
14569 var s = this.meta, Record = this.recordType,
14570 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14572 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14574 if(s.totalProperty) {
14575 this.getTotal = this.getJsonAccessor(s.totalProperty);
14577 if(s.successProperty) {
14578 this.getSuccess = this.getJsonAccessor(s.successProperty);
14580 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14582 var g = this.getJsonAccessor(s.id);
14583 this.getId = function(rec) {
14585 return (r === undefined || r === "") ? null : r;
14588 this.getId = function(){return null;};
14591 for(var jj = 0; jj < fl; jj++){
14593 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14594 this.ef[jj] = this.getJsonAccessor(map);
14598 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14599 if(s.totalProperty){
14600 var vt = parseInt(this.getTotal(o), 10);
14605 if(s.successProperty){
14606 var vs = this.getSuccess(o);
14607 if(vs === false || vs === 'false'){
14612 for(var i = 0; i < c; i++){
14615 var id = this.getId(n);
14616 for(var j = 0; j < fl; j++){
14618 var v = this.ef[j](n);
14620 Roo.log('missing convert for ' + f.name);
14624 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14626 var record = new Record(values, id);
14628 records[i] = record;
14634 totalRecords : totalRecords
14637 // used when loading children.. @see loadDataFromChildren
14638 toLoadData: function(rec)
14640 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14641 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14642 return { data : data, total : data.length };
14647 * Ext JS Library 1.1.1
14648 * Copyright(c) 2006-2007, Ext JS, LLC.
14650 * Originally Released Under LGPL - original licence link has changed is not relivant.
14653 * <script type="text/javascript">
14657 * @class Roo.data.ArrayReader
14658 * @extends Roo.data.DataReader
14659 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14660 * Each element of that Array represents a row of data fields. The
14661 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14662 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14666 var RecordDef = Roo.data.Record.create([
14667 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14668 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14670 var myReader = new Roo.data.ArrayReader({
14671 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14675 * This would consume an Array like this:
14677 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14681 * Create a new JsonReader
14682 * @param {Object} meta Metadata configuration options.
14683 * @param {Object|Array} recordType Either an Array of field definition objects
14685 * @cfg {Array} fields Array of field definition objects
14686 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14687 * as specified to {@link Roo.data.Record#create},
14688 * or an {@link Roo.data.Record} object
14691 * created using {@link Roo.data.Record#create}.
14693 Roo.data.ArrayReader = function(meta, recordType)
14695 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14698 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14701 * Create a data block containing Roo.data.Records from an XML document.
14702 * @param {Object} o An Array of row objects which represents the dataset.
14703 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14704 * a cache of Roo.data.Records.
14706 readRecords : function(o)
14708 var sid = this.meta ? this.meta.id : null;
14709 var recordType = this.recordType, fields = recordType.prototype.fields;
14712 for(var i = 0; i < root.length; i++){
14715 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14716 for(var j = 0, jlen = fields.length; j < jlen; j++){
14717 var f = fields.items[j];
14718 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14719 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14721 values[f.name] = v;
14723 var record = new recordType(values, id);
14725 records[records.length] = record;
14729 totalRecords : records.length
14732 // used when loading children.. @see loadDataFromChildren
14733 toLoadData: function(rec)
14735 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14736 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14747 * @class Roo.bootstrap.ComboBox
14748 * @extends Roo.bootstrap.TriggerField
14749 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14750 * @cfg {Boolean} append (true|false) default false
14751 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14752 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14753 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14754 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14755 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14756 * @cfg {Boolean} animate default true
14757 * @cfg {Boolean} emptyResultText only for touch device
14758 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14759 * @cfg {String} emptyTitle default ''
14760 * @cfg {Number} width fixed with? experimental
14762 * Create a new ComboBox.
14763 * @param {Object} config Configuration options
14765 Roo.bootstrap.ComboBox = function(config){
14766 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14770 * Fires when the dropdown list is expanded
14771 * @param {Roo.bootstrap.ComboBox} combo This combo box
14776 * Fires when the dropdown list is collapsed
14777 * @param {Roo.bootstrap.ComboBox} combo This combo box
14781 * @event beforeselect
14782 * Fires before a list item is selected. Return false to cancel the selection.
14783 * @param {Roo.bootstrap.ComboBox} combo This combo box
14784 * @param {Roo.data.Record} record The data record returned from the underlying store
14785 * @param {Number} index The index of the selected item in the dropdown list
14787 'beforeselect' : true,
14790 * Fires when a list item is selected
14791 * @param {Roo.bootstrap.ComboBox} combo This combo box
14792 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14793 * @param {Number} index The index of the selected item in the dropdown list
14797 * @event beforequery
14798 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14799 * The event object passed has these properties:
14800 * @param {Roo.bootstrap.ComboBox} combo This combo box
14801 * @param {String} query The query
14802 * @param {Boolean} forceAll true to force "all" query
14803 * @param {Boolean} cancel true to cancel the query
14804 * @param {Object} e The query event object
14806 'beforequery': true,
14809 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14810 * @param {Roo.bootstrap.ComboBox} combo This combo box
14815 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14816 * @param {Roo.bootstrap.ComboBox} combo This combo box
14817 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14822 * Fires when the remove value from the combobox array
14823 * @param {Roo.bootstrap.ComboBox} combo This combo box
14827 * @event afterremove
14828 * Fires when the remove value from the combobox array
14829 * @param {Roo.bootstrap.ComboBox} combo This combo box
14831 'afterremove' : true,
14833 * @event specialfilter
14834 * Fires when specialfilter
14835 * @param {Roo.bootstrap.ComboBox} combo This combo box
14837 'specialfilter' : true,
14840 * Fires when tick the element
14841 * @param {Roo.bootstrap.ComboBox} combo This combo box
14845 * @event touchviewdisplay
14846 * Fires when touch view require special display (default is using displayField)
14847 * @param {Roo.bootstrap.ComboBox} combo This combo box
14848 * @param {Object} cfg set html .
14850 'touchviewdisplay' : true
14855 this.tickItems = [];
14857 this.selectedIndex = -1;
14858 if(this.mode == 'local'){
14859 if(config.queryDelay === undefined){
14860 this.queryDelay = 10;
14862 if(config.minChars === undefined){
14868 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14871 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14872 * rendering into an Roo.Editor, defaults to false)
14875 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14876 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14879 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14882 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14883 * the dropdown list (defaults to undefined, with no header element)
14887 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
14891 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14893 listWidth: undefined,
14895 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14896 * mode = 'remote' or 'text' if mode = 'local')
14898 displayField: undefined,
14901 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14902 * mode = 'remote' or 'value' if mode = 'local').
14903 * Note: use of a valueField requires the user make a selection
14904 * in order for a value to be mapped.
14906 valueField: undefined,
14908 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14913 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14914 * field's data value (defaults to the underlying DOM element's name)
14916 hiddenName: undefined,
14918 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14922 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14924 selectedClass: 'active',
14927 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14931 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14932 * anchor positions (defaults to 'tl-bl')
14934 listAlign: 'tl-bl?',
14936 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14940 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
14941 * query specified by the allQuery config option (defaults to 'query')
14943 triggerAction: 'query',
14945 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14946 * (defaults to 4, does not apply if editable = false)
14950 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14951 * delay (typeAheadDelay) if it matches a known value (defaults to false)
14955 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14956 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14960 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14961 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
14965 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
14966 * when editable = true (defaults to false)
14968 selectOnFocus:false,
14970 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14972 queryParam: 'query',
14974 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
14975 * when mode = 'remote' (defaults to 'Loading...')
14977 loadingText: 'Loading...',
14979 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14983 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14987 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14988 * traditional select (defaults to true)
14992 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14996 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15000 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15001 * listWidth has a higher value)
15005 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15006 * allow the user to set arbitrary text into the field (defaults to false)
15008 forceSelection:false,
15010 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15011 * if typeAhead = true (defaults to 250)
15013 typeAheadDelay : 250,
15015 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15016 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15018 valueNotFoundText : undefined,
15020 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15022 blockFocus : false,
15025 * @cfg {Boolean} disableClear Disable showing of clear button.
15027 disableClear : false,
15029 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15031 alwaysQuery : false,
15034 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15039 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15041 invalidClass : "has-warning",
15044 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15046 validClass : "has-success",
15049 * @cfg {Boolean} specialFilter (true|false) special filter default false
15051 specialFilter : false,
15054 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15056 mobileTouchView : true,
15059 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15061 useNativeIOS : false,
15064 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15066 mobile_restrict_height : false,
15068 ios_options : false,
15080 btnPosition : 'right',
15081 triggerList : true,
15082 showToggleBtn : true,
15084 emptyResultText: 'Empty',
15085 triggerText : 'Select',
15089 // element that contains real text value.. (when hidden is used..)
15091 getAutoCreate : function()
15096 * Render classic select for iso
15099 if(Roo.isIOS && this.useNativeIOS){
15100 cfg = this.getAutoCreateNativeIOS();
15108 if(Roo.isTouch && this.mobileTouchView){
15109 cfg = this.getAutoCreateTouchView();
15116 if(!this.tickable){
15117 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15122 * ComboBox with tickable selections
15125 var align = this.labelAlign || this.parentLabelAlign();
15128 cls : 'form-group roo-combobox-tickable' //input-group
15131 var btn_text_select = '';
15132 var btn_text_done = '';
15133 var btn_text_cancel = '';
15135 if (this.btn_text_show) {
15136 btn_text_select = 'Select';
15137 btn_text_done = 'Done';
15138 btn_text_cancel = 'Cancel';
15143 cls : 'tickable-buttons',
15148 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15149 //html : this.triggerText
15150 html: btn_text_select
15156 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15158 html: btn_text_done
15164 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15166 html: btn_text_cancel
15172 buttons.cn.unshift({
15174 cls: 'roo-select2-search-field-input'
15180 Roo.each(buttons.cn, function(c){
15182 c.cls += ' btn-' + _this.size;
15185 if (_this.disabled) {
15192 style : 'display: contents',
15197 cls: 'form-hidden-field'
15201 cls: 'roo-select2-choices',
15205 cls: 'roo-select2-search-field',
15216 cls: 'roo-select2-container input-group roo-select2-container-multi',
15222 // cls: 'typeahead typeahead-long dropdown-menu',
15223 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15228 if(this.hasFeedback && !this.allowBlank){
15232 cls: 'glyphicon form-control-feedback'
15235 combobox.cn.push(feedback);
15242 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15243 tooltip : 'This field is required'
15245 if (Roo.bootstrap.version == 4) {
15248 style : 'display:none'
15251 if (align ==='left' && this.fieldLabel.length) {
15253 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15260 cls : 'control-label col-form-label',
15261 html : this.fieldLabel
15273 var labelCfg = cfg.cn[1];
15274 var contentCfg = cfg.cn[2];
15277 if(this.indicatorpos == 'right'){
15283 cls : 'control-label col-form-label',
15287 html : this.fieldLabel
15303 labelCfg = cfg.cn[0];
15304 contentCfg = cfg.cn[1];
15308 if(this.labelWidth > 12){
15309 labelCfg.style = "width: " + this.labelWidth + 'px';
15311 if(this.width * 1 > 0){
15312 contentCfg.style = "width: " + this.width + 'px';
15314 if(this.labelWidth < 13 && this.labelmd == 0){
15315 this.labelmd = this.labelWidth;
15318 if(this.labellg > 0){
15319 labelCfg.cls += ' col-lg-' + this.labellg;
15320 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15323 if(this.labelmd > 0){
15324 labelCfg.cls += ' col-md-' + this.labelmd;
15325 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15328 if(this.labelsm > 0){
15329 labelCfg.cls += ' col-sm-' + this.labelsm;
15330 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15333 if(this.labelxs > 0){
15334 labelCfg.cls += ' col-xs-' + this.labelxs;
15335 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15339 } else if ( this.fieldLabel.length) {
15340 // Roo.log(" label");
15345 //cls : 'input-group-addon',
15346 html : this.fieldLabel
15351 if(this.indicatorpos == 'right'){
15355 //cls : 'input-group-addon',
15356 html : this.fieldLabel
15366 // Roo.log(" no label && no align");
15373 ['xs','sm','md','lg'].map(function(size){
15374 if (settings[size]) {
15375 cfg.cls += ' col-' + size + '-' + settings[size];
15383 _initEventsCalled : false,
15386 initEvents: function()
15388 if (this._initEventsCalled) { // as we call render... prevent looping...
15391 this._initEventsCalled = true;
15394 throw "can not find store for combo";
15397 this.indicator = this.indicatorEl();
15399 this.store = Roo.factory(this.store, Roo.data);
15400 this.store.parent = this;
15402 // if we are building from html. then this element is so complex, that we can not really
15403 // use the rendered HTML.
15404 // so we have to trash and replace the previous code.
15405 if (Roo.XComponent.build_from_html) {
15406 // remove this element....
15407 var e = this.el.dom, k=0;
15408 while (e ) { e = e.previousSibling; ++k;}
15413 this.rendered = false;
15415 this.render(this.parent().getChildContainer(true), k);
15418 if(Roo.isIOS && this.useNativeIOS){
15419 this.initIOSView();
15427 if(Roo.isTouch && this.mobileTouchView){
15428 this.initTouchView();
15433 this.initTickableEvents();
15437 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15439 if(this.hiddenName){
15441 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15443 this.hiddenField.dom.value =
15444 this.hiddenValue !== undefined ? this.hiddenValue :
15445 this.value !== undefined ? this.value : '';
15447 // prevent input submission
15448 this.el.dom.removeAttribute('name');
15449 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15454 // this.el.dom.setAttribute('autocomplete', 'off');
15457 var cls = 'x-combo-list';
15459 //this.list = new Roo.Layer({
15460 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15466 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15467 _this.list.setWidth(lw);
15470 this.list.on('mouseover', this.onViewOver, this);
15471 this.list.on('mousemove', this.onViewMove, this);
15472 this.list.on('scroll', this.onViewScroll, this);
15475 this.list.swallowEvent('mousewheel');
15476 this.assetHeight = 0;
15479 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15480 this.assetHeight += this.header.getHeight();
15483 this.innerList = this.list.createChild({cls:cls+'-inner'});
15484 this.innerList.on('mouseover', this.onViewOver, this);
15485 this.innerList.on('mousemove', this.onViewMove, this);
15486 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15488 if(this.allowBlank && !this.pageSize && !this.disableClear){
15489 this.footer = this.list.createChild({cls:cls+'-ft'});
15490 this.pageTb = new Roo.Toolbar(this.footer);
15494 this.footer = this.list.createChild({cls:cls+'-ft'});
15495 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15496 {pageSize: this.pageSize});
15500 if (this.pageTb && this.allowBlank && !this.disableClear) {
15502 this.pageTb.add(new Roo.Toolbar.Fill(), {
15503 cls: 'x-btn-icon x-btn-clear',
15505 handler: function()
15508 _this.clearValue();
15509 _this.onSelect(false, -1);
15514 this.assetHeight += this.footer.getHeight();
15519 this.tpl = Roo.bootstrap.version == 4 ?
15520 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15521 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15524 this.view = new Roo.View(this.list, this.tpl, {
15525 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15527 //this.view.wrapEl.setDisplayed(false);
15528 this.view.on('click', this.onViewClick, this);
15531 this.store.on('beforeload', this.onBeforeLoad, this);
15532 this.store.on('load', this.onLoad, this);
15533 this.store.on('loadexception', this.onLoadException, this);
15535 if(this.resizable){
15536 this.resizer = new Roo.Resizable(this.list, {
15537 pinned:true, handles:'se'
15539 this.resizer.on('resize', function(r, w, h){
15540 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15541 this.listWidth = w;
15542 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15543 this.restrictHeight();
15545 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15548 if(!this.editable){
15549 this.editable = true;
15550 this.setEditable(false);
15555 if (typeof(this.events.add.listeners) != 'undefined') {
15557 this.addicon = this.wrap.createChild(
15558 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15560 this.addicon.on('click', function(e) {
15561 this.fireEvent('add', this);
15564 if (typeof(this.events.edit.listeners) != 'undefined') {
15566 this.editicon = this.wrap.createChild(
15567 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15568 if (this.addicon) {
15569 this.editicon.setStyle('margin-left', '40px');
15571 this.editicon.on('click', function(e) {
15573 // we fire even if inothing is selected..
15574 this.fireEvent('edit', this, this.lastData );
15580 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15581 "up" : function(e){
15582 this.inKeyMode = true;
15586 "down" : function(e){
15587 if(!this.isExpanded()){
15588 this.onTriggerClick();
15590 this.inKeyMode = true;
15595 "enter" : function(e){
15596 // this.onViewClick();
15600 if(this.fireEvent("specialkey", this, e)){
15601 this.onViewClick(false);
15607 "esc" : function(e){
15611 "tab" : function(e){
15614 if(this.fireEvent("specialkey", this, e)){
15615 this.onViewClick(false);
15623 doRelay : function(foo, bar, hname){
15624 if(hname == 'down' || this.scope.isExpanded()){
15625 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15634 this.queryDelay = Math.max(this.queryDelay || 10,
15635 this.mode == 'local' ? 10 : 250);
15638 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15640 if(this.typeAhead){
15641 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15643 if(this.editable !== false){
15644 this.inputEl().on("keyup", this.onKeyUp, this);
15646 if(this.forceSelection){
15647 this.inputEl().on('blur', this.doForce, this);
15651 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15652 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15656 initTickableEvents: function()
15660 if(this.hiddenName){
15662 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15664 this.hiddenField.dom.value =
15665 this.hiddenValue !== undefined ? this.hiddenValue :
15666 this.value !== undefined ? this.value : '';
15668 // prevent input submission
15669 this.el.dom.removeAttribute('name');
15670 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15675 // this.list = this.el.select('ul.dropdown-menu',true).first();
15677 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15678 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15679 if(this.triggerList){
15680 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15683 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15684 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15686 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15687 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15689 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15690 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15692 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15693 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15694 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15697 this.cancelBtn.hide();
15702 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15703 _this.list.setWidth(lw);
15706 this.list.on('mouseover', this.onViewOver, this);
15707 this.list.on('mousemove', this.onViewMove, this);
15709 this.list.on('scroll', this.onViewScroll, this);
15712 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15713 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15716 this.view = new Roo.View(this.list, this.tpl, {
15721 selectedClass: this.selectedClass
15724 //this.view.wrapEl.setDisplayed(false);
15725 this.view.on('click', this.onViewClick, this);
15729 this.store.on('beforeload', this.onBeforeLoad, this);
15730 this.store.on('load', this.onLoad, this);
15731 this.store.on('loadexception', this.onLoadException, this);
15734 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15735 "up" : function(e){
15736 this.inKeyMode = true;
15740 "down" : function(e){
15741 this.inKeyMode = true;
15745 "enter" : function(e){
15746 if(this.fireEvent("specialkey", this, e)){
15747 this.onViewClick(false);
15753 "esc" : function(e){
15754 this.onTickableFooterButtonClick(e, false, false);
15757 "tab" : function(e){
15758 this.fireEvent("specialkey", this, e);
15760 this.onTickableFooterButtonClick(e, false, false);
15767 doRelay : function(e, fn, key){
15768 if(this.scope.isExpanded()){
15769 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15778 this.queryDelay = Math.max(this.queryDelay || 10,
15779 this.mode == 'local' ? 10 : 250);
15782 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15784 if(this.typeAhead){
15785 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15788 if(this.editable !== false){
15789 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15792 this.indicator = this.indicatorEl();
15794 if(this.indicator){
15795 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15796 this.indicator.hide();
15801 onDestroy : function(){
15803 this.view.setStore(null);
15804 this.view.el.removeAllListeners();
15805 this.view.el.remove();
15806 this.view.purgeListeners();
15809 this.list.dom.innerHTML = '';
15813 this.store.un('beforeload', this.onBeforeLoad, this);
15814 this.store.un('load', this.onLoad, this);
15815 this.store.un('loadexception', this.onLoadException, this);
15817 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15821 fireKey : function(e){
15822 if(e.isNavKeyPress() && !this.list.isVisible()){
15823 this.fireEvent("specialkey", this, e);
15828 onResize: function(w, h)
15832 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15834 // if(typeof w != 'number'){
15835 // // we do not handle it!?!?
15838 // var tw = this.trigger.getWidth();
15839 // // tw += this.addicon ? this.addicon.getWidth() : 0;
15840 // // tw += this.editicon ? this.editicon.getWidth() : 0;
15842 // this.inputEl().setWidth( this.adjustWidth('input', x));
15844 // //this.trigger.setStyle('left', x+'px');
15846 // if(this.list && this.listWidth === undefined){
15847 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15848 // this.list.setWidth(lw);
15849 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15857 * Allow or prevent the user from directly editing the field text. If false is passed,
15858 * the user will only be able to select from the items defined in the dropdown list. This method
15859 * is the runtime equivalent of setting the 'editable' config option at config time.
15860 * @param {Boolean} value True to allow the user to directly edit the field text
15862 setEditable : function(value){
15863 if(value == this.editable){
15866 this.editable = value;
15868 this.inputEl().dom.setAttribute('readOnly', true);
15869 this.inputEl().on('mousedown', this.onTriggerClick, this);
15870 this.inputEl().addClass('x-combo-noedit');
15872 this.inputEl().dom.setAttribute('readOnly', false);
15873 this.inputEl().un('mousedown', this.onTriggerClick, this);
15874 this.inputEl().removeClass('x-combo-noedit');
15880 onBeforeLoad : function(combo,opts){
15881 if(!this.hasFocus){
15885 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15887 this.restrictHeight();
15888 this.selectedIndex = -1;
15892 onLoad : function(){
15894 this.hasQuery = false;
15896 if(!this.hasFocus){
15900 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15901 this.loading.hide();
15904 if(this.store.getCount() > 0){
15907 this.restrictHeight();
15908 if(this.lastQuery == this.allQuery){
15909 if(this.editable && !this.tickable){
15910 this.inputEl().dom.select();
15914 !this.selectByValue(this.value, true) &&
15917 !this.store.lastOptions ||
15918 typeof(this.store.lastOptions.add) == 'undefined' ||
15919 this.store.lastOptions.add != true
15922 this.select(0, true);
15925 if(this.autoFocus){
15928 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15929 this.taTask.delay(this.typeAheadDelay);
15933 this.onEmptyResults();
15939 onLoadException : function()
15941 this.hasQuery = false;
15943 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15944 this.loading.hide();
15947 if(this.tickable && this.editable){
15952 // only causes errors at present
15953 //Roo.log(this.store.reader.jsonData);
15954 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15956 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15962 onTypeAhead : function(){
15963 if(this.store.getCount() > 0){
15964 var r = this.store.getAt(0);
15965 var newValue = r.data[this.displayField];
15966 var len = newValue.length;
15967 var selStart = this.getRawValue().length;
15969 if(selStart != len){
15970 this.setRawValue(newValue);
15971 this.selectText(selStart, newValue.length);
15977 onSelect : function(record, index){
15979 if(this.fireEvent('beforeselect', this, record, index) !== false){
15981 this.setFromData(index > -1 ? record.data : false);
15984 this.fireEvent('select', this, record, index);
15989 * Returns the currently selected field value or empty string if no value is set.
15990 * @return {String} value The selected value
15992 getValue : function()
15994 if(Roo.isIOS && this.useNativeIOS){
15995 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15999 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16002 if(this.valueField){
16003 return typeof this.value != 'undefined' ? this.value : '';
16005 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16009 getRawValue : function()
16011 if(Roo.isIOS && this.useNativeIOS){
16012 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16015 var v = this.inputEl().getValue();
16021 * Clears any text/value currently set in the field
16023 clearValue : function(){
16025 if(this.hiddenField){
16026 this.hiddenField.dom.value = '';
16029 this.setRawValue('');
16030 this.lastSelectionText = '';
16031 this.lastData = false;
16033 var close = this.closeTriggerEl();
16044 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16045 * will be displayed in the field. If the value does not match the data value of an existing item,
16046 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16047 * Otherwise the field will be blank (although the value will still be set).
16048 * @param {String} value The value to match
16050 setValue : function(v)
16052 if(Roo.isIOS && this.useNativeIOS){
16053 this.setIOSValue(v);
16063 if(this.valueField){
16064 var r = this.findRecord(this.valueField, v);
16066 text = r.data[this.displayField];
16067 }else if(this.valueNotFoundText !== undefined){
16068 text = this.valueNotFoundText;
16071 this.lastSelectionText = text;
16072 if(this.hiddenField){
16073 this.hiddenField.dom.value = v;
16075 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16078 var close = this.closeTriggerEl();
16081 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16087 * @property {Object} the last set data for the element
16092 * Sets the value of the field based on a object which is related to the record format for the store.
16093 * @param {Object} value the value to set as. or false on reset?
16095 setFromData : function(o){
16102 var dv = ''; // display value
16103 var vv = ''; // value value..
16105 if (this.displayField) {
16106 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16108 // this is an error condition!!!
16109 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16112 if(this.valueField){
16113 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16116 var close = this.closeTriggerEl();
16119 if(dv.length || vv * 1 > 0){
16121 this.blockFocus=true;
16127 if(this.hiddenField){
16128 this.hiddenField.dom.value = vv;
16130 this.lastSelectionText = dv;
16131 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16135 // no hidden field.. - we store the value in 'value', but still display
16136 // display field!!!!
16137 this.lastSelectionText = dv;
16138 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16145 reset : function(){
16146 // overridden so that last data is reset..
16153 this.setValue(this.originalValue);
16154 //this.clearInvalid();
16155 this.lastData = false;
16157 this.view.clearSelections();
16163 findRecord : function(prop, value){
16165 if(this.store.getCount() > 0){
16166 this.store.each(function(r){
16167 if(r.data[prop] == value){
16177 getName: function()
16179 // returns hidden if it's set..
16180 if (!this.rendered) {return ''};
16181 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16185 onViewMove : function(e, t){
16186 this.inKeyMode = false;
16190 onViewOver : function(e, t){
16191 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16194 var item = this.view.findItemFromChild(t);
16197 var index = this.view.indexOf(item);
16198 this.select(index, false);
16203 onViewClick : function(view, doFocus, el, e)
16205 var index = this.view.getSelectedIndexes()[0];
16207 var r = this.store.getAt(index);
16211 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16218 Roo.each(this.tickItems, function(v,k){
16220 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16222 _this.tickItems.splice(k, 1);
16224 if(typeof(e) == 'undefined' && view == false){
16225 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16237 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16238 this.tickItems.push(r.data);
16241 if(typeof(e) == 'undefined' && view == false){
16242 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16249 this.onSelect(r, index);
16251 if(doFocus !== false && !this.blockFocus){
16252 this.inputEl().focus();
16257 restrictHeight : function(){
16258 //this.innerList.dom.style.height = '';
16259 //var inner = this.innerList.dom;
16260 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16261 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16262 //this.list.beginUpdate();
16263 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16264 this.list.alignTo(this.inputEl(), this.listAlign);
16265 this.list.alignTo(this.inputEl(), this.listAlign);
16266 //this.list.endUpdate();
16270 onEmptyResults : function(){
16272 if(this.tickable && this.editable){
16273 this.hasFocus = false;
16274 this.restrictHeight();
16282 * Returns true if the dropdown list is expanded, else false.
16284 isExpanded : function(){
16285 return this.list.isVisible();
16289 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16290 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16291 * @param {String} value The data value of the item to select
16292 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16293 * selected item if it is not currently in view (defaults to true)
16294 * @return {Boolean} True if the value matched an item in the list, else false
16296 selectByValue : function(v, scrollIntoView){
16297 if(v !== undefined && v !== null){
16298 var r = this.findRecord(this.valueField || this.displayField, v);
16300 this.select(this.store.indexOf(r), scrollIntoView);
16308 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16309 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16310 * @param {Number} index The zero-based index of the list item to select
16311 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16312 * selected item if it is not currently in view (defaults to true)
16314 select : function(index, scrollIntoView){
16315 this.selectedIndex = index;
16316 this.view.select(index);
16317 if(scrollIntoView !== false){
16318 var el = this.view.getNode(index);
16320 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16323 this.list.scrollChildIntoView(el, false);
16329 selectNext : function(){
16330 var ct = this.store.getCount();
16332 if(this.selectedIndex == -1){
16334 }else if(this.selectedIndex < ct-1){
16335 this.select(this.selectedIndex+1);
16341 selectPrev : function(){
16342 var ct = this.store.getCount();
16344 if(this.selectedIndex == -1){
16346 }else if(this.selectedIndex != 0){
16347 this.select(this.selectedIndex-1);
16353 onKeyUp : function(e){
16354 if(this.editable !== false && !e.isSpecialKey()){
16355 this.lastKey = e.getKey();
16356 this.dqTask.delay(this.queryDelay);
16361 validateBlur : function(){
16362 return !this.list || !this.list.isVisible();
16366 initQuery : function(){
16368 var v = this.getRawValue();
16370 if(this.tickable && this.editable){
16371 v = this.tickableInputEl().getValue();
16378 doForce : function(){
16379 if(this.inputEl().dom.value.length > 0){
16380 this.inputEl().dom.value =
16381 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16387 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16388 * query allowing the query action to be canceled if needed.
16389 * @param {String} query The SQL query to execute
16390 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16391 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16392 * saved in the current store (defaults to false)
16394 doQuery : function(q, forceAll){
16396 if(q === undefined || q === null){
16401 forceAll: forceAll,
16405 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16410 forceAll = qe.forceAll;
16411 if(forceAll === true || (q.length >= this.minChars)){
16413 this.hasQuery = true;
16415 if(this.lastQuery != q || this.alwaysQuery){
16416 this.lastQuery = q;
16417 if(this.mode == 'local'){
16418 this.selectedIndex = -1;
16420 this.store.clearFilter();
16423 if(this.specialFilter){
16424 this.fireEvent('specialfilter', this);
16429 this.store.filter(this.displayField, q);
16432 this.store.fireEvent("datachanged", this.store);
16439 this.store.baseParams[this.queryParam] = q;
16441 var options = {params : this.getParams(q)};
16444 options.add = true;
16445 options.params.start = this.page * this.pageSize;
16448 this.store.load(options);
16451 * this code will make the page width larger, at the beginning, the list not align correctly,
16452 * we should expand the list on onLoad
16453 * so command out it
16458 this.selectedIndex = -1;
16463 this.loadNext = false;
16467 getParams : function(q){
16469 //p[this.queryParam] = q;
16473 p.limit = this.pageSize;
16479 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16481 collapse : function(){
16482 if(!this.isExpanded()){
16488 this.hasFocus = false;
16492 this.cancelBtn.hide();
16493 this.trigger.show();
16496 this.tickableInputEl().dom.value = '';
16497 this.tickableInputEl().blur();
16502 Roo.get(document).un('mousedown', this.collapseIf, this);
16503 Roo.get(document).un('mousewheel', this.collapseIf, this);
16504 if (!this.editable) {
16505 Roo.get(document).un('keydown', this.listKeyPress, this);
16507 this.fireEvent('collapse', this);
16513 collapseIf : function(e){
16514 var in_combo = e.within(this.el);
16515 var in_list = e.within(this.list);
16516 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16518 if (in_combo || in_list || is_list) {
16519 //e.stopPropagation();
16524 this.onTickableFooterButtonClick(e, false, false);
16532 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16534 expand : function(){
16536 if(this.isExpanded() || !this.hasFocus){
16540 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16541 this.list.setWidth(lw);
16547 this.restrictHeight();
16551 this.tickItems = Roo.apply([], this.item);
16554 this.cancelBtn.show();
16555 this.trigger.hide();
16558 this.tickableInputEl().focus();
16563 Roo.get(document).on('mousedown', this.collapseIf, this);
16564 Roo.get(document).on('mousewheel', this.collapseIf, this);
16565 if (!this.editable) {
16566 Roo.get(document).on('keydown', this.listKeyPress, this);
16569 this.fireEvent('expand', this);
16573 // Implements the default empty TriggerField.onTriggerClick function
16574 onTriggerClick : function(e)
16576 Roo.log('trigger click');
16578 if(this.disabled || !this.triggerList){
16583 this.loadNext = false;
16585 if(this.isExpanded()){
16587 if (!this.blockFocus) {
16588 this.inputEl().focus();
16592 this.hasFocus = true;
16593 if(this.triggerAction == 'all') {
16594 this.doQuery(this.allQuery, true);
16596 this.doQuery(this.getRawValue());
16598 if (!this.blockFocus) {
16599 this.inputEl().focus();
16604 onTickableTriggerClick : function(e)
16611 this.loadNext = false;
16612 this.hasFocus = true;
16614 if(this.triggerAction == 'all') {
16615 this.doQuery(this.allQuery, true);
16617 this.doQuery(this.getRawValue());
16621 onSearchFieldClick : function(e)
16623 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16624 this.onTickableFooterButtonClick(e, false, false);
16628 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16633 this.loadNext = false;
16634 this.hasFocus = true;
16636 if(this.triggerAction == 'all') {
16637 this.doQuery(this.allQuery, true);
16639 this.doQuery(this.getRawValue());
16643 listKeyPress : function(e)
16645 //Roo.log('listkeypress');
16646 // scroll to first matching element based on key pres..
16647 if (e.isSpecialKey()) {
16650 var k = String.fromCharCode(e.getKey()).toUpperCase();
16653 var csel = this.view.getSelectedNodes();
16654 var cselitem = false;
16656 var ix = this.view.indexOf(csel[0]);
16657 cselitem = this.store.getAt(ix);
16658 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16664 this.store.each(function(v) {
16666 // start at existing selection.
16667 if (cselitem.id == v.id) {
16673 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16674 match = this.store.indexOf(v);
16680 if (match === false) {
16681 return true; // no more action?
16684 this.view.select(match);
16685 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16686 sn.scrollIntoView(sn.dom.parentNode, false);
16689 onViewScroll : function(e, t){
16691 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){
16695 this.hasQuery = true;
16697 this.loading = this.list.select('.loading', true).first();
16699 if(this.loading === null){
16700 this.list.createChild({
16702 cls: 'loading roo-select2-more-results roo-select2-active',
16703 html: 'Loading more results...'
16706 this.loading = this.list.select('.loading', true).first();
16708 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16710 this.loading.hide();
16713 this.loading.show();
16718 this.loadNext = true;
16720 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16725 addItem : function(o)
16727 var dv = ''; // display value
16729 if (this.displayField) {
16730 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16732 // this is an error condition!!!
16733 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16740 var choice = this.choices.createChild({
16742 cls: 'roo-select2-search-choice',
16751 cls: 'roo-select2-search-choice-close fa fa-times',
16756 }, this.searchField);
16758 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16760 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16768 this.inputEl().dom.value = '';
16773 onRemoveItem : function(e, _self, o)
16775 e.preventDefault();
16777 this.lastItem = Roo.apply([], this.item);
16779 var index = this.item.indexOf(o.data) * 1;
16782 Roo.log('not this item?!');
16786 this.item.splice(index, 1);
16791 this.fireEvent('remove', this, e);
16797 syncValue : function()
16799 if(!this.item.length){
16806 Roo.each(this.item, function(i){
16807 if(_this.valueField){
16808 value.push(i[_this.valueField]);
16815 this.value = value.join(',');
16817 if(this.hiddenField){
16818 this.hiddenField.dom.value = this.value;
16821 this.store.fireEvent("datachanged", this.store);
16826 clearItem : function()
16828 if(!this.multiple){
16834 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16842 if(this.tickable && !Roo.isTouch){
16843 this.view.refresh();
16847 inputEl: function ()
16849 if(Roo.isIOS && this.useNativeIOS){
16850 return this.el.select('select.roo-ios-select', true).first();
16853 if(Roo.isTouch && this.mobileTouchView){
16854 return this.el.select('input.form-control',true).first();
16858 return this.searchField;
16861 return this.el.select('input.form-control',true).first();
16864 onTickableFooterButtonClick : function(e, btn, el)
16866 e.preventDefault();
16868 this.lastItem = Roo.apply([], this.item);
16870 if(btn && btn.name == 'cancel'){
16871 this.tickItems = Roo.apply([], this.item);
16880 Roo.each(this.tickItems, function(o){
16888 validate : function()
16890 if(this.getVisibilityEl().hasClass('hidden')){
16894 var v = this.getRawValue();
16897 v = this.getValue();
16900 if(this.disabled || this.allowBlank || v.length){
16905 this.markInvalid();
16909 tickableInputEl : function()
16911 if(!this.tickable || !this.editable){
16912 return this.inputEl();
16915 return this.inputEl().select('.roo-select2-search-field-input', true).first();
16919 getAutoCreateTouchView : function()
16924 cls: 'form-group' //input-group
16930 type : this.inputType,
16931 cls : 'form-control x-combo-noedit',
16932 autocomplete: 'new-password',
16933 placeholder : this.placeholder || '',
16938 input.name = this.name;
16942 input.cls += ' input-' + this.size;
16945 if (this.disabled) {
16946 input.disabled = true;
16950 cls : 'roo-combobox-wrap',
16957 inputblock.cls += ' input-group';
16959 inputblock.cn.unshift({
16961 cls : 'input-group-addon input-group-prepend input-group-text',
16966 if(this.removable && !this.multiple){
16967 inputblock.cls += ' roo-removable';
16969 inputblock.cn.push({
16972 cls : 'roo-combo-removable-btn close'
16976 if(this.hasFeedback && !this.allowBlank){
16978 inputblock.cls += ' has-feedback';
16980 inputblock.cn.push({
16982 cls: 'glyphicon form-control-feedback'
16989 inputblock.cls += (this.before) ? '' : ' input-group';
16991 inputblock.cn.push({
16993 cls : 'input-group-addon input-group-append input-group-text',
16999 var ibwrap = inputblock;
17004 cls: 'roo-select2-choices',
17008 cls: 'roo-select2-search-field',
17021 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17026 cls: 'form-hidden-field'
17032 if(!this.multiple && this.showToggleBtn){
17038 if (this.caret != false) {
17041 cls: 'fa fa-' + this.caret
17048 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17050 Roo.bootstrap.version == 3 ? caret : '',
17053 cls: 'combobox-clear',
17067 combobox.cls += ' roo-select2-container-multi';
17070 var align = this.labelAlign || this.parentLabelAlign();
17072 if (align ==='left' && this.fieldLabel.length) {
17077 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17078 tooltip : 'This field is required'
17082 cls : 'control-label col-form-label',
17083 html : this.fieldLabel
17087 cls : 'roo-combobox-wrap ',
17094 var labelCfg = cfg.cn[1];
17095 var contentCfg = cfg.cn[2];
17098 if(this.indicatorpos == 'right'){
17103 cls : 'control-label col-form-label',
17107 html : this.fieldLabel
17111 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17112 tooltip : 'This field is required'
17117 cls : "roo-combobox-wrap ",
17125 labelCfg = cfg.cn[0];
17126 contentCfg = cfg.cn[1];
17131 if(this.labelWidth > 12){
17132 labelCfg.style = "width: " + this.labelWidth + 'px';
17135 if(this.labelWidth < 13 && this.labelmd == 0){
17136 this.labelmd = this.labelWidth;
17139 if(this.labellg > 0){
17140 labelCfg.cls += ' col-lg-' + this.labellg;
17141 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17144 if(this.labelmd > 0){
17145 labelCfg.cls += ' col-md-' + this.labelmd;
17146 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17149 if(this.labelsm > 0){
17150 labelCfg.cls += ' col-sm-' + this.labelsm;
17151 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17154 if(this.labelxs > 0){
17155 labelCfg.cls += ' col-xs-' + this.labelxs;
17156 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17160 } else if ( this.fieldLabel.length) {
17164 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17165 tooltip : 'This field is required'
17169 cls : 'control-label',
17170 html : this.fieldLabel
17181 if(this.indicatorpos == 'right'){
17185 cls : 'control-label',
17186 html : this.fieldLabel,
17190 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17191 tooltip : 'This field is required'
17208 var settings = this;
17210 ['xs','sm','md','lg'].map(function(size){
17211 if (settings[size]) {
17212 cfg.cls += ' col-' + size + '-' + settings[size];
17219 initTouchView : function()
17221 this.renderTouchView();
17223 this.touchViewEl.on('scroll', function(){
17224 this.el.dom.scrollTop = 0;
17227 this.originalValue = this.getValue();
17229 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17231 this.inputEl().on("click", this.showTouchView, this);
17232 if (this.triggerEl) {
17233 this.triggerEl.on("click", this.showTouchView, this);
17237 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17238 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17240 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17242 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17243 this.store.on('load', this.onTouchViewLoad, this);
17244 this.store.on('loadexception', this.onTouchViewLoadException, this);
17246 if(this.hiddenName){
17248 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17250 this.hiddenField.dom.value =
17251 this.hiddenValue !== undefined ? this.hiddenValue :
17252 this.value !== undefined ? this.value : '';
17254 this.el.dom.removeAttribute('name');
17255 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17259 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17260 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17263 if(this.removable && !this.multiple){
17264 var close = this.closeTriggerEl();
17266 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17267 close.on('click', this.removeBtnClick, this, close);
17271 * fix the bug in Safari iOS8
17273 this.inputEl().on("focus", function(e){
17274 document.activeElement.blur();
17277 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17284 renderTouchView : function()
17286 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17287 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17289 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17290 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17292 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17293 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17294 this.touchViewBodyEl.setStyle('overflow', 'auto');
17296 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17297 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17299 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17300 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17304 showTouchView : function()
17310 this.touchViewHeaderEl.hide();
17312 if(this.modalTitle.length){
17313 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17314 this.touchViewHeaderEl.show();
17317 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17318 this.touchViewEl.show();
17320 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17322 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17323 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17325 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17327 if(this.modalTitle.length){
17328 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17331 this.touchViewBodyEl.setHeight(bodyHeight);
17335 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17337 this.touchViewEl.addClass(['in','show']);
17340 if(this._touchViewMask){
17341 Roo.get(document.body).addClass("x-body-masked");
17342 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17343 this._touchViewMask.setStyle('z-index', 10000);
17344 this._touchViewMask.addClass('show');
17347 this.doTouchViewQuery();
17351 hideTouchView : function()
17353 this.touchViewEl.removeClass(['in','show']);
17357 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17359 this.touchViewEl.setStyle('display', 'none');
17362 if(this._touchViewMask){
17363 this._touchViewMask.removeClass('show');
17364 Roo.get(document.body).removeClass("x-body-masked");
17368 setTouchViewValue : function()
17375 Roo.each(this.tickItems, function(o){
17380 this.hideTouchView();
17383 doTouchViewQuery : function()
17392 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17396 if(!this.alwaysQuery || this.mode == 'local'){
17397 this.onTouchViewLoad();
17404 onTouchViewBeforeLoad : function(combo,opts)
17410 onTouchViewLoad : function()
17412 if(this.store.getCount() < 1){
17413 this.onTouchViewEmptyResults();
17417 this.clearTouchView();
17419 var rawValue = this.getRawValue();
17421 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17423 this.tickItems = [];
17425 this.store.data.each(function(d, rowIndex){
17426 var row = this.touchViewListGroup.createChild(template);
17428 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17429 row.addClass(d.data.cls);
17432 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17435 html : d.data[this.displayField]
17438 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17439 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17442 row.removeClass('selected');
17443 if(!this.multiple && this.valueField &&
17444 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17447 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17448 row.addClass('selected');
17451 if(this.multiple && this.valueField &&
17452 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17456 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17457 this.tickItems.push(d.data);
17460 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17464 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17466 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17468 if(this.modalTitle.length){
17469 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17472 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17474 if(this.mobile_restrict_height && listHeight < bodyHeight){
17475 this.touchViewBodyEl.setHeight(listHeight);
17480 if(firstChecked && listHeight > bodyHeight){
17481 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17486 onTouchViewLoadException : function()
17488 this.hideTouchView();
17491 onTouchViewEmptyResults : function()
17493 this.clearTouchView();
17495 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17497 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17501 clearTouchView : function()
17503 this.touchViewListGroup.dom.innerHTML = '';
17506 onTouchViewClick : function(e, el, o)
17508 e.preventDefault();
17511 var rowIndex = o.rowIndex;
17513 var r = this.store.getAt(rowIndex);
17515 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17517 if(!this.multiple){
17518 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17519 c.dom.removeAttribute('checked');
17522 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17524 this.setFromData(r.data);
17526 var close = this.closeTriggerEl();
17532 this.hideTouchView();
17534 this.fireEvent('select', this, r, rowIndex);
17539 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17540 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17541 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17545 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17546 this.addItem(r.data);
17547 this.tickItems.push(r.data);
17551 getAutoCreateNativeIOS : function()
17554 cls: 'form-group' //input-group,
17559 cls : 'roo-ios-select'
17563 combobox.name = this.name;
17566 if (this.disabled) {
17567 combobox.disabled = true;
17570 var settings = this;
17572 ['xs','sm','md','lg'].map(function(size){
17573 if (settings[size]) {
17574 cfg.cls += ' col-' + size + '-' + settings[size];
17584 initIOSView : function()
17586 this.store.on('load', this.onIOSViewLoad, this);
17591 onIOSViewLoad : function()
17593 if(this.store.getCount() < 1){
17597 this.clearIOSView();
17599 if(this.allowBlank) {
17601 var default_text = '-- SELECT --';
17603 if(this.placeholder.length){
17604 default_text = this.placeholder;
17607 if(this.emptyTitle.length){
17608 default_text += ' - ' + this.emptyTitle + ' -';
17611 var opt = this.inputEl().createChild({
17614 html : default_text
17618 o[this.valueField] = 0;
17619 o[this.displayField] = default_text;
17621 this.ios_options.push({
17628 this.store.data.each(function(d, rowIndex){
17632 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17633 html = d.data[this.displayField];
17638 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17639 value = d.data[this.valueField];
17648 if(this.value == d.data[this.valueField]){
17649 option['selected'] = true;
17652 var opt = this.inputEl().createChild(option);
17654 this.ios_options.push({
17661 this.inputEl().on('change', function(){
17662 this.fireEvent('select', this);
17667 clearIOSView: function()
17669 this.inputEl().dom.innerHTML = '';
17671 this.ios_options = [];
17674 setIOSValue: function(v)
17678 if(!this.ios_options){
17682 Roo.each(this.ios_options, function(opts){
17684 opts.el.dom.removeAttribute('selected');
17686 if(opts.data[this.valueField] != v){
17690 opts.el.dom.setAttribute('selected', true);
17696 * @cfg {Boolean} grow
17700 * @cfg {Number} growMin
17704 * @cfg {Number} growMax
17713 Roo.apply(Roo.bootstrap.ComboBox, {
17717 cls: 'modal-header',
17739 cls: 'list-group-item',
17743 cls: 'roo-combobox-list-group-item-value'
17747 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17761 listItemCheckbox : {
17763 cls: 'list-group-item',
17767 cls: 'roo-combobox-list-group-item-value'
17771 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17787 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17792 cls: 'modal-footer',
17800 cls: 'col-xs-6 text-left',
17803 cls: 'btn btn-danger roo-touch-view-cancel',
17809 cls: 'col-xs-6 text-right',
17812 cls: 'btn btn-success roo-touch-view-ok',
17823 Roo.apply(Roo.bootstrap.ComboBox, {
17825 touchViewTemplate : {
17827 cls: 'modal fade roo-combobox-touch-view',
17831 cls: 'modal-dialog',
17832 style : 'position:fixed', // we have to fix position....
17836 cls: 'modal-content',
17838 Roo.bootstrap.ComboBox.header,
17839 Roo.bootstrap.ComboBox.body,
17840 Roo.bootstrap.ComboBox.footer
17849 * Ext JS Library 1.1.1
17850 * Copyright(c) 2006-2007, Ext JS, LLC.
17852 * Originally Released Under LGPL - original licence link has changed is not relivant.
17855 * <script type="text/javascript">
17860 * @extends Roo.util.Observable
17861 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
17862 * This class also supports single and multi selection modes. <br>
17863 * Create a data model bound view:
17865 var store = new Roo.data.Store(...);
17867 var view = new Roo.View({
17869 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
17871 singleSelect: true,
17872 selectedClass: "ydataview-selected",
17876 // listen for node click?
17877 view.on("click", function(vw, index, node, e){
17878 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17882 dataModel.load("foobar.xml");
17884 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17886 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17887 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17889 * Note: old style constructor is still suported (container, template, config)
17892 * Create a new View
17893 * @param {Object} config The config object
17896 Roo.View = function(config, depreciated_tpl, depreciated_config){
17898 this.parent = false;
17900 if (typeof(depreciated_tpl) == 'undefined') {
17901 // new way.. - universal constructor.
17902 Roo.apply(this, config);
17903 this.el = Roo.get(this.el);
17906 this.el = Roo.get(config);
17907 this.tpl = depreciated_tpl;
17908 Roo.apply(this, depreciated_config);
17910 this.wrapEl = this.el.wrap().wrap();
17911 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17914 if(typeof(this.tpl) == "string"){
17915 this.tpl = new Roo.Template(this.tpl);
17917 // support xtype ctors..
17918 this.tpl = new Roo.factory(this.tpl, Roo);
17922 this.tpl.compile();
17927 * @event beforeclick
17928 * Fires before a click is processed. Returns false to cancel the default action.
17929 * @param {Roo.View} this
17930 * @param {Number} index The index of the target node
17931 * @param {HTMLElement} node The target node
17932 * @param {Roo.EventObject} e The raw event object
17934 "beforeclick" : true,
17937 * Fires when a template node is clicked.
17938 * @param {Roo.View} this
17939 * @param {Number} index The index of the target node
17940 * @param {HTMLElement} node The target node
17941 * @param {Roo.EventObject} e The raw event object
17946 * Fires when a template node is double clicked.
17947 * @param {Roo.View} this
17948 * @param {Number} index The index of the target node
17949 * @param {HTMLElement} node The target node
17950 * @param {Roo.EventObject} e The raw event object
17954 * @event contextmenu
17955 * Fires when a template node is right clicked.
17956 * @param {Roo.View} this
17957 * @param {Number} index The index of the target node
17958 * @param {HTMLElement} node The target node
17959 * @param {Roo.EventObject} e The raw event object
17961 "contextmenu" : true,
17963 * @event selectionchange
17964 * Fires when the selected nodes change.
17965 * @param {Roo.View} this
17966 * @param {Array} selections Array of the selected nodes
17968 "selectionchange" : true,
17971 * @event beforeselect
17972 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17973 * @param {Roo.View} this
17974 * @param {HTMLElement} node The node to be selected
17975 * @param {Array} selections Array of currently selected nodes
17977 "beforeselect" : true,
17979 * @event preparedata
17980 * Fires on every row to render, to allow you to change the data.
17981 * @param {Roo.View} this
17982 * @param {Object} data to be rendered (change this)
17984 "preparedata" : true
17992 "click": this.onClick,
17993 "dblclick": this.onDblClick,
17994 "contextmenu": this.onContextMenu,
17998 this.selections = [];
18000 this.cmp = new Roo.CompositeElementLite([]);
18002 this.store = Roo.factory(this.store, Roo.data);
18003 this.setStore(this.store, true);
18006 if ( this.footer && this.footer.xtype) {
18008 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18010 this.footer.dataSource = this.store;
18011 this.footer.container = fctr;
18012 this.footer = Roo.factory(this.footer, Roo);
18013 fctr.insertFirst(this.el);
18015 // this is a bit insane - as the paging toolbar seems to detach the el..
18016 // dom.parentNode.parentNode.parentNode
18017 // they get detached?
18021 Roo.View.superclass.constructor.call(this);
18026 Roo.extend(Roo.View, Roo.util.Observable, {
18029 * @cfg {Roo.data.Store} store Data store to load data from.
18034 * @cfg {String|Roo.Element} el The container element.
18039 * @cfg {String|Roo.Template} tpl The template used by this View
18043 * @cfg {String} dataName the named area of the template to use as the data area
18044 * Works with domtemplates roo-name="name"
18048 * @cfg {String} selectedClass The css class to add to selected nodes
18050 selectedClass : "x-view-selected",
18052 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18057 * @cfg {String} text to display on mask (default Loading)
18061 * @cfg {Boolean} multiSelect Allow multiple selection
18063 multiSelect : false,
18065 * @cfg {Boolean} singleSelect Allow single selection
18067 singleSelect: false,
18070 * @cfg {Boolean} toggleSelect - selecting
18072 toggleSelect : false,
18075 * @cfg {Boolean} tickable - selecting
18080 * Returns the element this view is bound to.
18081 * @return {Roo.Element}
18083 getEl : function(){
18084 return this.wrapEl;
18090 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18092 refresh : function(){
18093 //Roo.log('refresh');
18096 // if we are using something like 'domtemplate', then
18097 // the what gets used is:
18098 // t.applySubtemplate(NAME, data, wrapping data..)
18099 // the outer template then get' applied with
18100 // the store 'extra data'
18101 // and the body get's added to the
18102 // roo-name="data" node?
18103 // <span class='roo-tpl-{name}'></span> ?????
18107 this.clearSelections();
18108 this.el.update("");
18110 var records = this.store.getRange();
18111 if(records.length < 1) {
18113 // is this valid?? = should it render a template??
18115 this.el.update(this.emptyText);
18119 if (this.dataName) {
18120 this.el.update(t.apply(this.store.meta)); //????
18121 el = this.el.child('.roo-tpl-' + this.dataName);
18124 for(var i = 0, len = records.length; i < len; i++){
18125 var data = this.prepareData(records[i].data, i, records[i]);
18126 this.fireEvent("preparedata", this, data, i, records[i]);
18128 var d = Roo.apply({}, data);
18131 Roo.apply(d, {'roo-id' : Roo.id()});
18135 Roo.each(this.parent.item, function(item){
18136 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18139 Roo.apply(d, {'roo-data-checked' : 'checked'});
18143 html[html.length] = Roo.util.Format.trim(
18145 t.applySubtemplate(this.dataName, d, this.store.meta) :
18152 el.update(html.join(""));
18153 this.nodes = el.dom.childNodes;
18154 this.updateIndexes(0);
18159 * Function to override to reformat the data that is sent to
18160 * the template for each node.
18161 * DEPRICATED - use the preparedata event handler.
18162 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18163 * a JSON object for an UpdateManager bound view).
18165 prepareData : function(data, index, record)
18167 this.fireEvent("preparedata", this, data, index, record);
18171 onUpdate : function(ds, record){
18172 // Roo.log('on update');
18173 this.clearSelections();
18174 var index = this.store.indexOf(record);
18175 var n = this.nodes[index];
18176 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18177 n.parentNode.removeChild(n);
18178 this.updateIndexes(index, index);
18184 onAdd : function(ds, records, index)
18186 //Roo.log(['on Add', ds, records, index] );
18187 this.clearSelections();
18188 if(this.nodes.length == 0){
18192 var n = this.nodes[index];
18193 for(var i = 0, len = records.length; i < len; i++){
18194 var d = this.prepareData(records[i].data, i, records[i]);
18196 this.tpl.insertBefore(n, d);
18199 this.tpl.append(this.el, d);
18202 this.updateIndexes(index);
18205 onRemove : function(ds, record, index){
18206 // Roo.log('onRemove');
18207 this.clearSelections();
18208 var el = this.dataName ?
18209 this.el.child('.roo-tpl-' + this.dataName) :
18212 el.dom.removeChild(this.nodes[index]);
18213 this.updateIndexes(index);
18217 * Refresh an individual node.
18218 * @param {Number} index
18220 refreshNode : function(index){
18221 this.onUpdate(this.store, this.store.getAt(index));
18224 updateIndexes : function(startIndex, endIndex){
18225 var ns = this.nodes;
18226 startIndex = startIndex || 0;
18227 endIndex = endIndex || ns.length - 1;
18228 for(var i = startIndex; i <= endIndex; i++){
18229 ns[i].nodeIndex = i;
18234 * Changes the data store this view uses and refresh the view.
18235 * @param {Store} store
18237 setStore : function(store, initial){
18238 if(!initial && this.store){
18239 this.store.un("datachanged", this.refresh);
18240 this.store.un("add", this.onAdd);
18241 this.store.un("remove", this.onRemove);
18242 this.store.un("update", this.onUpdate);
18243 this.store.un("clear", this.refresh);
18244 this.store.un("beforeload", this.onBeforeLoad);
18245 this.store.un("load", this.onLoad);
18246 this.store.un("loadexception", this.onLoad);
18250 store.on("datachanged", this.refresh, this);
18251 store.on("add", this.onAdd, this);
18252 store.on("remove", this.onRemove, this);
18253 store.on("update", this.onUpdate, this);
18254 store.on("clear", this.refresh, this);
18255 store.on("beforeload", this.onBeforeLoad, this);
18256 store.on("load", this.onLoad, this);
18257 store.on("loadexception", this.onLoad, this);
18265 * onbeforeLoad - masks the loading area.
18268 onBeforeLoad : function(store,opts)
18270 //Roo.log('onBeforeLoad');
18272 this.el.update("");
18274 this.el.mask(this.mask ? this.mask : "Loading" );
18276 onLoad : function ()
18283 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18284 * @param {HTMLElement} node
18285 * @return {HTMLElement} The template node
18287 findItemFromChild : function(node){
18288 var el = this.dataName ?
18289 this.el.child('.roo-tpl-' + this.dataName,true) :
18292 if(!node || node.parentNode == el){
18295 var p = node.parentNode;
18296 while(p && p != el){
18297 if(p.parentNode == el){
18306 onClick : function(e){
18307 var item = this.findItemFromChild(e.getTarget());
18309 var index = this.indexOf(item);
18310 if(this.onItemClick(item, index, e) !== false){
18311 this.fireEvent("click", this, index, item, e);
18314 this.clearSelections();
18319 onContextMenu : function(e){
18320 var item = this.findItemFromChild(e.getTarget());
18322 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18327 onDblClick : function(e){
18328 var item = this.findItemFromChild(e.getTarget());
18330 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18334 onItemClick : function(item, index, e)
18336 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18339 if (this.toggleSelect) {
18340 var m = this.isSelected(item) ? 'unselect' : 'select';
18343 _t[m](item, true, false);
18346 if(this.multiSelect || this.singleSelect){
18347 if(this.multiSelect && e.shiftKey && this.lastSelection){
18348 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18350 this.select(item, this.multiSelect && e.ctrlKey);
18351 this.lastSelection = item;
18354 if(!this.tickable){
18355 e.preventDefault();
18363 * Get the number of selected nodes.
18366 getSelectionCount : function(){
18367 return this.selections.length;
18371 * Get the currently selected nodes.
18372 * @return {Array} An array of HTMLElements
18374 getSelectedNodes : function(){
18375 return this.selections;
18379 * Get the indexes of the selected nodes.
18382 getSelectedIndexes : function(){
18383 var indexes = [], s = this.selections;
18384 for(var i = 0, len = s.length; i < len; i++){
18385 indexes.push(s[i].nodeIndex);
18391 * Clear all selections
18392 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18394 clearSelections : function(suppressEvent){
18395 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18396 this.cmp.elements = this.selections;
18397 this.cmp.removeClass(this.selectedClass);
18398 this.selections = [];
18399 if(!suppressEvent){
18400 this.fireEvent("selectionchange", this, this.selections);
18406 * Returns true if the passed node is selected
18407 * @param {HTMLElement/Number} node The node or node index
18408 * @return {Boolean}
18410 isSelected : function(node){
18411 var s = this.selections;
18415 node = this.getNode(node);
18416 return s.indexOf(node) !== -1;
18421 * @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
18422 * @param {Boolean} keepExisting (optional) true to keep existing selections
18423 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18425 select : function(nodeInfo, keepExisting, suppressEvent){
18426 if(nodeInfo instanceof Array){
18428 this.clearSelections(true);
18430 for(var i = 0, len = nodeInfo.length; i < len; i++){
18431 this.select(nodeInfo[i], true, true);
18435 var node = this.getNode(nodeInfo);
18436 if(!node || this.isSelected(node)){
18437 return; // already selected.
18440 this.clearSelections(true);
18443 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18444 Roo.fly(node).addClass(this.selectedClass);
18445 this.selections.push(node);
18446 if(!suppressEvent){
18447 this.fireEvent("selectionchange", this, this.selections);
18455 * @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
18456 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18457 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18459 unselect : function(nodeInfo, keepExisting, suppressEvent)
18461 if(nodeInfo instanceof Array){
18462 Roo.each(this.selections, function(s) {
18463 this.unselect(s, nodeInfo);
18467 var node = this.getNode(nodeInfo);
18468 if(!node || !this.isSelected(node)){
18469 //Roo.log("not selected");
18470 return; // not selected.
18474 Roo.each(this.selections, function(s) {
18476 Roo.fly(node).removeClass(this.selectedClass);
18483 this.selections= ns;
18484 this.fireEvent("selectionchange", this, this.selections);
18488 * Gets a template node.
18489 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18490 * @return {HTMLElement} The node or null if it wasn't found
18492 getNode : function(nodeInfo){
18493 if(typeof nodeInfo == "string"){
18494 return document.getElementById(nodeInfo);
18495 }else if(typeof nodeInfo == "number"){
18496 return this.nodes[nodeInfo];
18502 * Gets a range template nodes.
18503 * @param {Number} startIndex
18504 * @param {Number} endIndex
18505 * @return {Array} An array of nodes
18507 getNodes : function(start, end){
18508 var ns = this.nodes;
18509 start = start || 0;
18510 end = typeof end == "undefined" ? ns.length - 1 : end;
18513 for(var i = start; i <= end; i++){
18517 for(var i = start; i >= end; i--){
18525 * Finds the index of the passed node
18526 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18527 * @return {Number} The index of the node or -1
18529 indexOf : function(node){
18530 node = this.getNode(node);
18531 if(typeof node.nodeIndex == "number"){
18532 return node.nodeIndex;
18534 var ns = this.nodes;
18535 for(var i = 0, len = ns.length; i < len; i++){
18546 * based on jquery fullcalendar
18550 Roo.bootstrap = Roo.bootstrap || {};
18552 * @class Roo.bootstrap.Calendar
18553 * @extends Roo.bootstrap.Component
18554 * Bootstrap Calendar class
18555 * @cfg {Boolean} loadMask (true|false) default false
18556 * @cfg {Object} header generate the user specific header of the calendar, default false
18559 * Create a new Container
18560 * @param {Object} config The config object
18565 Roo.bootstrap.Calendar = function(config){
18566 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18570 * Fires when a date is selected
18571 * @param {DatePicker} this
18572 * @param {Date} date The selected date
18576 * @event monthchange
18577 * Fires when the displayed month changes
18578 * @param {DatePicker} this
18579 * @param {Date} date The selected month
18581 'monthchange': true,
18583 * @event evententer
18584 * Fires when mouse over an event
18585 * @param {Calendar} this
18586 * @param {event} Event
18588 'evententer': true,
18590 * @event eventleave
18591 * Fires when the mouse leaves an
18592 * @param {Calendar} this
18595 'eventleave': true,
18597 * @event eventclick
18598 * Fires when the mouse click an
18599 * @param {Calendar} this
18608 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18611 * @cfg {Number} startDay
18612 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18620 getAutoCreate : function(){
18623 var fc_button = function(name, corner, style, content ) {
18624 return Roo.apply({},{
18626 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18628 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18631 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18642 style : 'width:100%',
18649 cls : 'fc-header-left',
18651 fc_button('prev', 'left', 'arrow', '‹' ),
18652 fc_button('next', 'right', 'arrow', '›' ),
18653 { tag: 'span', cls: 'fc-header-space' },
18654 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18662 cls : 'fc-header-center',
18666 cls: 'fc-header-title',
18669 html : 'month / year'
18677 cls : 'fc-header-right',
18679 /* fc_button('month', 'left', '', 'month' ),
18680 fc_button('week', '', '', 'week' ),
18681 fc_button('day', 'right', '', 'day' )
18693 header = this.header;
18696 var cal_heads = function() {
18698 // fixme - handle this.
18700 for (var i =0; i < Date.dayNames.length; i++) {
18701 var d = Date.dayNames[i];
18704 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18705 html : d.substring(0,3)
18709 ret[0].cls += ' fc-first';
18710 ret[6].cls += ' fc-last';
18713 var cal_cell = function(n) {
18716 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18721 cls: 'fc-day-number',
18725 cls: 'fc-day-content',
18729 style: 'position: relative;' // height: 17px;
18741 var cal_rows = function() {
18744 for (var r = 0; r < 6; r++) {
18751 for (var i =0; i < Date.dayNames.length; i++) {
18752 var d = Date.dayNames[i];
18753 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18756 row.cn[0].cls+=' fc-first';
18757 row.cn[0].cn[0].style = 'min-height:90px';
18758 row.cn[6].cls+=' fc-last';
18762 ret[0].cls += ' fc-first';
18763 ret[4].cls += ' fc-prev-last';
18764 ret[5].cls += ' fc-last';
18771 cls: 'fc-border-separate',
18772 style : 'width:100%',
18780 cls : 'fc-first fc-last',
18798 cls : 'fc-content',
18799 style : "position: relative;",
18802 cls : 'fc-view fc-view-month fc-grid',
18803 style : 'position: relative',
18804 unselectable : 'on',
18807 cls : 'fc-event-container',
18808 style : 'position:absolute;z-index:8;top:0;left:0;'
18826 initEvents : function()
18829 throw "can not find store for calendar";
18835 style: "text-align:center",
18839 style: "background-color:white;width:50%;margin:250 auto",
18843 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
18854 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18856 var size = this.el.select('.fc-content', true).first().getSize();
18857 this.maskEl.setSize(size.width, size.height);
18858 this.maskEl.enableDisplayMode("block");
18859 if(!this.loadMask){
18860 this.maskEl.hide();
18863 this.store = Roo.factory(this.store, Roo.data);
18864 this.store.on('load', this.onLoad, this);
18865 this.store.on('beforeload', this.onBeforeLoad, this);
18869 this.cells = this.el.select('.fc-day',true);
18870 //Roo.log(this.cells);
18871 this.textNodes = this.el.query('.fc-day-number');
18872 this.cells.addClassOnOver('fc-state-hover');
18874 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18875 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18876 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18877 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18879 this.on('monthchange', this.onMonthChange, this);
18881 this.update(new Date().clearTime());
18884 resize : function() {
18885 var sz = this.el.getSize();
18887 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18888 this.el.select('.fc-day-content div',true).setHeight(34);
18893 showPrevMonth : function(e){
18894 this.update(this.activeDate.add("mo", -1));
18896 showToday : function(e){
18897 this.update(new Date().clearTime());
18900 showNextMonth : function(e){
18901 this.update(this.activeDate.add("mo", 1));
18905 showPrevYear : function(){
18906 this.update(this.activeDate.add("y", -1));
18910 showNextYear : function(){
18911 this.update(this.activeDate.add("y", 1));
18916 update : function(date)
18918 var vd = this.activeDate;
18919 this.activeDate = date;
18920 // if(vd && this.el){
18921 // var t = date.getTime();
18922 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18923 // Roo.log('using add remove');
18925 // this.fireEvent('monthchange', this, date);
18927 // this.cells.removeClass("fc-state-highlight");
18928 // this.cells.each(function(c){
18929 // if(c.dateValue == t){
18930 // c.addClass("fc-state-highlight");
18931 // setTimeout(function(){
18932 // try{c.dom.firstChild.focus();}catch(e){}
18942 var days = date.getDaysInMonth();
18944 var firstOfMonth = date.getFirstDateOfMonth();
18945 var startingPos = firstOfMonth.getDay()-this.startDay;
18947 if(startingPos < this.startDay){
18951 var pm = date.add(Date.MONTH, -1);
18952 var prevStart = pm.getDaysInMonth()-startingPos;
18954 this.cells = this.el.select('.fc-day',true);
18955 this.textNodes = this.el.query('.fc-day-number');
18956 this.cells.addClassOnOver('fc-state-hover');
18958 var cells = this.cells.elements;
18959 var textEls = this.textNodes;
18961 Roo.each(cells, function(cell){
18962 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18965 days += startingPos;
18967 // convert everything to numbers so it's fast
18968 var day = 86400000;
18969 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18972 //Roo.log(prevStart);
18974 var today = new Date().clearTime().getTime();
18975 var sel = date.clearTime().getTime();
18976 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18977 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18978 var ddMatch = this.disabledDatesRE;
18979 var ddText = this.disabledDatesText;
18980 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18981 var ddaysText = this.disabledDaysText;
18982 var format = this.format;
18984 var setCellClass = function(cal, cell){
18988 //Roo.log('set Cell Class');
18990 var t = d.getTime();
18994 cell.dateValue = t;
18996 cell.className += " fc-today";
18997 cell.className += " fc-state-highlight";
18998 cell.title = cal.todayText;
19001 // disable highlight in other month..
19002 //cell.className += " fc-state-highlight";
19007 cell.className = " fc-state-disabled";
19008 cell.title = cal.minText;
19012 cell.className = " fc-state-disabled";
19013 cell.title = cal.maxText;
19017 if(ddays.indexOf(d.getDay()) != -1){
19018 cell.title = ddaysText;
19019 cell.className = " fc-state-disabled";
19022 if(ddMatch && format){
19023 var fvalue = d.dateFormat(format);
19024 if(ddMatch.test(fvalue)){
19025 cell.title = ddText.replace("%0", fvalue);
19026 cell.className = " fc-state-disabled";
19030 if (!cell.initialClassName) {
19031 cell.initialClassName = cell.dom.className;
19034 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19039 for(; i < startingPos; i++) {
19040 textEls[i].innerHTML = (++prevStart);
19041 d.setDate(d.getDate()+1);
19043 cells[i].className = "fc-past fc-other-month";
19044 setCellClass(this, cells[i]);
19049 for(; i < days; i++){
19050 intDay = i - startingPos + 1;
19051 textEls[i].innerHTML = (intDay);
19052 d.setDate(d.getDate()+1);
19054 cells[i].className = ''; // "x-date-active";
19055 setCellClass(this, cells[i]);
19059 for(; i < 42; i++) {
19060 textEls[i].innerHTML = (++extraDays);
19061 d.setDate(d.getDate()+1);
19063 cells[i].className = "fc-future fc-other-month";
19064 setCellClass(this, cells[i]);
19067 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19069 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19071 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19072 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19074 if(totalRows != 6){
19075 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19076 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19079 this.fireEvent('monthchange', this, date);
19083 if(!this.internalRender){
19084 var main = this.el.dom.firstChild;
19085 var w = main.offsetWidth;
19086 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19087 Roo.fly(main).setWidth(w);
19088 this.internalRender = true;
19089 // opera does not respect the auto grow header center column
19090 // then, after it gets a width opera refuses to recalculate
19091 // without a second pass
19092 if(Roo.isOpera && !this.secondPass){
19093 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19094 this.secondPass = true;
19095 this.update.defer(10, this, [date]);
19102 findCell : function(dt) {
19103 dt = dt.clearTime().getTime();
19105 this.cells.each(function(c){
19106 //Roo.log("check " +c.dateValue + '?=' + dt);
19107 if(c.dateValue == dt){
19117 findCells : function(ev) {
19118 var s = ev.start.clone().clearTime().getTime();
19120 var e= ev.end.clone().clearTime().getTime();
19123 this.cells.each(function(c){
19124 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19126 if(c.dateValue > e){
19129 if(c.dateValue < s){
19138 // findBestRow: function(cells)
19142 // for (var i =0 ; i < cells.length;i++) {
19143 // ret = Math.max(cells[i].rows || 0,ret);
19150 addItem : function(ev)
19152 // look for vertical location slot in
19153 var cells = this.findCells(ev);
19155 // ev.row = this.findBestRow(cells);
19157 // work out the location.
19161 for(var i =0; i < cells.length; i++) {
19163 cells[i].row = cells[0].row;
19166 cells[i].row = cells[i].row + 1;
19176 if (crow.start.getY() == cells[i].getY()) {
19178 crow.end = cells[i];
19195 cells[0].events.push(ev);
19197 this.calevents.push(ev);
19200 clearEvents: function() {
19202 if(!this.calevents){
19206 Roo.each(this.cells.elements, function(c){
19212 Roo.each(this.calevents, function(e) {
19213 Roo.each(e.els, function(el) {
19214 el.un('mouseenter' ,this.onEventEnter, this);
19215 el.un('mouseleave' ,this.onEventLeave, this);
19220 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19226 renderEvents: function()
19230 this.cells.each(function(c) {
19239 if(c.row != c.events.length){
19240 r = 4 - (4 - (c.row - c.events.length));
19243 c.events = ev.slice(0, r);
19244 c.more = ev.slice(r);
19246 if(c.more.length && c.more.length == 1){
19247 c.events.push(c.more.pop());
19250 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19254 this.cells.each(function(c) {
19256 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19259 for (var e = 0; e < c.events.length; e++){
19260 var ev = c.events[e];
19261 var rows = ev.rows;
19263 for(var i = 0; i < rows.length; i++) {
19265 // how many rows should it span..
19268 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19269 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19271 unselectable : "on",
19274 cls: 'fc-event-inner',
19278 // cls: 'fc-event-time',
19279 // html : cells.length > 1 ? '' : ev.time
19283 cls: 'fc-event-title',
19284 html : String.format('{0}', ev.title)
19291 cls: 'ui-resizable-handle ui-resizable-e',
19292 html : '  '
19299 cfg.cls += ' fc-event-start';
19301 if ((i+1) == rows.length) {
19302 cfg.cls += ' fc-event-end';
19305 var ctr = _this.el.select('.fc-event-container',true).first();
19306 var cg = ctr.createChild(cfg);
19308 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19309 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19311 var r = (c.more.length) ? 1 : 0;
19312 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19313 cg.setWidth(ebox.right - sbox.x -2);
19315 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19316 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19317 cg.on('click', _this.onEventClick, _this, ev);
19328 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19329 style : 'position: absolute',
19330 unselectable : "on",
19333 cls: 'fc-event-inner',
19337 cls: 'fc-event-title',
19345 cls: 'ui-resizable-handle ui-resizable-e',
19346 html : '  '
19352 var ctr = _this.el.select('.fc-event-container',true).first();
19353 var cg = ctr.createChild(cfg);
19355 var sbox = c.select('.fc-day-content',true).first().getBox();
19356 var ebox = c.select('.fc-day-content',true).first().getBox();
19358 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19359 cg.setWidth(ebox.right - sbox.x -2);
19361 cg.on('click', _this.onMoreEventClick, _this, c.more);
19371 onEventEnter: function (e, el,event,d) {
19372 this.fireEvent('evententer', this, el, event);
19375 onEventLeave: function (e, el,event,d) {
19376 this.fireEvent('eventleave', this, el, event);
19379 onEventClick: function (e, el,event,d) {
19380 this.fireEvent('eventclick', this, el, event);
19383 onMonthChange: function () {
19387 onMoreEventClick: function(e, el, more)
19391 this.calpopover.placement = 'right';
19392 this.calpopover.setTitle('More');
19394 this.calpopover.setContent('');
19396 var ctr = this.calpopover.el.select('.popover-content', true).first();
19398 Roo.each(more, function(m){
19400 cls : 'fc-event-hori fc-event-draggable',
19403 var cg = ctr.createChild(cfg);
19405 cg.on('click', _this.onEventClick, _this, m);
19408 this.calpopover.show(el);
19413 onLoad: function ()
19415 this.calevents = [];
19418 if(this.store.getCount() > 0){
19419 this.store.data.each(function(d){
19422 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19423 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19424 time : d.data.start_time,
19425 title : d.data.title,
19426 description : d.data.description,
19427 venue : d.data.venue
19432 this.renderEvents();
19434 if(this.calevents.length && this.loadMask){
19435 this.maskEl.hide();
19439 onBeforeLoad: function()
19441 this.clearEvents();
19443 this.maskEl.show();
19457 * @class Roo.bootstrap.Popover
19458 * @extends Roo.bootstrap.Component
19459 * Bootstrap Popover class
19460 * @cfg {String} html contents of the popover (or false to use children..)
19461 * @cfg {String} title of popover (or false to hide)
19462 * @cfg {String} placement how it is placed
19463 * @cfg {String} trigger click || hover (or false to trigger manually)
19464 * @cfg {String} over what (parent or false to trigger manually.)
19465 * @cfg {Number} delay - delay before showing
19468 * Create a new Popover
19469 * @param {Object} config The config object
19472 Roo.bootstrap.Popover = function(config){
19473 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19479 * After the popover show
19481 * @param {Roo.bootstrap.Popover} this
19486 * After the popover hide
19488 * @param {Roo.bootstrap.Popover} this
19494 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19496 title: 'Fill in a title',
19499 placement : 'right',
19500 trigger : 'hover', // hover
19506 can_build_overlaid : false,
19508 getChildContainer : function()
19510 return this.el.select('.popover-content',true).first();
19513 getAutoCreate : function(){
19516 cls : 'popover roo-dynamic',
19517 style: 'display:block',
19523 cls : 'popover-inner',
19527 cls: 'popover-title popover-header',
19531 cls : 'popover-content popover-body',
19542 setTitle: function(str)
19545 this.el.select('.popover-title',true).first().dom.innerHTML = str;
19547 setContent: function(str)
19550 this.el.select('.popover-content',true).first().dom.innerHTML = str;
19552 // as it get's added to the bottom of the page.
19553 onRender : function(ct, position)
19555 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19557 var cfg = Roo.apply({}, this.getAutoCreate());
19561 cfg.cls += ' ' + this.cls;
19564 cfg.style = this.style;
19566 //Roo.log("adding to ");
19567 this.el = Roo.get(document.body).createChild(cfg, position);
19568 // Roo.log(this.el);
19573 initEvents : function()
19575 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19576 this.el.enableDisplayMode('block');
19578 if (this.over === false) {
19581 if (this.triggers === false) {
19584 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19585 var triggers = this.trigger ? this.trigger.split(' ') : [];
19586 Roo.each(triggers, function(trigger) {
19588 if (trigger == 'click') {
19589 on_el.on('click', this.toggle, this);
19590 } else if (trigger != 'manual') {
19591 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19592 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19594 on_el.on(eventIn ,this.enter, this);
19595 on_el.on(eventOut, this.leave, this);
19606 toggle : function () {
19607 this.hoverState == 'in' ? this.leave() : this.enter();
19610 enter : function () {
19612 clearTimeout(this.timeout);
19614 this.hoverState = 'in';
19616 if (!this.delay || !this.delay.show) {
19621 this.timeout = setTimeout(function () {
19622 if (_t.hoverState == 'in') {
19625 }, this.delay.show)
19628 leave : function() {
19629 clearTimeout(this.timeout);
19631 this.hoverState = 'out';
19633 if (!this.delay || !this.delay.hide) {
19638 this.timeout = setTimeout(function () {
19639 if (_t.hoverState == 'out') {
19642 }, this.delay.hide)
19645 show : function (on_el)
19648 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19652 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19653 if (this.html !== false) {
19654 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19656 this.el.removeClass([
19657 'fade','top','bottom', 'left', 'right','in',
19658 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19660 if (!this.title.length) {
19661 this.el.select('.popover-title',true).hide();
19664 var placement = typeof this.placement == 'function' ?
19665 this.placement.call(this, this.el, on_el) :
19668 var autoToken = /\s?auto?\s?/i;
19669 var autoPlace = autoToken.test(placement);
19671 placement = placement.replace(autoToken, '') || 'top';
19675 //this.el.setXY([0,0]);
19677 this.el.dom.style.display='block';
19678 this.el.addClass(placement);
19680 //this.el.appendTo(on_el);
19682 var p = this.getPosition();
19683 var box = this.el.getBox();
19688 var align = Roo.bootstrap.Popover.alignment[placement];
19691 this.el.alignTo(on_el, align[0],align[1]);
19692 //var arrow = this.el.select('.arrow',true).first();
19693 //arrow.set(align[2],
19695 this.el.addClass('in');
19698 if (this.el.hasClass('fade')) {
19702 this.hoverState = 'in';
19704 this.fireEvent('show', this);
19709 this.el.setXY([0,0]);
19710 this.el.removeClass('in');
19712 this.hoverState = null;
19714 this.fireEvent('hide', this);
19719 Roo.bootstrap.Popover.alignment = {
19720 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19721 'right' : ['l-r', [10,0], 'left bs-popover-left'],
19722 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19723 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19734 * @class Roo.bootstrap.Progress
19735 * @extends Roo.bootstrap.Component
19736 * Bootstrap Progress class
19737 * @cfg {Boolean} striped striped of the progress bar
19738 * @cfg {Boolean} active animated of the progress bar
19742 * Create a new Progress
19743 * @param {Object} config The config object
19746 Roo.bootstrap.Progress = function(config){
19747 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19750 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
19755 getAutoCreate : function(){
19763 cfg.cls += ' progress-striped';
19767 cfg.cls += ' active';
19786 * @class Roo.bootstrap.ProgressBar
19787 * @extends Roo.bootstrap.Component
19788 * Bootstrap ProgressBar class
19789 * @cfg {Number} aria_valuenow aria-value now
19790 * @cfg {Number} aria_valuemin aria-value min
19791 * @cfg {Number} aria_valuemax aria-value max
19792 * @cfg {String} label label for the progress bar
19793 * @cfg {String} panel (success | info | warning | danger )
19794 * @cfg {String} role role of the progress bar
19795 * @cfg {String} sr_only text
19799 * Create a new ProgressBar
19800 * @param {Object} config The config object
19803 Roo.bootstrap.ProgressBar = function(config){
19804 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19807 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
19811 aria_valuemax : 100,
19817 getAutoCreate : function()
19822 cls: 'progress-bar',
19823 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19835 cfg.role = this.role;
19838 if(this.aria_valuenow){
19839 cfg['aria-valuenow'] = this.aria_valuenow;
19842 if(this.aria_valuemin){
19843 cfg['aria-valuemin'] = this.aria_valuemin;
19846 if(this.aria_valuemax){
19847 cfg['aria-valuemax'] = this.aria_valuemax;
19850 if(this.label && !this.sr_only){
19851 cfg.html = this.label;
19855 cfg.cls += ' progress-bar-' + this.panel;
19861 update : function(aria_valuenow)
19863 this.aria_valuenow = aria_valuenow;
19865 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19880 * @class Roo.bootstrap.TabGroup
19881 * @extends Roo.bootstrap.Column
19882 * Bootstrap Column class
19883 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19884 * @cfg {Boolean} carousel true to make the group behave like a carousel
19885 * @cfg {Boolean} bullets show bullets for the panels
19886 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19887 * @cfg {Number} timer auto slide timer .. default 0 millisecond
19888 * @cfg {Boolean} showarrow (true|false) show arrow default true
19891 * Create a new TabGroup
19892 * @param {Object} config The config object
19895 Roo.bootstrap.TabGroup = function(config){
19896 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19898 this.navId = Roo.id();
19901 Roo.bootstrap.TabGroup.register(this);
19905 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
19908 transition : false,
19913 slideOnTouch : false,
19916 getAutoCreate : function()
19918 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19920 cfg.cls += ' tab-content';
19922 if (this.carousel) {
19923 cfg.cls += ' carousel slide';
19926 cls : 'carousel-inner',
19930 if(this.bullets && !Roo.isTouch){
19933 cls : 'carousel-bullets',
19937 if(this.bullets_cls){
19938 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19945 cfg.cn[0].cn.push(bullets);
19948 if(this.showarrow){
19949 cfg.cn[0].cn.push({
19951 class : 'carousel-arrow',
19955 class : 'carousel-prev',
19959 class : 'fa fa-chevron-left'
19965 class : 'carousel-next',
19969 class : 'fa fa-chevron-right'
19982 initEvents: function()
19984 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19985 // this.el.on("touchstart", this.onTouchStart, this);
19988 if(this.autoslide){
19991 this.slideFn = window.setInterval(function() {
19992 _this.showPanelNext();
19996 if(this.showarrow){
19997 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19998 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20004 // onTouchStart : function(e, el, o)
20006 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20010 // this.showPanelNext();
20014 getChildContainer : function()
20016 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20020 * register a Navigation item
20021 * @param {Roo.bootstrap.NavItem} the navitem to add
20023 register : function(item)
20025 this.tabs.push( item);
20026 item.navId = this.navId; // not really needed..
20031 getActivePanel : function()
20034 Roo.each(this.tabs, function(t) {
20044 getPanelByName : function(n)
20047 Roo.each(this.tabs, function(t) {
20048 if (t.tabId == n) {
20056 indexOfPanel : function(p)
20059 Roo.each(this.tabs, function(t,i) {
20060 if (t.tabId == p.tabId) {
20069 * show a specific panel
20070 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20071 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20073 showPanel : function (pan)
20075 if(this.transition || typeof(pan) == 'undefined'){
20076 Roo.log("waiting for the transitionend");
20080 if (typeof(pan) == 'number') {
20081 pan = this.tabs[pan];
20084 if (typeof(pan) == 'string') {
20085 pan = this.getPanelByName(pan);
20088 var cur = this.getActivePanel();
20091 Roo.log('pan or acitve pan is undefined');
20095 if (pan.tabId == this.getActivePanel().tabId) {
20099 if (false === cur.fireEvent('beforedeactivate')) {
20103 if(this.bullets > 0 && !Roo.isTouch){
20104 this.setActiveBullet(this.indexOfPanel(pan));
20107 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20109 //class="carousel-item carousel-item-next carousel-item-left"
20111 this.transition = true;
20112 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20113 var lr = dir == 'next' ? 'left' : 'right';
20114 pan.el.addClass(dir); // or prev
20115 pan.el.addClass('carousel-item-' + dir); // or prev
20116 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20117 cur.el.addClass(lr); // or right
20118 pan.el.addClass(lr);
20119 cur.el.addClass('carousel-item-' +lr); // or right
20120 pan.el.addClass('carousel-item-' +lr);
20124 cur.el.on('transitionend', function() {
20125 Roo.log("trans end?");
20127 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20128 pan.setActive(true);
20130 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20131 cur.setActive(false);
20133 _this.transition = false;
20135 }, this, { single: true } );
20140 cur.setActive(false);
20141 pan.setActive(true);
20146 showPanelNext : function()
20148 var i = this.indexOfPanel(this.getActivePanel());
20150 if (i >= this.tabs.length - 1 && !this.autoslide) {
20154 if (i >= this.tabs.length - 1 && this.autoslide) {
20158 this.showPanel(this.tabs[i+1]);
20161 showPanelPrev : function()
20163 var i = this.indexOfPanel(this.getActivePanel());
20165 if (i < 1 && !this.autoslide) {
20169 if (i < 1 && this.autoslide) {
20170 i = this.tabs.length;
20173 this.showPanel(this.tabs[i-1]);
20177 addBullet: function()
20179 if(!this.bullets || Roo.isTouch){
20182 var ctr = this.el.select('.carousel-bullets',true).first();
20183 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20184 var bullet = ctr.createChild({
20185 cls : 'bullet bullet-' + i
20186 },ctr.dom.lastChild);
20191 bullet.on('click', (function(e, el, o, ii, t){
20193 e.preventDefault();
20195 this.showPanel(ii);
20197 if(this.autoslide && this.slideFn){
20198 clearInterval(this.slideFn);
20199 this.slideFn = window.setInterval(function() {
20200 _this.showPanelNext();
20204 }).createDelegate(this, [i, bullet], true));
20209 setActiveBullet : function(i)
20215 Roo.each(this.el.select('.bullet', true).elements, function(el){
20216 el.removeClass('selected');
20219 var bullet = this.el.select('.bullet-' + i, true).first();
20225 bullet.addClass('selected');
20236 Roo.apply(Roo.bootstrap.TabGroup, {
20240 * register a Navigation Group
20241 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20243 register : function(navgrp)
20245 this.groups[navgrp.navId] = navgrp;
20249 * fetch a Navigation Group based on the navigation ID
20250 * if one does not exist , it will get created.
20251 * @param {string} the navgroup to add
20252 * @returns {Roo.bootstrap.NavGroup} the navgroup
20254 get: function(navId) {
20255 if (typeof(this.groups[navId]) == 'undefined') {
20256 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20258 return this.groups[navId] ;
20273 * @class Roo.bootstrap.TabPanel
20274 * @extends Roo.bootstrap.Component
20275 * Bootstrap TabPanel class
20276 * @cfg {Boolean} active panel active
20277 * @cfg {String} html panel content
20278 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20279 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20280 * @cfg {String} href click to link..
20281 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20285 * Create a new TabPanel
20286 * @param {Object} config The config object
20289 Roo.bootstrap.TabPanel = function(config){
20290 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20294 * Fires when the active status changes
20295 * @param {Roo.bootstrap.TabPanel} this
20296 * @param {Boolean} state the new state
20301 * @event beforedeactivate
20302 * Fires before a tab is de-activated - can be used to do validation on a form.
20303 * @param {Roo.bootstrap.TabPanel} this
20304 * @return {Boolean} false if there is an error
20307 'beforedeactivate': true
20310 this.tabId = this.tabId || Roo.id();
20314 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20321 touchSlide : false,
20322 getAutoCreate : function(){
20327 // item is needed for carousel - not sure if it has any effect otherwise
20328 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20329 html: this.html || ''
20333 cfg.cls += ' active';
20337 cfg.tabId = this.tabId;
20345 initEvents: function()
20347 var p = this.parent();
20349 this.navId = this.navId || p.navId;
20351 if (typeof(this.navId) != 'undefined') {
20352 // not really needed.. but just in case.. parent should be a NavGroup.
20353 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20357 var i = tg.tabs.length - 1;
20359 if(this.active && tg.bullets > 0 && i < tg.bullets){
20360 tg.setActiveBullet(i);
20364 this.el.on('click', this.onClick, this);
20366 if(Roo.isTouch && this.touchSlide){
20367 this.el.on("touchstart", this.onTouchStart, this);
20368 this.el.on("touchmove", this.onTouchMove, this);
20369 this.el.on("touchend", this.onTouchEnd, this);
20374 onRender : function(ct, position)
20376 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20379 setActive : function(state)
20381 Roo.log("panel - set active " + this.tabId + "=" + state);
20383 this.active = state;
20385 this.el.removeClass('active');
20387 } else if (!this.el.hasClass('active')) {
20388 this.el.addClass('active');
20391 this.fireEvent('changed', this, state);
20394 onClick : function(e)
20396 e.preventDefault();
20398 if(!this.href.length){
20402 window.location.href = this.href;
20411 onTouchStart : function(e)
20413 this.swiping = false;
20415 this.startX = e.browserEvent.touches[0].clientX;
20416 this.startY = e.browserEvent.touches[0].clientY;
20419 onTouchMove : function(e)
20421 this.swiping = true;
20423 this.endX = e.browserEvent.touches[0].clientX;
20424 this.endY = e.browserEvent.touches[0].clientY;
20427 onTouchEnd : function(e)
20434 var tabGroup = this.parent();
20436 if(this.endX > this.startX){ // swiping right
20437 tabGroup.showPanelPrev();
20441 if(this.startX > this.endX){ // swiping left
20442 tabGroup.showPanelNext();
20461 * @class Roo.bootstrap.DateField
20462 * @extends Roo.bootstrap.Input
20463 * Bootstrap DateField class
20464 * @cfg {Number} weekStart default 0
20465 * @cfg {String} viewMode default empty, (months|years)
20466 * @cfg {String} minViewMode default empty, (months|years)
20467 * @cfg {Number} startDate default -Infinity
20468 * @cfg {Number} endDate default Infinity
20469 * @cfg {Boolean} todayHighlight default false
20470 * @cfg {Boolean} todayBtn default false
20471 * @cfg {Boolean} calendarWeeks default false
20472 * @cfg {Object} daysOfWeekDisabled default empty
20473 * @cfg {Boolean} singleMode default false (true | false)
20475 * @cfg {Boolean} keyboardNavigation default true
20476 * @cfg {String} language default en
20479 * Create a new DateField
20480 * @param {Object} config The config object
20483 Roo.bootstrap.DateField = function(config){
20484 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20488 * Fires when this field show.
20489 * @param {Roo.bootstrap.DateField} this
20490 * @param {Mixed} date The date value
20495 * Fires when this field hide.
20496 * @param {Roo.bootstrap.DateField} this
20497 * @param {Mixed} date The date value
20502 * Fires when select a date.
20503 * @param {Roo.bootstrap.DateField} this
20504 * @param {Mixed} date The date value
20508 * @event beforeselect
20509 * Fires when before select a date.
20510 * @param {Roo.bootstrap.DateField} this
20511 * @param {Mixed} date The date value
20513 beforeselect : true
20517 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20520 * @cfg {String} format
20521 * The default date format string which can be overriden for localization support. The format must be
20522 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20526 * @cfg {String} altFormats
20527 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20528 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20530 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20538 todayHighlight : false,
20544 keyboardNavigation: true,
20546 calendarWeeks: false,
20548 startDate: -Infinity,
20552 daysOfWeekDisabled: [],
20556 singleMode : false,
20558 UTCDate: function()
20560 return new Date(Date.UTC.apply(Date, arguments));
20563 UTCToday: function()
20565 var today = new Date();
20566 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20569 getDate: function() {
20570 var d = this.getUTCDate();
20571 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20574 getUTCDate: function() {
20578 setDate: function(d) {
20579 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20582 setUTCDate: function(d) {
20584 this.setValue(this.formatDate(this.date));
20587 onRender: function(ct, position)
20590 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20592 this.language = this.language || 'en';
20593 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20594 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20596 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20597 this.format = this.format || 'm/d/y';
20598 this.isInline = false;
20599 this.isInput = true;
20600 this.component = this.el.select('.add-on', true).first() || false;
20601 this.component = (this.component && this.component.length === 0) ? false : this.component;
20602 this.hasInput = this.component && this.inputEl().length;
20604 if (typeof(this.minViewMode === 'string')) {
20605 switch (this.minViewMode) {
20607 this.minViewMode = 1;
20610 this.minViewMode = 2;
20613 this.minViewMode = 0;
20618 if (typeof(this.viewMode === 'string')) {
20619 switch (this.viewMode) {
20632 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20634 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20636 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20638 this.picker().on('mousedown', this.onMousedown, this);
20639 this.picker().on('click', this.onClick, this);
20641 this.picker().addClass('datepicker-dropdown');
20643 this.startViewMode = this.viewMode;
20645 if(this.singleMode){
20646 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20647 v.setVisibilityMode(Roo.Element.DISPLAY);
20651 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20652 v.setStyle('width', '189px');
20656 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20657 if(!this.calendarWeeks){
20662 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20663 v.attr('colspan', function(i, val){
20664 return parseInt(val) + 1;
20669 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20671 this.setStartDate(this.startDate);
20672 this.setEndDate(this.endDate);
20674 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20681 if(this.isInline) {
20686 picker : function()
20688 return this.pickerEl;
20689 // return this.el.select('.datepicker', true).first();
20692 fillDow: function()
20694 var dowCnt = this.weekStart;
20703 if(this.calendarWeeks){
20711 while (dowCnt < this.weekStart + 7) {
20715 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20719 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20722 fillMonths: function()
20725 var months = this.picker().select('>.datepicker-months td', true).first();
20727 months.dom.innerHTML = '';
20733 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20736 months.createChild(month);
20743 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;
20745 if (this.date < this.startDate) {
20746 this.viewDate = new Date(this.startDate);
20747 } else if (this.date > this.endDate) {
20748 this.viewDate = new Date(this.endDate);
20750 this.viewDate = new Date(this.date);
20758 var d = new Date(this.viewDate),
20759 year = d.getUTCFullYear(),
20760 month = d.getUTCMonth(),
20761 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20762 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20763 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20764 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20765 currentDate = this.date && this.date.valueOf(),
20766 today = this.UTCToday();
20768 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20770 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20772 // this.picker.select('>tfoot th.today').
20773 // .text(dates[this.language].today)
20774 // .toggle(this.todayBtn !== false);
20776 this.updateNavArrows();
20779 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20781 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20783 prevMonth.setUTCDate(day);
20785 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20787 var nextMonth = new Date(prevMonth);
20789 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20791 nextMonth = nextMonth.valueOf();
20793 var fillMonths = false;
20795 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20797 while(prevMonth.valueOf() <= nextMonth) {
20800 if (prevMonth.getUTCDay() === this.weekStart) {
20802 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20810 if(this.calendarWeeks){
20811 // ISO 8601: First week contains first thursday.
20812 // ISO also states week starts on Monday, but we can be more abstract here.
20814 // Start of current week: based on weekstart/current date
20815 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20816 // Thursday of this week
20817 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20818 // First Thursday of year, year from thursday
20819 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20820 // Calendar week: ms between thursdays, div ms per day, div 7 days
20821 calWeek = (th - yth) / 864e5 / 7 + 1;
20823 fillMonths.cn.push({
20831 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20833 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20836 if (this.todayHighlight &&
20837 prevMonth.getUTCFullYear() == today.getFullYear() &&
20838 prevMonth.getUTCMonth() == today.getMonth() &&
20839 prevMonth.getUTCDate() == today.getDate()) {
20840 clsName += ' today';
20843 if (currentDate && prevMonth.valueOf() === currentDate) {
20844 clsName += ' active';
20847 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20848 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20849 clsName += ' disabled';
20852 fillMonths.cn.push({
20854 cls: 'day ' + clsName,
20855 html: prevMonth.getDate()
20858 prevMonth.setDate(prevMonth.getDate()+1);
20861 var currentYear = this.date && this.date.getUTCFullYear();
20862 var currentMonth = this.date && this.date.getUTCMonth();
20864 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20866 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20867 v.removeClass('active');
20869 if(currentYear === year && k === currentMonth){
20870 v.addClass('active');
20873 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20874 v.addClass('disabled');
20880 year = parseInt(year/10, 10) * 10;
20882 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20884 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20887 for (var i = -1; i < 11; i++) {
20888 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20890 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20898 showMode: function(dir)
20901 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20904 Roo.each(this.picker().select('>div',true).elements, function(v){
20905 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20908 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20913 if(this.isInline) {
20917 this.picker().removeClass(['bottom', 'top']);
20919 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20921 * place to the top of element!
20925 this.picker().addClass('top');
20926 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20931 this.picker().addClass('bottom');
20933 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20936 parseDate : function(value)
20938 if(!value || value instanceof Date){
20941 var v = Date.parseDate(value, this.format);
20942 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20943 v = Date.parseDate(value, 'Y-m-d');
20945 if(!v && this.altFormats){
20946 if(!this.altFormatsArray){
20947 this.altFormatsArray = this.altFormats.split("|");
20949 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20950 v = Date.parseDate(value, this.altFormatsArray[i]);
20956 formatDate : function(date, fmt)
20958 return (!date || !(date instanceof Date)) ?
20959 date : date.dateFormat(fmt || this.format);
20962 onFocus : function()
20964 Roo.bootstrap.DateField.superclass.onFocus.call(this);
20968 onBlur : function()
20970 Roo.bootstrap.DateField.superclass.onBlur.call(this);
20972 var d = this.inputEl().getValue();
20979 showPopup : function()
20981 this.picker().show();
20985 this.fireEvent('showpopup', this, this.date);
20988 hidePopup : function()
20990 if(this.isInline) {
20993 this.picker().hide();
20994 this.viewMode = this.startViewMode;
20997 this.fireEvent('hidepopup', this, this.date);
21001 onMousedown: function(e)
21003 e.stopPropagation();
21004 e.preventDefault();
21009 Roo.bootstrap.DateField.superclass.keyup.call(this);
21013 setValue: function(v)
21015 if(this.fireEvent('beforeselect', this, v) !== false){
21016 var d = new Date(this.parseDate(v) ).clearTime();
21018 if(isNaN(d.getTime())){
21019 this.date = this.viewDate = '';
21020 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21024 v = this.formatDate(d);
21026 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21028 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21032 this.fireEvent('select', this, this.date);
21036 getValue: function()
21038 return this.formatDate(this.date);
21041 fireKey: function(e)
21043 if (!this.picker().isVisible()){
21044 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21050 var dateChanged = false,
21052 newDate, newViewDate;
21057 e.preventDefault();
21061 if (!this.keyboardNavigation) {
21064 dir = e.keyCode == 37 ? -1 : 1;
21067 newDate = this.moveYear(this.date, dir);
21068 newViewDate = this.moveYear(this.viewDate, dir);
21069 } else if (e.shiftKey){
21070 newDate = this.moveMonth(this.date, dir);
21071 newViewDate = this.moveMonth(this.viewDate, dir);
21073 newDate = new Date(this.date);
21074 newDate.setUTCDate(this.date.getUTCDate() + dir);
21075 newViewDate = new Date(this.viewDate);
21076 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21078 if (this.dateWithinRange(newDate)){
21079 this.date = newDate;
21080 this.viewDate = newViewDate;
21081 this.setValue(this.formatDate(this.date));
21083 e.preventDefault();
21084 dateChanged = true;
21089 if (!this.keyboardNavigation) {
21092 dir = e.keyCode == 38 ? -1 : 1;
21094 newDate = this.moveYear(this.date, dir);
21095 newViewDate = this.moveYear(this.viewDate, dir);
21096 } else if (e.shiftKey){
21097 newDate = this.moveMonth(this.date, dir);
21098 newViewDate = this.moveMonth(this.viewDate, dir);
21100 newDate = new Date(this.date);
21101 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21102 newViewDate = new Date(this.viewDate);
21103 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21105 if (this.dateWithinRange(newDate)){
21106 this.date = newDate;
21107 this.viewDate = newViewDate;
21108 this.setValue(this.formatDate(this.date));
21110 e.preventDefault();
21111 dateChanged = true;
21115 this.setValue(this.formatDate(this.date));
21117 e.preventDefault();
21120 this.setValue(this.formatDate(this.date));
21134 onClick: function(e)
21136 e.stopPropagation();
21137 e.preventDefault();
21139 var target = e.getTarget();
21141 if(target.nodeName.toLowerCase() === 'i'){
21142 target = Roo.get(target).dom.parentNode;
21145 var nodeName = target.nodeName;
21146 var className = target.className;
21147 var html = target.innerHTML;
21148 //Roo.log(nodeName);
21150 switch(nodeName.toLowerCase()) {
21152 switch(className) {
21158 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21159 switch(this.viewMode){
21161 this.viewDate = this.moveMonth(this.viewDate, dir);
21165 this.viewDate = this.moveYear(this.viewDate, dir);
21171 var date = new Date();
21172 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21174 this.setValue(this.formatDate(this.date));
21181 if (className.indexOf('disabled') < 0) {
21182 this.viewDate.setUTCDate(1);
21183 if (className.indexOf('month') > -1) {
21184 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21186 var year = parseInt(html, 10) || 0;
21187 this.viewDate.setUTCFullYear(year);
21191 if(this.singleMode){
21192 this.setValue(this.formatDate(this.viewDate));
21203 //Roo.log(className);
21204 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21205 var day = parseInt(html, 10) || 1;
21206 var year = this.viewDate.getUTCFullYear(),
21207 month = this.viewDate.getUTCMonth();
21209 if (className.indexOf('old') > -1) {
21216 } else if (className.indexOf('new') > -1) {
21224 //Roo.log([year,month,day]);
21225 this.date = this.UTCDate(year, month, day,0,0,0,0);
21226 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21228 //Roo.log(this.formatDate(this.date));
21229 this.setValue(this.formatDate(this.date));
21236 setStartDate: function(startDate)
21238 this.startDate = startDate || -Infinity;
21239 if (this.startDate !== -Infinity) {
21240 this.startDate = this.parseDate(this.startDate);
21243 this.updateNavArrows();
21246 setEndDate: function(endDate)
21248 this.endDate = endDate || Infinity;
21249 if (this.endDate !== Infinity) {
21250 this.endDate = this.parseDate(this.endDate);
21253 this.updateNavArrows();
21256 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21258 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21259 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21260 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21262 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21263 return parseInt(d, 10);
21266 this.updateNavArrows();
21269 updateNavArrows: function()
21271 if(this.singleMode){
21275 var d = new Date(this.viewDate),
21276 year = d.getUTCFullYear(),
21277 month = d.getUTCMonth();
21279 Roo.each(this.picker().select('.prev', true).elements, function(v){
21281 switch (this.viewMode) {
21284 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21290 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21297 Roo.each(this.picker().select('.next', true).elements, function(v){
21299 switch (this.viewMode) {
21302 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21308 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21316 moveMonth: function(date, dir)
21321 var new_date = new Date(date.valueOf()),
21322 day = new_date.getUTCDate(),
21323 month = new_date.getUTCMonth(),
21324 mag = Math.abs(dir),
21326 dir = dir > 0 ? 1 : -1;
21329 // If going back one month, make sure month is not current month
21330 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21332 return new_date.getUTCMonth() == month;
21334 // If going forward one month, make sure month is as expected
21335 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21337 return new_date.getUTCMonth() != new_month;
21339 new_month = month + dir;
21340 new_date.setUTCMonth(new_month);
21341 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21342 if (new_month < 0 || new_month > 11) {
21343 new_month = (new_month + 12) % 12;
21346 // For magnitudes >1, move one month at a time...
21347 for (var i=0; i<mag; i++) {
21348 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21349 new_date = this.moveMonth(new_date, dir);
21351 // ...then reset the day, keeping it in the new month
21352 new_month = new_date.getUTCMonth();
21353 new_date.setUTCDate(day);
21355 return new_month != new_date.getUTCMonth();
21358 // Common date-resetting loop -- if date is beyond end of month, make it
21361 new_date.setUTCDate(--day);
21362 new_date.setUTCMonth(new_month);
21367 moveYear: function(date, dir)
21369 return this.moveMonth(date, dir*12);
21372 dateWithinRange: function(date)
21374 return date >= this.startDate && date <= this.endDate;
21380 this.picker().remove();
21383 validateValue : function(value)
21385 if(this.getVisibilityEl().hasClass('hidden')){
21389 if(value.length < 1) {
21390 if(this.allowBlank){
21396 if(value.length < this.minLength){
21399 if(value.length > this.maxLength){
21403 var vt = Roo.form.VTypes;
21404 if(!vt[this.vtype](value, this)){
21408 if(typeof this.validator == "function"){
21409 var msg = this.validator(value);
21415 if(this.regex && !this.regex.test(value)){
21419 if(typeof(this.parseDate(value)) == 'undefined'){
21423 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21427 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21437 this.date = this.viewDate = '';
21439 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21444 Roo.apply(Roo.bootstrap.DateField, {
21455 html: '<i class="fa fa-arrow-left"/>'
21465 html: '<i class="fa fa-arrow-right"/>'
21507 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21508 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21509 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21510 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21511 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21524 navFnc: 'FullYear',
21529 navFnc: 'FullYear',
21534 Roo.apply(Roo.bootstrap.DateField, {
21538 cls: 'datepicker dropdown-menu roo-dynamic',
21542 cls: 'datepicker-days',
21546 cls: 'table-condensed',
21548 Roo.bootstrap.DateField.head,
21552 Roo.bootstrap.DateField.footer
21559 cls: 'datepicker-months',
21563 cls: 'table-condensed',
21565 Roo.bootstrap.DateField.head,
21566 Roo.bootstrap.DateField.content,
21567 Roo.bootstrap.DateField.footer
21574 cls: 'datepicker-years',
21578 cls: 'table-condensed',
21580 Roo.bootstrap.DateField.head,
21581 Roo.bootstrap.DateField.content,
21582 Roo.bootstrap.DateField.footer
21601 * @class Roo.bootstrap.TimeField
21602 * @extends Roo.bootstrap.Input
21603 * Bootstrap DateField class
21607 * Create a new TimeField
21608 * @param {Object} config The config object
21611 Roo.bootstrap.TimeField = function(config){
21612 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21616 * Fires when this field show.
21617 * @param {Roo.bootstrap.DateField} thisthis
21618 * @param {Mixed} date The date value
21623 * Fires when this field hide.
21624 * @param {Roo.bootstrap.DateField} this
21625 * @param {Mixed} date The date value
21630 * Fires when select a date.
21631 * @param {Roo.bootstrap.DateField} this
21632 * @param {Mixed} date The date value
21638 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
21641 * @cfg {String} format
21642 * The default time format string which can be overriden for localization support. The format must be
21643 * valid according to {@link Date#parseDate} (defaults to 'H:i').
21647 onRender: function(ct, position)
21650 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21652 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21654 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21656 this.pop = this.picker().select('>.datepicker-time',true).first();
21657 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21659 this.picker().on('mousedown', this.onMousedown, this);
21660 this.picker().on('click', this.onClick, this);
21662 this.picker().addClass('datepicker-dropdown');
21667 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21668 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21669 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21670 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21671 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21672 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21676 fireKey: function(e){
21677 if (!this.picker().isVisible()){
21678 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21684 e.preventDefault();
21692 this.onTogglePeriod();
21695 this.onIncrementMinutes();
21698 this.onDecrementMinutes();
21707 onClick: function(e) {
21708 e.stopPropagation();
21709 e.preventDefault();
21712 picker : function()
21714 return this.el.select('.datepicker', true).first();
21717 fillTime: function()
21719 var time = this.pop.select('tbody', true).first();
21721 time.dom.innerHTML = '';
21736 cls: 'hours-up glyphicon glyphicon-chevron-up'
21756 cls: 'minutes-up glyphicon glyphicon-chevron-up'
21777 cls: 'timepicker-hour',
21792 cls: 'timepicker-minute',
21807 cls: 'btn btn-primary period',
21829 cls: 'hours-down glyphicon glyphicon-chevron-down'
21849 cls: 'minutes-down glyphicon glyphicon-chevron-down'
21867 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21874 var hours = this.time.getHours();
21875 var minutes = this.time.getMinutes();
21888 hours = hours - 12;
21892 hours = '0' + hours;
21896 minutes = '0' + minutes;
21899 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21900 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21901 this.pop.select('button', true).first().dom.innerHTML = period;
21907 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21909 var cls = ['bottom'];
21911 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21918 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21923 this.picker().addClass(cls.join('-'));
21927 Roo.each(cls, function(c){
21929 _this.picker().setTop(_this.inputEl().getHeight());
21933 _this.picker().setTop(0 - _this.picker().getHeight());
21938 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21942 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21949 onFocus : function()
21951 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21955 onBlur : function()
21957 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21963 this.picker().show();
21968 this.fireEvent('show', this, this.date);
21973 this.picker().hide();
21976 this.fireEvent('hide', this, this.date);
21979 setTime : function()
21982 this.setValue(this.time.format(this.format));
21984 this.fireEvent('select', this, this.date);
21989 onMousedown: function(e){
21990 e.stopPropagation();
21991 e.preventDefault();
21994 onIncrementHours: function()
21996 Roo.log('onIncrementHours');
21997 this.time = this.time.add(Date.HOUR, 1);
22002 onDecrementHours: function()
22004 Roo.log('onDecrementHours');
22005 this.time = this.time.add(Date.HOUR, -1);
22009 onIncrementMinutes: function()
22011 Roo.log('onIncrementMinutes');
22012 this.time = this.time.add(Date.MINUTE, 1);
22016 onDecrementMinutes: function()
22018 Roo.log('onDecrementMinutes');
22019 this.time = this.time.add(Date.MINUTE, -1);
22023 onTogglePeriod: function()
22025 Roo.log('onTogglePeriod');
22026 this.time = this.time.add(Date.HOUR, 12);
22033 Roo.apply(Roo.bootstrap.TimeField, {
22063 cls: 'btn btn-info ok',
22075 Roo.apply(Roo.bootstrap.TimeField, {
22079 cls: 'datepicker dropdown-menu',
22083 cls: 'datepicker-time',
22087 cls: 'table-condensed',
22089 Roo.bootstrap.TimeField.content,
22090 Roo.bootstrap.TimeField.footer
22109 * @class Roo.bootstrap.MonthField
22110 * @extends Roo.bootstrap.Input
22111 * Bootstrap MonthField class
22113 * @cfg {String} language default en
22116 * Create a new MonthField
22117 * @param {Object} config The config object
22120 Roo.bootstrap.MonthField = function(config){
22121 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22126 * Fires when this field show.
22127 * @param {Roo.bootstrap.MonthField} this
22128 * @param {Mixed} date The date value
22133 * Fires when this field hide.
22134 * @param {Roo.bootstrap.MonthField} this
22135 * @param {Mixed} date The date value
22140 * Fires when select a date.
22141 * @param {Roo.bootstrap.MonthField} this
22142 * @param {String} oldvalue The old value
22143 * @param {String} newvalue The new value
22149 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22151 onRender: function(ct, position)
22154 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22156 this.language = this.language || 'en';
22157 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22158 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22160 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22161 this.isInline = false;
22162 this.isInput = true;
22163 this.component = this.el.select('.add-on', true).first() || false;
22164 this.component = (this.component && this.component.length === 0) ? false : this.component;
22165 this.hasInput = this.component && this.inputEL().length;
22167 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22169 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22171 this.picker().on('mousedown', this.onMousedown, this);
22172 this.picker().on('click', this.onClick, this);
22174 this.picker().addClass('datepicker-dropdown');
22176 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22177 v.setStyle('width', '189px');
22184 if(this.isInline) {
22190 setValue: function(v, suppressEvent)
22192 var o = this.getValue();
22194 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22198 if(suppressEvent !== true){
22199 this.fireEvent('select', this, o, v);
22204 getValue: function()
22209 onClick: function(e)
22211 e.stopPropagation();
22212 e.preventDefault();
22214 var target = e.getTarget();
22216 if(target.nodeName.toLowerCase() === 'i'){
22217 target = Roo.get(target).dom.parentNode;
22220 var nodeName = target.nodeName;
22221 var className = target.className;
22222 var html = target.innerHTML;
22224 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22228 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22230 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22236 picker : function()
22238 return this.pickerEl;
22241 fillMonths: function()
22244 var months = this.picker().select('>.datepicker-months td', true).first();
22246 months.dom.innerHTML = '';
22252 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22255 months.createChild(month);
22264 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22265 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22268 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22269 e.removeClass('active');
22271 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22272 e.addClass('active');
22279 if(this.isInline) {
22283 this.picker().removeClass(['bottom', 'top']);
22285 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22287 * place to the top of element!
22291 this.picker().addClass('top');
22292 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22297 this.picker().addClass('bottom');
22299 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22302 onFocus : function()
22304 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22308 onBlur : function()
22310 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22312 var d = this.inputEl().getValue();
22321 this.picker().show();
22322 this.picker().select('>.datepicker-months', true).first().show();
22326 this.fireEvent('show', this, this.date);
22331 if(this.isInline) {
22334 this.picker().hide();
22335 this.fireEvent('hide', this, this.date);
22339 onMousedown: function(e)
22341 e.stopPropagation();
22342 e.preventDefault();
22347 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22351 fireKey: function(e)
22353 if (!this.picker().isVisible()){
22354 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22365 e.preventDefault();
22369 dir = e.keyCode == 37 ? -1 : 1;
22371 this.vIndex = this.vIndex + dir;
22373 if(this.vIndex < 0){
22377 if(this.vIndex > 11){
22381 if(isNaN(this.vIndex)){
22385 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22391 dir = e.keyCode == 38 ? -1 : 1;
22393 this.vIndex = this.vIndex + dir * 4;
22395 if(this.vIndex < 0){
22399 if(this.vIndex > 11){
22403 if(isNaN(this.vIndex)){
22407 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22412 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22413 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22417 e.preventDefault();
22420 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22421 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22437 this.picker().remove();
22442 Roo.apply(Roo.bootstrap.MonthField, {
22461 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22462 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22467 Roo.apply(Roo.bootstrap.MonthField, {
22471 cls: 'datepicker dropdown-menu roo-dynamic',
22475 cls: 'datepicker-months',
22479 cls: 'table-condensed',
22481 Roo.bootstrap.DateField.content
22501 * @class Roo.bootstrap.CheckBox
22502 * @extends Roo.bootstrap.Input
22503 * Bootstrap CheckBox class
22505 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22506 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22507 * @cfg {String} boxLabel The text that appears beside the checkbox
22508 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22509 * @cfg {Boolean} checked initnal the element
22510 * @cfg {Boolean} inline inline the element (default false)
22511 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22512 * @cfg {String} tooltip label tooltip
22515 * Create a new CheckBox
22516 * @param {Object} config The config object
22519 Roo.bootstrap.CheckBox = function(config){
22520 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22525 * Fires when the element is checked or unchecked.
22526 * @param {Roo.bootstrap.CheckBox} this This input
22527 * @param {Boolean} checked The new checked value
22532 * Fires when the element is click.
22533 * @param {Roo.bootstrap.CheckBox} this This input
22540 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22542 inputType: 'checkbox',
22551 // checkbox success does not make any sense really..
22556 getAutoCreate : function()
22558 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22564 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22567 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22573 type : this.inputType,
22574 value : this.inputValue,
22575 cls : 'roo-' + this.inputType, //'form-box',
22576 placeholder : this.placeholder || ''
22580 if(this.inputType != 'radio'){
22584 cls : 'roo-hidden-value',
22585 value : this.checked ? this.inputValue : this.valueOff
22590 if (this.weight) { // Validity check?
22591 cfg.cls += " " + this.inputType + "-" + this.weight;
22594 if (this.disabled) {
22595 input.disabled=true;
22599 input.checked = this.checked;
22604 input.name = this.name;
22606 if(this.inputType != 'radio'){
22607 hidden.name = this.name;
22608 input.name = '_hidden_' + this.name;
22613 input.cls += ' input-' + this.size;
22618 ['xs','sm','md','lg'].map(function(size){
22619 if (settings[size]) {
22620 cfg.cls += ' col-' + size + '-' + settings[size];
22624 var inputblock = input;
22626 if (this.before || this.after) {
22629 cls : 'input-group',
22634 inputblock.cn.push({
22636 cls : 'input-group-addon',
22641 inputblock.cn.push(input);
22643 if(this.inputType != 'radio'){
22644 inputblock.cn.push(hidden);
22648 inputblock.cn.push({
22650 cls : 'input-group-addon',
22656 var boxLabelCfg = false;
22662 //'for': id, // box label is handled by onclick - so no for...
22664 html: this.boxLabel
22667 boxLabelCfg.tooltip = this.tooltip;
22673 if (align ==='left' && this.fieldLabel.length) {
22674 // Roo.log("left and has label");
22679 cls : 'control-label',
22680 html : this.fieldLabel
22691 cfg.cn[1].cn.push(boxLabelCfg);
22694 if(this.labelWidth > 12){
22695 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22698 if(this.labelWidth < 13 && this.labelmd == 0){
22699 this.labelmd = this.labelWidth;
22702 if(this.labellg > 0){
22703 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22704 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22707 if(this.labelmd > 0){
22708 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22709 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22712 if(this.labelsm > 0){
22713 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22714 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22717 if(this.labelxs > 0){
22718 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22719 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22722 } else if ( this.fieldLabel.length) {
22723 // Roo.log(" label");
22727 tag: this.boxLabel ? 'span' : 'label',
22729 cls: 'control-label box-input-label',
22730 //cls : 'input-group-addon',
22731 html : this.fieldLabel
22738 cfg.cn.push(boxLabelCfg);
22743 // Roo.log(" no label && no align");
22744 cfg.cn = [ inputblock ] ;
22746 cfg.cn.push(boxLabelCfg);
22754 if(this.inputType != 'radio'){
22755 cfg.cn.push(hidden);
22763 * return the real input element.
22765 inputEl: function ()
22767 return this.el.select('input.roo-' + this.inputType,true).first();
22769 hiddenEl: function ()
22771 return this.el.select('input.roo-hidden-value',true).first();
22774 labelEl: function()
22776 return this.el.select('label.control-label',true).first();
22778 /* depricated... */
22782 return this.labelEl();
22785 boxLabelEl: function()
22787 return this.el.select('label.box-label',true).first();
22790 initEvents : function()
22792 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22794 this.inputEl().on('click', this.onClick, this);
22796 if (this.boxLabel) {
22797 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
22800 this.startValue = this.getValue();
22803 Roo.bootstrap.CheckBox.register(this);
22807 onClick : function(e)
22809 if(this.fireEvent('click', this, e) !== false){
22810 this.setChecked(!this.checked);
22815 setChecked : function(state,suppressEvent)
22817 this.startValue = this.getValue();
22819 if(this.inputType == 'radio'){
22821 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22822 e.dom.checked = false;
22825 this.inputEl().dom.checked = true;
22827 this.inputEl().dom.value = this.inputValue;
22829 if(suppressEvent !== true){
22830 this.fireEvent('check', this, true);
22838 this.checked = state;
22840 this.inputEl().dom.checked = state;
22843 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22845 if(suppressEvent !== true){
22846 this.fireEvent('check', this, state);
22852 getValue : function()
22854 if(this.inputType == 'radio'){
22855 return this.getGroupValue();
22858 return this.hiddenEl().dom.value;
22862 getGroupValue : function()
22864 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22868 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22871 setValue : function(v,suppressEvent)
22873 if(this.inputType == 'radio'){
22874 this.setGroupValue(v, suppressEvent);
22878 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22883 setGroupValue : function(v, suppressEvent)
22885 this.startValue = this.getValue();
22887 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22888 e.dom.checked = false;
22890 if(e.dom.value == v){
22891 e.dom.checked = true;
22895 if(suppressEvent !== true){
22896 this.fireEvent('check', this, true);
22904 validate : function()
22906 if(this.getVisibilityEl().hasClass('hidden')){
22912 (this.inputType == 'radio' && this.validateRadio()) ||
22913 (this.inputType == 'checkbox' && this.validateCheckbox())
22919 this.markInvalid();
22923 validateRadio : function()
22925 if(this.getVisibilityEl().hasClass('hidden')){
22929 if(this.allowBlank){
22935 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22936 if(!e.dom.checked){
22948 validateCheckbox : function()
22951 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22952 //return (this.getValue() == this.inputValue) ? true : false;
22955 var group = Roo.bootstrap.CheckBox.get(this.groupId);
22963 for(var i in group){
22964 if(group[i].el.isVisible(true)){
22972 for(var i in group){
22977 r = (group[i].getValue() == group[i].inputValue) ? true : false;
22984 * Mark this field as valid
22986 markValid : function()
22990 this.fireEvent('valid', this);
22992 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22995 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23002 if(this.inputType == 'radio'){
23003 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23004 var fg = e.findParent('.form-group', false, true);
23005 if (Roo.bootstrap.version == 3) {
23006 fg.removeClass([_this.invalidClass, _this.validClass]);
23007 fg.addClass(_this.validClass);
23009 fg.removeClass(['is-valid', 'is-invalid']);
23010 fg.addClass('is-valid');
23018 var fg = this.el.findParent('.form-group', false, true);
23019 if (Roo.bootstrap.version == 3) {
23020 fg.removeClass([this.invalidClass, this.validClass]);
23021 fg.addClass(this.validClass);
23023 fg.removeClass(['is-valid', 'is-invalid']);
23024 fg.addClass('is-valid');
23029 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23035 for(var i in group){
23036 var fg = group[i].el.findParent('.form-group', false, true);
23037 if (Roo.bootstrap.version == 3) {
23038 fg.removeClass([this.invalidClass, this.validClass]);
23039 fg.addClass(this.validClass);
23041 fg.removeClass(['is-valid', 'is-invalid']);
23042 fg.addClass('is-valid');
23048 * Mark this field as invalid
23049 * @param {String} msg The validation message
23051 markInvalid : function(msg)
23053 if(this.allowBlank){
23059 this.fireEvent('invalid', this, msg);
23061 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23064 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23068 label.markInvalid();
23071 if(this.inputType == 'radio'){
23073 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23074 var fg = e.findParent('.form-group', false, true);
23075 if (Roo.bootstrap.version == 3) {
23076 fg.removeClass([_this.invalidClass, _this.validClass]);
23077 fg.addClass(_this.invalidClass);
23079 fg.removeClass(['is-invalid', 'is-valid']);
23080 fg.addClass('is-invalid');
23088 var fg = this.el.findParent('.form-group', false, true);
23089 if (Roo.bootstrap.version == 3) {
23090 fg.removeClass([_this.invalidClass, _this.validClass]);
23091 fg.addClass(_this.invalidClass);
23093 fg.removeClass(['is-invalid', 'is-valid']);
23094 fg.addClass('is-invalid');
23099 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23105 for(var i in group){
23106 var fg = group[i].el.findParent('.form-group', false, true);
23107 if (Roo.bootstrap.version == 3) {
23108 fg.removeClass([_this.invalidClass, _this.validClass]);
23109 fg.addClass(_this.invalidClass);
23111 fg.removeClass(['is-invalid', 'is-valid']);
23112 fg.addClass('is-invalid');
23118 clearInvalid : function()
23120 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23122 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23124 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23126 if (label && label.iconEl) {
23127 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23128 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23132 disable : function()
23134 if(this.inputType != 'radio'){
23135 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23142 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23143 _this.getActionEl().addClass(this.disabledClass);
23144 e.dom.disabled = true;
23148 this.disabled = true;
23149 this.fireEvent("disable", this);
23153 enable : function()
23155 if(this.inputType != 'radio'){
23156 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23163 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23164 _this.getActionEl().removeClass(this.disabledClass);
23165 e.dom.disabled = false;
23169 this.disabled = false;
23170 this.fireEvent("enable", this);
23174 setBoxLabel : function(v)
23179 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23185 Roo.apply(Roo.bootstrap.CheckBox, {
23190 * register a CheckBox Group
23191 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23193 register : function(checkbox)
23195 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23196 this.groups[checkbox.groupId] = {};
23199 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23203 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23207 * fetch a CheckBox Group based on the group ID
23208 * @param {string} the group ID
23209 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23211 get: function(groupId) {
23212 if (typeof(this.groups[groupId]) == 'undefined') {
23216 return this.groups[groupId] ;
23229 * @class Roo.bootstrap.Radio
23230 * @extends Roo.bootstrap.Component
23231 * Bootstrap Radio class
23232 * @cfg {String} boxLabel - the label associated
23233 * @cfg {String} value - the value of radio
23236 * Create a new Radio
23237 * @param {Object} config The config object
23239 Roo.bootstrap.Radio = function(config){
23240 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23244 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23250 getAutoCreate : function()
23254 cls : 'form-group radio',
23259 html : this.boxLabel
23267 initEvents : function()
23269 this.parent().register(this);
23271 this.el.on('click', this.onClick, this);
23275 onClick : function(e)
23277 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23278 this.setChecked(true);
23282 setChecked : function(state, suppressEvent)
23284 this.parent().setValue(this.value, suppressEvent);
23288 setBoxLabel : function(v)
23293 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23308 * @class Roo.bootstrap.SecurePass
23309 * @extends Roo.bootstrap.Input
23310 * Bootstrap SecurePass class
23314 * Create a new SecurePass
23315 * @param {Object} config The config object
23318 Roo.bootstrap.SecurePass = function (config) {
23319 // these go here, so the translation tool can replace them..
23321 PwdEmpty: "Please type a password, and then retype it to confirm.",
23322 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23323 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23324 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23325 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23326 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23327 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23328 TooWeak: "Your password is Too Weak."
23330 this.meterLabel = "Password strength:";
23331 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23332 this.meterClass = [
23333 "roo-password-meter-tooweak",
23334 "roo-password-meter-weak",
23335 "roo-password-meter-medium",
23336 "roo-password-meter-strong",
23337 "roo-password-meter-grey"
23342 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23345 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23347 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23349 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23350 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23351 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23352 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23353 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23354 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23355 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23365 * @cfg {String/Object} Label for the strength meter (defaults to
23366 * 'Password strength:')
23371 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23372 * ['Weak', 'Medium', 'Strong'])
23375 pwdStrengths: false,
23388 initEvents: function ()
23390 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23392 if (this.el.is('input[type=password]') && Roo.isSafari) {
23393 this.el.on('keydown', this.SafariOnKeyDown, this);
23396 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23399 onRender: function (ct, position)
23401 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23402 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23403 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23405 this.trigger.createChild({
23410 cls: 'roo-password-meter-grey col-xs-12',
23413 //width: this.meterWidth + 'px'
23417 cls: 'roo-password-meter-text'
23423 if (this.hideTrigger) {
23424 this.trigger.setDisplayed(false);
23426 this.setSize(this.width || '', this.height || '');
23429 onDestroy: function ()
23431 if (this.trigger) {
23432 this.trigger.removeAllListeners();
23433 this.trigger.remove();
23436 this.wrap.remove();
23438 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23441 checkStrength: function ()
23443 var pwd = this.inputEl().getValue();
23444 if (pwd == this._lastPwd) {
23449 if (this.ClientSideStrongPassword(pwd)) {
23451 } else if (this.ClientSideMediumPassword(pwd)) {
23453 } else if (this.ClientSideWeakPassword(pwd)) {
23459 Roo.log('strength1: ' + strength);
23461 //var pm = this.trigger.child('div/div/div').dom;
23462 var pm = this.trigger.child('div/div');
23463 pm.removeClass(this.meterClass);
23464 pm.addClass(this.meterClass[strength]);
23467 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23469 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23471 this._lastPwd = pwd;
23475 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23477 this._lastPwd = '';
23479 var pm = this.trigger.child('div/div');
23480 pm.removeClass(this.meterClass);
23481 pm.addClass('roo-password-meter-grey');
23484 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23487 this.inputEl().dom.type='password';
23490 validateValue: function (value)
23492 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23495 if (value.length == 0) {
23496 if (this.allowBlank) {
23497 this.clearInvalid();
23501 this.markInvalid(this.errors.PwdEmpty);
23502 this.errorMsg = this.errors.PwdEmpty;
23510 if (!value.match(/[\x21-\x7e]+/)) {
23511 this.markInvalid(this.errors.PwdBadChar);
23512 this.errorMsg = this.errors.PwdBadChar;
23515 if (value.length < 6) {
23516 this.markInvalid(this.errors.PwdShort);
23517 this.errorMsg = this.errors.PwdShort;
23520 if (value.length > 16) {
23521 this.markInvalid(this.errors.PwdLong);
23522 this.errorMsg = this.errors.PwdLong;
23526 if (this.ClientSideStrongPassword(value)) {
23528 } else if (this.ClientSideMediumPassword(value)) {
23530 } else if (this.ClientSideWeakPassword(value)) {
23537 if (strength < 2) {
23538 //this.markInvalid(this.errors.TooWeak);
23539 this.errorMsg = this.errors.TooWeak;
23544 console.log('strength2: ' + strength);
23546 //var pm = this.trigger.child('div/div/div').dom;
23548 var pm = this.trigger.child('div/div');
23549 pm.removeClass(this.meterClass);
23550 pm.addClass(this.meterClass[strength]);
23552 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23554 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23556 this.errorMsg = '';
23560 CharacterSetChecks: function (type)
23563 this.fResult = false;
23566 isctype: function (character, type)
23569 case this.kCapitalLetter:
23570 if (character >= 'A' && character <= 'Z') {
23575 case this.kSmallLetter:
23576 if (character >= 'a' && character <= 'z') {
23582 if (character >= '0' && character <= '9') {
23587 case this.kPunctuation:
23588 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23599 IsLongEnough: function (pwd, size)
23601 return !(pwd == null || isNaN(size) || pwd.length < size);
23604 SpansEnoughCharacterSets: function (word, nb)
23606 if (!this.IsLongEnough(word, nb))
23611 var characterSetChecks = new Array(
23612 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23613 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23616 for (var index = 0; index < word.length; ++index) {
23617 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23618 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23619 characterSetChecks[nCharSet].fResult = true;
23626 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23627 if (characterSetChecks[nCharSet].fResult) {
23632 if (nCharSets < nb) {
23638 ClientSideStrongPassword: function (pwd)
23640 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23643 ClientSideMediumPassword: function (pwd)
23645 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23648 ClientSideWeakPassword: function (pwd)
23650 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23653 })//<script type="text/javascript">
23656 * Based Ext JS Library 1.1.1
23657 * Copyright(c) 2006-2007, Ext JS, LLC.
23663 * @class Roo.HtmlEditorCore
23664 * @extends Roo.Component
23665 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23667 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23670 Roo.HtmlEditorCore = function(config){
23673 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23678 * @event initialize
23679 * Fires when the editor is fully initialized (including the iframe)
23680 * @param {Roo.HtmlEditorCore} this
23685 * Fires when the editor is first receives the focus. Any insertion must wait
23686 * until after this event.
23687 * @param {Roo.HtmlEditorCore} this
23691 * @event beforesync
23692 * Fires before the textarea is updated with content from the editor iframe. Return false
23693 * to cancel the sync.
23694 * @param {Roo.HtmlEditorCore} this
23695 * @param {String} html
23699 * @event beforepush
23700 * Fires before the iframe editor is updated with content from the textarea. Return false
23701 * to cancel the push.
23702 * @param {Roo.HtmlEditorCore} this
23703 * @param {String} html
23708 * Fires when the textarea is updated with content from the editor iframe.
23709 * @param {Roo.HtmlEditorCore} this
23710 * @param {String} html
23715 * Fires when the iframe editor is updated with content from the textarea.
23716 * @param {Roo.HtmlEditorCore} this
23717 * @param {String} html
23722 * @event editorevent
23723 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23724 * @param {Roo.HtmlEditorCore} this
23730 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23732 // defaults : white / black...
23733 this.applyBlacklists();
23740 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
23744 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
23750 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23755 * @cfg {Number} height (in pixels)
23759 * @cfg {Number} width (in pixels)
23764 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23767 stylesheets: false,
23772 // private properties
23773 validationEvent : false,
23775 initialized : false,
23777 sourceEditMode : false,
23778 onFocus : Roo.emptyFn,
23780 hideMode:'offsets',
23784 // blacklist + whitelisted elements..
23791 * Protected method that will not generally be called directly. It
23792 * is called when the editor initializes the iframe with HTML contents. Override this method if you
23793 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23795 getDocMarkup : function(){
23799 // inherit styels from page...??
23800 if (this.stylesheets === false) {
23802 Roo.get(document.head).select('style').each(function(node) {
23803 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23806 Roo.get(document.head).select('link').each(function(node) {
23807 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23810 } else if (!this.stylesheets.length) {
23812 st = '<style type="text/css">' +
23813 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23816 for (var i in this.stylesheets) {
23817 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
23822 st += '<style type="text/css">' +
23823 'IMG { cursor: pointer } ' +
23826 var cls = 'roo-htmleditor-body';
23828 if(this.bodyCls.length){
23829 cls += ' ' + this.bodyCls;
23832 return '<html><head>' + st +
23833 //<style type="text/css">' +
23834 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23836 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
23840 onRender : function(ct, position)
23843 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23844 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23847 this.el.dom.style.border = '0 none';
23848 this.el.dom.setAttribute('tabIndex', -1);
23849 this.el.addClass('x-hidden hide');
23853 if(Roo.isIE){ // fix IE 1px bogus margin
23854 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23858 this.frameId = Roo.id();
23862 var iframe = this.owner.wrap.createChild({
23864 cls: 'form-control', // bootstrap..
23866 name: this.frameId,
23867 frameBorder : 'no',
23868 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
23873 this.iframe = iframe.dom;
23875 this.assignDocWin();
23877 this.doc.designMode = 'on';
23880 this.doc.write(this.getDocMarkup());
23884 var task = { // must defer to wait for browser to be ready
23886 //console.log("run task?" + this.doc.readyState);
23887 this.assignDocWin();
23888 if(this.doc.body || this.doc.readyState == 'complete'){
23890 this.doc.designMode="on";
23894 Roo.TaskMgr.stop(task);
23895 this.initEditor.defer(10, this);
23902 Roo.TaskMgr.start(task);
23907 onResize : function(w, h)
23909 Roo.log('resize: ' +w + ',' + h );
23910 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23914 if(typeof w == 'number'){
23916 this.iframe.style.width = w + 'px';
23918 if(typeof h == 'number'){
23920 this.iframe.style.height = h + 'px';
23922 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23929 * Toggles the editor between standard and source edit mode.
23930 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23932 toggleSourceEdit : function(sourceEditMode){
23934 this.sourceEditMode = sourceEditMode === true;
23936 if(this.sourceEditMode){
23938 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
23941 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23942 //this.iframe.className = '';
23945 //this.setSize(this.owner.wrap.getSize());
23946 //this.fireEvent('editmodechange', this, this.sourceEditMode);
23953 * Protected method that will not generally be called directly. If you need/want
23954 * custom HTML cleanup, this is the method you should override.
23955 * @param {String} html The HTML to be cleaned
23956 * return {String} The cleaned HTML
23958 cleanHtml : function(html){
23959 html = String(html);
23960 if(html.length > 5){
23961 if(Roo.isSafari){ // strip safari nonsense
23962 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23965 if(html == ' '){
23972 * HTML Editor -> Textarea
23973 * Protected method that will not generally be called directly. Syncs the contents
23974 * of the editor iframe with the textarea.
23976 syncValue : function(){
23977 if(this.initialized){
23978 var bd = (this.doc.body || this.doc.documentElement);
23979 //this.cleanUpPaste(); -- this is done else where and causes havoc..
23980 var html = bd.innerHTML;
23982 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23983 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23985 html = '<div style="'+m[0]+'">' + html + '</div>';
23988 html = this.cleanHtml(html);
23989 // fix up the special chars.. normaly like back quotes in word...
23990 // however we do not want to do this with chinese..
23991 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23993 var cc = match.charCodeAt();
23995 // Get the character value, handling surrogate pairs
23996 if (match.length == 2) {
23997 // It's a surrogate pair, calculate the Unicode code point
23998 var high = match.charCodeAt(0) - 0xD800;
23999 var low = match.charCodeAt(1) - 0xDC00;
24000 cc = (high * 0x400) + low + 0x10000;
24002 (cc >= 0x4E00 && cc < 0xA000 ) ||
24003 (cc >= 0x3400 && cc < 0x4E00 ) ||
24004 (cc >= 0xf900 && cc < 0xfb00 )
24009 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24010 return "&#" + cc + ";";
24017 if(this.owner.fireEvent('beforesync', this, html) !== false){
24018 this.el.dom.value = html;
24019 this.owner.fireEvent('sync', this, html);
24025 * Protected method that will not generally be called directly. Pushes the value of the textarea
24026 * into the iframe editor.
24028 pushValue : function(){
24029 if(this.initialized){
24030 var v = this.el.dom.value.trim();
24032 // if(v.length < 1){
24036 if(this.owner.fireEvent('beforepush', this, v) !== false){
24037 var d = (this.doc.body || this.doc.documentElement);
24039 this.cleanUpPaste();
24040 this.el.dom.value = d.innerHTML;
24041 this.owner.fireEvent('push', this, v);
24047 deferFocus : function(){
24048 this.focus.defer(10, this);
24052 focus : function(){
24053 if(this.win && !this.sourceEditMode){
24060 assignDocWin: function()
24062 var iframe = this.iframe;
24065 this.doc = iframe.contentWindow.document;
24066 this.win = iframe.contentWindow;
24068 // if (!Roo.get(this.frameId)) {
24071 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24072 // this.win = Roo.get(this.frameId).dom.contentWindow;
24074 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24078 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24079 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24084 initEditor : function(){
24085 //console.log("INIT EDITOR");
24086 this.assignDocWin();
24090 this.doc.designMode="on";
24092 this.doc.write(this.getDocMarkup());
24095 var dbody = (this.doc.body || this.doc.documentElement);
24096 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24097 // this copies styles from the containing element into thsi one..
24098 // not sure why we need all of this..
24099 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24101 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24102 //ss['background-attachment'] = 'fixed'; // w3c
24103 dbody.bgProperties = 'fixed'; // ie
24104 //Roo.DomHelper.applyStyles(dbody, ss);
24105 Roo.EventManager.on(this.doc, {
24106 //'mousedown': this.onEditorEvent,
24107 'mouseup': this.onEditorEvent,
24108 'dblclick': this.onEditorEvent,
24109 'click': this.onEditorEvent,
24110 'keyup': this.onEditorEvent,
24115 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24117 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24118 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24120 this.initialized = true;
24122 this.owner.fireEvent('initialize', this);
24127 onDestroy : function(){
24133 //for (var i =0; i < this.toolbars.length;i++) {
24134 // // fixme - ask toolbars for heights?
24135 // this.toolbars[i].onDestroy();
24138 //this.wrap.dom.innerHTML = '';
24139 //this.wrap.remove();
24144 onFirstFocus : function(){
24146 this.assignDocWin();
24149 this.activated = true;
24152 if(Roo.isGecko){ // prevent silly gecko errors
24154 var s = this.win.getSelection();
24155 if(!s.focusNode || s.focusNode.nodeType != 3){
24156 var r = s.getRangeAt(0);
24157 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24162 this.execCmd('useCSS', true);
24163 this.execCmd('styleWithCSS', false);
24166 this.owner.fireEvent('activate', this);
24170 adjustFont: function(btn){
24171 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24172 //if(Roo.isSafari){ // safari
24175 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24176 if(Roo.isSafari){ // safari
24177 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24178 v = (v < 10) ? 10 : v;
24179 v = (v > 48) ? 48 : v;
24180 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24185 v = Math.max(1, v+adjust);
24187 this.execCmd('FontSize', v );
24190 onEditorEvent : function(e)
24192 this.owner.fireEvent('editorevent', this, e);
24193 // this.updateToolbar();
24194 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24197 insertTag : function(tg)
24199 // could be a bit smarter... -> wrap the current selected tRoo..
24200 if (tg.toLowerCase() == 'span' ||
24201 tg.toLowerCase() == 'code' ||
24202 tg.toLowerCase() == 'sup' ||
24203 tg.toLowerCase() == 'sub'
24206 range = this.createRange(this.getSelection());
24207 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24208 wrappingNode.appendChild(range.extractContents());
24209 range.insertNode(wrappingNode);
24216 this.execCmd("formatblock", tg);
24220 insertText : function(txt)
24224 var range = this.createRange();
24225 range.deleteContents();
24226 //alert(Sender.getAttribute('label'));
24228 range.insertNode(this.doc.createTextNode(txt));
24234 * Executes a Midas editor command on the editor document and performs necessary focus and
24235 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24236 * @param {String} cmd The Midas command
24237 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24239 relayCmd : function(cmd, value){
24241 this.execCmd(cmd, value);
24242 this.owner.fireEvent('editorevent', this);
24243 //this.updateToolbar();
24244 this.owner.deferFocus();
24248 * Executes a Midas editor command directly on the editor document.
24249 * For visual commands, you should use {@link #relayCmd} instead.
24250 * <b>This should only be called after the editor is initialized.</b>
24251 * @param {String} cmd The Midas command
24252 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24254 execCmd : function(cmd, value){
24255 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24262 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24264 * @param {String} text | dom node..
24266 insertAtCursor : function(text)
24269 if(!this.activated){
24275 var r = this.doc.selection.createRange();
24286 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24290 // from jquery ui (MIT licenced)
24292 var win = this.win;
24294 if (win.getSelection && win.getSelection().getRangeAt) {
24295 range = win.getSelection().getRangeAt(0);
24296 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24297 range.insertNode(node);
24298 } else if (win.document.selection && win.document.selection.createRange) {
24299 // no firefox support
24300 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24301 win.document.selection.createRange().pasteHTML(txt);
24303 // no firefox support
24304 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24305 this.execCmd('InsertHTML', txt);
24314 mozKeyPress : function(e){
24316 var c = e.getCharCode(), cmd;
24319 c = String.fromCharCode(c).toLowerCase();
24333 this.cleanUpPaste.defer(100, this);
24341 e.preventDefault();
24349 fixKeys : function(){ // load time branching for fastest keydown performance
24351 return function(e){
24352 var k = e.getKey(), r;
24355 r = this.doc.selection.createRange();
24358 r.pasteHTML('    ');
24365 r = this.doc.selection.createRange();
24367 var target = r.parentElement();
24368 if(!target || target.tagName.toLowerCase() != 'li'){
24370 r.pasteHTML('<br />');
24376 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24377 this.cleanUpPaste.defer(100, this);
24383 }else if(Roo.isOpera){
24384 return function(e){
24385 var k = e.getKey();
24389 this.execCmd('InsertHTML','    ');
24392 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24393 this.cleanUpPaste.defer(100, this);
24398 }else if(Roo.isSafari){
24399 return function(e){
24400 var k = e.getKey();
24404 this.execCmd('InsertText','\t');
24408 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24409 this.cleanUpPaste.defer(100, this);
24417 getAllAncestors: function()
24419 var p = this.getSelectedNode();
24422 a.push(p); // push blank onto stack..
24423 p = this.getParentElement();
24427 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24431 a.push(this.doc.body);
24435 lastSelNode : false,
24438 getSelection : function()
24440 this.assignDocWin();
24441 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24444 getSelectedNode: function()
24446 // this may only work on Gecko!!!
24448 // should we cache this!!!!
24453 var range = this.createRange(this.getSelection()).cloneRange();
24456 var parent = range.parentElement();
24458 var testRange = range.duplicate();
24459 testRange.moveToElementText(parent);
24460 if (testRange.inRange(range)) {
24463 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24466 parent = parent.parentElement;
24471 // is ancestor a text element.
24472 var ac = range.commonAncestorContainer;
24473 if (ac.nodeType == 3) {
24474 ac = ac.parentNode;
24477 var ar = ac.childNodes;
24480 var other_nodes = [];
24481 var has_other_nodes = false;
24482 for (var i=0;i<ar.length;i++) {
24483 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24486 // fullly contained node.
24488 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24493 // probably selected..
24494 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24495 other_nodes.push(ar[i]);
24499 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24504 has_other_nodes = true;
24506 if (!nodes.length && other_nodes.length) {
24507 nodes= other_nodes;
24509 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24515 createRange: function(sel)
24517 // this has strange effects when using with
24518 // top toolbar - not sure if it's a great idea.
24519 //this.editor.contentWindow.focus();
24520 if (typeof sel != "undefined") {
24522 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24524 return this.doc.createRange();
24527 return this.doc.createRange();
24530 getParentElement: function()
24533 this.assignDocWin();
24534 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24536 var range = this.createRange(sel);
24539 var p = range.commonAncestorContainer;
24540 while (p.nodeType == 3) { // text node
24551 * Range intersection.. the hard stuff...
24555 * [ -- selected range --- ]
24559 * if end is before start or hits it. fail.
24560 * if start is after end or hits it fail.
24562 * if either hits (but other is outside. - then it's not
24568 // @see http://www.thismuchiknow.co.uk/?p=64.
24569 rangeIntersectsNode : function(range, node)
24571 var nodeRange = node.ownerDocument.createRange();
24573 nodeRange.selectNode(node);
24575 nodeRange.selectNodeContents(node);
24578 var rangeStartRange = range.cloneRange();
24579 rangeStartRange.collapse(true);
24581 var rangeEndRange = range.cloneRange();
24582 rangeEndRange.collapse(false);
24584 var nodeStartRange = nodeRange.cloneRange();
24585 nodeStartRange.collapse(true);
24587 var nodeEndRange = nodeRange.cloneRange();
24588 nodeEndRange.collapse(false);
24590 return rangeStartRange.compareBoundaryPoints(
24591 Range.START_TO_START, nodeEndRange) == -1 &&
24592 rangeEndRange.compareBoundaryPoints(
24593 Range.START_TO_START, nodeStartRange) == 1;
24597 rangeCompareNode : function(range, node)
24599 var nodeRange = node.ownerDocument.createRange();
24601 nodeRange.selectNode(node);
24603 nodeRange.selectNodeContents(node);
24607 range.collapse(true);
24609 nodeRange.collapse(true);
24611 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24612 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
24614 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24616 var nodeIsBefore = ss == 1;
24617 var nodeIsAfter = ee == -1;
24619 if (nodeIsBefore && nodeIsAfter) {
24622 if (!nodeIsBefore && nodeIsAfter) {
24623 return 1; //right trailed.
24626 if (nodeIsBefore && !nodeIsAfter) {
24627 return 2; // left trailed.
24633 // private? - in a new class?
24634 cleanUpPaste : function()
24636 // cleans up the whole document..
24637 Roo.log('cleanuppaste');
24639 this.cleanUpChildren(this.doc.body);
24640 var clean = this.cleanWordChars(this.doc.body.innerHTML);
24641 if (clean != this.doc.body.innerHTML) {
24642 this.doc.body.innerHTML = clean;
24647 cleanWordChars : function(input) {// change the chars to hex code
24648 var he = Roo.HtmlEditorCore;
24650 var output = input;
24651 Roo.each(he.swapCodes, function(sw) {
24652 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24654 output = output.replace(swapper, sw[1]);
24661 cleanUpChildren : function (n)
24663 if (!n.childNodes.length) {
24666 for (var i = n.childNodes.length-1; i > -1 ; i--) {
24667 this.cleanUpChild(n.childNodes[i]);
24674 cleanUpChild : function (node)
24677 //console.log(node);
24678 if (node.nodeName == "#text") {
24679 // clean up silly Windows -- stuff?
24682 if (node.nodeName == "#comment") {
24683 node.parentNode.removeChild(node);
24684 // clean up silly Windows -- stuff?
24687 var lcname = node.tagName.toLowerCase();
24688 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24689 // whitelist of tags..
24691 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24693 node.parentNode.removeChild(node);
24698 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24700 // spans with no attributes - just remove them..
24701 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
24702 remove_keep_children = true;
24705 // remove <a name=....> as rendering on yahoo mailer is borked with this.
24706 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24708 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24709 // remove_keep_children = true;
24712 if (remove_keep_children) {
24713 this.cleanUpChildren(node);
24714 // inserts everything just before this node...
24715 while (node.childNodes.length) {
24716 var cn = node.childNodes[0];
24717 node.removeChild(cn);
24718 node.parentNode.insertBefore(cn, node);
24720 node.parentNode.removeChild(node);
24724 if (!node.attributes || !node.attributes.length) {
24729 this.cleanUpChildren(node);
24733 function cleanAttr(n,v)
24736 if (v.match(/^\./) || v.match(/^\//)) {
24739 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24742 if (v.match(/^#/)) {
24745 if (v.match(/^\{/)) { // allow template editing.
24748 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24749 node.removeAttribute(n);
24753 var cwhite = this.cwhite;
24754 var cblack = this.cblack;
24756 function cleanStyle(n,v)
24758 if (v.match(/expression/)) { //XSS?? should we even bother..
24759 node.removeAttribute(n);
24763 var parts = v.split(/;/);
24766 Roo.each(parts, function(p) {
24767 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24771 var l = p.split(':').shift().replace(/\s+/g,'');
24772 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24774 if ( cwhite.length && cblack.indexOf(l) > -1) {
24775 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24776 //node.removeAttribute(n);
24780 // only allow 'c whitelisted system attributes'
24781 if ( cwhite.length && cwhite.indexOf(l) < 0) {
24782 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24783 //node.removeAttribute(n);
24793 if (clean.length) {
24794 node.setAttribute(n, clean.join(';'));
24796 node.removeAttribute(n);
24802 for (var i = node.attributes.length-1; i > -1 ; i--) {
24803 var a = node.attributes[i];
24806 if (a.name.toLowerCase().substr(0,2)=='on') {
24807 node.removeAttribute(a.name);
24810 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24811 node.removeAttribute(a.name);
24814 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24815 cleanAttr(a.name,a.value); // fixme..
24818 if (a.name == 'style') {
24819 cleanStyle(a.name,a.value);
24822 /// clean up MS crap..
24823 // tecnically this should be a list of valid class'es..
24826 if (a.name == 'class') {
24827 if (a.value.match(/^Mso/)) {
24828 node.removeAttribute('class');
24831 if (a.value.match(/^body$/)) {
24832 node.removeAttribute('class');
24843 this.cleanUpChildren(node);
24849 * Clean up MS wordisms...
24851 cleanWord : function(node)
24854 this.cleanWord(this.doc.body);
24859 node.nodeName == 'SPAN' &&
24860 !node.hasAttributes() &&
24861 node.childNodes.length == 1 &&
24862 node.firstChild.nodeName == "#text"
24864 var textNode = node.firstChild;
24865 node.removeChild(textNode);
24866 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24867 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24869 node.parentNode.insertBefore(textNode, node);
24870 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
24871 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24873 node.parentNode.removeChild(node);
24876 if (node.nodeName == "#text") {
24877 // clean up silly Windows -- stuff?
24880 if (node.nodeName == "#comment") {
24881 node.parentNode.removeChild(node);
24882 // clean up silly Windows -- stuff?
24886 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24887 node.parentNode.removeChild(node);
24890 //Roo.log(node.tagName);
24891 // remove - but keep children..
24892 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24893 //Roo.log('-- removed');
24894 while (node.childNodes.length) {
24895 var cn = node.childNodes[0];
24896 node.removeChild(cn);
24897 node.parentNode.insertBefore(cn, node);
24898 // move node to parent - and clean it..
24899 this.cleanWord(cn);
24901 node.parentNode.removeChild(node);
24902 /// no need to iterate chidlren = it's got none..
24903 //this.iterateChildren(node, this.cleanWord);
24907 if (node.className.length) {
24909 var cn = node.className.split(/\W+/);
24911 Roo.each(cn, function(cls) {
24912 if (cls.match(/Mso[a-zA-Z]+/)) {
24917 node.className = cna.length ? cna.join(' ') : '';
24919 node.removeAttribute("class");
24923 if (node.hasAttribute("lang")) {
24924 node.removeAttribute("lang");
24927 if (node.hasAttribute("style")) {
24929 var styles = node.getAttribute("style").split(";");
24931 Roo.each(styles, function(s) {
24932 if (!s.match(/:/)) {
24935 var kv = s.split(":");
24936 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24939 // what ever is left... we allow.
24942 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24943 if (!nstyle.length) {
24944 node.removeAttribute('style');
24947 this.iterateChildren(node, this.cleanWord);
24953 * iterateChildren of a Node, calling fn each time, using this as the scole..
24954 * @param {DomNode} node node to iterate children of.
24955 * @param {Function} fn method of this class to call on each item.
24957 iterateChildren : function(node, fn)
24959 if (!node.childNodes.length) {
24962 for (var i = node.childNodes.length-1; i > -1 ; i--) {
24963 fn.call(this, node.childNodes[i])
24969 * cleanTableWidths.
24971 * Quite often pasting from word etc.. results in tables with column and widths.
24972 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24975 cleanTableWidths : function(node)
24980 this.cleanTableWidths(this.doc.body);
24985 if (node.nodeName == "#text" || node.nodeName == "#comment") {
24988 Roo.log(node.tagName);
24989 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24990 this.iterateChildren(node, this.cleanTableWidths);
24993 if (node.hasAttribute('width')) {
24994 node.removeAttribute('width');
24998 if (node.hasAttribute("style")) {
25001 var styles = node.getAttribute("style").split(";");
25003 Roo.each(styles, function(s) {
25004 if (!s.match(/:/)) {
25007 var kv = s.split(":");
25008 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25011 // what ever is left... we allow.
25014 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25015 if (!nstyle.length) {
25016 node.removeAttribute('style');
25020 this.iterateChildren(node, this.cleanTableWidths);
25028 domToHTML : function(currentElement, depth, nopadtext) {
25030 depth = depth || 0;
25031 nopadtext = nopadtext || false;
25033 if (!currentElement) {
25034 return this.domToHTML(this.doc.body);
25037 //Roo.log(currentElement);
25039 var allText = false;
25040 var nodeName = currentElement.nodeName;
25041 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25043 if (nodeName == '#text') {
25045 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25050 if (nodeName != 'BODY') {
25053 // Prints the node tagName, such as <A>, <IMG>, etc
25056 for(i = 0; i < currentElement.attributes.length;i++) {
25058 var aname = currentElement.attributes.item(i).name;
25059 if (!currentElement.attributes.item(i).value.length) {
25062 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25065 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25074 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25077 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25082 // Traverse the tree
25084 var currentElementChild = currentElement.childNodes.item(i);
25085 var allText = true;
25086 var innerHTML = '';
25088 while (currentElementChild) {
25089 // Formatting code (indent the tree so it looks nice on the screen)
25090 var nopad = nopadtext;
25091 if (lastnode == 'SPAN') {
25095 if (currentElementChild.nodeName == '#text') {
25096 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25097 toadd = nopadtext ? toadd : toadd.trim();
25098 if (!nopad && toadd.length > 80) {
25099 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25101 innerHTML += toadd;
25104 currentElementChild = currentElement.childNodes.item(i);
25110 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25112 // Recursively traverse the tree structure of the child node
25113 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25114 lastnode = currentElementChild.nodeName;
25116 currentElementChild=currentElement.childNodes.item(i);
25122 // The remaining code is mostly for formatting the tree
25123 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25128 ret+= "</"+tagName+">";
25134 applyBlacklists : function()
25136 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25137 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25141 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25142 if (b.indexOf(tag) > -1) {
25145 this.white.push(tag);
25149 Roo.each(w, function(tag) {
25150 if (b.indexOf(tag) > -1) {
25153 if (this.white.indexOf(tag) > -1) {
25156 this.white.push(tag);
25161 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25162 if (w.indexOf(tag) > -1) {
25165 this.black.push(tag);
25169 Roo.each(b, function(tag) {
25170 if (w.indexOf(tag) > -1) {
25173 if (this.black.indexOf(tag) > -1) {
25176 this.black.push(tag);
25181 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25182 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25186 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25187 if (b.indexOf(tag) > -1) {
25190 this.cwhite.push(tag);
25194 Roo.each(w, function(tag) {
25195 if (b.indexOf(tag) > -1) {
25198 if (this.cwhite.indexOf(tag) > -1) {
25201 this.cwhite.push(tag);
25206 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25207 if (w.indexOf(tag) > -1) {
25210 this.cblack.push(tag);
25214 Roo.each(b, function(tag) {
25215 if (w.indexOf(tag) > -1) {
25218 if (this.cblack.indexOf(tag) > -1) {
25221 this.cblack.push(tag);
25226 setStylesheets : function(stylesheets)
25228 if(typeof(stylesheets) == 'string'){
25229 Roo.get(this.iframe.contentDocument.head).createChild({
25231 rel : 'stylesheet',
25240 Roo.each(stylesheets, function(s) {
25245 Roo.get(_this.iframe.contentDocument.head).createChild({
25247 rel : 'stylesheet',
25256 removeStylesheets : function()
25260 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25265 setStyle : function(style)
25267 Roo.get(this.iframe.contentDocument.head).createChild({
25276 // hide stuff that is not compatible
25290 * @event specialkey
25294 * @cfg {String} fieldClass @hide
25297 * @cfg {String} focusClass @hide
25300 * @cfg {String} autoCreate @hide
25303 * @cfg {String} inputType @hide
25306 * @cfg {String} invalidClass @hide
25309 * @cfg {String} invalidText @hide
25312 * @cfg {String} msgFx @hide
25315 * @cfg {String} validateOnBlur @hide
25319 Roo.HtmlEditorCore.white = [
25320 'area', 'br', 'img', 'input', 'hr', 'wbr',
25322 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25323 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25324 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25325 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25326 'table', 'ul', 'xmp',
25328 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25331 'dir', 'menu', 'ol', 'ul', 'dl',
25337 Roo.HtmlEditorCore.black = [
25338 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25340 'base', 'basefont', 'bgsound', 'blink', 'body',
25341 'frame', 'frameset', 'head', 'html', 'ilayer',
25342 'iframe', 'layer', 'link', 'meta', 'object',
25343 'script', 'style' ,'title', 'xml' // clean later..
25345 Roo.HtmlEditorCore.clean = [
25346 'script', 'style', 'title', 'xml'
25348 Roo.HtmlEditorCore.remove = [
25353 Roo.HtmlEditorCore.ablack = [
25357 Roo.HtmlEditorCore.aclean = [
25358 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25362 Roo.HtmlEditorCore.pwhite= [
25363 'http', 'https', 'mailto'
25366 // white listed style attributes.
25367 Roo.HtmlEditorCore.cwhite= [
25368 // 'text-align', /// default is to allow most things..
25374 // black listed style attributes.
25375 Roo.HtmlEditorCore.cblack= [
25376 // 'font-size' -- this can be set by the project
25380 Roo.HtmlEditorCore.swapCodes =[
25399 * @class Roo.bootstrap.HtmlEditor
25400 * @extends Roo.bootstrap.TextArea
25401 * Bootstrap HtmlEditor class
25404 * Create a new HtmlEditor
25405 * @param {Object} config The config object
25408 Roo.bootstrap.HtmlEditor = function(config){
25409 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25410 if (!this.toolbars) {
25411 this.toolbars = [];
25414 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25417 * @event initialize
25418 * Fires when the editor is fully initialized (including the iframe)
25419 * @param {HtmlEditor} this
25424 * Fires when the editor is first receives the focus. Any insertion must wait
25425 * until after this event.
25426 * @param {HtmlEditor} this
25430 * @event beforesync
25431 * Fires before the textarea is updated with content from the editor iframe. Return false
25432 * to cancel the sync.
25433 * @param {HtmlEditor} this
25434 * @param {String} html
25438 * @event beforepush
25439 * Fires before the iframe editor is updated with content from the textarea. Return false
25440 * to cancel the push.
25441 * @param {HtmlEditor} this
25442 * @param {String} html
25447 * Fires when the textarea is updated with content from the editor iframe.
25448 * @param {HtmlEditor} this
25449 * @param {String} html
25454 * Fires when the iframe editor is updated with content from the textarea.
25455 * @param {HtmlEditor} this
25456 * @param {String} html
25460 * @event editmodechange
25461 * Fires when the editor switches edit modes
25462 * @param {HtmlEditor} this
25463 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25465 editmodechange: true,
25467 * @event editorevent
25468 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25469 * @param {HtmlEditor} this
25473 * @event firstfocus
25474 * Fires when on first focus - needed by toolbars..
25475 * @param {HtmlEditor} this
25480 * Auto save the htmlEditor value as a file into Events
25481 * @param {HtmlEditor} this
25485 * @event savedpreview
25486 * preview the saved version of htmlEditor
25487 * @param {HtmlEditor} this
25494 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25498 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25503 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25508 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25513 * @cfg {Number} height (in pixels)
25517 * @cfg {Number} width (in pixels)
25522 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25525 stylesheets: false,
25530 // private properties
25531 validationEvent : false,
25533 initialized : false,
25536 onFocus : Roo.emptyFn,
25538 hideMode:'offsets',
25540 tbContainer : false,
25544 toolbarContainer :function() {
25545 return this.wrap.select('.x-html-editor-tb',true).first();
25549 * Protected method that will not generally be called directly. It
25550 * is called when the editor creates its toolbar. Override this method if you need to
25551 * add custom toolbar buttons.
25552 * @param {HtmlEditor} editor
25554 createToolbar : function(){
25555 Roo.log('renewing');
25556 Roo.log("create toolbars");
25558 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25559 this.toolbars[0].render(this.toolbarContainer());
25563 // if (!editor.toolbars || !editor.toolbars.length) {
25564 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25567 // for (var i =0 ; i < editor.toolbars.length;i++) {
25568 // editor.toolbars[i] = Roo.factory(
25569 // typeof(editor.toolbars[i]) == 'string' ?
25570 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25571 // Roo.bootstrap.HtmlEditor);
25572 // editor.toolbars[i].init(editor);
25578 onRender : function(ct, position)
25580 // Roo.log("Call onRender: " + this.xtype);
25582 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25584 this.wrap = this.inputEl().wrap({
25585 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25588 this.editorcore.onRender(ct, position);
25590 if (this.resizable) {
25591 this.resizeEl = new Roo.Resizable(this.wrap, {
25595 minHeight : this.height,
25596 height: this.height,
25597 handles : this.resizable,
25600 resize : function(r, w, h) {
25601 _t.onResize(w,h); // -something
25607 this.createToolbar(this);
25610 if(!this.width && this.resizable){
25611 this.setSize(this.wrap.getSize());
25613 if (this.resizeEl) {
25614 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25615 // should trigger onReize..
25621 onResize : function(w, h)
25623 Roo.log('resize: ' +w + ',' + h );
25624 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25628 if(this.inputEl() ){
25629 if(typeof w == 'number'){
25630 var aw = w - this.wrap.getFrameWidth('lr');
25631 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25634 if(typeof h == 'number'){
25635 var tbh = -11; // fixme it needs to tool bar size!
25636 for (var i =0; i < this.toolbars.length;i++) {
25637 // fixme - ask toolbars for heights?
25638 tbh += this.toolbars[i].el.getHeight();
25639 //if (this.toolbars[i].footer) {
25640 // tbh += this.toolbars[i].footer.el.getHeight();
25648 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25649 ah -= 5; // knock a few pixes off for look..
25650 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25654 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25655 this.editorcore.onResize(ew,eh);
25660 * Toggles the editor between standard and source edit mode.
25661 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25663 toggleSourceEdit : function(sourceEditMode)
25665 this.editorcore.toggleSourceEdit(sourceEditMode);
25667 if(this.editorcore.sourceEditMode){
25668 Roo.log('editor - showing textarea');
25671 // Roo.log(this.syncValue());
25673 this.inputEl().removeClass(['hide', 'x-hidden']);
25674 this.inputEl().dom.removeAttribute('tabIndex');
25675 this.inputEl().focus();
25677 Roo.log('editor - hiding textarea');
25679 // Roo.log(this.pushValue());
25682 this.inputEl().addClass(['hide', 'x-hidden']);
25683 this.inputEl().dom.setAttribute('tabIndex', -1);
25684 //this.deferFocus();
25687 if(this.resizable){
25688 this.setSize(this.wrap.getSize());
25691 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25694 // private (for BoxComponent)
25695 adjustSize : Roo.BoxComponent.prototype.adjustSize,
25697 // private (for BoxComponent)
25698 getResizeEl : function(){
25702 // private (for BoxComponent)
25703 getPositionEl : function(){
25708 initEvents : function(){
25709 this.originalValue = this.getValue();
25713 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25716 // markInvalid : Roo.emptyFn,
25718 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25721 // clearInvalid : Roo.emptyFn,
25723 setValue : function(v){
25724 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25725 this.editorcore.pushValue();
25730 deferFocus : function(){
25731 this.focus.defer(10, this);
25735 focus : function(){
25736 this.editorcore.focus();
25742 onDestroy : function(){
25748 for (var i =0; i < this.toolbars.length;i++) {
25749 // fixme - ask toolbars for heights?
25750 this.toolbars[i].onDestroy();
25753 this.wrap.dom.innerHTML = '';
25754 this.wrap.remove();
25759 onFirstFocus : function(){
25760 //Roo.log("onFirstFocus");
25761 this.editorcore.onFirstFocus();
25762 for (var i =0; i < this.toolbars.length;i++) {
25763 this.toolbars[i].onFirstFocus();
25769 syncValue : function()
25771 this.editorcore.syncValue();
25774 pushValue : function()
25776 this.editorcore.pushValue();
25780 // hide stuff that is not compatible
25794 * @event specialkey
25798 * @cfg {String} fieldClass @hide
25801 * @cfg {String} focusClass @hide
25804 * @cfg {String} autoCreate @hide
25807 * @cfg {String} inputType @hide
25811 * @cfg {String} invalidText @hide
25814 * @cfg {String} msgFx @hide
25817 * @cfg {String} validateOnBlur @hide
25826 Roo.namespace('Roo.bootstrap.htmleditor');
25828 * @class Roo.bootstrap.HtmlEditorToolbar1
25834 new Roo.bootstrap.HtmlEditor({
25837 new Roo.bootstrap.HtmlEditorToolbar1({
25838 disable : { fonts: 1 , format: 1, ..., ... , ...],
25844 * @cfg {Object} disable List of elements to disable..
25845 * @cfg {Array} btns List of additional buttons.
25849 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25852 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25855 Roo.apply(this, config);
25857 // default disabled, based on 'good practice'..
25858 this.disable = this.disable || {};
25859 Roo.applyIf(this.disable, {
25862 specialElements : true
25864 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25866 this.editor = config.editor;
25867 this.editorcore = config.editor.editorcore;
25869 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25871 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25872 // dont call parent... till later.
25874 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
25879 editorcore : false,
25884 "h1","h2","h3","h4","h5","h6",
25886 "abbr", "acronym", "address", "cite", "samp", "var",
25890 onRender : function(ct, position)
25892 // Roo.log("Call onRender: " + this.xtype);
25894 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25896 this.el.dom.style.marginBottom = '0';
25898 var editorcore = this.editorcore;
25899 var editor= this.editor;
25902 var btn = function(id,cmd , toggle, handler, html){
25904 var event = toggle ? 'toggle' : 'click';
25909 xns: Roo.bootstrap,
25913 enableToggle:toggle !== false,
25915 pressed : toggle ? false : null,
25918 a.listeners[toggle ? 'toggle' : 'click'] = function() {
25919 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
25925 // var cb_box = function...
25930 xns: Roo.bootstrap,
25935 xns: Roo.bootstrap,
25939 Roo.each(this.formats, function(f) {
25940 style.menu.items.push({
25942 xns: Roo.bootstrap,
25943 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25948 editorcore.insertTag(this.tagname);
25955 children.push(style);
25957 btn('bold',false,true);
25958 btn('italic',false,true);
25959 btn('align-left', 'justifyleft',true);
25960 btn('align-center', 'justifycenter',true);
25961 btn('align-right' , 'justifyright',true);
25962 btn('link', false, false, function(btn) {
25963 //Roo.log("create link?");
25964 var url = prompt(this.createLinkText, this.defaultLinkValue);
25965 if(url && url != 'http:/'+'/'){
25966 this.editorcore.relayCmd('createlink', url);
25969 btn('list','insertunorderedlist',true);
25970 btn('pencil', false,true, function(btn){
25972 this.toggleSourceEdit(btn.pressed);
25975 if (this.editor.btns.length > 0) {
25976 for (var i = 0; i<this.editor.btns.length; i++) {
25977 children.push(this.editor.btns[i]);
25985 xns: Roo.bootstrap,
25990 xns: Roo.bootstrap,
25995 cog.menu.items.push({
25997 xns: Roo.bootstrap,
25998 html : Clean styles,
26003 editorcore.insertTag(this.tagname);
26012 this.xtype = 'NavSimplebar';
26014 for(var i=0;i< children.length;i++) {
26016 this.buttons.add(this.addxtypeChild(children[i]));
26020 editor.on('editorevent', this.updateToolbar, this);
26022 onBtnClick : function(id)
26024 this.editorcore.relayCmd(id);
26025 this.editorcore.focus();
26029 * Protected method that will not generally be called directly. It triggers
26030 * a toolbar update by reading the markup state of the current selection in the editor.
26032 updateToolbar: function(){
26034 if(!this.editorcore.activated){
26035 this.editor.onFirstFocus(); // is this neeed?
26039 var btns = this.buttons;
26040 var doc = this.editorcore.doc;
26041 btns.get('bold').setActive(doc.queryCommandState('bold'));
26042 btns.get('italic').setActive(doc.queryCommandState('italic'));
26043 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26045 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26046 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26047 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26049 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26050 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26053 var ans = this.editorcore.getAllAncestors();
26054 if (this.formatCombo) {
26057 var store = this.formatCombo.store;
26058 this.formatCombo.setValue("");
26059 for (var i =0; i < ans.length;i++) {
26060 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26062 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26070 // hides menus... - so this cant be on a menu...
26071 Roo.bootstrap.MenuMgr.hideAll();
26073 Roo.bootstrap.MenuMgr.hideAll();
26074 //this.editorsyncValue();
26076 onFirstFocus: function() {
26077 this.buttons.each(function(item){
26081 toggleSourceEdit : function(sourceEditMode){
26084 if(sourceEditMode){
26085 Roo.log("disabling buttons");
26086 this.buttons.each( function(item){
26087 if(item.cmd != 'pencil'){
26093 Roo.log("enabling buttons");
26094 if(this.editorcore.initialized){
26095 this.buttons.each( function(item){
26101 Roo.log("calling toggole on editor");
26102 // tell the editor that it's been pressed..
26103 this.editor.toggleSourceEdit(sourceEditMode);
26117 * @class Roo.bootstrap.Markdown
26118 * @extends Roo.bootstrap.TextArea
26119 * Bootstrap Showdown editable area
26120 * @cfg {string} content
26123 * Create a new Showdown
26126 Roo.bootstrap.Markdown = function(config){
26127 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26131 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26135 initEvents : function()
26138 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26139 this.markdownEl = this.el.createChild({
26140 cls : 'roo-markdown-area'
26142 this.inputEl().addClass('d-none');
26143 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26144 this.markdownEl.on('click', this.toggleTextEdit, this);
26145 this.on('blur', this.toggleTextEdit, this);
26146 this.on('specialkey', this.resizeTextArea, this);
26149 toggleTextEdit : function()
26151 var sh = this.markdownEl.getHeight();
26152 this.inputEl().addClass('d-none');
26153 this.markdownEl.addClass('d-none');
26154 if (!this.editing) {
26156 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26157 this.inputEl().removeClass('d-none');
26158 this.inputEl().focus();
26159 this.editing = true;
26162 // show showdown...
26163 this.updateMarkdown();
26164 this.markdownEl.removeClass('d-none');
26165 this.editing = false;
26168 updateMarkdown : function()
26170 if (this.getValue() == '') {
26171 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder);
26174 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26177 resizeTextArea: function () {
26180 Roo.log([sh, this.getValue().split("\n").length * 30]);
26181 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26183 setValue : function(val)
26185 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26186 if (!this.editing) {
26187 this.updateMarkdown();
26193 if (!this.editing) {
26194 this.toggleTextEdit();
26202 * @class Roo.bootstrap.Table.AbstractSelectionModel
26203 * @extends Roo.util.Observable
26204 * Abstract base class for grid SelectionModels. It provides the interface that should be
26205 * implemented by descendant classes. This class should not be directly instantiated.
26208 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26209 this.locked = false;
26210 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26214 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26215 /** @ignore Called by the grid automatically. Do not call directly. */
26216 init : function(grid){
26222 * Locks the selections.
26225 this.locked = true;
26229 * Unlocks the selections.
26231 unlock : function(){
26232 this.locked = false;
26236 * Returns true if the selections are locked.
26237 * @return {Boolean}
26239 isLocked : function(){
26240 return this.locked;
26244 initEvents : function ()
26250 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26251 * @class Roo.bootstrap.Table.RowSelectionModel
26252 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26253 * It supports multiple selections and keyboard selection/navigation.
26255 * @param {Object} config
26258 Roo.bootstrap.Table.RowSelectionModel = function(config){
26259 Roo.apply(this, config);
26260 this.selections = new Roo.util.MixedCollection(false, function(o){
26265 this.lastActive = false;
26269 * @event selectionchange
26270 * Fires when the selection changes
26271 * @param {SelectionModel} this
26273 "selectionchange" : true,
26275 * @event afterselectionchange
26276 * Fires after the selection changes (eg. by key press or clicking)
26277 * @param {SelectionModel} this
26279 "afterselectionchange" : true,
26281 * @event beforerowselect
26282 * Fires when a row is selected being selected, return false to cancel.
26283 * @param {SelectionModel} this
26284 * @param {Number} rowIndex The selected index
26285 * @param {Boolean} keepExisting False if other selections will be cleared
26287 "beforerowselect" : true,
26290 * Fires when a row is selected.
26291 * @param {SelectionModel} this
26292 * @param {Number} rowIndex The selected index
26293 * @param {Roo.data.Record} r The record
26295 "rowselect" : true,
26297 * @event rowdeselect
26298 * Fires when a row is deselected.
26299 * @param {SelectionModel} this
26300 * @param {Number} rowIndex The selected index
26302 "rowdeselect" : true
26304 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26305 this.locked = false;
26308 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26310 * @cfg {Boolean} singleSelect
26311 * True to allow selection of only one row at a time (defaults to false)
26313 singleSelect : false,
26316 initEvents : function()
26319 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26320 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26321 //}else{ // allow click to work like normal
26322 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26324 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26325 this.grid.on("rowclick", this.handleMouseDown, this);
26327 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26328 "up" : function(e){
26330 this.selectPrevious(e.shiftKey);
26331 }else if(this.last !== false && this.lastActive !== false){
26332 var last = this.last;
26333 this.selectRange(this.last, this.lastActive-1);
26334 this.grid.getView().focusRow(this.lastActive);
26335 if(last !== false){
26339 this.selectFirstRow();
26341 this.fireEvent("afterselectionchange", this);
26343 "down" : function(e){
26345 this.selectNext(e.shiftKey);
26346 }else if(this.last !== false && this.lastActive !== false){
26347 var last = this.last;
26348 this.selectRange(this.last, this.lastActive+1);
26349 this.grid.getView().focusRow(this.lastActive);
26350 if(last !== false){
26354 this.selectFirstRow();
26356 this.fireEvent("afterselectionchange", this);
26360 this.grid.store.on('load', function(){
26361 this.selections.clear();
26364 var view = this.grid.view;
26365 view.on("refresh", this.onRefresh, this);
26366 view.on("rowupdated", this.onRowUpdated, this);
26367 view.on("rowremoved", this.onRemove, this);
26372 onRefresh : function()
26374 var ds = this.grid.store, i, v = this.grid.view;
26375 var s = this.selections;
26376 s.each(function(r){
26377 if((i = ds.indexOfId(r.id)) != -1){
26386 onRemove : function(v, index, r){
26387 this.selections.remove(r);
26391 onRowUpdated : function(v, index, r){
26392 if(this.isSelected(r)){
26393 v.onRowSelect(index);
26399 * @param {Array} records The records to select
26400 * @param {Boolean} keepExisting (optional) True to keep existing selections
26402 selectRecords : function(records, keepExisting)
26405 this.clearSelections();
26407 var ds = this.grid.store;
26408 for(var i = 0, len = records.length; i < len; i++){
26409 this.selectRow(ds.indexOf(records[i]), true);
26414 * Gets the number of selected rows.
26417 getCount : function(){
26418 return this.selections.length;
26422 * Selects the first row in the grid.
26424 selectFirstRow : function(){
26429 * Select the last row.
26430 * @param {Boolean} keepExisting (optional) True to keep existing selections
26432 selectLastRow : function(keepExisting){
26433 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26434 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26438 * Selects the row immediately following the last selected row.
26439 * @param {Boolean} keepExisting (optional) True to keep existing selections
26441 selectNext : function(keepExisting)
26443 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26444 this.selectRow(this.last+1, keepExisting);
26445 this.grid.getView().focusRow(this.last);
26450 * Selects the row that precedes the last selected row.
26451 * @param {Boolean} keepExisting (optional) True to keep existing selections
26453 selectPrevious : function(keepExisting){
26455 this.selectRow(this.last-1, keepExisting);
26456 this.grid.getView().focusRow(this.last);
26461 * Returns the selected records
26462 * @return {Array} Array of selected records
26464 getSelections : function(){
26465 return [].concat(this.selections.items);
26469 * Returns the first selected record.
26472 getSelected : function(){
26473 return this.selections.itemAt(0);
26478 * Clears all selections.
26480 clearSelections : function(fast)
26486 var ds = this.grid.store;
26487 var s = this.selections;
26488 s.each(function(r){
26489 this.deselectRow(ds.indexOfId(r.id));
26493 this.selections.clear();
26500 * Selects all rows.
26502 selectAll : function(){
26506 this.selections.clear();
26507 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26508 this.selectRow(i, true);
26513 * Returns True if there is a selection.
26514 * @return {Boolean}
26516 hasSelection : function(){
26517 return this.selections.length > 0;
26521 * Returns True if the specified row is selected.
26522 * @param {Number/Record} record The record or index of the record to check
26523 * @return {Boolean}
26525 isSelected : function(index){
26526 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26527 return (r && this.selections.key(r.id) ? true : false);
26531 * Returns True if the specified record id is selected.
26532 * @param {String} id The id of record to check
26533 * @return {Boolean}
26535 isIdSelected : function(id){
26536 return (this.selections.key(id) ? true : false);
26541 handleMouseDBClick : function(e, t){
26545 handleMouseDown : function(e, t)
26547 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26548 if(this.isLocked() || rowIndex < 0 ){
26551 if(e.shiftKey && this.last !== false){
26552 var last = this.last;
26553 this.selectRange(last, rowIndex, e.ctrlKey);
26554 this.last = last; // reset the last
26558 var isSelected = this.isSelected(rowIndex);
26559 //Roo.log("select row:" + rowIndex);
26561 this.deselectRow(rowIndex);
26563 this.selectRow(rowIndex, true);
26567 if(e.button !== 0 && isSelected){
26568 alert('rowIndex 2: ' + rowIndex);
26569 view.focusRow(rowIndex);
26570 }else if(e.ctrlKey && isSelected){
26571 this.deselectRow(rowIndex);
26572 }else if(!isSelected){
26573 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26574 view.focusRow(rowIndex);
26578 this.fireEvent("afterselectionchange", this);
26581 handleDragableRowClick : function(grid, rowIndex, e)
26583 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26584 this.selectRow(rowIndex, false);
26585 grid.view.focusRow(rowIndex);
26586 this.fireEvent("afterselectionchange", this);
26591 * Selects multiple rows.
26592 * @param {Array} rows Array of the indexes of the row to select
26593 * @param {Boolean} keepExisting (optional) True to keep existing selections
26595 selectRows : function(rows, keepExisting){
26597 this.clearSelections();
26599 for(var i = 0, len = rows.length; i < len; i++){
26600 this.selectRow(rows[i], true);
26605 * Selects a range of rows. All rows in between startRow and endRow are also selected.
26606 * @param {Number} startRow The index of the first row in the range
26607 * @param {Number} endRow The index of the last row in the range
26608 * @param {Boolean} keepExisting (optional) True to retain existing selections
26610 selectRange : function(startRow, endRow, keepExisting){
26615 this.clearSelections();
26617 if(startRow <= endRow){
26618 for(var i = startRow; i <= endRow; i++){
26619 this.selectRow(i, true);
26622 for(var i = startRow; i >= endRow; i--){
26623 this.selectRow(i, true);
26629 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26630 * @param {Number} startRow The index of the first row in the range
26631 * @param {Number} endRow The index of the last row in the range
26633 deselectRange : function(startRow, endRow, preventViewNotify){
26637 for(var i = startRow; i <= endRow; i++){
26638 this.deselectRow(i, preventViewNotify);
26644 * @param {Number} row The index of the row to select
26645 * @param {Boolean} keepExisting (optional) True to keep existing selections
26647 selectRow : function(index, keepExisting, preventViewNotify)
26649 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26652 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26653 if(!keepExisting || this.singleSelect){
26654 this.clearSelections();
26657 var r = this.grid.store.getAt(index);
26658 //console.log('selectRow - record id :' + r.id);
26660 this.selections.add(r);
26661 this.last = this.lastActive = index;
26662 if(!preventViewNotify){
26663 var proxy = new Roo.Element(
26664 this.grid.getRowDom(index)
26666 proxy.addClass('bg-info info');
26668 this.fireEvent("rowselect", this, index, r);
26669 this.fireEvent("selectionchange", this);
26675 * @param {Number} row The index of the row to deselect
26677 deselectRow : function(index, preventViewNotify)
26682 if(this.last == index){
26685 if(this.lastActive == index){
26686 this.lastActive = false;
26689 var r = this.grid.store.getAt(index);
26694 this.selections.remove(r);
26695 //.console.log('deselectRow - record id :' + r.id);
26696 if(!preventViewNotify){
26698 var proxy = new Roo.Element(
26699 this.grid.getRowDom(index)
26701 proxy.removeClass('bg-info info');
26703 this.fireEvent("rowdeselect", this, index);
26704 this.fireEvent("selectionchange", this);
26708 restoreLast : function(){
26710 this.last = this._last;
26715 acceptsNav : function(row, col, cm){
26716 return !cm.isHidden(col) && cm.isCellEditable(col, row);
26720 onEditorKey : function(field, e){
26721 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26726 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26728 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26730 }else if(k == e.ENTER && !e.ctrlKey){
26734 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26736 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26738 }else if(k == e.ESC){
26742 g.startEditing(newCell[0], newCell[1]);
26748 * Ext JS Library 1.1.1
26749 * Copyright(c) 2006-2007, Ext JS, LLC.
26751 * Originally Released Under LGPL - original licence link has changed is not relivant.
26754 * <script type="text/javascript">
26758 * @class Roo.bootstrap.PagingToolbar
26759 * @extends Roo.bootstrap.NavSimplebar
26760 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26762 * Create a new PagingToolbar
26763 * @param {Object} config The config object
26764 * @param {Roo.data.Store} store
26766 Roo.bootstrap.PagingToolbar = function(config)
26768 // old args format still supported... - xtype is prefered..
26769 // created from xtype...
26771 this.ds = config.dataSource;
26773 if (config.store && !this.ds) {
26774 this.store= Roo.factory(config.store, Roo.data);
26775 this.ds = this.store;
26776 this.ds.xmodule = this.xmodule || false;
26779 this.toolbarItems = [];
26780 if (config.items) {
26781 this.toolbarItems = config.items;
26784 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26789 this.bind(this.ds);
26792 if (Roo.bootstrap.version == 4) {
26793 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26795 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26800 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26802 * @cfg {Roo.data.Store} dataSource
26803 * The underlying data store providing the paged data
26806 * @cfg {String/HTMLElement/Element} container
26807 * container The id or element that will contain the toolbar
26810 * @cfg {Boolean} displayInfo
26811 * True to display the displayMsg (defaults to false)
26814 * @cfg {Number} pageSize
26815 * The number of records to display per page (defaults to 20)
26819 * @cfg {String} displayMsg
26820 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26822 displayMsg : 'Displaying {0} - {1} of {2}',
26824 * @cfg {String} emptyMsg
26825 * The message to display when no records are found (defaults to "No data to display")
26827 emptyMsg : 'No data to display',
26829 * Customizable piece of the default paging text (defaults to "Page")
26832 beforePageText : "Page",
26834 * Customizable piece of the default paging text (defaults to "of %0")
26837 afterPageText : "of {0}",
26839 * Customizable piece of the default paging text (defaults to "First Page")
26842 firstText : "First Page",
26844 * Customizable piece of the default paging text (defaults to "Previous Page")
26847 prevText : "Previous Page",
26849 * Customizable piece of the default paging text (defaults to "Next Page")
26852 nextText : "Next Page",
26854 * Customizable piece of the default paging text (defaults to "Last Page")
26857 lastText : "Last Page",
26859 * Customizable piece of the default paging text (defaults to "Refresh")
26862 refreshText : "Refresh",
26866 onRender : function(ct, position)
26868 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26869 this.navgroup.parentId = this.id;
26870 this.navgroup.onRender(this.el, null);
26871 // add the buttons to the navgroup
26873 if(this.displayInfo){
26874 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26875 this.displayEl = this.el.select('.x-paging-info', true).first();
26876 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26877 // this.displayEl = navel.el.select('span',true).first();
26883 Roo.each(_this.buttons, function(e){ // this might need to use render????
26884 Roo.factory(e).render(_this.el);
26888 Roo.each(_this.toolbarItems, function(e) {
26889 _this.navgroup.addItem(e);
26893 this.first = this.navgroup.addItem({
26894 tooltip: this.firstText,
26895 cls: "prev btn-outline-secondary",
26896 html : ' <i class="fa fa-step-backward"></i>',
26898 preventDefault: true,
26899 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26902 this.prev = this.navgroup.addItem({
26903 tooltip: this.prevText,
26904 cls: "prev btn-outline-secondary",
26905 html : ' <i class="fa fa-backward"></i>',
26907 preventDefault: true,
26908 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
26910 //this.addSeparator();
26913 var field = this.navgroup.addItem( {
26915 cls : 'x-paging-position btn-outline-secondary',
26917 html : this.beforePageText +
26918 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26919 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
26922 this.field = field.el.select('input', true).first();
26923 this.field.on("keydown", this.onPagingKeydown, this);
26924 this.field.on("focus", function(){this.dom.select();});
26927 this.afterTextEl = field.el.select('.x-paging-after',true).first();
26928 //this.field.setHeight(18);
26929 //this.addSeparator();
26930 this.next = this.navgroup.addItem({
26931 tooltip: this.nextText,
26932 cls: "next btn-outline-secondary",
26933 html : ' <i class="fa fa-forward"></i>',
26935 preventDefault: true,
26936 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
26938 this.last = this.navgroup.addItem({
26939 tooltip: this.lastText,
26940 html : ' <i class="fa fa-step-forward"></i>',
26941 cls: "next btn-outline-secondary",
26943 preventDefault: true,
26944 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
26946 //this.addSeparator();
26947 this.loading = this.navgroup.addItem({
26948 tooltip: this.refreshText,
26949 cls: "btn-outline-secondary",
26950 html : ' <i class="fa fa-refresh"></i>',
26951 preventDefault: true,
26952 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26958 updateInfo : function(){
26959 if(this.displayEl){
26960 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26961 var msg = count == 0 ?
26965 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
26967 this.displayEl.update(msg);
26972 onLoad : function(ds, r, o)
26974 this.cursor = o.params.start ? o.params.start : 0;
26976 var d = this.getPageData(),
26981 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26982 this.field.dom.value = ap;
26983 this.first.setDisabled(ap == 1);
26984 this.prev.setDisabled(ap == 1);
26985 this.next.setDisabled(ap == ps);
26986 this.last.setDisabled(ap == ps);
26987 this.loading.enable();
26992 getPageData : function(){
26993 var total = this.ds.getTotalCount();
26996 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26997 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27002 onLoadError : function(){
27003 this.loading.enable();
27007 onPagingKeydown : function(e){
27008 var k = e.getKey();
27009 var d = this.getPageData();
27011 var v = this.field.dom.value, pageNum;
27012 if(!v || isNaN(pageNum = parseInt(v, 10))){
27013 this.field.dom.value = d.activePage;
27016 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27017 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27020 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))
27022 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27023 this.field.dom.value = pageNum;
27024 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27027 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27029 var v = this.field.dom.value, pageNum;
27030 var increment = (e.shiftKey) ? 10 : 1;
27031 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27034 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27035 this.field.dom.value = d.activePage;
27038 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27040 this.field.dom.value = parseInt(v, 10) + increment;
27041 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27042 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27049 beforeLoad : function(){
27051 this.loading.disable();
27056 onClick : function(which){
27065 ds.load({params:{start: 0, limit: this.pageSize}});
27068 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27071 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27074 var total = ds.getTotalCount();
27075 var extra = total % this.pageSize;
27076 var lastStart = extra ? (total - extra) : total-this.pageSize;
27077 ds.load({params:{start: lastStart, limit: this.pageSize}});
27080 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27086 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27087 * @param {Roo.data.Store} store The data store to unbind
27089 unbind : function(ds){
27090 ds.un("beforeload", this.beforeLoad, this);
27091 ds.un("load", this.onLoad, this);
27092 ds.un("loadexception", this.onLoadError, this);
27093 ds.un("remove", this.updateInfo, this);
27094 ds.un("add", this.updateInfo, this);
27095 this.ds = undefined;
27099 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27100 * @param {Roo.data.Store} store The data store to bind
27102 bind : function(ds){
27103 ds.on("beforeload", this.beforeLoad, this);
27104 ds.on("load", this.onLoad, this);
27105 ds.on("loadexception", this.onLoadError, this);
27106 ds.on("remove", this.updateInfo, this);
27107 ds.on("add", this.updateInfo, this);
27118 * @class Roo.bootstrap.MessageBar
27119 * @extends Roo.bootstrap.Component
27120 * Bootstrap MessageBar class
27121 * @cfg {String} html contents of the MessageBar
27122 * @cfg {String} weight (info | success | warning | danger) default info
27123 * @cfg {String} beforeClass insert the bar before the given class
27124 * @cfg {Boolean} closable (true | false) default false
27125 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27128 * Create a new Element
27129 * @param {Object} config The config object
27132 Roo.bootstrap.MessageBar = function(config){
27133 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27136 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27142 beforeClass: 'bootstrap-sticky-wrap',
27144 getAutoCreate : function(){
27148 cls: 'alert alert-dismissable alert-' + this.weight,
27153 html: this.html || ''
27159 cfg.cls += ' alert-messages-fixed';
27173 onRender : function(ct, position)
27175 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27178 var cfg = Roo.apply({}, this.getAutoCreate());
27182 cfg.cls += ' ' + this.cls;
27185 cfg.style = this.style;
27187 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27189 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27192 this.el.select('>button.close').on('click', this.hide, this);
27198 if (!this.rendered) {
27204 this.fireEvent('show', this);
27210 if (!this.rendered) {
27216 this.fireEvent('hide', this);
27219 update : function()
27221 // var e = this.el.dom.firstChild;
27223 // if(this.closable){
27224 // e = e.nextSibling;
27227 // e.data = this.html || '';
27229 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27245 * @class Roo.bootstrap.Graph
27246 * @extends Roo.bootstrap.Component
27247 * Bootstrap Graph class
27251 @cfg {String} graphtype bar | vbar | pie
27252 @cfg {number} g_x coodinator | centre x (pie)
27253 @cfg {number} g_y coodinator | centre y (pie)
27254 @cfg {number} g_r radius (pie)
27255 @cfg {number} g_height height of the chart (respected by all elements in the set)
27256 @cfg {number} g_width width of the chart (respected by all elements in the set)
27257 @cfg {Object} title The title of the chart
27260 -opts (object) options for the chart
27262 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27263 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27265 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.
27266 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27268 o stretch (boolean)
27270 -opts (object) options for the pie
27273 o startAngle (number)
27274 o endAngle (number)
27278 * Create a new Input
27279 * @param {Object} config The config object
27282 Roo.bootstrap.Graph = function(config){
27283 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27289 * The img click event for the img.
27290 * @param {Roo.EventObject} e
27296 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27307 //g_colors: this.colors,
27314 getAutoCreate : function(){
27325 onRender : function(ct,position){
27328 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27330 if (typeof(Raphael) == 'undefined') {
27331 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27335 this.raphael = Raphael(this.el.dom);
27337 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27338 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27339 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27340 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27342 r.text(160, 10, "Single Series Chart").attr(txtattr);
27343 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27344 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27345 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27347 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27348 r.barchart(330, 10, 300, 220, data1);
27349 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27350 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27353 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27354 // r.barchart(30, 30, 560, 250, xdata, {
27355 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27356 // axis : "0 0 1 1",
27357 // axisxlabels : xdata
27358 // //yvalues : cols,
27361 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27363 // this.load(null,xdata,{
27364 // axis : "0 0 1 1",
27365 // axisxlabels : xdata
27370 load : function(graphtype,xdata,opts)
27372 this.raphael.clear();
27374 graphtype = this.graphtype;
27379 var r = this.raphael,
27380 fin = function () {
27381 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27383 fout = function () {
27384 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27386 pfin = function() {
27387 this.sector.stop();
27388 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27391 this.label[0].stop();
27392 this.label[0].attr({ r: 7.5 });
27393 this.label[1].attr({ "font-weight": 800 });
27396 pfout = function() {
27397 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27400 this.label[0].animate({ r: 5 }, 500, "bounce");
27401 this.label[1].attr({ "font-weight": 400 });
27407 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27410 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27413 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27414 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27416 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27423 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27428 setTitle: function(o)
27433 initEvents: function() {
27436 this.el.on('click', this.onClick, this);
27440 onClick : function(e)
27442 Roo.log('img onclick');
27443 this.fireEvent('click', this, e);
27455 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27458 * @class Roo.bootstrap.dash.NumberBox
27459 * @extends Roo.bootstrap.Component
27460 * Bootstrap NumberBox class
27461 * @cfg {String} headline Box headline
27462 * @cfg {String} content Box content
27463 * @cfg {String} icon Box icon
27464 * @cfg {String} footer Footer text
27465 * @cfg {String} fhref Footer href
27468 * Create a new NumberBox
27469 * @param {Object} config The config object
27473 Roo.bootstrap.dash.NumberBox = function(config){
27474 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27478 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27487 getAutoCreate : function(){
27491 cls : 'small-box ',
27499 cls : 'roo-headline',
27500 html : this.headline
27504 cls : 'roo-content',
27505 html : this.content
27519 cls : 'ion ' + this.icon
27528 cls : 'small-box-footer',
27529 href : this.fhref || '#',
27533 cfg.cn.push(footer);
27540 onRender : function(ct,position){
27541 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27548 setHeadline: function (value)
27550 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27553 setFooter: function (value, href)
27555 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27558 this.el.select('a.small-box-footer',true).first().attr('href', href);
27563 setContent: function (value)
27565 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27568 initEvents: function()
27582 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27585 * @class Roo.bootstrap.dash.TabBox
27586 * @extends Roo.bootstrap.Component
27587 * Bootstrap TabBox class
27588 * @cfg {String} title Title of the TabBox
27589 * @cfg {String} icon Icon of the TabBox
27590 * @cfg {Boolean} showtabs (true|false) show the tabs default true
27591 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27594 * Create a new TabBox
27595 * @param {Object} config The config object
27599 Roo.bootstrap.dash.TabBox = function(config){
27600 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27605 * When a pane is added
27606 * @param {Roo.bootstrap.dash.TabPane} pane
27610 * @event activatepane
27611 * When a pane is activated
27612 * @param {Roo.bootstrap.dash.TabPane} pane
27614 "activatepane" : true
27622 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
27627 tabScrollable : false,
27629 getChildContainer : function()
27631 return this.el.select('.tab-content', true).first();
27634 getAutoCreate : function(){
27638 cls: 'pull-left header',
27646 cls: 'fa ' + this.icon
27652 cls: 'nav nav-tabs pull-right',
27658 if(this.tabScrollable){
27665 cls: 'nav nav-tabs pull-right',
27676 cls: 'nav-tabs-custom',
27681 cls: 'tab-content no-padding',
27689 initEvents : function()
27691 //Roo.log('add add pane handler');
27692 this.on('addpane', this.onAddPane, this);
27695 * Updates the box title
27696 * @param {String} html to set the title to.
27698 setTitle : function(value)
27700 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27702 onAddPane : function(pane)
27704 this.panes.push(pane);
27705 //Roo.log('addpane');
27707 // tabs are rendere left to right..
27708 if(!this.showtabs){
27712 var ctr = this.el.select('.nav-tabs', true).first();
27715 var existing = ctr.select('.nav-tab',true);
27716 var qty = existing.getCount();;
27719 var tab = ctr.createChild({
27721 cls : 'nav-tab' + (qty ? '' : ' active'),
27729 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27732 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27734 pane.el.addClass('active');
27739 onTabClick : function(ev,un,ob,pane)
27741 //Roo.log('tab - prev default');
27742 ev.preventDefault();
27745 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27746 pane.tab.addClass('active');
27747 //Roo.log(pane.title);
27748 this.getChildContainer().select('.tab-pane',true).removeClass('active');
27749 // technically we should have a deactivate event.. but maybe add later.
27750 // and it should not de-activate the selected tab...
27751 this.fireEvent('activatepane', pane);
27752 pane.el.addClass('active');
27753 pane.fireEvent('activate');
27758 getActivePane : function()
27761 Roo.each(this.panes, function(p) {
27762 if(p.el.hasClass('active')){
27783 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27785 * @class Roo.bootstrap.TabPane
27786 * @extends Roo.bootstrap.Component
27787 * Bootstrap TabPane class
27788 * @cfg {Boolean} active (false | true) Default false
27789 * @cfg {String} title title of panel
27793 * Create a new TabPane
27794 * @param {Object} config The config object
27797 Roo.bootstrap.dash.TabPane = function(config){
27798 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27804 * When a pane is activated
27805 * @param {Roo.bootstrap.dash.TabPane} pane
27812 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
27817 // the tabBox that this is attached to.
27820 getAutoCreate : function()
27828 cfg.cls += ' active';
27833 initEvents : function()
27835 //Roo.log('trigger add pane handler');
27836 this.parent().fireEvent('addpane', this)
27840 * Updates the tab title
27841 * @param {String} html to set the title to.
27843 setTitle: function(str)
27849 this.tab.select('a', true).first().dom.innerHTML = str;
27866 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27869 * @class Roo.bootstrap.menu.Menu
27870 * @extends Roo.bootstrap.Component
27871 * Bootstrap Menu class - container for Menu
27872 * @cfg {String} html Text of the menu
27873 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27874 * @cfg {String} icon Font awesome icon
27875 * @cfg {String} pos Menu align to (top | bottom) default bottom
27879 * Create a new Menu
27880 * @param {Object} config The config object
27884 Roo.bootstrap.menu.Menu = function(config){
27885 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27889 * @event beforeshow
27890 * Fires before this menu is displayed
27891 * @param {Roo.bootstrap.menu.Menu} this
27895 * @event beforehide
27896 * Fires before this menu is hidden
27897 * @param {Roo.bootstrap.menu.Menu} this
27902 * Fires after this menu is displayed
27903 * @param {Roo.bootstrap.menu.Menu} this
27908 * Fires after this menu is hidden
27909 * @param {Roo.bootstrap.menu.Menu} this
27914 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27915 * @param {Roo.bootstrap.menu.Menu} this
27916 * @param {Roo.EventObject} e
27923 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
27927 weight : 'default',
27932 getChildContainer : function() {
27933 if(this.isSubMenu){
27937 return this.el.select('ul.dropdown-menu', true).first();
27940 getAutoCreate : function()
27945 cls : 'roo-menu-text',
27953 cls : 'fa ' + this.icon
27964 cls : 'dropdown-button btn btn-' + this.weight,
27969 cls : 'dropdown-toggle btn btn-' + this.weight,
27979 cls : 'dropdown-menu'
27985 if(this.pos == 'top'){
27986 cfg.cls += ' dropup';
27989 if(this.isSubMenu){
27992 cls : 'dropdown-menu'
27999 onRender : function(ct, position)
28001 this.isSubMenu = ct.hasClass('dropdown-submenu');
28003 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28006 initEvents : function()
28008 if(this.isSubMenu){
28012 this.hidden = true;
28014 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28015 this.triggerEl.on('click', this.onTriggerPress, this);
28017 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28018 this.buttonEl.on('click', this.onClick, this);
28024 if(this.isSubMenu){
28028 return this.el.select('ul.dropdown-menu', true).first();
28031 onClick : function(e)
28033 this.fireEvent("click", this, e);
28036 onTriggerPress : function(e)
28038 if (this.isVisible()) {
28045 isVisible : function(){
28046 return !this.hidden;
28051 this.fireEvent("beforeshow", this);
28053 this.hidden = false;
28054 this.el.addClass('open');
28056 Roo.get(document).on("mouseup", this.onMouseUp, this);
28058 this.fireEvent("show", this);
28065 this.fireEvent("beforehide", this);
28067 this.hidden = true;
28068 this.el.removeClass('open');
28070 Roo.get(document).un("mouseup", this.onMouseUp);
28072 this.fireEvent("hide", this);
28075 onMouseUp : function()
28089 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28092 * @class Roo.bootstrap.menu.Item
28093 * @extends Roo.bootstrap.Component
28094 * Bootstrap MenuItem class
28095 * @cfg {Boolean} submenu (true | false) default false
28096 * @cfg {String} html text of the item
28097 * @cfg {String} href the link
28098 * @cfg {Boolean} disable (true | false) default false
28099 * @cfg {Boolean} preventDefault (true | false) default true
28100 * @cfg {String} icon Font awesome icon
28101 * @cfg {String} pos Submenu align to (left | right) default right
28105 * Create a new Item
28106 * @param {Object} config The config object
28110 Roo.bootstrap.menu.Item = function(config){
28111 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28115 * Fires when the mouse is hovering over this menu
28116 * @param {Roo.bootstrap.menu.Item} this
28117 * @param {Roo.EventObject} e
28122 * Fires when the mouse exits this menu
28123 * @param {Roo.bootstrap.menu.Item} this
28124 * @param {Roo.EventObject} e
28130 * The raw click event for the entire grid.
28131 * @param {Roo.EventObject} e
28137 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28142 preventDefault: true,
28147 getAutoCreate : function()
28152 cls : 'roo-menu-item-text',
28160 cls : 'fa ' + this.icon
28169 href : this.href || '#',
28176 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28180 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28182 if(this.pos == 'left'){
28183 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28190 initEvents : function()
28192 this.el.on('mouseover', this.onMouseOver, this);
28193 this.el.on('mouseout', this.onMouseOut, this);
28195 this.el.select('a', true).first().on('click', this.onClick, this);
28199 onClick : function(e)
28201 if(this.preventDefault){
28202 e.preventDefault();
28205 this.fireEvent("click", this, e);
28208 onMouseOver : function(e)
28210 if(this.submenu && this.pos == 'left'){
28211 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28214 this.fireEvent("mouseover", this, e);
28217 onMouseOut : function(e)
28219 this.fireEvent("mouseout", this, e);
28231 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28234 * @class Roo.bootstrap.menu.Separator
28235 * @extends Roo.bootstrap.Component
28236 * Bootstrap Separator class
28239 * Create a new Separator
28240 * @param {Object} config The config object
28244 Roo.bootstrap.menu.Separator = function(config){
28245 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28248 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28250 getAutoCreate : function(){
28271 * @class Roo.bootstrap.Tooltip
28272 * Bootstrap Tooltip class
28273 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28274 * to determine which dom element triggers the tooltip.
28276 * It needs to add support for additional attributes like tooltip-position
28279 * Create a new Toolti
28280 * @param {Object} config The config object
28283 Roo.bootstrap.Tooltip = function(config){
28284 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28286 this.alignment = Roo.bootstrap.Tooltip.alignment;
28288 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28289 this.alignment = config.alignment;
28294 Roo.apply(Roo.bootstrap.Tooltip, {
28296 * @function init initialize tooltip monitoring.
28300 currentTip : false,
28301 currentRegion : false,
28307 Roo.get(document).on('mouseover', this.enter ,this);
28308 Roo.get(document).on('mouseout', this.leave, this);
28311 this.currentTip = new Roo.bootstrap.Tooltip();
28314 enter : function(ev)
28316 var dom = ev.getTarget();
28318 //Roo.log(['enter',dom]);
28319 var el = Roo.fly(dom);
28320 if (this.currentEl) {
28322 //Roo.log(this.currentEl);
28323 //Roo.log(this.currentEl.contains(dom));
28324 if (this.currentEl == el) {
28327 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28333 if (this.currentTip.el) {
28334 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28338 if(!el || el.dom == document){
28344 // you can not look for children, as if el is the body.. then everythign is the child..
28345 if (!el.attr('tooltip')) { //
28346 if (!el.select("[tooltip]").elements.length) {
28349 // is the mouse over this child...?
28350 bindEl = el.select("[tooltip]").first();
28351 var xy = ev.getXY();
28352 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28353 //Roo.log("not in region.");
28356 //Roo.log("child element over..");
28359 this.currentEl = bindEl;
28360 this.currentTip.bind(bindEl);
28361 this.currentRegion = Roo.lib.Region.getRegion(dom);
28362 this.currentTip.enter();
28365 leave : function(ev)
28367 var dom = ev.getTarget();
28368 //Roo.log(['leave',dom]);
28369 if (!this.currentEl) {
28374 if (dom != this.currentEl.dom) {
28377 var xy = ev.getXY();
28378 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28381 // only activate leave if mouse cursor is outside... bounding box..
28386 if (this.currentTip) {
28387 this.currentTip.leave();
28389 //Roo.log('clear currentEl');
28390 this.currentEl = false;
28395 'left' : ['r-l', [-2,0], 'right'],
28396 'right' : ['l-r', [2,0], 'left'],
28397 'bottom' : ['t-b', [0,2], 'top'],
28398 'top' : [ 'b-t', [0,-2], 'bottom']
28404 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28409 delay : null, // can be { show : 300 , hide: 500}
28413 hoverState : null, //???
28415 placement : 'bottom',
28419 getAutoCreate : function(){
28426 cls : 'tooltip-arrow arrow'
28429 cls : 'tooltip-inner'
28436 bind : function(el)
28441 initEvents : function()
28443 this.arrowEl = this.el.select('.arrow', true).first();
28444 this.innerEl = this.el.select('.tooltip-inner', true).first();
28447 enter : function () {
28449 if (this.timeout != null) {
28450 clearTimeout(this.timeout);
28453 this.hoverState = 'in';
28454 //Roo.log("enter - show");
28455 if (!this.delay || !this.delay.show) {
28460 this.timeout = setTimeout(function () {
28461 if (_t.hoverState == 'in') {
28464 }, this.delay.show);
28468 clearTimeout(this.timeout);
28470 this.hoverState = 'out';
28471 if (!this.delay || !this.delay.hide) {
28477 this.timeout = setTimeout(function () {
28478 //Roo.log("leave - timeout");
28480 if (_t.hoverState == 'out') {
28482 Roo.bootstrap.Tooltip.currentEl = false;
28487 show : function (msg)
28490 this.render(document.body);
28493 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28495 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28497 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28499 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28500 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28502 var placement = typeof this.placement == 'function' ?
28503 this.placement.call(this, this.el, on_el) :
28506 var autoToken = /\s?auto?\s?/i;
28507 var autoPlace = autoToken.test(placement);
28509 placement = placement.replace(autoToken, '') || 'top';
28513 //this.el.setXY([0,0]);
28515 //this.el.dom.style.display='block';
28517 //this.el.appendTo(on_el);
28519 var p = this.getPosition();
28520 var box = this.el.getBox();
28526 var align = this.alignment[placement];
28528 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28530 if(placement == 'top' || placement == 'bottom'){
28532 placement = 'right';
28535 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28536 placement = 'left';
28539 var scroll = Roo.select('body', true).first().getScroll();
28541 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28545 align = this.alignment[placement];
28547 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28551 this.el.alignTo(this.bindEl, align[0],align[1]);
28552 //var arrow = this.el.select('.arrow',true).first();
28553 //arrow.set(align[2],
28555 this.el.addClass(placement);
28556 this.el.addClass("bs-tooltip-"+ placement);
28558 this.el.addClass('in fade show');
28560 this.hoverState = null;
28562 if (this.el.hasClass('fade')) {
28577 //this.el.setXY([0,0]);
28578 this.el.removeClass(['show', 'in']);
28594 * @class Roo.bootstrap.LocationPicker
28595 * @extends Roo.bootstrap.Component
28596 * Bootstrap LocationPicker class
28597 * @cfg {Number} latitude Position when init default 0
28598 * @cfg {Number} longitude Position when init default 0
28599 * @cfg {Number} zoom default 15
28600 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28601 * @cfg {Boolean} mapTypeControl default false
28602 * @cfg {Boolean} disableDoubleClickZoom default false
28603 * @cfg {Boolean} scrollwheel default true
28604 * @cfg {Boolean} streetViewControl default false
28605 * @cfg {Number} radius default 0
28606 * @cfg {String} locationName
28607 * @cfg {Boolean} draggable default true
28608 * @cfg {Boolean} enableAutocomplete default false
28609 * @cfg {Boolean} enableReverseGeocode default true
28610 * @cfg {String} markerTitle
28613 * Create a new LocationPicker
28614 * @param {Object} config The config object
28618 Roo.bootstrap.LocationPicker = function(config){
28620 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28625 * Fires when the picker initialized.
28626 * @param {Roo.bootstrap.LocationPicker} this
28627 * @param {Google Location} location
28631 * @event positionchanged
28632 * Fires when the picker position changed.
28633 * @param {Roo.bootstrap.LocationPicker} this
28634 * @param {Google Location} location
28636 positionchanged : true,
28639 * Fires when the map resize.
28640 * @param {Roo.bootstrap.LocationPicker} this
28645 * Fires when the map show.
28646 * @param {Roo.bootstrap.LocationPicker} this
28651 * Fires when the map hide.
28652 * @param {Roo.bootstrap.LocationPicker} this
28657 * Fires when click the map.
28658 * @param {Roo.bootstrap.LocationPicker} this
28659 * @param {Map event} e
28663 * @event mapRightClick
28664 * Fires when right click the map.
28665 * @param {Roo.bootstrap.LocationPicker} this
28666 * @param {Map event} e
28668 mapRightClick : true,
28670 * @event markerClick
28671 * Fires when click the marker.
28672 * @param {Roo.bootstrap.LocationPicker} this
28673 * @param {Map event} e
28675 markerClick : true,
28677 * @event markerRightClick
28678 * Fires when right click the marker.
28679 * @param {Roo.bootstrap.LocationPicker} this
28680 * @param {Map event} e
28682 markerRightClick : true,
28684 * @event OverlayViewDraw
28685 * Fires when OverlayView Draw
28686 * @param {Roo.bootstrap.LocationPicker} this
28688 OverlayViewDraw : true,
28690 * @event OverlayViewOnAdd
28691 * Fires when OverlayView Draw
28692 * @param {Roo.bootstrap.LocationPicker} this
28694 OverlayViewOnAdd : true,
28696 * @event OverlayViewOnRemove
28697 * Fires when OverlayView Draw
28698 * @param {Roo.bootstrap.LocationPicker} this
28700 OverlayViewOnRemove : true,
28702 * @event OverlayViewShow
28703 * Fires when OverlayView Draw
28704 * @param {Roo.bootstrap.LocationPicker} this
28705 * @param {Pixel} cpx
28707 OverlayViewShow : true,
28709 * @event OverlayViewHide
28710 * Fires when OverlayView Draw
28711 * @param {Roo.bootstrap.LocationPicker} this
28713 OverlayViewHide : true,
28715 * @event loadexception
28716 * Fires when load google lib failed.
28717 * @param {Roo.bootstrap.LocationPicker} this
28719 loadexception : true
28724 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
28726 gMapContext: false,
28732 mapTypeControl: false,
28733 disableDoubleClickZoom: false,
28735 streetViewControl: false,
28739 enableAutocomplete: false,
28740 enableReverseGeocode: true,
28743 getAutoCreate: function()
28748 cls: 'roo-location-picker'
28754 initEvents: function(ct, position)
28756 if(!this.el.getWidth() || this.isApplied()){
28760 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28765 initial: function()
28767 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28768 this.fireEvent('loadexception', this);
28772 if(!this.mapTypeId){
28773 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28776 this.gMapContext = this.GMapContext();
28778 this.initOverlayView();
28780 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28784 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28785 _this.setPosition(_this.gMapContext.marker.position);
28788 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28789 _this.fireEvent('mapClick', this, event);
28793 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28794 _this.fireEvent('mapRightClick', this, event);
28798 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28799 _this.fireEvent('markerClick', this, event);
28803 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28804 _this.fireEvent('markerRightClick', this, event);
28808 this.setPosition(this.gMapContext.location);
28810 this.fireEvent('initial', this, this.gMapContext.location);
28813 initOverlayView: function()
28817 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28821 _this.fireEvent('OverlayViewDraw', _this);
28826 _this.fireEvent('OverlayViewOnAdd', _this);
28829 onRemove: function()
28831 _this.fireEvent('OverlayViewOnRemove', _this);
28834 show: function(cpx)
28836 _this.fireEvent('OverlayViewShow', _this, cpx);
28841 _this.fireEvent('OverlayViewHide', _this);
28847 fromLatLngToContainerPixel: function(event)
28849 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28852 isApplied: function()
28854 return this.getGmapContext() == false ? false : true;
28857 getGmapContext: function()
28859 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28862 GMapContext: function()
28864 var position = new google.maps.LatLng(this.latitude, this.longitude);
28866 var _map = new google.maps.Map(this.el.dom, {
28869 mapTypeId: this.mapTypeId,
28870 mapTypeControl: this.mapTypeControl,
28871 disableDoubleClickZoom: this.disableDoubleClickZoom,
28872 scrollwheel: this.scrollwheel,
28873 streetViewControl: this.streetViewControl,
28874 locationName: this.locationName,
28875 draggable: this.draggable,
28876 enableAutocomplete: this.enableAutocomplete,
28877 enableReverseGeocode: this.enableReverseGeocode
28880 var _marker = new google.maps.Marker({
28881 position: position,
28883 title: this.markerTitle,
28884 draggable: this.draggable
28891 location: position,
28892 radius: this.radius,
28893 locationName: this.locationName,
28894 addressComponents: {
28895 formatted_address: null,
28896 addressLine1: null,
28897 addressLine2: null,
28899 streetNumber: null,
28903 stateOrProvince: null
28906 domContainer: this.el.dom,
28907 geodecoder: new google.maps.Geocoder()
28911 drawCircle: function(center, radius, options)
28913 if (this.gMapContext.circle != null) {
28914 this.gMapContext.circle.setMap(null);
28918 options = Roo.apply({}, options, {
28919 strokeColor: "#0000FF",
28920 strokeOpacity: .35,
28922 fillColor: "#0000FF",
28926 options.map = this.gMapContext.map;
28927 options.radius = radius;
28928 options.center = center;
28929 this.gMapContext.circle = new google.maps.Circle(options);
28930 return this.gMapContext.circle;
28936 setPosition: function(location)
28938 this.gMapContext.location = location;
28939 this.gMapContext.marker.setPosition(location);
28940 this.gMapContext.map.panTo(location);
28941 this.drawCircle(location, this.gMapContext.radius, {});
28945 if (this.gMapContext.settings.enableReverseGeocode) {
28946 this.gMapContext.geodecoder.geocode({
28947 latLng: this.gMapContext.location
28948 }, function(results, status) {
28950 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28951 _this.gMapContext.locationName = results[0].formatted_address;
28952 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28954 _this.fireEvent('positionchanged', this, location);
28961 this.fireEvent('positionchanged', this, location);
28966 google.maps.event.trigger(this.gMapContext.map, "resize");
28968 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28970 this.fireEvent('resize', this);
28973 setPositionByLatLng: function(latitude, longitude)
28975 this.setPosition(new google.maps.LatLng(latitude, longitude));
28978 getCurrentPosition: function()
28981 latitude: this.gMapContext.location.lat(),
28982 longitude: this.gMapContext.location.lng()
28986 getAddressName: function()
28988 return this.gMapContext.locationName;
28991 getAddressComponents: function()
28993 return this.gMapContext.addressComponents;
28996 address_component_from_google_geocode: function(address_components)
29000 for (var i = 0; i < address_components.length; i++) {
29001 var component = address_components[i];
29002 if (component.types.indexOf("postal_code") >= 0) {
29003 result.postalCode = component.short_name;
29004 } else if (component.types.indexOf("street_number") >= 0) {
29005 result.streetNumber = component.short_name;
29006 } else if (component.types.indexOf("route") >= 0) {
29007 result.streetName = component.short_name;
29008 } else if (component.types.indexOf("neighborhood") >= 0) {
29009 result.city = component.short_name;
29010 } else if (component.types.indexOf("locality") >= 0) {
29011 result.city = component.short_name;
29012 } else if (component.types.indexOf("sublocality") >= 0) {
29013 result.district = component.short_name;
29014 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29015 result.stateOrProvince = component.short_name;
29016 } else if (component.types.indexOf("country") >= 0) {
29017 result.country = component.short_name;
29021 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29022 result.addressLine2 = "";
29026 setZoomLevel: function(zoom)
29028 this.gMapContext.map.setZoom(zoom);
29041 this.fireEvent('show', this);
29052 this.fireEvent('hide', this);
29057 Roo.apply(Roo.bootstrap.LocationPicker, {
29059 OverlayView : function(map, options)
29061 options = options || {};
29068 * @class Roo.bootstrap.Alert
29069 * @extends Roo.bootstrap.Component
29070 * Bootstrap Alert class - shows an alert area box
29072 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29073 Enter a valid email address
29076 * @cfg {String} title The title of alert
29077 * @cfg {String} html The content of alert
29078 * @cfg {String} weight ( success | info | warning | danger )
29079 * @cfg {String} faicon font-awesomeicon
29082 * Create a new alert
29083 * @param {Object} config The config object
29087 Roo.bootstrap.Alert = function(config){
29088 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29092 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29099 getAutoCreate : function()
29108 cls : 'roo-alert-icon'
29113 cls : 'roo-alert-title',
29118 cls : 'roo-alert-text',
29125 cfg.cn[0].cls += ' fa ' + this.faicon;
29129 cfg.cls += ' alert-' + this.weight;
29135 initEvents: function()
29137 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29140 setTitle : function(str)
29142 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29145 setText : function(str)
29147 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29150 setWeight : function(weight)
29153 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29156 this.weight = weight;
29158 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29161 setIcon : function(icon)
29164 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29167 this.faicon = icon;
29169 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29190 * @class Roo.bootstrap.UploadCropbox
29191 * @extends Roo.bootstrap.Component
29192 * Bootstrap UploadCropbox class
29193 * @cfg {String} emptyText show when image has been loaded
29194 * @cfg {String} rotateNotify show when image too small to rotate
29195 * @cfg {Number} errorTimeout default 3000
29196 * @cfg {Number} minWidth default 300
29197 * @cfg {Number} minHeight default 300
29198 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29199 * @cfg {Boolean} isDocument (true|false) default false
29200 * @cfg {String} url action url
29201 * @cfg {String} paramName default 'imageUpload'
29202 * @cfg {String} method default POST
29203 * @cfg {Boolean} loadMask (true|false) default true
29204 * @cfg {Boolean} loadingText default 'Loading...'
29207 * Create a new UploadCropbox
29208 * @param {Object} config The config object
29211 Roo.bootstrap.UploadCropbox = function(config){
29212 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29216 * @event beforeselectfile
29217 * Fire before select file
29218 * @param {Roo.bootstrap.UploadCropbox} this
29220 "beforeselectfile" : true,
29223 * Fire after initEvent
29224 * @param {Roo.bootstrap.UploadCropbox} this
29229 * Fire after initEvent
29230 * @param {Roo.bootstrap.UploadCropbox} this
29231 * @param {String} data
29236 * Fire when preparing the file data
29237 * @param {Roo.bootstrap.UploadCropbox} this
29238 * @param {Object} file
29243 * Fire when get exception
29244 * @param {Roo.bootstrap.UploadCropbox} this
29245 * @param {XMLHttpRequest} xhr
29247 "exception" : true,
29249 * @event beforeloadcanvas
29250 * Fire before load the canvas
29251 * @param {Roo.bootstrap.UploadCropbox} this
29252 * @param {String} src
29254 "beforeloadcanvas" : true,
29257 * Fire when trash image
29258 * @param {Roo.bootstrap.UploadCropbox} this
29263 * Fire when download the image
29264 * @param {Roo.bootstrap.UploadCropbox} this
29268 * @event footerbuttonclick
29269 * Fire when footerbuttonclick
29270 * @param {Roo.bootstrap.UploadCropbox} this
29271 * @param {String} type
29273 "footerbuttonclick" : true,
29277 * @param {Roo.bootstrap.UploadCropbox} this
29282 * Fire when rotate the image
29283 * @param {Roo.bootstrap.UploadCropbox} this
29284 * @param {String} pos
29289 * Fire when inspect the file
29290 * @param {Roo.bootstrap.UploadCropbox} this
29291 * @param {Object} file
29296 * Fire when xhr upload the file
29297 * @param {Roo.bootstrap.UploadCropbox} this
29298 * @param {Object} data
29303 * Fire when arrange the file data
29304 * @param {Roo.bootstrap.UploadCropbox} this
29305 * @param {Object} formData
29310 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29313 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29315 emptyText : 'Click to upload image',
29316 rotateNotify : 'Image is too small to rotate',
29317 errorTimeout : 3000,
29331 cropType : 'image/jpeg',
29333 canvasLoaded : false,
29334 isDocument : false,
29336 paramName : 'imageUpload',
29338 loadingText : 'Loading...',
29341 getAutoCreate : function()
29345 cls : 'roo-upload-cropbox',
29349 cls : 'roo-upload-cropbox-selector',
29354 cls : 'roo-upload-cropbox-body',
29355 style : 'cursor:pointer',
29359 cls : 'roo-upload-cropbox-preview'
29363 cls : 'roo-upload-cropbox-thumb'
29367 cls : 'roo-upload-cropbox-empty-notify',
29368 html : this.emptyText
29372 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29373 html : this.rotateNotify
29379 cls : 'roo-upload-cropbox-footer',
29382 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29392 onRender : function(ct, position)
29394 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29396 if (this.buttons.length) {
29398 Roo.each(this.buttons, function(bb) {
29400 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29402 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29408 this.maskEl = this.el;
29412 initEvents : function()
29414 this.urlAPI = (window.createObjectURL && window) ||
29415 (window.URL && URL.revokeObjectURL && URL) ||
29416 (window.webkitURL && webkitURL);
29418 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29419 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29421 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29422 this.selectorEl.hide();
29424 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29425 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29427 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29428 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29429 this.thumbEl.hide();
29431 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29432 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29434 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29435 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29436 this.errorEl.hide();
29438 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29439 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29440 this.footerEl.hide();
29442 this.setThumbBoxSize();
29448 this.fireEvent('initial', this);
29455 window.addEventListener("resize", function() { _this.resize(); } );
29457 this.bodyEl.on('click', this.beforeSelectFile, this);
29460 this.bodyEl.on('touchstart', this.onTouchStart, this);
29461 this.bodyEl.on('touchmove', this.onTouchMove, this);
29462 this.bodyEl.on('touchend', this.onTouchEnd, this);
29466 this.bodyEl.on('mousedown', this.onMouseDown, this);
29467 this.bodyEl.on('mousemove', this.onMouseMove, this);
29468 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29469 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29470 Roo.get(document).on('mouseup', this.onMouseUp, this);
29473 this.selectorEl.on('change', this.onFileSelected, this);
29479 this.baseScale = 1;
29481 this.baseRotate = 1;
29482 this.dragable = false;
29483 this.pinching = false;
29486 this.cropData = false;
29487 this.notifyEl.dom.innerHTML = this.emptyText;
29489 this.selectorEl.dom.value = '';
29493 resize : function()
29495 if(this.fireEvent('resize', this) != false){
29496 this.setThumbBoxPosition();
29497 this.setCanvasPosition();
29501 onFooterButtonClick : function(e, el, o, type)
29504 case 'rotate-left' :
29505 this.onRotateLeft(e);
29507 case 'rotate-right' :
29508 this.onRotateRight(e);
29511 this.beforeSelectFile(e);
29526 this.fireEvent('footerbuttonclick', this, type);
29529 beforeSelectFile : function(e)
29531 e.preventDefault();
29533 if(this.fireEvent('beforeselectfile', this) != false){
29534 this.selectorEl.dom.click();
29538 onFileSelected : function(e)
29540 e.preventDefault();
29542 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29546 var file = this.selectorEl.dom.files[0];
29548 if(this.fireEvent('inspect', this, file) != false){
29549 this.prepare(file);
29554 trash : function(e)
29556 this.fireEvent('trash', this);
29559 download : function(e)
29561 this.fireEvent('download', this);
29564 loadCanvas : function(src)
29566 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29570 this.imageEl = document.createElement('img');
29574 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29576 this.imageEl.src = src;
29580 onLoadCanvas : function()
29582 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29583 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29585 this.bodyEl.un('click', this.beforeSelectFile, this);
29587 this.notifyEl.hide();
29588 this.thumbEl.show();
29589 this.footerEl.show();
29591 this.baseRotateLevel();
29593 if(this.isDocument){
29594 this.setThumbBoxSize();
29597 this.setThumbBoxPosition();
29599 this.baseScaleLevel();
29605 this.canvasLoaded = true;
29608 this.maskEl.unmask();
29613 setCanvasPosition : function()
29615 if(!this.canvasEl){
29619 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29620 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29622 this.previewEl.setLeft(pw);
29623 this.previewEl.setTop(ph);
29627 onMouseDown : function(e)
29631 this.dragable = true;
29632 this.pinching = false;
29634 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29635 this.dragable = false;
29639 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29640 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29644 onMouseMove : function(e)
29648 if(!this.canvasLoaded){
29652 if (!this.dragable){
29656 var minX = Math.ceil(this.thumbEl.getLeft(true));
29657 var minY = Math.ceil(this.thumbEl.getTop(true));
29659 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29660 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29662 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29663 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29665 x = x - this.mouseX;
29666 y = y - this.mouseY;
29668 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29669 var bgY = Math.ceil(y + this.previewEl.getTop(true));
29671 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29672 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29674 this.previewEl.setLeft(bgX);
29675 this.previewEl.setTop(bgY);
29677 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29678 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29681 onMouseUp : function(e)
29685 this.dragable = false;
29688 onMouseWheel : function(e)
29692 this.startScale = this.scale;
29694 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29696 if(!this.zoomable()){
29697 this.scale = this.startScale;
29706 zoomable : function()
29708 var minScale = this.thumbEl.getWidth() / this.minWidth;
29710 if(this.minWidth < this.minHeight){
29711 minScale = this.thumbEl.getHeight() / this.minHeight;
29714 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29715 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29719 (this.rotate == 0 || this.rotate == 180) &&
29721 width > this.imageEl.OriginWidth ||
29722 height > this.imageEl.OriginHeight ||
29723 (width < this.minWidth && height < this.minHeight)
29731 (this.rotate == 90 || this.rotate == 270) &&
29733 width > this.imageEl.OriginWidth ||
29734 height > this.imageEl.OriginHeight ||
29735 (width < this.minHeight && height < this.minWidth)
29742 !this.isDocument &&
29743 (this.rotate == 0 || this.rotate == 180) &&
29745 width < this.minWidth ||
29746 width > this.imageEl.OriginWidth ||
29747 height < this.minHeight ||
29748 height > this.imageEl.OriginHeight
29755 !this.isDocument &&
29756 (this.rotate == 90 || this.rotate == 270) &&
29758 width < this.minHeight ||
29759 width > this.imageEl.OriginWidth ||
29760 height < this.minWidth ||
29761 height > this.imageEl.OriginHeight
29771 onRotateLeft : function(e)
29773 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29775 var minScale = this.thumbEl.getWidth() / this.minWidth;
29777 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29778 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29780 this.startScale = this.scale;
29782 while (this.getScaleLevel() < minScale){
29784 this.scale = this.scale + 1;
29786 if(!this.zoomable()){
29791 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29792 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29797 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29804 this.scale = this.startScale;
29806 this.onRotateFail();
29811 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29813 if(this.isDocument){
29814 this.setThumbBoxSize();
29815 this.setThumbBoxPosition();
29816 this.setCanvasPosition();
29821 this.fireEvent('rotate', this, 'left');
29825 onRotateRight : function(e)
29827 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29829 var minScale = this.thumbEl.getWidth() / this.minWidth;
29831 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29832 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29834 this.startScale = this.scale;
29836 while (this.getScaleLevel() < minScale){
29838 this.scale = this.scale + 1;
29840 if(!this.zoomable()){
29845 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29846 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29851 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29858 this.scale = this.startScale;
29860 this.onRotateFail();
29865 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29867 if(this.isDocument){
29868 this.setThumbBoxSize();
29869 this.setThumbBoxPosition();
29870 this.setCanvasPosition();
29875 this.fireEvent('rotate', this, 'right');
29878 onRotateFail : function()
29880 this.errorEl.show(true);
29884 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29889 this.previewEl.dom.innerHTML = '';
29891 var canvasEl = document.createElement("canvas");
29893 var contextEl = canvasEl.getContext("2d");
29895 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29896 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29897 var center = this.imageEl.OriginWidth / 2;
29899 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29900 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29901 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29902 center = this.imageEl.OriginHeight / 2;
29905 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29907 contextEl.translate(center, center);
29908 contextEl.rotate(this.rotate * Math.PI / 180);
29910 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29912 this.canvasEl = document.createElement("canvas");
29914 this.contextEl = this.canvasEl.getContext("2d");
29916 switch (this.rotate) {
29919 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29920 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29922 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29927 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29928 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29930 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29931 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);
29935 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29940 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29941 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29943 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29944 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);
29948 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);
29953 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29954 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29956 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29957 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29961 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);
29968 this.previewEl.appendChild(this.canvasEl);
29970 this.setCanvasPosition();
29975 if(!this.canvasLoaded){
29979 var imageCanvas = document.createElement("canvas");
29981 var imageContext = imageCanvas.getContext("2d");
29983 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29984 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29986 var center = imageCanvas.width / 2;
29988 imageContext.translate(center, center);
29990 imageContext.rotate(this.rotate * Math.PI / 180);
29992 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29994 var canvas = document.createElement("canvas");
29996 var context = canvas.getContext("2d");
29998 canvas.width = this.minWidth;
29999 canvas.height = this.minHeight;
30001 switch (this.rotate) {
30004 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30005 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30007 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30008 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30010 var targetWidth = this.minWidth - 2 * x;
30011 var targetHeight = this.minHeight - 2 * y;
30015 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30016 scale = targetWidth / width;
30019 if(x > 0 && y == 0){
30020 scale = targetHeight / height;
30023 if(x > 0 && y > 0){
30024 scale = targetWidth / width;
30026 if(width < height){
30027 scale = targetHeight / height;
30031 context.scale(scale, scale);
30033 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30034 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30036 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30037 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30039 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30044 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30045 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30047 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30048 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30050 var targetWidth = this.minWidth - 2 * x;
30051 var targetHeight = this.minHeight - 2 * y;
30055 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30056 scale = targetWidth / width;
30059 if(x > 0 && y == 0){
30060 scale = targetHeight / height;
30063 if(x > 0 && y > 0){
30064 scale = targetWidth / width;
30066 if(width < height){
30067 scale = targetHeight / height;
30071 context.scale(scale, scale);
30073 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30074 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30076 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30077 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30079 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30081 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30086 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30087 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30089 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30090 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30092 var targetWidth = this.minWidth - 2 * x;
30093 var targetHeight = this.minHeight - 2 * y;
30097 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30098 scale = targetWidth / width;
30101 if(x > 0 && y == 0){
30102 scale = targetHeight / height;
30105 if(x > 0 && y > 0){
30106 scale = targetWidth / width;
30108 if(width < height){
30109 scale = targetHeight / height;
30113 context.scale(scale, scale);
30115 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30116 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30118 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30119 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30121 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30122 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30124 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30129 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30130 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30132 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30133 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30135 var targetWidth = this.minWidth - 2 * x;
30136 var targetHeight = this.minHeight - 2 * y;
30140 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30141 scale = targetWidth / width;
30144 if(x > 0 && y == 0){
30145 scale = targetHeight / height;
30148 if(x > 0 && y > 0){
30149 scale = targetWidth / width;
30151 if(width < height){
30152 scale = targetHeight / height;
30156 context.scale(scale, scale);
30158 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30159 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30161 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30162 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30164 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30166 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30173 this.cropData = canvas.toDataURL(this.cropType);
30175 if(this.fireEvent('crop', this, this.cropData) !== false){
30176 this.process(this.file, this.cropData);
30183 setThumbBoxSize : function()
30187 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30188 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30189 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30191 this.minWidth = width;
30192 this.minHeight = height;
30194 if(this.rotate == 90 || this.rotate == 270){
30195 this.minWidth = height;
30196 this.minHeight = width;
30201 width = Math.ceil(this.minWidth * height / this.minHeight);
30203 if(this.minWidth > this.minHeight){
30205 height = Math.ceil(this.minHeight * width / this.minWidth);
30208 this.thumbEl.setStyle({
30209 width : width + 'px',
30210 height : height + 'px'
30217 setThumbBoxPosition : function()
30219 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30220 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30222 this.thumbEl.setLeft(x);
30223 this.thumbEl.setTop(y);
30227 baseRotateLevel : function()
30229 this.baseRotate = 1;
30232 typeof(this.exif) != 'undefined' &&
30233 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30234 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30236 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30239 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30243 baseScaleLevel : function()
30247 if(this.isDocument){
30249 if(this.baseRotate == 6 || this.baseRotate == 8){
30251 height = this.thumbEl.getHeight();
30252 this.baseScale = height / this.imageEl.OriginWidth;
30254 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30255 width = this.thumbEl.getWidth();
30256 this.baseScale = width / this.imageEl.OriginHeight;
30262 height = this.thumbEl.getHeight();
30263 this.baseScale = height / this.imageEl.OriginHeight;
30265 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30266 width = this.thumbEl.getWidth();
30267 this.baseScale = width / this.imageEl.OriginWidth;
30273 if(this.baseRotate == 6 || this.baseRotate == 8){
30275 width = this.thumbEl.getHeight();
30276 this.baseScale = width / this.imageEl.OriginHeight;
30278 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30279 height = this.thumbEl.getWidth();
30280 this.baseScale = height / this.imageEl.OriginHeight;
30283 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30284 height = this.thumbEl.getWidth();
30285 this.baseScale = height / this.imageEl.OriginHeight;
30287 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30288 width = this.thumbEl.getHeight();
30289 this.baseScale = width / this.imageEl.OriginWidth;
30296 width = this.thumbEl.getWidth();
30297 this.baseScale = width / this.imageEl.OriginWidth;
30299 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30300 height = this.thumbEl.getHeight();
30301 this.baseScale = height / this.imageEl.OriginHeight;
30304 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30306 height = this.thumbEl.getHeight();
30307 this.baseScale = height / this.imageEl.OriginHeight;
30309 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30310 width = this.thumbEl.getWidth();
30311 this.baseScale = width / this.imageEl.OriginWidth;
30319 getScaleLevel : function()
30321 return this.baseScale * Math.pow(1.1, this.scale);
30324 onTouchStart : function(e)
30326 if(!this.canvasLoaded){
30327 this.beforeSelectFile(e);
30331 var touches = e.browserEvent.touches;
30337 if(touches.length == 1){
30338 this.onMouseDown(e);
30342 if(touches.length != 2){
30348 for(var i = 0, finger; finger = touches[i]; i++){
30349 coords.push(finger.pageX, finger.pageY);
30352 var x = Math.pow(coords[0] - coords[2], 2);
30353 var y = Math.pow(coords[1] - coords[3], 2);
30355 this.startDistance = Math.sqrt(x + y);
30357 this.startScale = this.scale;
30359 this.pinching = true;
30360 this.dragable = false;
30364 onTouchMove : function(e)
30366 if(!this.pinching && !this.dragable){
30370 var touches = e.browserEvent.touches;
30377 this.onMouseMove(e);
30383 for(var i = 0, finger; finger = touches[i]; i++){
30384 coords.push(finger.pageX, finger.pageY);
30387 var x = Math.pow(coords[0] - coords[2], 2);
30388 var y = Math.pow(coords[1] - coords[3], 2);
30390 this.endDistance = Math.sqrt(x + y);
30392 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30394 if(!this.zoomable()){
30395 this.scale = this.startScale;
30403 onTouchEnd : function(e)
30405 this.pinching = false;
30406 this.dragable = false;
30410 process : function(file, crop)
30413 this.maskEl.mask(this.loadingText);
30416 this.xhr = new XMLHttpRequest();
30418 file.xhr = this.xhr;
30420 this.xhr.open(this.method, this.url, true);
30423 "Accept": "application/json",
30424 "Cache-Control": "no-cache",
30425 "X-Requested-With": "XMLHttpRequest"
30428 for (var headerName in headers) {
30429 var headerValue = headers[headerName];
30431 this.xhr.setRequestHeader(headerName, headerValue);
30437 this.xhr.onload = function()
30439 _this.xhrOnLoad(_this.xhr);
30442 this.xhr.onerror = function()
30444 _this.xhrOnError(_this.xhr);
30447 var formData = new FormData();
30449 formData.append('returnHTML', 'NO');
30452 formData.append('crop', crop);
30455 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30456 formData.append(this.paramName, file, file.name);
30459 if(typeof(file.filename) != 'undefined'){
30460 formData.append('filename', file.filename);
30463 if(typeof(file.mimetype) != 'undefined'){
30464 formData.append('mimetype', file.mimetype);
30467 if(this.fireEvent('arrange', this, formData) != false){
30468 this.xhr.send(formData);
30472 xhrOnLoad : function(xhr)
30475 this.maskEl.unmask();
30478 if (xhr.readyState !== 4) {
30479 this.fireEvent('exception', this, xhr);
30483 var response = Roo.decode(xhr.responseText);
30485 if(!response.success){
30486 this.fireEvent('exception', this, xhr);
30490 var response = Roo.decode(xhr.responseText);
30492 this.fireEvent('upload', this, response);
30496 xhrOnError : function()
30499 this.maskEl.unmask();
30502 Roo.log('xhr on error');
30504 var response = Roo.decode(xhr.responseText);
30510 prepare : function(file)
30513 this.maskEl.mask(this.loadingText);
30519 if(typeof(file) === 'string'){
30520 this.loadCanvas(file);
30524 if(!file || !this.urlAPI){
30529 this.cropType = file.type;
30533 if(this.fireEvent('prepare', this, this.file) != false){
30535 var reader = new FileReader();
30537 reader.onload = function (e) {
30538 if (e.target.error) {
30539 Roo.log(e.target.error);
30543 var buffer = e.target.result,
30544 dataView = new DataView(buffer),
30546 maxOffset = dataView.byteLength - 4,
30550 if (dataView.getUint16(0) === 0xffd8) {
30551 while (offset < maxOffset) {
30552 markerBytes = dataView.getUint16(offset);
30554 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30555 markerLength = dataView.getUint16(offset + 2) + 2;
30556 if (offset + markerLength > dataView.byteLength) {
30557 Roo.log('Invalid meta data: Invalid segment size.');
30561 if(markerBytes == 0xffe1){
30562 _this.parseExifData(
30569 offset += markerLength;
30579 var url = _this.urlAPI.createObjectURL(_this.file);
30581 _this.loadCanvas(url);
30586 reader.readAsArrayBuffer(this.file);
30592 parseExifData : function(dataView, offset, length)
30594 var tiffOffset = offset + 10,
30598 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30599 // No Exif data, might be XMP data instead
30603 // Check for the ASCII code for "Exif" (0x45786966):
30604 if (dataView.getUint32(offset + 4) !== 0x45786966) {
30605 // No Exif data, might be XMP data instead
30608 if (tiffOffset + 8 > dataView.byteLength) {
30609 Roo.log('Invalid Exif data: Invalid segment size.');
30612 // Check for the two null bytes:
30613 if (dataView.getUint16(offset + 8) !== 0x0000) {
30614 Roo.log('Invalid Exif data: Missing byte alignment offset.');
30617 // Check the byte alignment:
30618 switch (dataView.getUint16(tiffOffset)) {
30620 littleEndian = true;
30623 littleEndian = false;
30626 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30629 // Check for the TIFF tag marker (0x002A):
30630 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30631 Roo.log('Invalid Exif data: Missing TIFF marker.');
30634 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30635 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30637 this.parseExifTags(
30640 tiffOffset + dirOffset,
30645 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30650 if (dirOffset + 6 > dataView.byteLength) {
30651 Roo.log('Invalid Exif data: Invalid directory offset.');
30654 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30655 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30656 if (dirEndOffset + 4 > dataView.byteLength) {
30657 Roo.log('Invalid Exif data: Invalid directory size.');
30660 for (i = 0; i < tagsNumber; i += 1) {
30664 dirOffset + 2 + 12 * i, // tag offset
30668 // Return the offset to the next directory:
30669 return dataView.getUint32(dirEndOffset, littleEndian);
30672 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
30674 var tag = dataView.getUint16(offset, littleEndian);
30676 this.exif[tag] = this.getExifValue(
30680 dataView.getUint16(offset + 2, littleEndian), // tag type
30681 dataView.getUint32(offset + 4, littleEndian), // tag length
30686 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30688 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30697 Roo.log('Invalid Exif data: Invalid tag type.');
30701 tagSize = tagType.size * length;
30702 // Determine if the value is contained in the dataOffset bytes,
30703 // or if the value at the dataOffset is a pointer to the actual data:
30704 dataOffset = tagSize > 4 ?
30705 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30706 if (dataOffset + tagSize > dataView.byteLength) {
30707 Roo.log('Invalid Exif data: Invalid data offset.');
30710 if (length === 1) {
30711 return tagType.getValue(dataView, dataOffset, littleEndian);
30714 for (i = 0; i < length; i += 1) {
30715 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30718 if (tagType.ascii) {
30720 // Concatenate the chars:
30721 for (i = 0; i < values.length; i += 1) {
30723 // Ignore the terminating NULL byte(s):
30724 if (c === '\u0000') {
30736 Roo.apply(Roo.bootstrap.UploadCropbox, {
30738 'Orientation': 0x0112
30742 1: 0, //'top-left',
30744 3: 180, //'bottom-right',
30745 // 4: 'bottom-left',
30747 6: 90, //'right-top',
30748 // 7: 'right-bottom',
30749 8: 270 //'left-bottom'
30753 // byte, 8-bit unsigned int:
30755 getValue: function (dataView, dataOffset) {
30756 return dataView.getUint8(dataOffset);
30760 // ascii, 8-bit byte:
30762 getValue: function (dataView, dataOffset) {
30763 return String.fromCharCode(dataView.getUint8(dataOffset));
30768 // short, 16 bit int:
30770 getValue: function (dataView, dataOffset, littleEndian) {
30771 return dataView.getUint16(dataOffset, littleEndian);
30775 // long, 32 bit int:
30777 getValue: function (dataView, dataOffset, littleEndian) {
30778 return dataView.getUint32(dataOffset, littleEndian);
30782 // rational = two long values, first is numerator, second is denominator:
30784 getValue: function (dataView, dataOffset, littleEndian) {
30785 return dataView.getUint32(dataOffset, littleEndian) /
30786 dataView.getUint32(dataOffset + 4, littleEndian);
30790 // slong, 32 bit signed int:
30792 getValue: function (dataView, dataOffset, littleEndian) {
30793 return dataView.getInt32(dataOffset, littleEndian);
30797 // srational, two slongs, first is numerator, second is denominator:
30799 getValue: function (dataView, dataOffset, littleEndian) {
30800 return dataView.getInt32(dataOffset, littleEndian) /
30801 dataView.getInt32(dataOffset + 4, littleEndian);
30811 cls : 'btn-group roo-upload-cropbox-rotate-left',
30812 action : 'rotate-left',
30816 cls : 'btn btn-default',
30817 html : '<i class="fa fa-undo"></i>'
30823 cls : 'btn-group roo-upload-cropbox-picture',
30824 action : 'picture',
30828 cls : 'btn btn-default',
30829 html : '<i class="fa fa-picture-o"></i>'
30835 cls : 'btn-group roo-upload-cropbox-rotate-right',
30836 action : 'rotate-right',
30840 cls : 'btn btn-default',
30841 html : '<i class="fa fa-repeat"></i>'
30849 cls : 'btn-group roo-upload-cropbox-rotate-left',
30850 action : 'rotate-left',
30854 cls : 'btn btn-default',
30855 html : '<i class="fa fa-undo"></i>'
30861 cls : 'btn-group roo-upload-cropbox-download',
30862 action : 'download',
30866 cls : 'btn btn-default',
30867 html : '<i class="fa fa-download"></i>'
30873 cls : 'btn-group roo-upload-cropbox-crop',
30878 cls : 'btn btn-default',
30879 html : '<i class="fa fa-crop"></i>'
30885 cls : 'btn-group roo-upload-cropbox-trash',
30890 cls : 'btn btn-default',
30891 html : '<i class="fa fa-trash"></i>'
30897 cls : 'btn-group roo-upload-cropbox-rotate-right',
30898 action : 'rotate-right',
30902 cls : 'btn btn-default',
30903 html : '<i class="fa fa-repeat"></i>'
30911 cls : 'btn-group roo-upload-cropbox-rotate-left',
30912 action : 'rotate-left',
30916 cls : 'btn btn-default',
30917 html : '<i class="fa fa-undo"></i>'
30923 cls : 'btn-group roo-upload-cropbox-rotate-right',
30924 action : 'rotate-right',
30928 cls : 'btn btn-default',
30929 html : '<i class="fa fa-repeat"></i>'
30942 * @class Roo.bootstrap.DocumentManager
30943 * @extends Roo.bootstrap.Component
30944 * Bootstrap DocumentManager class
30945 * @cfg {String} paramName default 'imageUpload'
30946 * @cfg {String} toolTipName default 'filename'
30947 * @cfg {String} method default POST
30948 * @cfg {String} url action url
30949 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30950 * @cfg {Boolean} multiple multiple upload default true
30951 * @cfg {Number} thumbSize default 300
30952 * @cfg {String} fieldLabel
30953 * @cfg {Number} labelWidth default 4
30954 * @cfg {String} labelAlign (left|top) default left
30955 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30956 * @cfg {Number} labellg set the width of label (1-12)
30957 * @cfg {Number} labelmd set the width of label (1-12)
30958 * @cfg {Number} labelsm set the width of label (1-12)
30959 * @cfg {Number} labelxs set the width of label (1-12)
30962 * Create a new DocumentManager
30963 * @param {Object} config The config object
30966 Roo.bootstrap.DocumentManager = function(config){
30967 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30970 this.delegates = [];
30975 * Fire when initial the DocumentManager
30976 * @param {Roo.bootstrap.DocumentManager} this
30981 * inspect selected file
30982 * @param {Roo.bootstrap.DocumentManager} this
30983 * @param {File} file
30988 * Fire when xhr load exception
30989 * @param {Roo.bootstrap.DocumentManager} this
30990 * @param {XMLHttpRequest} xhr
30992 "exception" : true,
30994 * @event afterupload
30995 * Fire when xhr load exception
30996 * @param {Roo.bootstrap.DocumentManager} this
30997 * @param {XMLHttpRequest} xhr
30999 "afterupload" : true,
31002 * prepare the form data
31003 * @param {Roo.bootstrap.DocumentManager} this
31004 * @param {Object} formData
31009 * Fire when remove the file
31010 * @param {Roo.bootstrap.DocumentManager} this
31011 * @param {Object} file
31016 * Fire after refresh the file
31017 * @param {Roo.bootstrap.DocumentManager} this
31022 * Fire after click the image
31023 * @param {Roo.bootstrap.DocumentManager} this
31024 * @param {Object} file
31029 * Fire when upload a image and editable set to true
31030 * @param {Roo.bootstrap.DocumentManager} this
31031 * @param {Object} file
31035 * @event beforeselectfile
31036 * Fire before select file
31037 * @param {Roo.bootstrap.DocumentManager} this
31039 "beforeselectfile" : true,
31042 * Fire before process file
31043 * @param {Roo.bootstrap.DocumentManager} this
31044 * @param {Object} file
31048 * @event previewrendered
31049 * Fire when preview rendered
31050 * @param {Roo.bootstrap.DocumentManager} this
31051 * @param {Object} file
31053 "previewrendered" : true,
31056 "previewResize" : true
31061 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31070 paramName : 'imageUpload',
31071 toolTipName : 'filename',
31074 labelAlign : 'left',
31084 getAutoCreate : function()
31086 var managerWidget = {
31088 cls : 'roo-document-manager',
31092 cls : 'roo-document-manager-selector',
31097 cls : 'roo-document-manager-uploader',
31101 cls : 'roo-document-manager-upload-btn',
31102 html : '<i class="fa fa-plus"></i>'
31113 cls : 'column col-md-12',
31118 if(this.fieldLabel.length){
31123 cls : 'column col-md-12',
31124 html : this.fieldLabel
31128 cls : 'column col-md-12',
31133 if(this.labelAlign == 'left'){
31138 html : this.fieldLabel
31147 if(this.labelWidth > 12){
31148 content[0].style = "width: " + this.labelWidth + 'px';
31151 if(this.labelWidth < 13 && this.labelmd == 0){
31152 this.labelmd = this.labelWidth;
31155 if(this.labellg > 0){
31156 content[0].cls += ' col-lg-' + this.labellg;
31157 content[1].cls += ' col-lg-' + (12 - this.labellg);
31160 if(this.labelmd > 0){
31161 content[0].cls += ' col-md-' + this.labelmd;
31162 content[1].cls += ' col-md-' + (12 - this.labelmd);
31165 if(this.labelsm > 0){
31166 content[0].cls += ' col-sm-' + this.labelsm;
31167 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31170 if(this.labelxs > 0){
31171 content[0].cls += ' col-xs-' + this.labelxs;
31172 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31180 cls : 'row clearfix',
31188 initEvents : function()
31190 this.managerEl = this.el.select('.roo-document-manager', true).first();
31191 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31193 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31194 this.selectorEl.hide();
31197 this.selectorEl.attr('multiple', 'multiple');
31200 this.selectorEl.on('change', this.onFileSelected, this);
31202 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31203 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31205 this.uploader.on('click', this.onUploaderClick, this);
31207 this.renderProgressDialog();
31211 window.addEventListener("resize", function() { _this.refresh(); } );
31213 this.fireEvent('initial', this);
31216 renderProgressDialog : function()
31220 this.progressDialog = new Roo.bootstrap.Modal({
31221 cls : 'roo-document-manager-progress-dialog',
31222 allow_close : false,
31233 btnclick : function() {
31234 _this.uploadCancel();
31240 this.progressDialog.render(Roo.get(document.body));
31242 this.progress = new Roo.bootstrap.Progress({
31243 cls : 'roo-document-manager-progress',
31248 this.progress.render(this.progressDialog.getChildContainer());
31250 this.progressBar = new Roo.bootstrap.ProgressBar({
31251 cls : 'roo-document-manager-progress-bar',
31254 aria_valuemax : 12,
31258 this.progressBar.render(this.progress.getChildContainer());
31261 onUploaderClick : function(e)
31263 e.preventDefault();
31265 if(this.fireEvent('beforeselectfile', this) != false){
31266 this.selectorEl.dom.click();
31271 onFileSelected : function(e)
31273 e.preventDefault();
31275 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31279 Roo.each(this.selectorEl.dom.files, function(file){
31280 if(this.fireEvent('inspect', this, file) != false){
31281 this.files.push(file);
31291 this.selectorEl.dom.value = '';
31293 if(!this.files || !this.files.length){
31297 if(this.boxes > 0 && this.files.length > this.boxes){
31298 this.files = this.files.slice(0, this.boxes);
31301 this.uploader.show();
31303 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31304 this.uploader.hide();
31313 Roo.each(this.files, function(file){
31315 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31316 var f = this.renderPreview(file);
31321 if(file.type.indexOf('image') != -1){
31322 this.delegates.push(
31324 _this.process(file);
31325 }).createDelegate(this)
31333 _this.process(file);
31334 }).createDelegate(this)
31339 this.files = files;
31341 this.delegates = this.delegates.concat(docs);
31343 if(!this.delegates.length){
31348 this.progressBar.aria_valuemax = this.delegates.length;
31355 arrange : function()
31357 if(!this.delegates.length){
31358 this.progressDialog.hide();
31363 var delegate = this.delegates.shift();
31365 this.progressDialog.show();
31367 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31369 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31374 refresh : function()
31376 this.uploader.show();
31378 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31379 this.uploader.hide();
31382 Roo.isTouch ? this.closable(false) : this.closable(true);
31384 this.fireEvent('refresh', this);
31387 onRemove : function(e, el, o)
31389 e.preventDefault();
31391 this.fireEvent('remove', this, o);
31395 remove : function(o)
31399 Roo.each(this.files, function(file){
31400 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31409 this.files = files;
31416 Roo.each(this.files, function(file){
31421 file.target.remove();
31430 onClick : function(e, el, o)
31432 e.preventDefault();
31434 this.fireEvent('click', this, o);
31438 closable : function(closable)
31440 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31442 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31454 xhrOnLoad : function(xhr)
31456 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31460 if (xhr.readyState !== 4) {
31462 this.fireEvent('exception', this, xhr);
31466 var response = Roo.decode(xhr.responseText);
31468 if(!response.success){
31470 this.fireEvent('exception', this, xhr);
31474 var file = this.renderPreview(response.data);
31476 this.files.push(file);
31480 this.fireEvent('afterupload', this, xhr);
31484 xhrOnError : function(xhr)
31486 Roo.log('xhr on error');
31488 var response = Roo.decode(xhr.responseText);
31495 process : function(file)
31497 if(this.fireEvent('process', this, file) !== false){
31498 if(this.editable && file.type.indexOf('image') != -1){
31499 this.fireEvent('edit', this, file);
31503 this.uploadStart(file, false);
31510 uploadStart : function(file, crop)
31512 this.xhr = new XMLHttpRequest();
31514 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31519 file.xhr = this.xhr;
31521 this.managerEl.createChild({
31523 cls : 'roo-document-manager-loading',
31527 tooltip : file.name,
31528 cls : 'roo-document-manager-thumb',
31529 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31535 this.xhr.open(this.method, this.url, true);
31538 "Accept": "application/json",
31539 "Cache-Control": "no-cache",
31540 "X-Requested-With": "XMLHttpRequest"
31543 for (var headerName in headers) {
31544 var headerValue = headers[headerName];
31546 this.xhr.setRequestHeader(headerName, headerValue);
31552 this.xhr.onload = function()
31554 _this.xhrOnLoad(_this.xhr);
31557 this.xhr.onerror = function()
31559 _this.xhrOnError(_this.xhr);
31562 var formData = new FormData();
31564 formData.append('returnHTML', 'NO');
31567 formData.append('crop', crop);
31570 formData.append(this.paramName, file, file.name);
31577 if(this.fireEvent('prepare', this, formData, options) != false){
31579 if(options.manually){
31583 this.xhr.send(formData);
31587 this.uploadCancel();
31590 uploadCancel : function()
31596 this.delegates = [];
31598 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31605 renderPreview : function(file)
31607 if(typeof(file.target) != 'undefined' && file.target){
31611 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31613 var previewEl = this.managerEl.createChild({
31615 cls : 'roo-document-manager-preview',
31619 tooltip : file[this.toolTipName],
31620 cls : 'roo-document-manager-thumb',
31621 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31626 html : '<i class="fa fa-times-circle"></i>'
31631 var close = previewEl.select('button.close', true).first();
31633 close.on('click', this.onRemove, this, file);
31635 file.target = previewEl;
31637 var image = previewEl.select('img', true).first();
31641 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31643 image.on('click', this.onClick, this, file);
31645 this.fireEvent('previewrendered', this, file);
31651 onPreviewLoad : function(file, image)
31653 if(typeof(file.target) == 'undefined' || !file.target){
31657 var width = image.dom.naturalWidth || image.dom.width;
31658 var height = image.dom.naturalHeight || image.dom.height;
31660 if(!this.previewResize) {
31664 if(width > height){
31665 file.target.addClass('wide');
31669 file.target.addClass('tall');
31674 uploadFromSource : function(file, crop)
31676 this.xhr = new XMLHttpRequest();
31678 this.managerEl.createChild({
31680 cls : 'roo-document-manager-loading',
31684 tooltip : file.name,
31685 cls : 'roo-document-manager-thumb',
31686 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31692 this.xhr.open(this.method, this.url, true);
31695 "Accept": "application/json",
31696 "Cache-Control": "no-cache",
31697 "X-Requested-With": "XMLHttpRequest"
31700 for (var headerName in headers) {
31701 var headerValue = headers[headerName];
31703 this.xhr.setRequestHeader(headerName, headerValue);
31709 this.xhr.onload = function()
31711 _this.xhrOnLoad(_this.xhr);
31714 this.xhr.onerror = function()
31716 _this.xhrOnError(_this.xhr);
31719 var formData = new FormData();
31721 formData.append('returnHTML', 'NO');
31723 formData.append('crop', crop);
31725 if(typeof(file.filename) != 'undefined'){
31726 formData.append('filename', file.filename);
31729 if(typeof(file.mimetype) != 'undefined'){
31730 formData.append('mimetype', file.mimetype);
31735 if(this.fireEvent('prepare', this, formData) != false){
31736 this.xhr.send(formData);
31746 * @class Roo.bootstrap.DocumentViewer
31747 * @extends Roo.bootstrap.Component
31748 * Bootstrap DocumentViewer class
31749 * @cfg {Boolean} showDownload (true|false) show download button (default true)
31750 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31753 * Create a new DocumentViewer
31754 * @param {Object} config The config object
31757 Roo.bootstrap.DocumentViewer = function(config){
31758 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31763 * Fire after initEvent
31764 * @param {Roo.bootstrap.DocumentViewer} this
31770 * @param {Roo.bootstrap.DocumentViewer} this
31775 * Fire after download button
31776 * @param {Roo.bootstrap.DocumentViewer} this
31781 * Fire after trash button
31782 * @param {Roo.bootstrap.DocumentViewer} this
31789 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
31791 showDownload : true,
31795 getAutoCreate : function()
31799 cls : 'roo-document-viewer',
31803 cls : 'roo-document-viewer-body',
31807 cls : 'roo-document-viewer-thumb',
31811 cls : 'roo-document-viewer-image'
31819 cls : 'roo-document-viewer-footer',
31822 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31826 cls : 'btn-group roo-document-viewer-download',
31830 cls : 'btn btn-default',
31831 html : '<i class="fa fa-download"></i>'
31837 cls : 'btn-group roo-document-viewer-trash',
31841 cls : 'btn btn-default',
31842 html : '<i class="fa fa-trash"></i>'
31855 initEvents : function()
31857 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31858 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31860 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31861 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31863 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31864 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31866 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31867 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31869 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31870 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31872 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31873 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31875 this.bodyEl.on('click', this.onClick, this);
31876 this.downloadBtn.on('click', this.onDownload, this);
31877 this.trashBtn.on('click', this.onTrash, this);
31879 this.downloadBtn.hide();
31880 this.trashBtn.hide();
31882 if(this.showDownload){
31883 this.downloadBtn.show();
31886 if(this.showTrash){
31887 this.trashBtn.show();
31890 if(!this.showDownload && !this.showTrash) {
31891 this.footerEl.hide();
31896 initial : function()
31898 this.fireEvent('initial', this);
31902 onClick : function(e)
31904 e.preventDefault();
31906 this.fireEvent('click', this);
31909 onDownload : function(e)
31911 e.preventDefault();
31913 this.fireEvent('download', this);
31916 onTrash : function(e)
31918 e.preventDefault();
31920 this.fireEvent('trash', this);
31932 * @class Roo.bootstrap.NavProgressBar
31933 * @extends Roo.bootstrap.Component
31934 * Bootstrap NavProgressBar class
31937 * Create a new nav progress bar
31938 * @param {Object} config The config object
31941 Roo.bootstrap.NavProgressBar = function(config){
31942 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31944 this.bullets = this.bullets || [];
31946 // Roo.bootstrap.NavProgressBar.register(this);
31950 * Fires when the active item changes
31951 * @param {Roo.bootstrap.NavProgressBar} this
31952 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31953 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
31960 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
31965 getAutoCreate : function()
31967 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31971 cls : 'roo-navigation-bar-group',
31975 cls : 'roo-navigation-top-bar'
31979 cls : 'roo-navigation-bullets-bar',
31983 cls : 'roo-navigation-bar'
31990 cls : 'roo-navigation-bottom-bar'
32000 initEvents: function()
32005 onRender : function(ct, position)
32007 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32009 if(this.bullets.length){
32010 Roo.each(this.bullets, function(b){
32019 addItem : function(cfg)
32021 var item = new Roo.bootstrap.NavProgressItem(cfg);
32023 item.parentId = this.id;
32024 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32027 var top = new Roo.bootstrap.Element({
32029 cls : 'roo-navigation-bar-text'
32032 var bottom = new Roo.bootstrap.Element({
32034 cls : 'roo-navigation-bar-text'
32037 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32038 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32040 var topText = new Roo.bootstrap.Element({
32042 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32045 var bottomText = new Roo.bootstrap.Element({
32047 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32050 topText.onRender(top.el, null);
32051 bottomText.onRender(bottom.el, null);
32054 item.bottomEl = bottom;
32057 this.barItems.push(item);
32062 getActive : function()
32064 var active = false;
32066 Roo.each(this.barItems, function(v){
32068 if (!v.isActive()) {
32080 setActiveItem : function(item)
32084 Roo.each(this.barItems, function(v){
32085 if (v.rid == item.rid) {
32089 if (v.isActive()) {
32090 v.setActive(false);
32095 item.setActive(true);
32097 this.fireEvent('changed', this, item, prev);
32100 getBarItem: function(rid)
32104 Roo.each(this.barItems, function(e) {
32105 if (e.rid != rid) {
32116 indexOfItem : function(item)
32120 Roo.each(this.barItems, function(v, i){
32122 if (v.rid != item.rid) {
32133 setActiveNext : function()
32135 var i = this.indexOfItem(this.getActive());
32137 if (i > this.barItems.length) {
32141 this.setActiveItem(this.barItems[i+1]);
32144 setActivePrev : function()
32146 var i = this.indexOfItem(this.getActive());
32152 this.setActiveItem(this.barItems[i-1]);
32155 format : function()
32157 if(!this.barItems.length){
32161 var width = 100 / this.barItems.length;
32163 Roo.each(this.barItems, function(i){
32164 i.el.setStyle('width', width + '%');
32165 i.topEl.el.setStyle('width', width + '%');
32166 i.bottomEl.el.setStyle('width', width + '%');
32175 * Nav Progress Item
32180 * @class Roo.bootstrap.NavProgressItem
32181 * @extends Roo.bootstrap.Component
32182 * Bootstrap NavProgressItem class
32183 * @cfg {String} rid the reference id
32184 * @cfg {Boolean} active (true|false) Is item active default false
32185 * @cfg {Boolean} disabled (true|false) Is item active default false
32186 * @cfg {String} html
32187 * @cfg {String} position (top|bottom) text position default bottom
32188 * @cfg {String} icon show icon instead of number
32191 * Create a new NavProgressItem
32192 * @param {Object} config The config object
32194 Roo.bootstrap.NavProgressItem = function(config){
32195 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32200 * The raw click event for the entire grid.
32201 * @param {Roo.bootstrap.NavProgressItem} this
32202 * @param {Roo.EventObject} e
32209 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32215 position : 'bottom',
32218 getAutoCreate : function()
32220 var iconCls = 'roo-navigation-bar-item-icon';
32222 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32226 cls: 'roo-navigation-bar-item',
32236 cfg.cls += ' active';
32239 cfg.cls += ' disabled';
32245 disable : function()
32247 this.setDisabled(true);
32250 enable : function()
32252 this.setDisabled(false);
32255 initEvents: function()
32257 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32259 this.iconEl.on('click', this.onClick, this);
32262 onClick : function(e)
32264 e.preventDefault();
32270 if(this.fireEvent('click', this, e) === false){
32274 this.parent().setActiveItem(this);
32277 isActive: function ()
32279 return this.active;
32282 setActive : function(state)
32284 if(this.active == state){
32288 this.active = state;
32291 this.el.addClass('active');
32295 this.el.removeClass('active');
32300 setDisabled : function(state)
32302 if(this.disabled == state){
32306 this.disabled = state;
32309 this.el.addClass('disabled');
32313 this.el.removeClass('disabled');
32316 tooltipEl : function()
32318 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32331 * @class Roo.bootstrap.FieldLabel
32332 * @extends Roo.bootstrap.Component
32333 * Bootstrap FieldLabel class
32334 * @cfg {String} html contents of the element
32335 * @cfg {String} tag tag of the element default label
32336 * @cfg {String} cls class of the element
32337 * @cfg {String} target label target
32338 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32339 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32340 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32341 * @cfg {String} iconTooltip default "This field is required"
32342 * @cfg {String} indicatorpos (left|right) default left
32345 * Create a new FieldLabel
32346 * @param {Object} config The config object
32349 Roo.bootstrap.FieldLabel = function(config){
32350 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32355 * Fires after the field has been marked as invalid.
32356 * @param {Roo.form.FieldLabel} this
32357 * @param {String} msg The validation message
32362 * Fires after the field has been validated with no errors.
32363 * @param {Roo.form.FieldLabel} this
32369 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32376 invalidClass : 'has-warning',
32377 validClass : 'has-success',
32378 iconTooltip : 'This field is required',
32379 indicatorpos : 'left',
32381 getAutoCreate : function(){
32384 if (!this.allowBlank) {
32390 cls : 'roo-bootstrap-field-label ' + this.cls,
32395 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32396 tooltip : this.iconTooltip
32405 if(this.indicatorpos == 'right'){
32408 cls : 'roo-bootstrap-field-label ' + this.cls,
32417 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32418 tooltip : this.iconTooltip
32427 initEvents: function()
32429 Roo.bootstrap.Element.superclass.initEvents.call(this);
32431 this.indicator = this.indicatorEl();
32433 if(this.indicator){
32434 this.indicator.removeClass('visible');
32435 this.indicator.addClass('invisible');
32438 Roo.bootstrap.FieldLabel.register(this);
32441 indicatorEl : function()
32443 var indicator = this.el.select('i.roo-required-indicator',true).first();
32454 * Mark this field as valid
32456 markValid : function()
32458 if(this.indicator){
32459 this.indicator.removeClass('visible');
32460 this.indicator.addClass('invisible');
32462 if (Roo.bootstrap.version == 3) {
32463 this.el.removeClass(this.invalidClass);
32464 this.el.addClass(this.validClass);
32466 this.el.removeClass('is-invalid');
32467 this.el.addClass('is-valid');
32471 this.fireEvent('valid', this);
32475 * Mark this field as invalid
32476 * @param {String} msg The validation message
32478 markInvalid : function(msg)
32480 if(this.indicator){
32481 this.indicator.removeClass('invisible');
32482 this.indicator.addClass('visible');
32484 if (Roo.bootstrap.version == 3) {
32485 this.el.removeClass(this.validClass);
32486 this.el.addClass(this.invalidClass);
32488 this.el.removeClass('is-valid');
32489 this.el.addClass('is-invalid');
32493 this.fireEvent('invalid', this, msg);
32499 Roo.apply(Roo.bootstrap.FieldLabel, {
32504 * register a FieldLabel Group
32505 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32507 register : function(label)
32509 if(this.groups.hasOwnProperty(label.target)){
32513 this.groups[label.target] = label;
32517 * fetch a FieldLabel Group based on the target
32518 * @param {string} target
32519 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32521 get: function(target) {
32522 if (typeof(this.groups[target]) == 'undefined') {
32526 return this.groups[target] ;
32535 * page DateSplitField.
32541 * @class Roo.bootstrap.DateSplitField
32542 * @extends Roo.bootstrap.Component
32543 * Bootstrap DateSplitField class
32544 * @cfg {string} fieldLabel - the label associated
32545 * @cfg {Number} labelWidth set the width of label (0-12)
32546 * @cfg {String} labelAlign (top|left)
32547 * @cfg {Boolean} dayAllowBlank (true|false) default false
32548 * @cfg {Boolean} monthAllowBlank (true|false) default false
32549 * @cfg {Boolean} yearAllowBlank (true|false) default false
32550 * @cfg {string} dayPlaceholder
32551 * @cfg {string} monthPlaceholder
32552 * @cfg {string} yearPlaceholder
32553 * @cfg {string} dayFormat default 'd'
32554 * @cfg {string} monthFormat default 'm'
32555 * @cfg {string} yearFormat default 'Y'
32556 * @cfg {Number} labellg set the width of label (1-12)
32557 * @cfg {Number} labelmd set the width of label (1-12)
32558 * @cfg {Number} labelsm set the width of label (1-12)
32559 * @cfg {Number} labelxs set the width of label (1-12)
32563 * Create a new DateSplitField
32564 * @param {Object} config The config object
32567 Roo.bootstrap.DateSplitField = function(config){
32568 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32574 * getting the data of years
32575 * @param {Roo.bootstrap.DateSplitField} this
32576 * @param {Object} years
32581 * getting the data of days
32582 * @param {Roo.bootstrap.DateSplitField} this
32583 * @param {Object} days
32588 * Fires after the field has been marked as invalid.
32589 * @param {Roo.form.Field} this
32590 * @param {String} msg The validation message
32595 * Fires after the field has been validated with no errors.
32596 * @param {Roo.form.Field} this
32602 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
32605 labelAlign : 'top',
32607 dayAllowBlank : false,
32608 monthAllowBlank : false,
32609 yearAllowBlank : false,
32610 dayPlaceholder : '',
32611 monthPlaceholder : '',
32612 yearPlaceholder : '',
32616 isFormField : true,
32622 getAutoCreate : function()
32626 cls : 'row roo-date-split-field-group',
32631 cls : 'form-hidden-field roo-date-split-field-group-value',
32637 var labelCls = 'col-md-12';
32638 var contentCls = 'col-md-4';
32640 if(this.fieldLabel){
32644 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32648 html : this.fieldLabel
32653 if(this.labelAlign == 'left'){
32655 if(this.labelWidth > 12){
32656 label.style = "width: " + this.labelWidth + 'px';
32659 if(this.labelWidth < 13 && this.labelmd == 0){
32660 this.labelmd = this.labelWidth;
32663 if(this.labellg > 0){
32664 labelCls = ' col-lg-' + this.labellg;
32665 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32668 if(this.labelmd > 0){
32669 labelCls = ' col-md-' + this.labelmd;
32670 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32673 if(this.labelsm > 0){
32674 labelCls = ' col-sm-' + this.labelsm;
32675 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32678 if(this.labelxs > 0){
32679 labelCls = ' col-xs-' + this.labelxs;
32680 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32684 label.cls += ' ' + labelCls;
32686 cfg.cn.push(label);
32689 Roo.each(['day', 'month', 'year'], function(t){
32692 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32699 inputEl: function ()
32701 return this.el.select('.roo-date-split-field-group-value', true).first();
32704 onRender : function(ct, position)
32708 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32710 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32712 this.dayField = new Roo.bootstrap.ComboBox({
32713 allowBlank : this.dayAllowBlank,
32714 alwaysQuery : true,
32715 displayField : 'value',
32718 forceSelection : true,
32720 placeholder : this.dayPlaceholder,
32721 selectOnFocus : true,
32722 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32723 triggerAction : 'all',
32725 valueField : 'value',
32726 store : new Roo.data.SimpleStore({
32727 data : (function() {
32729 _this.fireEvent('days', _this, days);
32732 fields : [ 'value' ]
32735 select : function (_self, record, index)
32737 _this.setValue(_this.getValue());
32742 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32744 this.monthField = new Roo.bootstrap.MonthField({
32745 after : '<i class=\"fa fa-calendar\"></i>',
32746 allowBlank : this.monthAllowBlank,
32747 placeholder : this.monthPlaceholder,
32750 render : function (_self)
32752 this.el.select('span.input-group-addon', true).first().on('click', function(e){
32753 e.preventDefault();
32757 select : function (_self, oldvalue, newvalue)
32759 _this.setValue(_this.getValue());
32764 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32766 this.yearField = new Roo.bootstrap.ComboBox({
32767 allowBlank : this.yearAllowBlank,
32768 alwaysQuery : true,
32769 displayField : 'value',
32772 forceSelection : true,
32774 placeholder : this.yearPlaceholder,
32775 selectOnFocus : true,
32776 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32777 triggerAction : 'all',
32779 valueField : 'value',
32780 store : new Roo.data.SimpleStore({
32781 data : (function() {
32783 _this.fireEvent('years', _this, years);
32786 fields : [ 'value' ]
32789 select : function (_self, record, index)
32791 _this.setValue(_this.getValue());
32796 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32799 setValue : function(v, format)
32801 this.inputEl.dom.value = v;
32803 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32805 var d = Date.parseDate(v, f);
32812 this.setDay(d.format(this.dayFormat));
32813 this.setMonth(d.format(this.monthFormat));
32814 this.setYear(d.format(this.yearFormat));
32821 setDay : function(v)
32823 this.dayField.setValue(v);
32824 this.inputEl.dom.value = this.getValue();
32829 setMonth : function(v)
32831 this.monthField.setValue(v, true);
32832 this.inputEl.dom.value = this.getValue();
32837 setYear : function(v)
32839 this.yearField.setValue(v);
32840 this.inputEl.dom.value = this.getValue();
32845 getDay : function()
32847 return this.dayField.getValue();
32850 getMonth : function()
32852 return this.monthField.getValue();
32855 getYear : function()
32857 return this.yearField.getValue();
32860 getValue : function()
32862 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32864 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32874 this.inputEl.dom.value = '';
32879 validate : function()
32881 var d = this.dayField.validate();
32882 var m = this.monthField.validate();
32883 var y = this.yearField.validate();
32888 (!this.dayAllowBlank && !d) ||
32889 (!this.monthAllowBlank && !m) ||
32890 (!this.yearAllowBlank && !y)
32895 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32904 this.markInvalid();
32909 markValid : function()
32912 var label = this.el.select('label', true).first();
32913 var icon = this.el.select('i.fa-star', true).first();
32919 this.fireEvent('valid', this);
32923 * Mark this field as invalid
32924 * @param {String} msg The validation message
32926 markInvalid : function(msg)
32929 var label = this.el.select('label', true).first();
32930 var icon = this.el.select('i.fa-star', true).first();
32932 if(label && !icon){
32933 this.el.select('.roo-date-split-field-label', true).createChild({
32935 cls : 'text-danger fa fa-lg fa-star',
32936 tooltip : 'This field is required',
32937 style : 'margin-right:5px;'
32941 this.fireEvent('invalid', this, msg);
32944 clearInvalid : function()
32946 var label = this.el.select('label', true).first();
32947 var icon = this.el.select('i.fa-star', true).first();
32953 this.fireEvent('valid', this);
32956 getName: function()
32966 * http://masonry.desandro.com
32968 * The idea is to render all the bricks based on vertical width...
32970 * The original code extends 'outlayer' - we might need to use that....
32976 * @class Roo.bootstrap.LayoutMasonry
32977 * @extends Roo.bootstrap.Component
32978 * Bootstrap Layout Masonry class
32981 * Create a new Element
32982 * @param {Object} config The config object
32985 Roo.bootstrap.LayoutMasonry = function(config){
32987 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32991 Roo.bootstrap.LayoutMasonry.register(this);
32997 * Fire after layout the items
32998 * @param {Roo.bootstrap.LayoutMasonry} this
32999 * @param {Roo.EventObject} e
33006 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33009 * @cfg {Boolean} isLayoutInstant = no animation?
33011 isLayoutInstant : false, // needed?
33014 * @cfg {Number} boxWidth width of the columns
33019 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33024 * @cfg {Number} padWidth padding below box..
33029 * @cfg {Number} gutter gutter width..
33034 * @cfg {Number} maxCols maximum number of columns
33040 * @cfg {Boolean} isAutoInitial defalut true
33042 isAutoInitial : true,
33047 * @cfg {Boolean} isHorizontal defalut false
33049 isHorizontal : false,
33051 currentSize : null,
33057 bricks: null, //CompositeElement
33061 _isLayoutInited : false,
33063 // isAlternative : false, // only use for vertical layout...
33066 * @cfg {Number} alternativePadWidth padding below box..
33068 alternativePadWidth : 50,
33070 selectedBrick : [],
33072 getAutoCreate : function(){
33074 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33078 cls: 'blog-masonary-wrapper ' + this.cls,
33080 cls : 'mas-boxes masonary'
33087 getChildContainer: function( )
33089 if (this.boxesEl) {
33090 return this.boxesEl;
33093 this.boxesEl = this.el.select('.mas-boxes').first();
33095 return this.boxesEl;
33099 initEvents : function()
33103 if(this.isAutoInitial){
33104 Roo.log('hook children rendered');
33105 this.on('childrenrendered', function() {
33106 Roo.log('children rendered');
33112 initial : function()
33114 this.selectedBrick = [];
33116 this.currentSize = this.el.getBox(true);
33118 Roo.EventManager.onWindowResize(this.resize, this);
33120 if(!this.isAutoInitial){
33128 //this.layout.defer(500,this);
33132 resize : function()
33134 var cs = this.el.getBox(true);
33137 this.currentSize.width == cs.width &&
33138 this.currentSize.x == cs.x &&
33139 this.currentSize.height == cs.height &&
33140 this.currentSize.y == cs.y
33142 Roo.log("no change in with or X or Y");
33146 this.currentSize = cs;
33152 layout : function()
33154 this._resetLayout();
33156 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33158 this.layoutItems( isInstant );
33160 this._isLayoutInited = true;
33162 this.fireEvent('layout', this);
33166 _resetLayout : function()
33168 if(this.isHorizontal){
33169 this.horizontalMeasureColumns();
33173 this.verticalMeasureColumns();
33177 verticalMeasureColumns : function()
33179 this.getContainerWidth();
33181 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33182 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33186 var boxWidth = this.boxWidth + this.padWidth;
33188 if(this.containerWidth < this.boxWidth){
33189 boxWidth = this.containerWidth
33192 var containerWidth = this.containerWidth;
33194 var cols = Math.floor(containerWidth / boxWidth);
33196 this.cols = Math.max( cols, 1 );
33198 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33200 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33202 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33204 this.colWidth = boxWidth + avail - this.padWidth;
33206 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33207 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33210 horizontalMeasureColumns : function()
33212 this.getContainerWidth();
33214 var boxWidth = this.boxWidth;
33216 if(this.containerWidth < boxWidth){
33217 boxWidth = this.containerWidth;
33220 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33222 this.el.setHeight(boxWidth);
33226 getContainerWidth : function()
33228 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33231 layoutItems : function( isInstant )
33233 Roo.log(this.bricks);
33235 var items = Roo.apply([], this.bricks);
33237 if(this.isHorizontal){
33238 this._horizontalLayoutItems( items , isInstant );
33242 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33243 // this._verticalAlternativeLayoutItems( items , isInstant );
33247 this._verticalLayoutItems( items , isInstant );
33251 _verticalLayoutItems : function ( items , isInstant)
33253 if ( !items || !items.length ) {
33258 ['xs', 'xs', 'xs', 'tall'],
33259 ['xs', 'xs', 'tall'],
33260 ['xs', 'xs', 'sm'],
33261 ['xs', 'xs', 'xs'],
33267 ['sm', 'xs', 'xs'],
33271 ['tall', 'xs', 'xs', 'xs'],
33272 ['tall', 'xs', 'xs'],
33284 Roo.each(items, function(item, k){
33286 switch (item.size) {
33287 // these layouts take up a full box,
33298 boxes.push([item]);
33321 var filterPattern = function(box, length)
33329 var pattern = box.slice(0, length);
33333 Roo.each(pattern, function(i){
33334 format.push(i.size);
33337 Roo.each(standard, function(s){
33339 if(String(s) != String(format)){
33348 if(!match && length == 1){
33353 filterPattern(box, length - 1);
33357 queue.push(pattern);
33359 box = box.slice(length, box.length);
33361 filterPattern(box, 4);
33367 Roo.each(boxes, function(box, k){
33373 if(box.length == 1){
33378 filterPattern(box, 4);
33382 this._processVerticalLayoutQueue( queue, isInstant );
33386 // _verticalAlternativeLayoutItems : function( items , isInstant )
33388 // if ( !items || !items.length ) {
33392 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33396 _horizontalLayoutItems : function ( items , isInstant)
33398 if ( !items || !items.length || items.length < 3) {
33404 var eItems = items.slice(0, 3);
33406 items = items.slice(3, items.length);
33409 ['xs', 'xs', 'xs', 'wide'],
33410 ['xs', 'xs', 'wide'],
33411 ['xs', 'xs', 'sm'],
33412 ['xs', 'xs', 'xs'],
33418 ['sm', 'xs', 'xs'],
33422 ['wide', 'xs', 'xs', 'xs'],
33423 ['wide', 'xs', 'xs'],
33436 Roo.each(items, function(item, k){
33438 switch (item.size) {
33449 boxes.push([item]);
33473 var filterPattern = function(box, length)
33481 var pattern = box.slice(0, length);
33485 Roo.each(pattern, function(i){
33486 format.push(i.size);
33489 Roo.each(standard, function(s){
33491 if(String(s) != String(format)){
33500 if(!match && length == 1){
33505 filterPattern(box, length - 1);
33509 queue.push(pattern);
33511 box = box.slice(length, box.length);
33513 filterPattern(box, 4);
33519 Roo.each(boxes, function(box, k){
33525 if(box.length == 1){
33530 filterPattern(box, 4);
33537 var pos = this.el.getBox(true);
33541 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33543 var hit_end = false;
33545 Roo.each(queue, function(box){
33549 Roo.each(box, function(b){
33551 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33561 Roo.each(box, function(b){
33563 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33566 mx = Math.max(mx, b.x);
33570 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33574 Roo.each(box, function(b){
33576 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33590 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33593 /** Sets position of item in DOM
33594 * @param {Element} item
33595 * @param {Number} x - horizontal position
33596 * @param {Number} y - vertical position
33597 * @param {Boolean} isInstant - disables transitions
33599 _processVerticalLayoutQueue : function( queue, isInstant )
33601 var pos = this.el.getBox(true);
33606 for (var i = 0; i < this.cols; i++){
33610 Roo.each(queue, function(box, k){
33612 var col = k % this.cols;
33614 Roo.each(box, function(b,kk){
33616 b.el.position('absolute');
33618 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33619 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33621 if(b.size == 'md-left' || b.size == 'md-right'){
33622 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33623 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33626 b.el.setWidth(width);
33627 b.el.setHeight(height);
33629 b.el.select('iframe',true).setSize(width,height);
33633 for (var i = 0; i < this.cols; i++){
33635 if(maxY[i] < maxY[col]){
33640 col = Math.min(col, i);
33644 x = pos.x + col * (this.colWidth + this.padWidth);
33648 var positions = [];
33650 switch (box.length){
33652 positions = this.getVerticalOneBoxColPositions(x, y, box);
33655 positions = this.getVerticalTwoBoxColPositions(x, y, box);
33658 positions = this.getVerticalThreeBoxColPositions(x, y, box);
33661 positions = this.getVerticalFourBoxColPositions(x, y, box);
33667 Roo.each(box, function(b,kk){
33669 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33671 var sz = b.el.getSize();
33673 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33681 for (var i = 0; i < this.cols; i++){
33682 mY = Math.max(mY, maxY[i]);
33685 this.el.setHeight(mY - pos.y);
33689 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33691 // var pos = this.el.getBox(true);
33694 // var maxX = pos.right;
33696 // var maxHeight = 0;
33698 // Roo.each(items, function(item, k){
33702 // item.el.position('absolute');
33704 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33706 // item.el.setWidth(width);
33708 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33710 // item.el.setHeight(height);
33713 // item.el.setXY([x, y], isInstant ? false : true);
33715 // item.el.setXY([maxX - width, y], isInstant ? false : true);
33718 // y = y + height + this.alternativePadWidth;
33720 // maxHeight = maxHeight + height + this.alternativePadWidth;
33724 // this.el.setHeight(maxHeight);
33728 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33730 var pos = this.el.getBox(true);
33735 var maxX = pos.right;
33737 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33739 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33741 Roo.each(queue, function(box, k){
33743 Roo.each(box, function(b, kk){
33745 b.el.position('absolute');
33747 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33748 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33750 if(b.size == 'md-left' || b.size == 'md-right'){
33751 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33752 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33755 b.el.setWidth(width);
33756 b.el.setHeight(height);
33764 var positions = [];
33766 switch (box.length){
33768 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33771 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33774 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33777 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33783 Roo.each(box, function(b,kk){
33785 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33787 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33795 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33797 Roo.each(eItems, function(b,k){
33799 b.size = (k == 0) ? 'sm' : 'xs';
33800 b.x = (k == 0) ? 2 : 1;
33801 b.y = (k == 0) ? 2 : 1;
33803 b.el.position('absolute');
33805 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33807 b.el.setWidth(width);
33809 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33811 b.el.setHeight(height);
33815 var positions = [];
33818 x : maxX - this.unitWidth * 2 - this.gutter,
33823 x : maxX - this.unitWidth,
33824 y : minY + (this.unitWidth + this.gutter) * 2
33828 x : maxX - this.unitWidth * 3 - this.gutter * 2,
33832 Roo.each(eItems, function(b,k){
33834 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33840 getVerticalOneBoxColPositions : function(x, y, box)
33844 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33846 if(box[0].size == 'md-left'){
33850 if(box[0].size == 'md-right'){
33855 x : x + (this.unitWidth + this.gutter) * rand,
33862 getVerticalTwoBoxColPositions : function(x, y, box)
33866 if(box[0].size == 'xs'){
33870 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33874 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33888 x : x + (this.unitWidth + this.gutter) * 2,
33889 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33896 getVerticalThreeBoxColPositions : function(x, y, box)
33900 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33908 x : x + (this.unitWidth + this.gutter) * 1,
33913 x : x + (this.unitWidth + this.gutter) * 2,
33921 if(box[0].size == 'xs' && box[1].size == 'xs'){
33930 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33934 x : x + (this.unitWidth + this.gutter) * 1,
33948 x : x + (this.unitWidth + this.gutter) * 2,
33953 x : x + (this.unitWidth + this.gutter) * 2,
33954 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33961 getVerticalFourBoxColPositions : function(x, y, box)
33965 if(box[0].size == 'xs'){
33974 y : y + (this.unitHeight + this.gutter) * 1
33979 y : y + (this.unitHeight + this.gutter) * 2
33983 x : x + (this.unitWidth + this.gutter) * 1,
33997 x : x + (this.unitWidth + this.gutter) * 2,
34002 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34003 y : y + (this.unitHeight + this.gutter) * 1
34007 x : x + (this.unitWidth + this.gutter) * 2,
34008 y : y + (this.unitWidth + this.gutter) * 2
34015 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34019 if(box[0].size == 'md-left'){
34021 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34028 if(box[0].size == 'md-right'){
34030 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34031 y : minY + (this.unitWidth + this.gutter) * 1
34037 var rand = Math.floor(Math.random() * (4 - box[0].y));
34040 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34041 y : minY + (this.unitWidth + this.gutter) * rand
34048 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34052 if(box[0].size == 'xs'){
34055 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34060 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34061 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34069 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34074 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34075 y : minY + (this.unitWidth + this.gutter) * 2
34082 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34086 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34089 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34094 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34095 y : minY + (this.unitWidth + this.gutter) * 1
34099 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34100 y : minY + (this.unitWidth + this.gutter) * 2
34107 if(box[0].size == 'xs' && box[1].size == 'xs'){
34110 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34115 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34120 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34121 y : minY + (this.unitWidth + this.gutter) * 1
34129 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34134 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34135 y : minY + (this.unitWidth + this.gutter) * 2
34139 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34140 y : minY + (this.unitWidth + this.gutter) * 2
34147 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34151 if(box[0].size == 'xs'){
34154 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34159 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34164 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),
34169 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34170 y : minY + (this.unitWidth + this.gutter) * 1
34178 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34183 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34184 y : minY + (this.unitWidth + this.gutter) * 2
34188 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34189 y : minY + (this.unitWidth + this.gutter) * 2
34193 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),
34194 y : minY + (this.unitWidth + this.gutter) * 2
34202 * remove a Masonry Brick
34203 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34205 removeBrick : function(brick_id)
34211 for (var i = 0; i<this.bricks.length; i++) {
34212 if (this.bricks[i].id == brick_id) {
34213 this.bricks.splice(i,1);
34214 this.el.dom.removeChild(Roo.get(brick_id).dom);
34221 * adds a Masonry Brick
34222 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34224 addBrick : function(cfg)
34226 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34227 //this.register(cn);
34228 cn.parentId = this.id;
34229 cn.render(this.el);
34234 * register a Masonry Brick
34235 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34238 register : function(brick)
34240 this.bricks.push(brick);
34241 brick.masonryId = this.id;
34245 * clear all the Masonry Brick
34247 clearAll : function()
34250 //this.getChildContainer().dom.innerHTML = "";
34251 this.el.dom.innerHTML = '';
34254 getSelected : function()
34256 if (!this.selectedBrick) {
34260 return this.selectedBrick;
34264 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34268 * register a Masonry Layout
34269 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34272 register : function(layout)
34274 this.groups[layout.id] = layout;
34277 * fetch a Masonry Layout based on the masonry layout ID
34278 * @param {string} the masonry layout to add
34279 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34282 get: function(layout_id) {
34283 if (typeof(this.groups[layout_id]) == 'undefined') {
34286 return this.groups[layout_id] ;
34298 * http://masonry.desandro.com
34300 * The idea is to render all the bricks based on vertical width...
34302 * The original code extends 'outlayer' - we might need to use that....
34308 * @class Roo.bootstrap.LayoutMasonryAuto
34309 * @extends Roo.bootstrap.Component
34310 * Bootstrap Layout Masonry class
34313 * Create a new Element
34314 * @param {Object} config The config object
34317 Roo.bootstrap.LayoutMasonryAuto = function(config){
34318 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34321 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34324 * @cfg {Boolean} isFitWidth - resize the width..
34326 isFitWidth : false, // options..
34328 * @cfg {Boolean} isOriginLeft = left align?
34330 isOriginLeft : true,
34332 * @cfg {Boolean} isOriginTop = top align?
34334 isOriginTop : false,
34336 * @cfg {Boolean} isLayoutInstant = no animation?
34338 isLayoutInstant : false, // needed?
34340 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34342 isResizingContainer : true,
34344 * @cfg {Number} columnWidth width of the columns
34350 * @cfg {Number} maxCols maximum number of columns
34355 * @cfg {Number} padHeight padding below box..
34361 * @cfg {Boolean} isAutoInitial defalut true
34364 isAutoInitial : true,
34370 initialColumnWidth : 0,
34371 currentSize : null,
34373 colYs : null, // array.
34380 bricks: null, //CompositeElement
34381 cols : 0, // array?
34382 // element : null, // wrapped now this.el
34383 _isLayoutInited : null,
34386 getAutoCreate : function(){
34390 cls: 'blog-masonary-wrapper ' + this.cls,
34392 cls : 'mas-boxes masonary'
34399 getChildContainer: function( )
34401 if (this.boxesEl) {
34402 return this.boxesEl;
34405 this.boxesEl = this.el.select('.mas-boxes').first();
34407 return this.boxesEl;
34411 initEvents : function()
34415 if(this.isAutoInitial){
34416 Roo.log('hook children rendered');
34417 this.on('childrenrendered', function() {
34418 Roo.log('children rendered');
34425 initial : function()
34427 this.reloadItems();
34429 this.currentSize = this.el.getBox(true);
34431 /// was window resize... - let's see if this works..
34432 Roo.EventManager.onWindowResize(this.resize, this);
34434 if(!this.isAutoInitial){
34439 this.layout.defer(500,this);
34442 reloadItems: function()
34444 this.bricks = this.el.select('.masonry-brick', true);
34446 this.bricks.each(function(b) {
34447 //Roo.log(b.getSize());
34448 if (!b.attr('originalwidth')) {
34449 b.attr('originalwidth', b.getSize().width);
34454 Roo.log(this.bricks.elements.length);
34457 resize : function()
34460 var cs = this.el.getBox(true);
34462 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34463 Roo.log("no change in with or X");
34466 this.currentSize = cs;
34470 layout : function()
34473 this._resetLayout();
34474 //this._manageStamps();
34476 // don't animate first layout
34477 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34478 this.layoutItems( isInstant );
34480 // flag for initalized
34481 this._isLayoutInited = true;
34484 layoutItems : function( isInstant )
34486 //var items = this._getItemsForLayout( this.items );
34487 // original code supports filtering layout items.. we just ignore it..
34489 this._layoutItems( this.bricks , isInstant );
34491 this._postLayout();
34493 _layoutItems : function ( items , isInstant)
34495 //this.fireEvent( 'layout', this, items );
34498 if ( !items || !items.elements.length ) {
34499 // no items, emit event with empty array
34504 items.each(function(item) {
34505 Roo.log("layout item");
34507 // get x/y object from method
34508 var position = this._getItemLayoutPosition( item );
34510 position.item = item;
34511 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34512 queue.push( position );
34515 this._processLayoutQueue( queue );
34517 /** Sets position of item in DOM
34518 * @param {Element} item
34519 * @param {Number} x - horizontal position
34520 * @param {Number} y - vertical position
34521 * @param {Boolean} isInstant - disables transitions
34523 _processLayoutQueue : function( queue )
34525 for ( var i=0, len = queue.length; i < len; i++ ) {
34526 var obj = queue[i];
34527 obj.item.position('absolute');
34528 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34534 * Any logic you want to do after each layout,
34535 * i.e. size the container
34537 _postLayout : function()
34539 this.resizeContainer();
34542 resizeContainer : function()
34544 if ( !this.isResizingContainer ) {
34547 var size = this._getContainerSize();
34549 this.el.setSize(size.width,size.height);
34550 this.boxesEl.setSize(size.width,size.height);
34556 _resetLayout : function()
34558 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34559 this.colWidth = this.el.getWidth();
34560 //this.gutter = this.el.getWidth();
34562 this.measureColumns();
34568 this.colYs.push( 0 );
34574 measureColumns : function()
34576 this.getContainerWidth();
34577 // if columnWidth is 0, default to outerWidth of first item
34578 if ( !this.columnWidth ) {
34579 var firstItem = this.bricks.first();
34580 Roo.log(firstItem);
34581 this.columnWidth = this.containerWidth;
34582 if (firstItem && firstItem.attr('originalwidth') ) {
34583 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34585 // columnWidth fall back to item of first element
34586 Roo.log("set column width?");
34587 this.initialColumnWidth = this.columnWidth ;
34589 // if first elem has no width, default to size of container
34594 if (this.initialColumnWidth) {
34595 this.columnWidth = this.initialColumnWidth;
34600 // column width is fixed at the top - however if container width get's smaller we should
34603 // this bit calcs how man columns..
34605 var columnWidth = this.columnWidth += this.gutter;
34607 // calculate columns
34608 var containerWidth = this.containerWidth + this.gutter;
34610 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34611 // fix rounding errors, typically with gutters
34612 var excess = columnWidth - containerWidth % columnWidth;
34615 // if overshoot is less than a pixel, round up, otherwise floor it
34616 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34617 cols = Math[ mathMethod ]( cols );
34618 this.cols = Math.max( cols, 1 );
34619 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34621 // padding positioning..
34622 var totalColWidth = this.cols * this.columnWidth;
34623 var padavail = this.containerWidth - totalColWidth;
34624 // so for 2 columns - we need 3 'pads'
34626 var padNeeded = (1+this.cols) * this.padWidth;
34628 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34630 this.columnWidth += padExtra
34631 //this.padWidth = Math.floor(padavail / ( this.cols));
34633 // adjust colum width so that padding is fixed??
34635 // we have 3 columns ... total = width * 3
34636 // we have X left over... that should be used by
34638 //if (this.expandC) {
34646 getContainerWidth : function()
34648 /* // container is parent if fit width
34649 var container = this.isFitWidth ? this.element.parentNode : this.element;
34650 // check that this.size and size are there
34651 // IE8 triggers resize on body size change, so they might not be
34653 var size = getSize( container ); //FIXME
34654 this.containerWidth = size && size.innerWidth; //FIXME
34657 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34661 _getItemLayoutPosition : function( item ) // what is item?
34663 // we resize the item to our columnWidth..
34665 item.setWidth(this.columnWidth);
34666 item.autoBoxAdjust = false;
34668 var sz = item.getSize();
34670 // how many columns does this brick span
34671 var remainder = this.containerWidth % this.columnWidth;
34673 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34674 // round if off by 1 pixel, otherwise use ceil
34675 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
34676 colSpan = Math.min( colSpan, this.cols );
34678 // normally this should be '1' as we dont' currently allow multi width columns..
34680 var colGroup = this._getColGroup( colSpan );
34681 // get the minimum Y value from the columns
34682 var minimumY = Math.min.apply( Math, colGroup );
34683 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34685 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
34687 // position the brick
34689 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34690 y: this.currentSize.y + minimumY + this.padHeight
34694 // apply setHeight to necessary columns
34695 var setHeight = minimumY + sz.height + this.padHeight;
34696 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
34698 var setSpan = this.cols + 1 - colGroup.length;
34699 for ( var i = 0; i < setSpan; i++ ) {
34700 this.colYs[ shortColIndex + i ] = setHeight ;
34707 * @param {Number} colSpan - number of columns the element spans
34708 * @returns {Array} colGroup
34710 _getColGroup : function( colSpan )
34712 if ( colSpan < 2 ) {
34713 // if brick spans only one column, use all the column Ys
34718 // how many different places could this brick fit horizontally
34719 var groupCount = this.cols + 1 - colSpan;
34720 // for each group potential horizontal position
34721 for ( var i = 0; i < groupCount; i++ ) {
34722 // make an array of colY values for that one group
34723 var groupColYs = this.colYs.slice( i, i + colSpan );
34724 // and get the max value of the array
34725 colGroup[i] = Math.max.apply( Math, groupColYs );
34730 _manageStamp : function( stamp )
34732 var stampSize = stamp.getSize();
34733 var offset = stamp.getBox();
34734 // get the columns that this stamp affects
34735 var firstX = this.isOriginLeft ? offset.x : offset.right;
34736 var lastX = firstX + stampSize.width;
34737 var firstCol = Math.floor( firstX / this.columnWidth );
34738 firstCol = Math.max( 0, firstCol );
34740 var lastCol = Math.floor( lastX / this.columnWidth );
34741 // lastCol should not go over if multiple of columnWidth #425
34742 lastCol -= lastX % this.columnWidth ? 0 : 1;
34743 lastCol = Math.min( this.cols - 1, lastCol );
34745 // set colYs to bottom of the stamp
34746 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34749 for ( var i = firstCol; i <= lastCol; i++ ) {
34750 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34755 _getContainerSize : function()
34757 this.maxY = Math.max.apply( Math, this.colYs );
34762 if ( this.isFitWidth ) {
34763 size.width = this._getContainerFitWidth();
34769 _getContainerFitWidth : function()
34771 var unusedCols = 0;
34772 // count unused columns
34775 if ( this.colYs[i] !== 0 ) {
34780 // fit container to columns that have been used
34781 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34784 needsResizeLayout : function()
34786 var previousWidth = this.containerWidth;
34787 this.getContainerWidth();
34788 return previousWidth !== this.containerWidth;
34803 * @class Roo.bootstrap.MasonryBrick
34804 * @extends Roo.bootstrap.Component
34805 * Bootstrap MasonryBrick class
34808 * Create a new MasonryBrick
34809 * @param {Object} config The config object
34812 Roo.bootstrap.MasonryBrick = function(config){
34814 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34816 Roo.bootstrap.MasonryBrick.register(this);
34822 * When a MasonryBrick is clcik
34823 * @param {Roo.bootstrap.MasonryBrick} this
34824 * @param {Roo.EventObject} e
34830 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
34833 * @cfg {String} title
34837 * @cfg {String} html
34841 * @cfg {String} bgimage
34845 * @cfg {String} videourl
34849 * @cfg {String} cls
34853 * @cfg {String} href
34857 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34862 * @cfg {String} placetitle (center|bottom)
34867 * @cfg {Boolean} isFitContainer defalut true
34869 isFitContainer : true,
34872 * @cfg {Boolean} preventDefault defalut false
34874 preventDefault : false,
34877 * @cfg {Boolean} inverse defalut false
34879 maskInverse : false,
34881 getAutoCreate : function()
34883 if(!this.isFitContainer){
34884 return this.getSplitAutoCreate();
34887 var cls = 'masonry-brick masonry-brick-full';
34889 if(this.href.length){
34890 cls += ' masonry-brick-link';
34893 if(this.bgimage.length){
34894 cls += ' masonry-brick-image';
34897 if(this.maskInverse){
34898 cls += ' mask-inverse';
34901 if(!this.html.length && !this.maskInverse && !this.videourl.length){
34902 cls += ' enable-mask';
34906 cls += ' masonry-' + this.size + '-brick';
34909 if(this.placetitle.length){
34911 switch (this.placetitle) {
34913 cls += ' masonry-center-title';
34916 cls += ' masonry-bottom-title';
34923 if(!this.html.length && !this.bgimage.length){
34924 cls += ' masonry-center-title';
34927 if(!this.html.length && this.bgimage.length){
34928 cls += ' masonry-bottom-title';
34933 cls += ' ' + this.cls;
34937 tag: (this.href.length) ? 'a' : 'div',
34942 cls: 'masonry-brick-mask'
34946 cls: 'masonry-brick-paragraph',
34952 if(this.href.length){
34953 cfg.href = this.href;
34956 var cn = cfg.cn[1].cn;
34958 if(this.title.length){
34961 cls: 'masonry-brick-title',
34966 if(this.html.length){
34969 cls: 'masonry-brick-text',
34974 if (!this.title.length && !this.html.length) {
34975 cfg.cn[1].cls += ' hide';
34978 if(this.bgimage.length){
34981 cls: 'masonry-brick-image-view',
34986 if(this.videourl.length){
34987 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34988 // youtube support only?
34991 cls: 'masonry-brick-image-view',
34994 allowfullscreen : true
35002 getSplitAutoCreate : function()
35004 var cls = 'masonry-brick masonry-brick-split';
35006 if(this.href.length){
35007 cls += ' masonry-brick-link';
35010 if(this.bgimage.length){
35011 cls += ' masonry-brick-image';
35015 cls += ' masonry-' + this.size + '-brick';
35018 switch (this.placetitle) {
35020 cls += ' masonry-center-title';
35023 cls += ' masonry-bottom-title';
35026 if(!this.bgimage.length){
35027 cls += ' masonry-center-title';
35030 if(this.bgimage.length){
35031 cls += ' masonry-bottom-title';
35037 cls += ' ' + this.cls;
35041 tag: (this.href.length) ? 'a' : 'div',
35046 cls: 'masonry-brick-split-head',
35050 cls: 'masonry-brick-paragraph',
35057 cls: 'masonry-brick-split-body',
35063 if(this.href.length){
35064 cfg.href = this.href;
35067 if(this.title.length){
35068 cfg.cn[0].cn[0].cn.push({
35070 cls: 'masonry-brick-title',
35075 if(this.html.length){
35076 cfg.cn[1].cn.push({
35078 cls: 'masonry-brick-text',
35083 if(this.bgimage.length){
35084 cfg.cn[0].cn.push({
35086 cls: 'masonry-brick-image-view',
35091 if(this.videourl.length){
35092 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35093 // youtube support only?
35094 cfg.cn[0].cn.cn.push({
35096 cls: 'masonry-brick-image-view',
35099 allowfullscreen : true
35106 initEvents: function()
35108 switch (this.size) {
35141 this.el.on('touchstart', this.onTouchStart, this);
35142 this.el.on('touchmove', this.onTouchMove, this);
35143 this.el.on('touchend', this.onTouchEnd, this);
35144 this.el.on('contextmenu', this.onContextMenu, this);
35146 this.el.on('mouseenter' ,this.enter, this);
35147 this.el.on('mouseleave', this.leave, this);
35148 this.el.on('click', this.onClick, this);
35151 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35152 this.parent().bricks.push(this);
35157 onClick: function(e, el)
35159 var time = this.endTimer - this.startTimer;
35160 // Roo.log(e.preventDefault());
35163 e.preventDefault();
35168 if(!this.preventDefault){
35172 e.preventDefault();
35174 if (this.activeClass != '') {
35175 this.selectBrick();
35178 this.fireEvent('click', this, e);
35181 enter: function(e, el)
35183 e.preventDefault();
35185 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35189 if(this.bgimage.length && this.html.length){
35190 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35194 leave: function(e, el)
35196 e.preventDefault();
35198 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35202 if(this.bgimage.length && this.html.length){
35203 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35207 onTouchStart: function(e, el)
35209 // e.preventDefault();
35211 this.touchmoved = false;
35213 if(!this.isFitContainer){
35217 if(!this.bgimage.length || !this.html.length){
35221 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35223 this.timer = new Date().getTime();
35227 onTouchMove: function(e, el)
35229 this.touchmoved = true;
35232 onContextMenu : function(e,el)
35234 e.preventDefault();
35235 e.stopPropagation();
35239 onTouchEnd: function(e, el)
35241 // e.preventDefault();
35243 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35250 if(!this.bgimage.length || !this.html.length){
35252 if(this.href.length){
35253 window.location.href = this.href;
35259 if(!this.isFitContainer){
35263 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35265 window.location.href = this.href;
35268 //selection on single brick only
35269 selectBrick : function() {
35271 if (!this.parentId) {
35275 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35276 var index = m.selectedBrick.indexOf(this.id);
35279 m.selectedBrick.splice(index,1);
35280 this.el.removeClass(this.activeClass);
35284 for(var i = 0; i < m.selectedBrick.length; i++) {
35285 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35286 b.el.removeClass(b.activeClass);
35289 m.selectedBrick = [];
35291 m.selectedBrick.push(this.id);
35292 this.el.addClass(this.activeClass);
35296 isSelected : function(){
35297 return this.el.hasClass(this.activeClass);
35302 Roo.apply(Roo.bootstrap.MasonryBrick, {
35305 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35307 * register a Masonry Brick
35308 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35311 register : function(brick)
35313 //this.groups[brick.id] = brick;
35314 this.groups.add(brick.id, brick);
35317 * fetch a masonry brick based on the masonry brick ID
35318 * @param {string} the masonry brick to add
35319 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35322 get: function(brick_id)
35324 // if (typeof(this.groups[brick_id]) == 'undefined') {
35327 // return this.groups[brick_id] ;
35329 if(this.groups.key(brick_id)) {
35330 return this.groups.key(brick_id);
35348 * @class Roo.bootstrap.Brick
35349 * @extends Roo.bootstrap.Component
35350 * Bootstrap Brick class
35353 * Create a new Brick
35354 * @param {Object} config The config object
35357 Roo.bootstrap.Brick = function(config){
35358 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35364 * When a Brick is click
35365 * @param {Roo.bootstrap.Brick} this
35366 * @param {Roo.EventObject} e
35372 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35375 * @cfg {String} title
35379 * @cfg {String} html
35383 * @cfg {String} bgimage
35387 * @cfg {String} cls
35391 * @cfg {String} href
35395 * @cfg {String} video
35399 * @cfg {Boolean} square
35403 getAutoCreate : function()
35405 var cls = 'roo-brick';
35407 if(this.href.length){
35408 cls += ' roo-brick-link';
35411 if(this.bgimage.length){
35412 cls += ' roo-brick-image';
35415 if(!this.html.length && !this.bgimage.length){
35416 cls += ' roo-brick-center-title';
35419 if(!this.html.length && this.bgimage.length){
35420 cls += ' roo-brick-bottom-title';
35424 cls += ' ' + this.cls;
35428 tag: (this.href.length) ? 'a' : 'div',
35433 cls: 'roo-brick-paragraph',
35439 if(this.href.length){
35440 cfg.href = this.href;
35443 var cn = cfg.cn[0].cn;
35445 if(this.title.length){
35448 cls: 'roo-brick-title',
35453 if(this.html.length){
35456 cls: 'roo-brick-text',
35463 if(this.bgimage.length){
35466 cls: 'roo-brick-image-view',
35474 initEvents: function()
35476 if(this.title.length || this.html.length){
35477 this.el.on('mouseenter' ,this.enter, this);
35478 this.el.on('mouseleave', this.leave, this);
35481 Roo.EventManager.onWindowResize(this.resize, this);
35483 if(this.bgimage.length){
35484 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35485 this.imageEl.on('load', this.onImageLoad, this);
35492 onImageLoad : function()
35497 resize : function()
35499 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35501 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35503 if(this.bgimage.length){
35504 var image = this.el.select('.roo-brick-image-view', true).first();
35506 image.setWidth(paragraph.getWidth());
35509 image.setHeight(paragraph.getWidth());
35512 this.el.setHeight(image.getHeight());
35513 paragraph.setHeight(image.getHeight());
35519 enter: function(e, el)
35521 e.preventDefault();
35523 if(this.bgimage.length){
35524 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35525 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35529 leave: function(e, el)
35531 e.preventDefault();
35533 if(this.bgimage.length){
35534 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35535 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35550 * @class Roo.bootstrap.NumberField
35551 * @extends Roo.bootstrap.Input
35552 * Bootstrap NumberField class
35558 * Create a new NumberField
35559 * @param {Object} config The config object
35562 Roo.bootstrap.NumberField = function(config){
35563 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35566 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35569 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35571 allowDecimals : true,
35573 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35575 decimalSeparator : ".",
35577 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35579 decimalPrecision : 2,
35581 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35583 allowNegative : true,
35586 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35590 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35592 minValue : Number.NEGATIVE_INFINITY,
35594 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35596 maxValue : Number.MAX_VALUE,
35598 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35600 minText : "The minimum value for this field is {0}",
35602 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35604 maxText : "The maximum value for this field is {0}",
35606 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
35607 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35609 nanText : "{0} is not a valid number",
35611 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35613 thousandsDelimiter : false,
35615 * @cfg {String} valueAlign alignment of value
35617 valueAlign : "left",
35619 getAutoCreate : function()
35621 var hiddenInput = {
35625 cls: 'hidden-number-input'
35629 hiddenInput.name = this.name;
35634 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35636 this.name = hiddenInput.name;
35638 if(cfg.cn.length > 0) {
35639 cfg.cn.push(hiddenInput);
35646 initEvents : function()
35648 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35650 var allowed = "0123456789";
35652 if(this.allowDecimals){
35653 allowed += this.decimalSeparator;
35656 if(this.allowNegative){
35660 if(this.thousandsDelimiter) {
35664 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35666 var keyPress = function(e){
35668 var k = e.getKey();
35670 var c = e.getCharCode();
35673 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35674 allowed.indexOf(String.fromCharCode(c)) === -1
35680 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35684 if(allowed.indexOf(String.fromCharCode(c)) === -1){
35689 this.el.on("keypress", keyPress, this);
35692 validateValue : function(value)
35695 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35699 var num = this.parseValue(value);
35702 this.markInvalid(String.format(this.nanText, value));
35706 if(num < this.minValue){
35707 this.markInvalid(String.format(this.minText, this.minValue));
35711 if(num > this.maxValue){
35712 this.markInvalid(String.format(this.maxText, this.maxValue));
35719 getValue : function()
35721 var v = this.hiddenEl().getValue();
35723 return this.fixPrecision(this.parseValue(v));
35726 parseValue : function(value)
35728 if(this.thousandsDelimiter) {
35730 r = new RegExp(",", "g");
35731 value = value.replace(r, "");
35734 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35735 return isNaN(value) ? '' : value;
35738 fixPrecision : function(value)
35740 if(this.thousandsDelimiter) {
35742 r = new RegExp(",", "g");
35743 value = value.replace(r, "");
35746 var nan = isNaN(value);
35748 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35749 return nan ? '' : value;
35751 return parseFloat(value).toFixed(this.decimalPrecision);
35754 setValue : function(v)
35756 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35762 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35764 this.inputEl().dom.value = (v == '') ? '' :
35765 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35767 if(!this.allowZero && v === '0') {
35768 this.hiddenEl().dom.value = '';
35769 this.inputEl().dom.value = '';
35776 decimalPrecisionFcn : function(v)
35778 return Math.floor(v);
35781 beforeBlur : function()
35783 var v = this.parseValue(this.getRawValue());
35785 if(v || v === 0 || v === ''){
35790 hiddenEl : function()
35792 return this.el.select('input.hidden-number-input',true).first();
35804 * @class Roo.bootstrap.DocumentSlider
35805 * @extends Roo.bootstrap.Component
35806 * Bootstrap DocumentSlider class
35809 * Create a new DocumentViewer
35810 * @param {Object} config The config object
35813 Roo.bootstrap.DocumentSlider = function(config){
35814 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35821 * Fire after initEvent
35822 * @param {Roo.bootstrap.DocumentSlider} this
35827 * Fire after update
35828 * @param {Roo.bootstrap.DocumentSlider} this
35834 * @param {Roo.bootstrap.DocumentSlider} this
35840 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
35846 getAutoCreate : function()
35850 cls : 'roo-document-slider',
35854 cls : 'roo-document-slider-header',
35858 cls : 'roo-document-slider-header-title'
35864 cls : 'roo-document-slider-body',
35868 cls : 'roo-document-slider-prev',
35872 cls : 'fa fa-chevron-left'
35878 cls : 'roo-document-slider-thumb',
35882 cls : 'roo-document-slider-image'
35888 cls : 'roo-document-slider-next',
35892 cls : 'fa fa-chevron-right'
35904 initEvents : function()
35906 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35907 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35909 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35910 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35912 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35913 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35915 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35916 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35918 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35919 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35921 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35922 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35924 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35925 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35927 this.thumbEl.on('click', this.onClick, this);
35929 this.prevIndicator.on('click', this.prev, this);
35931 this.nextIndicator.on('click', this.next, this);
35935 initial : function()
35937 if(this.files.length){
35938 this.indicator = 1;
35942 this.fireEvent('initial', this);
35945 update : function()
35947 this.imageEl.attr('src', this.files[this.indicator - 1]);
35949 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35951 this.prevIndicator.show();
35953 if(this.indicator == 1){
35954 this.prevIndicator.hide();
35957 this.nextIndicator.show();
35959 if(this.indicator == this.files.length){
35960 this.nextIndicator.hide();
35963 this.thumbEl.scrollTo('top');
35965 this.fireEvent('update', this);
35968 onClick : function(e)
35970 e.preventDefault();
35972 this.fireEvent('click', this);
35977 e.preventDefault();
35979 this.indicator = Math.max(1, this.indicator - 1);
35986 e.preventDefault();
35988 this.indicator = Math.min(this.files.length, this.indicator + 1);
36002 * @class Roo.bootstrap.RadioSet
36003 * @extends Roo.bootstrap.Input
36004 * Bootstrap RadioSet class
36005 * @cfg {String} indicatorpos (left|right) default left
36006 * @cfg {Boolean} inline (true|false) inline the element (default true)
36007 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36009 * Create a new RadioSet
36010 * @param {Object} config The config object
36013 Roo.bootstrap.RadioSet = function(config){
36015 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36019 Roo.bootstrap.RadioSet.register(this);
36024 * Fires when the element is checked or unchecked.
36025 * @param {Roo.bootstrap.RadioSet} this This radio
36026 * @param {Roo.bootstrap.Radio} item The checked item
36031 * Fires when the element is click.
36032 * @param {Roo.bootstrap.RadioSet} this This radio set
36033 * @param {Roo.bootstrap.Radio} item The checked item
36034 * @param {Roo.EventObject} e The event object
36041 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36049 indicatorpos : 'left',
36051 getAutoCreate : function()
36055 cls : 'roo-radio-set-label',
36059 html : this.fieldLabel
36063 if (Roo.bootstrap.version == 3) {
36066 if(this.indicatorpos == 'left'){
36069 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36070 tooltip : 'This field is required'
36075 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36076 tooltip : 'This field is required'
36082 cls : 'roo-radio-set-items'
36085 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36087 if (align === 'left' && this.fieldLabel.length) {
36090 cls : "roo-radio-set-right",
36096 if(this.labelWidth > 12){
36097 label.style = "width: " + this.labelWidth + 'px';
36100 if(this.labelWidth < 13 && this.labelmd == 0){
36101 this.labelmd = this.labelWidth;
36104 if(this.labellg > 0){
36105 label.cls += ' col-lg-' + this.labellg;
36106 items.cls += ' col-lg-' + (12 - this.labellg);
36109 if(this.labelmd > 0){
36110 label.cls += ' col-md-' + this.labelmd;
36111 items.cls += ' col-md-' + (12 - this.labelmd);
36114 if(this.labelsm > 0){
36115 label.cls += ' col-sm-' + this.labelsm;
36116 items.cls += ' col-sm-' + (12 - this.labelsm);
36119 if(this.labelxs > 0){
36120 label.cls += ' col-xs-' + this.labelxs;
36121 items.cls += ' col-xs-' + (12 - this.labelxs);
36127 cls : 'roo-radio-set',
36131 cls : 'roo-radio-set-input',
36134 value : this.value ? this.value : ''
36141 if(this.weight.length){
36142 cfg.cls += ' roo-radio-' + this.weight;
36146 cfg.cls += ' roo-radio-set-inline';
36150 ['xs','sm','md','lg'].map(function(size){
36151 if (settings[size]) {
36152 cfg.cls += ' col-' + size + '-' + settings[size];
36160 initEvents : function()
36162 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36163 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36165 if(!this.fieldLabel.length){
36166 this.labelEl.hide();
36169 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36170 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36172 this.indicator = this.indicatorEl();
36174 if(this.indicator){
36175 this.indicator.addClass('invisible');
36178 this.originalValue = this.getValue();
36182 inputEl: function ()
36184 return this.el.select('.roo-radio-set-input', true).first();
36187 getChildContainer : function()
36189 return this.itemsEl;
36192 register : function(item)
36194 this.radioes.push(item);
36198 validate : function()
36200 if(this.getVisibilityEl().hasClass('hidden')){
36206 Roo.each(this.radioes, function(i){
36215 if(this.allowBlank) {
36219 if(this.disabled || valid){
36224 this.markInvalid();
36229 markValid : function()
36231 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36232 this.indicatorEl().removeClass('visible');
36233 this.indicatorEl().addClass('invisible');
36237 if (Roo.bootstrap.version == 3) {
36238 this.el.removeClass([this.invalidClass, this.validClass]);
36239 this.el.addClass(this.validClass);
36241 this.el.removeClass(['is-invalid','is-valid']);
36242 this.el.addClass(['is-valid']);
36244 this.fireEvent('valid', this);
36247 markInvalid : function(msg)
36249 if(this.allowBlank || this.disabled){
36253 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36254 this.indicatorEl().removeClass('invisible');
36255 this.indicatorEl().addClass('visible');
36257 if (Roo.bootstrap.version == 3) {
36258 this.el.removeClass([this.invalidClass, this.validClass]);
36259 this.el.addClass(this.invalidClass);
36261 this.el.removeClass(['is-invalid','is-valid']);
36262 this.el.addClass(['is-invalid']);
36265 this.fireEvent('invalid', this, msg);
36269 setValue : function(v, suppressEvent)
36271 if(this.value === v){
36278 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36281 Roo.each(this.radioes, function(i){
36283 i.el.removeClass('checked');
36286 Roo.each(this.radioes, function(i){
36288 if(i.value === v || i.value.toString() === v.toString()){
36290 i.el.addClass('checked');
36292 if(suppressEvent !== true){
36293 this.fireEvent('check', this, i);
36304 clearInvalid : function(){
36306 if(!this.el || this.preventMark){
36310 this.el.removeClass([this.invalidClass]);
36312 this.fireEvent('valid', this);
36317 Roo.apply(Roo.bootstrap.RadioSet, {
36321 register : function(set)
36323 this.groups[set.name] = set;
36326 get: function(name)
36328 if (typeof(this.groups[name]) == 'undefined') {
36332 return this.groups[name] ;
36338 * Ext JS Library 1.1.1
36339 * Copyright(c) 2006-2007, Ext JS, LLC.
36341 * Originally Released Under LGPL - original licence link has changed is not relivant.
36344 * <script type="text/javascript">
36349 * @class Roo.bootstrap.SplitBar
36350 * @extends Roo.util.Observable
36351 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36355 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36356 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36357 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36358 split.minSize = 100;
36359 split.maxSize = 600;
36360 split.animate = true;
36361 split.on('moved', splitterMoved);
36364 * Create a new SplitBar
36365 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36366 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36367 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36368 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36369 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36370 position of the SplitBar).
36372 Roo.bootstrap.SplitBar = function(cfg){
36377 // dragElement : elm
36378 // resizingElement: el,
36380 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36381 // placement : Roo.bootstrap.SplitBar.LEFT ,
36382 // existingProxy ???
36385 this.el = Roo.get(cfg.dragElement, true);
36386 this.el.dom.unselectable = "on";
36388 this.resizingEl = Roo.get(cfg.resizingElement, true);
36392 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36393 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36396 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36399 * The minimum size of the resizing element. (Defaults to 0)
36405 * The maximum size of the resizing element. (Defaults to 2000)
36408 this.maxSize = 2000;
36411 * Whether to animate the transition to the new size
36414 this.animate = false;
36417 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36420 this.useShim = false;
36425 if(!cfg.existingProxy){
36427 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36429 this.proxy = Roo.get(cfg.existingProxy).dom;
36432 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36435 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36438 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36441 this.dragSpecs = {};
36444 * @private The adapter to use to positon and resize elements
36446 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36447 this.adapter.init(this);
36449 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36451 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36452 this.el.addClass("roo-splitbar-h");
36455 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36456 this.el.addClass("roo-splitbar-v");
36462 * Fires when the splitter is moved (alias for {@link #event-moved})
36463 * @param {Roo.bootstrap.SplitBar} this
36464 * @param {Number} newSize the new width or height
36469 * Fires when the splitter is moved
36470 * @param {Roo.bootstrap.SplitBar} this
36471 * @param {Number} newSize the new width or height
36475 * @event beforeresize
36476 * Fires before the splitter is dragged
36477 * @param {Roo.bootstrap.SplitBar} this
36479 "beforeresize" : true,
36481 "beforeapply" : true
36484 Roo.util.Observable.call(this);
36487 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36488 onStartProxyDrag : function(x, y){
36489 this.fireEvent("beforeresize", this);
36491 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36493 o.enableDisplayMode("block");
36494 // all splitbars share the same overlay
36495 Roo.bootstrap.SplitBar.prototype.overlay = o;
36497 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36498 this.overlay.show();
36499 Roo.get(this.proxy).setDisplayed("block");
36500 var size = this.adapter.getElementSize(this);
36501 this.activeMinSize = this.getMinimumSize();;
36502 this.activeMaxSize = this.getMaximumSize();;
36503 var c1 = size - this.activeMinSize;
36504 var c2 = Math.max(this.activeMaxSize - size, 0);
36505 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36506 this.dd.resetConstraints();
36507 this.dd.setXConstraint(
36508 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36509 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36511 this.dd.setYConstraint(0, 0);
36513 this.dd.resetConstraints();
36514 this.dd.setXConstraint(0, 0);
36515 this.dd.setYConstraint(
36516 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36517 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36520 this.dragSpecs.startSize = size;
36521 this.dragSpecs.startPoint = [x, y];
36522 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36526 * @private Called after the drag operation by the DDProxy
36528 onEndProxyDrag : function(e){
36529 Roo.get(this.proxy).setDisplayed(false);
36530 var endPoint = Roo.lib.Event.getXY(e);
36532 this.overlay.hide();
36535 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36536 newSize = this.dragSpecs.startSize +
36537 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36538 endPoint[0] - this.dragSpecs.startPoint[0] :
36539 this.dragSpecs.startPoint[0] - endPoint[0]
36542 newSize = this.dragSpecs.startSize +
36543 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36544 endPoint[1] - this.dragSpecs.startPoint[1] :
36545 this.dragSpecs.startPoint[1] - endPoint[1]
36548 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36549 if(newSize != this.dragSpecs.startSize){
36550 if(this.fireEvent('beforeapply', this, newSize) !== false){
36551 this.adapter.setElementSize(this, newSize);
36552 this.fireEvent("moved", this, newSize);
36553 this.fireEvent("resize", this, newSize);
36559 * Get the adapter this SplitBar uses
36560 * @return The adapter object
36562 getAdapter : function(){
36563 return this.adapter;
36567 * Set the adapter this SplitBar uses
36568 * @param {Object} adapter A SplitBar adapter object
36570 setAdapter : function(adapter){
36571 this.adapter = adapter;
36572 this.adapter.init(this);
36576 * Gets the minimum size for the resizing element
36577 * @return {Number} The minimum size
36579 getMinimumSize : function(){
36580 return this.minSize;
36584 * Sets the minimum size for the resizing element
36585 * @param {Number} minSize The minimum size
36587 setMinimumSize : function(minSize){
36588 this.minSize = minSize;
36592 * Gets the maximum size for the resizing element
36593 * @return {Number} The maximum size
36595 getMaximumSize : function(){
36596 return this.maxSize;
36600 * Sets the maximum size for the resizing element
36601 * @param {Number} maxSize The maximum size
36603 setMaximumSize : function(maxSize){
36604 this.maxSize = maxSize;
36608 * Sets the initialize size for the resizing element
36609 * @param {Number} size The initial size
36611 setCurrentSize : function(size){
36612 var oldAnimate = this.animate;
36613 this.animate = false;
36614 this.adapter.setElementSize(this, size);
36615 this.animate = oldAnimate;
36619 * Destroy this splitbar.
36620 * @param {Boolean} removeEl True to remove the element
36622 destroy : function(removeEl){
36624 this.shim.remove();
36627 this.proxy.parentNode.removeChild(this.proxy);
36635 * @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.
36637 Roo.bootstrap.SplitBar.createProxy = function(dir){
36638 var proxy = new Roo.Element(document.createElement("div"));
36639 proxy.unselectable();
36640 var cls = 'roo-splitbar-proxy';
36641 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36642 document.body.appendChild(proxy.dom);
36647 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36648 * Default Adapter. It assumes the splitter and resizing element are not positioned
36649 * elements and only gets/sets the width of the element. Generally used for table based layouts.
36651 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36654 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36655 // do nothing for now
36656 init : function(s){
36660 * Called before drag operations to get the current size of the resizing element.
36661 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36663 getElementSize : function(s){
36664 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36665 return s.resizingEl.getWidth();
36667 return s.resizingEl.getHeight();
36672 * Called after drag operations to set the size of the resizing element.
36673 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36674 * @param {Number} newSize The new size to set
36675 * @param {Function} onComplete A function to be invoked when resizing is complete
36677 setElementSize : function(s, newSize, onComplete){
36678 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36680 s.resizingEl.setWidth(newSize);
36682 onComplete(s, newSize);
36685 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36690 s.resizingEl.setHeight(newSize);
36692 onComplete(s, newSize);
36695 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36702 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36703 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36704 * Adapter that moves the splitter element to align with the resized sizing element.
36705 * Used with an absolute positioned SplitBar.
36706 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36707 * document.body, make sure you assign an id to the body element.
36709 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36710 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36711 this.container = Roo.get(container);
36714 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36715 init : function(s){
36716 this.basic.init(s);
36719 getElementSize : function(s){
36720 return this.basic.getElementSize(s);
36723 setElementSize : function(s, newSize, onComplete){
36724 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36727 moveSplitter : function(s){
36728 var yes = Roo.bootstrap.SplitBar;
36729 switch(s.placement){
36731 s.el.setX(s.resizingEl.getRight());
36734 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36737 s.el.setY(s.resizingEl.getBottom());
36740 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36747 * Orientation constant - Create a vertical SplitBar
36751 Roo.bootstrap.SplitBar.VERTICAL = 1;
36754 * Orientation constant - Create a horizontal SplitBar
36758 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36761 * Placement constant - The resizing element is to the left of the splitter element
36765 Roo.bootstrap.SplitBar.LEFT = 1;
36768 * Placement constant - The resizing element is to the right of the splitter element
36772 Roo.bootstrap.SplitBar.RIGHT = 2;
36775 * Placement constant - The resizing element is positioned above the splitter element
36779 Roo.bootstrap.SplitBar.TOP = 3;
36782 * Placement constant - The resizing element is positioned under splitter element
36786 Roo.bootstrap.SplitBar.BOTTOM = 4;
36787 Roo.namespace("Roo.bootstrap.layout");/*
36789 * Ext JS Library 1.1.1
36790 * Copyright(c) 2006-2007, Ext JS, LLC.
36792 * Originally Released Under LGPL - original licence link has changed is not relivant.
36795 * <script type="text/javascript">
36799 * @class Roo.bootstrap.layout.Manager
36800 * @extends Roo.bootstrap.Component
36801 * Base class for layout managers.
36803 Roo.bootstrap.layout.Manager = function(config)
36805 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36811 /** false to disable window resize monitoring @type Boolean */
36812 this.monitorWindowResize = true;
36817 * Fires when a layout is performed.
36818 * @param {Roo.LayoutManager} this
36822 * @event regionresized
36823 * Fires when the user resizes a region.
36824 * @param {Roo.LayoutRegion} region The resized region
36825 * @param {Number} newSize The new size (width for east/west, height for north/south)
36827 "regionresized" : true,
36829 * @event regioncollapsed
36830 * Fires when a region is collapsed.
36831 * @param {Roo.LayoutRegion} region The collapsed region
36833 "regioncollapsed" : true,
36835 * @event regionexpanded
36836 * Fires when a region is expanded.
36837 * @param {Roo.LayoutRegion} region The expanded region
36839 "regionexpanded" : true
36841 this.updating = false;
36844 this.el = Roo.get(config.el);
36850 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36855 monitorWindowResize : true,
36861 onRender : function(ct, position)
36864 this.el = Roo.get(ct);
36867 //this.fireEvent('render',this);
36871 initEvents: function()
36875 // ie scrollbar fix
36876 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36877 document.body.scroll = "no";
36878 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36879 this.el.position('relative');
36881 this.id = this.el.id;
36882 this.el.addClass("roo-layout-container");
36883 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36884 if(this.el.dom != document.body ) {
36885 this.el.on('resize', this.layout,this);
36886 this.el.on('show', this.layout,this);
36892 * Returns true if this layout is currently being updated
36893 * @return {Boolean}
36895 isUpdating : function(){
36896 return this.updating;
36900 * Suspend the LayoutManager from doing auto-layouts while
36901 * making multiple add or remove calls
36903 beginUpdate : function(){
36904 this.updating = true;
36908 * Restore auto-layouts and optionally disable the manager from performing a layout
36909 * @param {Boolean} noLayout true to disable a layout update
36911 endUpdate : function(noLayout){
36912 this.updating = false;
36918 layout: function(){
36922 onRegionResized : function(region, newSize){
36923 this.fireEvent("regionresized", region, newSize);
36927 onRegionCollapsed : function(region){
36928 this.fireEvent("regioncollapsed", region);
36931 onRegionExpanded : function(region){
36932 this.fireEvent("regionexpanded", region);
36936 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36937 * performs box-model adjustments.
36938 * @return {Object} The size as an object {width: (the width), height: (the height)}
36940 getViewSize : function()
36943 if(this.el.dom != document.body){
36944 size = this.el.getSize();
36946 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36948 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36949 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36954 * Returns the Element this layout is bound to.
36955 * @return {Roo.Element}
36957 getEl : function(){
36962 * Returns the specified region.
36963 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36964 * @return {Roo.LayoutRegion}
36966 getRegion : function(target){
36967 return this.regions[target.toLowerCase()];
36970 onWindowResize : function(){
36971 if(this.monitorWindowResize){
36978 * Ext JS Library 1.1.1
36979 * Copyright(c) 2006-2007, Ext JS, LLC.
36981 * Originally Released Under LGPL - original licence link has changed is not relivant.
36984 * <script type="text/javascript">
36987 * @class Roo.bootstrap.layout.Border
36988 * @extends Roo.bootstrap.layout.Manager
36989 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36990 * please see: examples/bootstrap/nested.html<br><br>
36992 <b>The container the layout is rendered into can be either the body element or any other element.
36993 If it is not the body element, the container needs to either be an absolute positioned element,
36994 or you will need to add "position:relative" to the css of the container. You will also need to specify
36995 the container size if it is not the body element.</b>
36998 * Create a new Border
36999 * @param {Object} config Configuration options
37001 Roo.bootstrap.layout.Border = function(config){
37002 config = config || {};
37003 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37007 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37008 if(config[region]){
37009 config[region].region = region;
37010 this.addRegion(config[region]);
37016 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
37018 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37020 parent : false, // this might point to a 'nest' or a ???
37023 * Creates and adds a new region if it doesn't already exist.
37024 * @param {String} target The target region key (north, south, east, west or center).
37025 * @param {Object} config The regions config object
37026 * @return {BorderLayoutRegion} The new region
37028 addRegion : function(config)
37030 if(!this.regions[config.region]){
37031 var r = this.factory(config);
37032 this.bindRegion(r);
37034 return this.regions[config.region];
37038 bindRegion : function(r){
37039 this.regions[r.config.region] = r;
37041 r.on("visibilitychange", this.layout, this);
37042 r.on("paneladded", this.layout, this);
37043 r.on("panelremoved", this.layout, this);
37044 r.on("invalidated", this.layout, this);
37045 r.on("resized", this.onRegionResized, this);
37046 r.on("collapsed", this.onRegionCollapsed, this);
37047 r.on("expanded", this.onRegionExpanded, this);
37051 * Performs a layout update.
37053 layout : function()
37055 if(this.updating) {
37059 // render all the rebions if they have not been done alreayd?
37060 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37061 if(this.regions[region] && !this.regions[region].bodyEl){
37062 this.regions[region].onRender(this.el)
37066 var size = this.getViewSize();
37067 var w = size.width;
37068 var h = size.height;
37073 //var x = 0, y = 0;
37075 var rs = this.regions;
37076 var north = rs["north"];
37077 var south = rs["south"];
37078 var west = rs["west"];
37079 var east = rs["east"];
37080 var center = rs["center"];
37081 //if(this.hideOnLayout){ // not supported anymore
37082 //c.el.setStyle("display", "none");
37084 if(north && north.isVisible()){
37085 var b = north.getBox();
37086 var m = north.getMargins();
37087 b.width = w - (m.left+m.right);
37090 centerY = b.height + b.y + m.bottom;
37091 centerH -= centerY;
37092 north.updateBox(this.safeBox(b));
37094 if(south && south.isVisible()){
37095 var b = south.getBox();
37096 var m = south.getMargins();
37097 b.width = w - (m.left+m.right);
37099 var totalHeight = (b.height + m.top + m.bottom);
37100 b.y = h - totalHeight + m.top;
37101 centerH -= totalHeight;
37102 south.updateBox(this.safeBox(b));
37104 if(west && west.isVisible()){
37105 var b = west.getBox();
37106 var m = west.getMargins();
37107 b.height = centerH - (m.top+m.bottom);
37109 b.y = centerY + m.top;
37110 var totalWidth = (b.width + m.left + m.right);
37111 centerX += totalWidth;
37112 centerW -= totalWidth;
37113 west.updateBox(this.safeBox(b));
37115 if(east && east.isVisible()){
37116 var b = east.getBox();
37117 var m = east.getMargins();
37118 b.height = centerH - (m.top+m.bottom);
37119 var totalWidth = (b.width + m.left + m.right);
37120 b.x = w - totalWidth + m.left;
37121 b.y = centerY + m.top;
37122 centerW -= totalWidth;
37123 east.updateBox(this.safeBox(b));
37126 var m = center.getMargins();
37128 x: centerX + m.left,
37129 y: centerY + m.top,
37130 width: centerW - (m.left+m.right),
37131 height: centerH - (m.top+m.bottom)
37133 //if(this.hideOnLayout){
37134 //center.el.setStyle("display", "block");
37136 center.updateBox(this.safeBox(centerBox));
37139 this.fireEvent("layout", this);
37143 safeBox : function(box){
37144 box.width = Math.max(0, box.width);
37145 box.height = Math.max(0, box.height);
37150 * Adds a ContentPanel (or subclass) to this layout.
37151 * @param {String} target The target region key (north, south, east, west or center).
37152 * @param {Roo.ContentPanel} panel The panel to add
37153 * @return {Roo.ContentPanel} The added panel
37155 add : function(target, panel){
37157 target = target.toLowerCase();
37158 return this.regions[target].add(panel);
37162 * Remove a ContentPanel (or subclass) to this layout.
37163 * @param {String} target The target region key (north, south, east, west or center).
37164 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37165 * @return {Roo.ContentPanel} The removed panel
37167 remove : function(target, panel){
37168 target = target.toLowerCase();
37169 return this.regions[target].remove(panel);
37173 * Searches all regions for a panel with the specified id
37174 * @param {String} panelId
37175 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37177 findPanel : function(panelId){
37178 var rs = this.regions;
37179 for(var target in rs){
37180 if(typeof rs[target] != "function"){
37181 var p = rs[target].getPanel(panelId);
37191 * Searches all regions for a panel with the specified id and activates (shows) it.
37192 * @param {String/ContentPanel} panelId The panels id or the panel itself
37193 * @return {Roo.ContentPanel} The shown panel or null
37195 showPanel : function(panelId) {
37196 var rs = this.regions;
37197 for(var target in rs){
37198 var r = rs[target];
37199 if(typeof r != "function"){
37200 if(r.hasPanel(panelId)){
37201 return r.showPanel(panelId);
37209 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37210 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37213 restoreState : function(provider){
37215 provider = Roo.state.Manager;
37217 var sm = new Roo.LayoutStateManager();
37218 sm.init(this, provider);
37224 * Adds a xtype elements to the layout.
37228 xtype : 'ContentPanel',
37235 xtype : 'NestedLayoutPanel',
37241 items : [ ... list of content panels or nested layout panels.. ]
37245 * @param {Object} cfg Xtype definition of item to add.
37247 addxtype : function(cfg)
37249 // basically accepts a pannel...
37250 // can accept a layout region..!?!?
37251 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37254 // theory? children can only be panels??
37256 //if (!cfg.xtype.match(/Panel$/)) {
37261 if (typeof(cfg.region) == 'undefined') {
37262 Roo.log("Failed to add Panel, region was not set");
37266 var region = cfg.region;
37272 xitems = cfg.items;
37277 if ( region == 'center') {
37278 Roo.log("Center: " + cfg.title);
37284 case 'Content': // ContentPanel (el, cfg)
37285 case 'Scroll': // ContentPanel (el, cfg)
37287 cfg.autoCreate = cfg.autoCreate || true;
37288 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37290 // var el = this.el.createChild();
37291 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37294 this.add(region, ret);
37298 case 'TreePanel': // our new panel!
37299 cfg.el = this.el.createChild();
37300 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37301 this.add(region, ret);
37306 // create a new Layout (which is a Border Layout...
37308 var clayout = cfg.layout;
37309 clayout.el = this.el.createChild();
37310 clayout.items = clayout.items || [];
37314 // replace this exitems with the clayout ones..
37315 xitems = clayout.items;
37317 // force background off if it's in center...
37318 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37319 cfg.background = false;
37321 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37324 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37325 //console.log('adding nested layout panel ' + cfg.toSource());
37326 this.add(region, ret);
37327 nb = {}; /// find first...
37332 // needs grid and region
37334 //var el = this.getRegion(region).el.createChild();
37336 *var el = this.el.createChild();
37337 // create the grid first...
37338 cfg.grid.container = el;
37339 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37342 if (region == 'center' && this.active ) {
37343 cfg.background = false;
37346 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37348 this.add(region, ret);
37350 if (cfg.background) {
37351 // render grid on panel activation (if panel background)
37352 ret.on('activate', function(gp) {
37353 if (!gp.grid.rendered) {
37354 // gp.grid.render(el);
37358 // cfg.grid.render(el);
37364 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37365 // it was the old xcomponent building that caused this before.
37366 // espeically if border is the top element in the tree.
37376 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37378 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37379 this.add(region, ret);
37383 throw "Can not add '" + cfg.xtype + "' to Border";
37389 this.beginUpdate();
37393 Roo.each(xitems, function(i) {
37394 region = nb && i.region ? i.region : false;
37396 var add = ret.addxtype(i);
37399 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37400 if (!i.background) {
37401 abn[region] = nb[region] ;
37408 // make the last non-background panel active..
37409 //if (nb) { Roo.log(abn); }
37412 for(var r in abn) {
37413 region = this.getRegion(r);
37415 // tried using nb[r], but it does not work..
37417 region.showPanel(abn[r]);
37428 factory : function(cfg)
37431 var validRegions = Roo.bootstrap.layout.Border.regions;
37433 var target = cfg.region;
37436 var r = Roo.bootstrap.layout;
37440 return new r.North(cfg);
37442 return new r.South(cfg);
37444 return new r.East(cfg);
37446 return new r.West(cfg);
37448 return new r.Center(cfg);
37450 throw 'Layout region "'+target+'" not supported.';
37457 * Ext JS Library 1.1.1
37458 * Copyright(c) 2006-2007, Ext JS, LLC.
37460 * Originally Released Under LGPL - original licence link has changed is not relivant.
37463 * <script type="text/javascript">
37467 * @class Roo.bootstrap.layout.Basic
37468 * @extends Roo.util.Observable
37469 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37470 * and does not have a titlebar, tabs or any other features. All it does is size and position
37471 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37472 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37473 * @cfg {string} region the region that it inhabits..
37474 * @cfg {bool} skipConfig skip config?
37478 Roo.bootstrap.layout.Basic = function(config){
37480 this.mgr = config.mgr;
37482 this.position = config.region;
37484 var skipConfig = config.skipConfig;
37488 * @scope Roo.BasicLayoutRegion
37492 * @event beforeremove
37493 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37494 * @param {Roo.LayoutRegion} this
37495 * @param {Roo.ContentPanel} panel The panel
37496 * @param {Object} e The cancel event object
37498 "beforeremove" : true,
37500 * @event invalidated
37501 * Fires when the layout for this region is changed.
37502 * @param {Roo.LayoutRegion} this
37504 "invalidated" : true,
37506 * @event visibilitychange
37507 * Fires when this region is shown or hidden
37508 * @param {Roo.LayoutRegion} this
37509 * @param {Boolean} visibility true or false
37511 "visibilitychange" : true,
37513 * @event paneladded
37514 * Fires when a panel is added.
37515 * @param {Roo.LayoutRegion} this
37516 * @param {Roo.ContentPanel} panel The panel
37518 "paneladded" : true,
37520 * @event panelremoved
37521 * Fires when a panel is removed.
37522 * @param {Roo.LayoutRegion} this
37523 * @param {Roo.ContentPanel} panel The panel
37525 "panelremoved" : true,
37527 * @event beforecollapse
37528 * Fires when this region before collapse.
37529 * @param {Roo.LayoutRegion} this
37531 "beforecollapse" : true,
37534 * Fires when this region is collapsed.
37535 * @param {Roo.LayoutRegion} this
37537 "collapsed" : true,
37540 * Fires when this region is expanded.
37541 * @param {Roo.LayoutRegion} this
37546 * Fires when this region is slid into view.
37547 * @param {Roo.LayoutRegion} this
37549 "slideshow" : true,
37552 * Fires when this region slides out of view.
37553 * @param {Roo.LayoutRegion} this
37555 "slidehide" : true,
37557 * @event panelactivated
37558 * Fires when a panel is activated.
37559 * @param {Roo.LayoutRegion} this
37560 * @param {Roo.ContentPanel} panel The activated panel
37562 "panelactivated" : true,
37565 * Fires when the user resizes this region.
37566 * @param {Roo.LayoutRegion} this
37567 * @param {Number} newSize The new size (width for east/west, height for north/south)
37571 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37572 this.panels = new Roo.util.MixedCollection();
37573 this.panels.getKey = this.getPanelId.createDelegate(this);
37575 this.activePanel = null;
37576 // ensure listeners are added...
37578 if (config.listeners || config.events) {
37579 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37580 listeners : config.listeners || {},
37581 events : config.events || {}
37585 if(skipConfig !== true){
37586 this.applyConfig(config);
37590 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37592 getPanelId : function(p){
37596 applyConfig : function(config){
37597 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37598 this.config = config;
37603 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
37604 * the width, for horizontal (north, south) the height.
37605 * @param {Number} newSize The new width or height
37607 resizeTo : function(newSize){
37608 var el = this.el ? this.el :
37609 (this.activePanel ? this.activePanel.getEl() : null);
37611 switch(this.position){
37614 el.setWidth(newSize);
37615 this.fireEvent("resized", this, newSize);
37619 el.setHeight(newSize);
37620 this.fireEvent("resized", this, newSize);
37626 getBox : function(){
37627 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37630 getMargins : function(){
37631 return this.margins;
37634 updateBox : function(box){
37636 var el = this.activePanel.getEl();
37637 el.dom.style.left = box.x + "px";
37638 el.dom.style.top = box.y + "px";
37639 this.activePanel.setSize(box.width, box.height);
37643 * Returns the container element for this region.
37644 * @return {Roo.Element}
37646 getEl : function(){
37647 return this.activePanel;
37651 * Returns true if this region is currently visible.
37652 * @return {Boolean}
37654 isVisible : function(){
37655 return this.activePanel ? true : false;
37658 setActivePanel : function(panel){
37659 panel = this.getPanel(panel);
37660 if(this.activePanel && this.activePanel != panel){
37661 this.activePanel.setActiveState(false);
37662 this.activePanel.getEl().setLeftTop(-10000,-10000);
37664 this.activePanel = panel;
37665 panel.setActiveState(true);
37667 panel.setSize(this.box.width, this.box.height);
37669 this.fireEvent("panelactivated", this, panel);
37670 this.fireEvent("invalidated");
37674 * Show the specified panel.
37675 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37676 * @return {Roo.ContentPanel} The shown panel or null
37678 showPanel : function(panel){
37679 panel = this.getPanel(panel);
37681 this.setActivePanel(panel);
37687 * Get the active panel for this region.
37688 * @return {Roo.ContentPanel} The active panel or null
37690 getActivePanel : function(){
37691 return this.activePanel;
37695 * Add the passed ContentPanel(s)
37696 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37697 * @return {Roo.ContentPanel} The panel added (if only one was added)
37699 add : function(panel){
37700 if(arguments.length > 1){
37701 for(var i = 0, len = arguments.length; i < len; i++) {
37702 this.add(arguments[i]);
37706 if(this.hasPanel(panel)){
37707 this.showPanel(panel);
37710 var el = panel.getEl();
37711 if(el.dom.parentNode != this.mgr.el.dom){
37712 this.mgr.el.dom.appendChild(el.dom);
37714 if(panel.setRegion){
37715 panel.setRegion(this);
37717 this.panels.add(panel);
37718 el.setStyle("position", "absolute");
37719 if(!panel.background){
37720 this.setActivePanel(panel);
37721 if(this.config.initialSize && this.panels.getCount()==1){
37722 this.resizeTo(this.config.initialSize);
37725 this.fireEvent("paneladded", this, panel);
37730 * Returns true if the panel is in this region.
37731 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37732 * @return {Boolean}
37734 hasPanel : function(panel){
37735 if(typeof panel == "object"){ // must be panel obj
37736 panel = panel.getId();
37738 return this.getPanel(panel) ? true : false;
37742 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37743 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37744 * @param {Boolean} preservePanel Overrides the config preservePanel option
37745 * @return {Roo.ContentPanel} The panel that was removed
37747 remove : function(panel, preservePanel){
37748 panel = this.getPanel(panel);
37753 this.fireEvent("beforeremove", this, panel, e);
37754 if(e.cancel === true){
37757 var panelId = panel.getId();
37758 this.panels.removeKey(panelId);
37763 * Returns the panel specified or null if it's not in this region.
37764 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37765 * @return {Roo.ContentPanel}
37767 getPanel : function(id){
37768 if(typeof id == "object"){ // must be panel obj
37771 return this.panels.get(id);
37775 * Returns this regions position (north/south/east/west/center).
37778 getPosition: function(){
37779 return this.position;
37783 * Ext JS Library 1.1.1
37784 * Copyright(c) 2006-2007, Ext JS, LLC.
37786 * Originally Released Under LGPL - original licence link has changed is not relivant.
37789 * <script type="text/javascript">
37793 * @class Roo.bootstrap.layout.Region
37794 * @extends Roo.bootstrap.layout.Basic
37795 * This class represents a region in a layout manager.
37797 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37798 * @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})
37799 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
37800 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
37801 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
37802 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
37803 * @cfg {String} title The title for the region (overrides panel titles)
37804 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
37805 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37806 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
37807 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37808 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
37809 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37810 * the space available, similar to FireFox 1.5 tabs (defaults to false)
37811 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
37812 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
37813 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
37815 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
37816 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
37817 * @cfg {Boolean} disableTabTips True to disable tab tooltips
37818 * @cfg {Number} width For East/West panels
37819 * @cfg {Number} height For North/South panels
37820 * @cfg {Boolean} split To show the splitter
37821 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
37823 * @cfg {string} cls Extra CSS classes to add to region
37825 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37826 * @cfg {string} region the region that it inhabits..
37829 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
37830 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
37832 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
37833 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
37834 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
37836 Roo.bootstrap.layout.Region = function(config)
37838 this.applyConfig(config);
37840 var mgr = config.mgr;
37841 var pos = config.region;
37842 config.skipConfig = true;
37843 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37846 this.onRender(mgr.el);
37849 this.visible = true;
37850 this.collapsed = false;
37851 this.unrendered_panels = [];
37854 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37856 position: '', // set by wrapper (eg. north/south etc..)
37857 unrendered_panels : null, // unrendered panels.
37859 tabPosition : false,
37861 mgr: false, // points to 'Border'
37864 createBody : function(){
37865 /** This region's body element
37866 * @type Roo.Element */
37867 this.bodyEl = this.el.createChild({
37869 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37873 onRender: function(ctr, pos)
37875 var dh = Roo.DomHelper;
37876 /** This region's container element
37877 * @type Roo.Element */
37878 this.el = dh.append(ctr.dom, {
37880 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37882 /** This region's title element
37883 * @type Roo.Element */
37885 this.titleEl = dh.append(this.el.dom, {
37887 unselectable: "on",
37888 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37890 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
37891 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37895 this.titleEl.enableDisplayMode();
37896 /** This region's title text element
37897 * @type HTMLElement */
37898 this.titleTextEl = this.titleEl.dom.firstChild;
37899 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37901 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37902 this.closeBtn.enableDisplayMode();
37903 this.closeBtn.on("click", this.closeClicked, this);
37904 this.closeBtn.hide();
37906 this.createBody(this.config);
37907 if(this.config.hideWhenEmpty){
37909 this.on("paneladded", this.validateVisibility, this);
37910 this.on("panelremoved", this.validateVisibility, this);
37912 if(this.autoScroll){
37913 this.bodyEl.setStyle("overflow", "auto");
37915 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37917 //if(c.titlebar !== false){
37918 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37919 this.titleEl.hide();
37921 this.titleEl.show();
37922 if(this.config.title){
37923 this.titleTextEl.innerHTML = this.config.title;
37927 if(this.config.collapsed){
37928 this.collapse(true);
37930 if(this.config.hidden){
37934 if (this.unrendered_panels && this.unrendered_panels.length) {
37935 for (var i =0;i< this.unrendered_panels.length; i++) {
37936 this.add(this.unrendered_panels[i]);
37938 this.unrendered_panels = null;
37944 applyConfig : function(c)
37947 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37948 var dh = Roo.DomHelper;
37949 if(c.titlebar !== false){
37950 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37951 this.collapseBtn.on("click", this.collapse, this);
37952 this.collapseBtn.enableDisplayMode();
37954 if(c.showPin === true || this.showPin){
37955 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37956 this.stickBtn.enableDisplayMode();
37957 this.stickBtn.on("click", this.expand, this);
37958 this.stickBtn.hide();
37963 /** This region's collapsed element
37964 * @type Roo.Element */
37967 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37968 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37971 if(c.floatable !== false){
37972 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37973 this.collapsedEl.on("click", this.collapseClick, this);
37976 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37977 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37978 id: "message", unselectable: "on", style:{"float":"left"}});
37979 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37981 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37982 this.expandBtn.on("click", this.expand, this);
37986 if(this.collapseBtn){
37987 this.collapseBtn.setVisible(c.collapsible == true);
37990 this.cmargins = c.cmargins || this.cmargins ||
37991 (this.position == "west" || this.position == "east" ?
37992 {top: 0, left: 2, right:2, bottom: 0} :
37993 {top: 2, left: 0, right:0, bottom: 2});
37995 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37998 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38000 this.autoScroll = c.autoScroll || false;
38005 this.duration = c.duration || .30;
38006 this.slideDuration = c.slideDuration || .45;
38011 * Returns true if this region is currently visible.
38012 * @return {Boolean}
38014 isVisible : function(){
38015 return this.visible;
38019 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38020 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38022 //setCollapsedTitle : function(title){
38023 // title = title || " ";
38024 // if(this.collapsedTitleTextEl){
38025 // this.collapsedTitleTextEl.innerHTML = title;
38029 getBox : function(){
38031 // if(!this.collapsed){
38032 b = this.el.getBox(false, true);
38034 // b = this.collapsedEl.getBox(false, true);
38039 getMargins : function(){
38040 return this.margins;
38041 //return this.collapsed ? this.cmargins : this.margins;
38044 highlight : function(){
38045 this.el.addClass("x-layout-panel-dragover");
38048 unhighlight : function(){
38049 this.el.removeClass("x-layout-panel-dragover");
38052 updateBox : function(box)
38054 if (!this.bodyEl) {
38055 return; // not rendered yet..
38059 if(!this.collapsed){
38060 this.el.dom.style.left = box.x + "px";
38061 this.el.dom.style.top = box.y + "px";
38062 this.updateBody(box.width, box.height);
38064 this.collapsedEl.dom.style.left = box.x + "px";
38065 this.collapsedEl.dom.style.top = box.y + "px";
38066 this.collapsedEl.setSize(box.width, box.height);
38069 this.tabs.autoSizeTabs();
38073 updateBody : function(w, h)
38076 this.el.setWidth(w);
38077 w -= this.el.getBorderWidth("rl");
38078 if(this.config.adjustments){
38079 w += this.config.adjustments[0];
38082 if(h !== null && h > 0){
38083 this.el.setHeight(h);
38084 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38085 h -= this.el.getBorderWidth("tb");
38086 if(this.config.adjustments){
38087 h += this.config.adjustments[1];
38089 this.bodyEl.setHeight(h);
38091 h = this.tabs.syncHeight(h);
38094 if(this.panelSize){
38095 w = w !== null ? w : this.panelSize.width;
38096 h = h !== null ? h : this.panelSize.height;
38098 if(this.activePanel){
38099 var el = this.activePanel.getEl();
38100 w = w !== null ? w : el.getWidth();
38101 h = h !== null ? h : el.getHeight();
38102 this.panelSize = {width: w, height: h};
38103 this.activePanel.setSize(w, h);
38105 if(Roo.isIE && this.tabs){
38106 this.tabs.el.repaint();
38111 * Returns the container element for this region.
38112 * @return {Roo.Element}
38114 getEl : function(){
38119 * Hides this region.
38122 //if(!this.collapsed){
38123 this.el.dom.style.left = "-2000px";
38126 // this.collapsedEl.dom.style.left = "-2000px";
38127 // this.collapsedEl.hide();
38129 this.visible = false;
38130 this.fireEvent("visibilitychange", this, false);
38134 * Shows this region if it was previously hidden.
38137 //if(!this.collapsed){
38140 // this.collapsedEl.show();
38142 this.visible = true;
38143 this.fireEvent("visibilitychange", this, true);
38146 closeClicked : function(){
38147 if(this.activePanel){
38148 this.remove(this.activePanel);
38152 collapseClick : function(e){
38154 e.stopPropagation();
38157 e.stopPropagation();
38163 * Collapses this region.
38164 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38167 collapse : function(skipAnim, skipCheck = false){
38168 if(this.collapsed) {
38172 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38174 this.collapsed = true;
38176 this.split.el.hide();
38178 if(this.config.animate && skipAnim !== true){
38179 this.fireEvent("invalidated", this);
38180 this.animateCollapse();
38182 this.el.setLocation(-20000,-20000);
38184 this.collapsedEl.show();
38185 this.fireEvent("collapsed", this);
38186 this.fireEvent("invalidated", this);
38192 animateCollapse : function(){
38197 * Expands this region if it was previously collapsed.
38198 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38199 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38202 expand : function(e, skipAnim){
38204 e.stopPropagation();
38206 if(!this.collapsed || this.el.hasActiveFx()) {
38210 this.afterSlideIn();
38213 this.collapsed = false;
38214 if(this.config.animate && skipAnim !== true){
38215 this.animateExpand();
38219 this.split.el.show();
38221 this.collapsedEl.setLocation(-2000,-2000);
38222 this.collapsedEl.hide();
38223 this.fireEvent("invalidated", this);
38224 this.fireEvent("expanded", this);
38228 animateExpand : function(){
38232 initTabs : function()
38234 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38236 var ts = new Roo.bootstrap.panel.Tabs({
38237 el: this.bodyEl.dom,
38239 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38240 disableTooltips: this.config.disableTabTips,
38241 toolbar : this.config.toolbar
38244 if(this.config.hideTabs){
38245 ts.stripWrap.setDisplayed(false);
38248 ts.resizeTabs = this.config.resizeTabs === true;
38249 ts.minTabWidth = this.config.minTabWidth || 40;
38250 ts.maxTabWidth = this.config.maxTabWidth || 250;
38251 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38252 ts.monitorResize = false;
38253 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38254 ts.bodyEl.addClass('roo-layout-tabs-body');
38255 this.panels.each(this.initPanelAsTab, this);
38258 initPanelAsTab : function(panel){
38259 var ti = this.tabs.addTab(
38263 this.config.closeOnTab && panel.isClosable(),
38266 if(panel.tabTip !== undefined){
38267 ti.setTooltip(panel.tabTip);
38269 ti.on("activate", function(){
38270 this.setActivePanel(panel);
38273 if(this.config.closeOnTab){
38274 ti.on("beforeclose", function(t, e){
38276 this.remove(panel);
38280 panel.tabItem = ti;
38285 updatePanelTitle : function(panel, title)
38287 if(this.activePanel == panel){
38288 this.updateTitle(title);
38291 var ti = this.tabs.getTab(panel.getEl().id);
38293 if(panel.tabTip !== undefined){
38294 ti.setTooltip(panel.tabTip);
38299 updateTitle : function(title){
38300 if(this.titleTextEl && !this.config.title){
38301 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38305 setActivePanel : function(panel)
38307 panel = this.getPanel(panel);
38308 if(this.activePanel && this.activePanel != panel){
38309 if(this.activePanel.setActiveState(false) === false){
38313 this.activePanel = panel;
38314 panel.setActiveState(true);
38315 if(this.panelSize){
38316 panel.setSize(this.panelSize.width, this.panelSize.height);
38319 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38321 this.updateTitle(panel.getTitle());
38323 this.fireEvent("invalidated", this);
38325 this.fireEvent("panelactivated", this, panel);
38329 * Shows the specified panel.
38330 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38331 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38333 showPanel : function(panel)
38335 panel = this.getPanel(panel);
38338 var tab = this.tabs.getTab(panel.getEl().id);
38339 if(tab.isHidden()){
38340 this.tabs.unhideTab(tab.id);
38344 this.setActivePanel(panel);
38351 * Get the active panel for this region.
38352 * @return {Roo.ContentPanel} The active panel or null
38354 getActivePanel : function(){
38355 return this.activePanel;
38358 validateVisibility : function(){
38359 if(this.panels.getCount() < 1){
38360 this.updateTitle(" ");
38361 this.closeBtn.hide();
38364 if(!this.isVisible()){
38371 * Adds the passed ContentPanel(s) to this region.
38372 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38373 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38375 add : function(panel)
38377 if(arguments.length > 1){
38378 for(var i = 0, len = arguments.length; i < len; i++) {
38379 this.add(arguments[i]);
38384 // if we have not been rendered yet, then we can not really do much of this..
38385 if (!this.bodyEl) {
38386 this.unrendered_panels.push(panel);
38393 if(this.hasPanel(panel)){
38394 this.showPanel(panel);
38397 panel.setRegion(this);
38398 this.panels.add(panel);
38399 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38400 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38401 // and hide them... ???
38402 this.bodyEl.dom.appendChild(panel.getEl().dom);
38403 if(panel.background !== true){
38404 this.setActivePanel(panel);
38406 this.fireEvent("paneladded", this, panel);
38413 this.initPanelAsTab(panel);
38417 if(panel.background !== true){
38418 this.tabs.activate(panel.getEl().id);
38420 this.fireEvent("paneladded", this, panel);
38425 * Hides the tab for the specified panel.
38426 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38428 hidePanel : function(panel){
38429 if(this.tabs && (panel = this.getPanel(panel))){
38430 this.tabs.hideTab(panel.getEl().id);
38435 * Unhides the tab for a previously hidden panel.
38436 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38438 unhidePanel : function(panel){
38439 if(this.tabs && (panel = this.getPanel(panel))){
38440 this.tabs.unhideTab(panel.getEl().id);
38444 clearPanels : function(){
38445 while(this.panels.getCount() > 0){
38446 this.remove(this.panels.first());
38451 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38452 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38453 * @param {Boolean} preservePanel Overrides the config preservePanel option
38454 * @return {Roo.ContentPanel} The panel that was removed
38456 remove : function(panel, preservePanel)
38458 panel = this.getPanel(panel);
38463 this.fireEvent("beforeremove", this, panel, e);
38464 if(e.cancel === true){
38467 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38468 var panelId = panel.getId();
38469 this.panels.removeKey(panelId);
38471 document.body.appendChild(panel.getEl().dom);
38474 this.tabs.removeTab(panel.getEl().id);
38475 }else if (!preservePanel){
38476 this.bodyEl.dom.removeChild(panel.getEl().dom);
38478 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38479 var p = this.panels.first();
38480 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38481 tempEl.appendChild(p.getEl().dom);
38482 this.bodyEl.update("");
38483 this.bodyEl.dom.appendChild(p.getEl().dom);
38485 this.updateTitle(p.getTitle());
38487 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38488 this.setActivePanel(p);
38490 panel.setRegion(null);
38491 if(this.activePanel == panel){
38492 this.activePanel = null;
38494 if(this.config.autoDestroy !== false && preservePanel !== true){
38495 try{panel.destroy();}catch(e){}
38497 this.fireEvent("panelremoved", this, panel);
38502 * Returns the TabPanel component used by this region
38503 * @return {Roo.TabPanel}
38505 getTabs : function(){
38509 createTool : function(parentEl, className){
38510 var btn = Roo.DomHelper.append(parentEl, {
38512 cls: "x-layout-tools-button",
38515 cls: "roo-layout-tools-button-inner " + className,
38519 btn.addClassOnOver("roo-layout-tools-button-over");
38524 * Ext JS Library 1.1.1
38525 * Copyright(c) 2006-2007, Ext JS, LLC.
38527 * Originally Released Under LGPL - original licence link has changed is not relivant.
38530 * <script type="text/javascript">
38536 * @class Roo.SplitLayoutRegion
38537 * @extends Roo.LayoutRegion
38538 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38540 Roo.bootstrap.layout.Split = function(config){
38541 this.cursor = config.cursor;
38542 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38545 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38547 splitTip : "Drag to resize.",
38548 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38549 useSplitTips : false,
38551 applyConfig : function(config){
38552 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38555 onRender : function(ctr,pos) {
38557 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38558 if(!this.config.split){
38563 var splitEl = Roo.DomHelper.append(ctr.dom, {
38565 id: this.el.id + "-split",
38566 cls: "roo-layout-split roo-layout-split-"+this.position,
38569 /** The SplitBar for this region
38570 * @type Roo.SplitBar */
38571 // does not exist yet...
38572 Roo.log([this.position, this.orientation]);
38574 this.split = new Roo.bootstrap.SplitBar({
38575 dragElement : splitEl,
38576 resizingElement: this.el,
38577 orientation : this.orientation
38580 this.split.on("moved", this.onSplitMove, this);
38581 this.split.useShim = this.config.useShim === true;
38582 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38583 if(this.useSplitTips){
38584 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38586 //if(config.collapsible){
38587 // this.split.el.on("dblclick", this.collapse, this);
38590 if(typeof this.config.minSize != "undefined"){
38591 this.split.minSize = this.config.minSize;
38593 if(typeof this.config.maxSize != "undefined"){
38594 this.split.maxSize = this.config.maxSize;
38596 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38597 this.hideSplitter();
38602 getHMaxSize : function(){
38603 var cmax = this.config.maxSize || 10000;
38604 var center = this.mgr.getRegion("center");
38605 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38608 getVMaxSize : function(){
38609 var cmax = this.config.maxSize || 10000;
38610 var center = this.mgr.getRegion("center");
38611 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38614 onSplitMove : function(split, newSize){
38615 this.fireEvent("resized", this, newSize);
38619 * Returns the {@link Roo.SplitBar} for this region.
38620 * @return {Roo.SplitBar}
38622 getSplitBar : function(){
38627 this.hideSplitter();
38628 Roo.bootstrap.layout.Split.superclass.hide.call(this);
38631 hideSplitter : function(){
38633 this.split.el.setLocation(-2000,-2000);
38634 this.split.el.hide();
38640 this.split.el.show();
38642 Roo.bootstrap.layout.Split.superclass.show.call(this);
38645 beforeSlide: function(){
38646 if(Roo.isGecko){// firefox overflow auto bug workaround
38647 this.bodyEl.clip();
38649 this.tabs.bodyEl.clip();
38651 if(this.activePanel){
38652 this.activePanel.getEl().clip();
38654 if(this.activePanel.beforeSlide){
38655 this.activePanel.beforeSlide();
38661 afterSlide : function(){
38662 if(Roo.isGecko){// firefox overflow auto bug workaround
38663 this.bodyEl.unclip();
38665 this.tabs.bodyEl.unclip();
38667 if(this.activePanel){
38668 this.activePanel.getEl().unclip();
38669 if(this.activePanel.afterSlide){
38670 this.activePanel.afterSlide();
38676 initAutoHide : function(){
38677 if(this.autoHide !== false){
38678 if(!this.autoHideHd){
38679 var st = new Roo.util.DelayedTask(this.slideIn, this);
38680 this.autoHideHd = {
38681 "mouseout": function(e){
38682 if(!e.within(this.el, true)){
38686 "mouseover" : function(e){
38692 this.el.on(this.autoHideHd);
38696 clearAutoHide : function(){
38697 if(this.autoHide !== false){
38698 this.el.un("mouseout", this.autoHideHd.mouseout);
38699 this.el.un("mouseover", this.autoHideHd.mouseover);
38703 clearMonitor : function(){
38704 Roo.get(document).un("click", this.slideInIf, this);
38707 // these names are backwards but not changed for compat
38708 slideOut : function(){
38709 if(this.isSlid || this.el.hasActiveFx()){
38712 this.isSlid = true;
38713 if(this.collapseBtn){
38714 this.collapseBtn.hide();
38716 this.closeBtnState = this.closeBtn.getStyle('display');
38717 this.closeBtn.hide();
38719 this.stickBtn.show();
38722 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38723 this.beforeSlide();
38724 this.el.setStyle("z-index", 10001);
38725 this.el.slideIn(this.getSlideAnchor(), {
38726 callback: function(){
38728 this.initAutoHide();
38729 Roo.get(document).on("click", this.slideInIf, this);
38730 this.fireEvent("slideshow", this);
38737 afterSlideIn : function(){
38738 this.clearAutoHide();
38739 this.isSlid = false;
38740 this.clearMonitor();
38741 this.el.setStyle("z-index", "");
38742 if(this.collapseBtn){
38743 this.collapseBtn.show();
38745 this.closeBtn.setStyle('display', this.closeBtnState);
38747 this.stickBtn.hide();
38749 this.fireEvent("slidehide", this);
38752 slideIn : function(cb){
38753 if(!this.isSlid || this.el.hasActiveFx()){
38757 this.isSlid = false;
38758 this.beforeSlide();
38759 this.el.slideOut(this.getSlideAnchor(), {
38760 callback: function(){
38761 this.el.setLeftTop(-10000, -10000);
38763 this.afterSlideIn();
38771 slideInIf : function(e){
38772 if(!e.within(this.el)){
38777 animateCollapse : function(){
38778 this.beforeSlide();
38779 this.el.setStyle("z-index", 20000);
38780 var anchor = this.getSlideAnchor();
38781 this.el.slideOut(anchor, {
38782 callback : function(){
38783 this.el.setStyle("z-index", "");
38784 this.collapsedEl.slideIn(anchor, {duration:.3});
38786 this.el.setLocation(-10000,-10000);
38788 this.fireEvent("collapsed", this);
38795 animateExpand : function(){
38796 this.beforeSlide();
38797 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38798 this.el.setStyle("z-index", 20000);
38799 this.collapsedEl.hide({
38802 this.el.slideIn(this.getSlideAnchor(), {
38803 callback : function(){
38804 this.el.setStyle("z-index", "");
38807 this.split.el.show();
38809 this.fireEvent("invalidated", this);
38810 this.fireEvent("expanded", this);
38838 getAnchor : function(){
38839 return this.anchors[this.position];
38842 getCollapseAnchor : function(){
38843 return this.canchors[this.position];
38846 getSlideAnchor : function(){
38847 return this.sanchors[this.position];
38850 getAlignAdj : function(){
38851 var cm = this.cmargins;
38852 switch(this.position){
38868 getExpandAdj : function(){
38869 var c = this.collapsedEl, cm = this.cmargins;
38870 switch(this.position){
38872 return [-(cm.right+c.getWidth()+cm.left), 0];
38875 return [cm.right+c.getWidth()+cm.left, 0];
38878 return [0, -(cm.top+cm.bottom+c.getHeight())];
38881 return [0, cm.top+cm.bottom+c.getHeight()];
38887 * Ext JS Library 1.1.1
38888 * Copyright(c) 2006-2007, Ext JS, LLC.
38890 * Originally Released Under LGPL - original licence link has changed is not relivant.
38893 * <script type="text/javascript">
38896 * These classes are private internal classes
38898 Roo.bootstrap.layout.Center = function(config){
38899 config.region = "center";
38900 Roo.bootstrap.layout.Region.call(this, config);
38901 this.visible = true;
38902 this.minWidth = config.minWidth || 20;
38903 this.minHeight = config.minHeight || 20;
38906 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38908 // center panel can't be hidden
38912 // center panel can't be hidden
38915 getMinWidth: function(){
38916 return this.minWidth;
38919 getMinHeight: function(){
38920 return this.minHeight;
38934 Roo.bootstrap.layout.North = function(config)
38936 config.region = 'north';
38937 config.cursor = 'n-resize';
38939 Roo.bootstrap.layout.Split.call(this, config);
38943 this.split.placement = Roo.bootstrap.SplitBar.TOP;
38944 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38945 this.split.el.addClass("roo-layout-split-v");
38947 var size = config.initialSize || config.height;
38948 if(typeof size != "undefined"){
38949 this.el.setHeight(size);
38952 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38954 orientation: Roo.bootstrap.SplitBar.VERTICAL,
38958 getBox : function(){
38959 if(this.collapsed){
38960 return this.collapsedEl.getBox();
38962 var box = this.el.getBox();
38964 box.height += this.split.el.getHeight();
38969 updateBox : function(box){
38970 if(this.split && !this.collapsed){
38971 box.height -= this.split.el.getHeight();
38972 this.split.el.setLeft(box.x);
38973 this.split.el.setTop(box.y+box.height);
38974 this.split.el.setWidth(box.width);
38976 if(this.collapsed){
38977 this.updateBody(box.width, null);
38979 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38987 Roo.bootstrap.layout.South = function(config){
38988 config.region = 'south';
38989 config.cursor = 's-resize';
38990 Roo.bootstrap.layout.Split.call(this, config);
38992 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38993 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38994 this.split.el.addClass("roo-layout-split-v");
38996 var size = config.initialSize || config.height;
38997 if(typeof size != "undefined"){
38998 this.el.setHeight(size);
39002 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39003 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39004 getBox : function(){
39005 if(this.collapsed){
39006 return this.collapsedEl.getBox();
39008 var box = this.el.getBox();
39010 var sh = this.split.el.getHeight();
39017 updateBox : function(box){
39018 if(this.split && !this.collapsed){
39019 var sh = this.split.el.getHeight();
39022 this.split.el.setLeft(box.x);
39023 this.split.el.setTop(box.y-sh);
39024 this.split.el.setWidth(box.width);
39026 if(this.collapsed){
39027 this.updateBody(box.width, null);
39029 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39033 Roo.bootstrap.layout.East = function(config){
39034 config.region = "east";
39035 config.cursor = "e-resize";
39036 Roo.bootstrap.layout.Split.call(this, config);
39038 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39039 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39040 this.split.el.addClass("roo-layout-split-h");
39042 var size = config.initialSize || config.width;
39043 if(typeof size != "undefined"){
39044 this.el.setWidth(size);
39047 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39048 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39049 getBox : function(){
39050 if(this.collapsed){
39051 return this.collapsedEl.getBox();
39053 var box = this.el.getBox();
39055 var sw = this.split.el.getWidth();
39062 updateBox : function(box){
39063 if(this.split && !this.collapsed){
39064 var sw = this.split.el.getWidth();
39066 this.split.el.setLeft(box.x);
39067 this.split.el.setTop(box.y);
39068 this.split.el.setHeight(box.height);
39071 if(this.collapsed){
39072 this.updateBody(null, box.height);
39074 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39078 Roo.bootstrap.layout.West = function(config){
39079 config.region = "west";
39080 config.cursor = "w-resize";
39082 Roo.bootstrap.layout.Split.call(this, config);
39084 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39085 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39086 this.split.el.addClass("roo-layout-split-h");
39090 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39091 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39093 onRender: function(ctr, pos)
39095 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39096 var size = this.config.initialSize || this.config.width;
39097 if(typeof size != "undefined"){
39098 this.el.setWidth(size);
39102 getBox : function(){
39103 if(this.collapsed){
39104 return this.collapsedEl.getBox();
39106 var box = this.el.getBox();
39108 box.width += this.split.el.getWidth();
39113 updateBox : function(box){
39114 if(this.split && !this.collapsed){
39115 var sw = this.split.el.getWidth();
39117 this.split.el.setLeft(box.x+box.width);
39118 this.split.el.setTop(box.y);
39119 this.split.el.setHeight(box.height);
39121 if(this.collapsed){
39122 this.updateBody(null, box.height);
39124 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39126 });Roo.namespace("Roo.bootstrap.panel");/*
39128 * Ext JS Library 1.1.1
39129 * Copyright(c) 2006-2007, Ext JS, LLC.
39131 * Originally Released Under LGPL - original licence link has changed is not relivant.
39134 * <script type="text/javascript">
39137 * @class Roo.ContentPanel
39138 * @extends Roo.util.Observable
39139 * A basic ContentPanel element.
39140 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39141 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39142 * @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
39143 * @cfg {Boolean} closable True if the panel can be closed/removed
39144 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39145 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39146 * @cfg {Toolbar} toolbar A toolbar for this panel
39147 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39148 * @cfg {String} title The title for this panel
39149 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39150 * @cfg {String} url Calls {@link #setUrl} with this value
39151 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39152 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39153 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39154 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39155 * @cfg {Boolean} badges render the badges
39156 * @cfg {String} cls extra classes to use
39157 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39160 * Create a new ContentPanel.
39161 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39162 * @param {String/Object} config A string to set only the title or a config object
39163 * @param {String} content (optional) Set the HTML content for this panel
39164 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39166 Roo.bootstrap.panel.Content = function( config){
39168 this.tpl = config.tpl || false;
39170 var el = config.el;
39171 var content = config.content;
39173 if(config.autoCreate){ // xtype is available if this is called from factory
39176 this.el = Roo.get(el);
39177 if(!this.el && config && config.autoCreate){
39178 if(typeof config.autoCreate == "object"){
39179 if(!config.autoCreate.id){
39180 config.autoCreate.id = config.id||el;
39182 this.el = Roo.DomHelper.append(document.body,
39183 config.autoCreate, true);
39187 cls: (config.cls || '') +
39188 (config.background ? ' bg-' + config.background : '') +
39189 " roo-layout-inactive-content",
39193 elcfg.html = config.html;
39197 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39200 this.closable = false;
39201 this.loaded = false;
39202 this.active = false;
39205 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39207 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39209 this.wrapEl = this.el; //this.el.wrap();
39211 if (config.toolbar.items) {
39212 ti = config.toolbar.items ;
39213 delete config.toolbar.items ;
39217 this.toolbar.render(this.wrapEl, 'before');
39218 for(var i =0;i < ti.length;i++) {
39219 // Roo.log(['add child', items[i]]);
39220 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39222 this.toolbar.items = nitems;
39223 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39224 delete config.toolbar;
39228 // xtype created footer. - not sure if will work as we normally have to render first..
39229 if (this.footer && !this.footer.el && this.footer.xtype) {
39230 if (!this.wrapEl) {
39231 this.wrapEl = this.el.wrap();
39234 this.footer.container = this.wrapEl.createChild();
39236 this.footer = Roo.factory(this.footer, Roo);
39241 if(typeof config == "string"){
39242 this.title = config;
39244 Roo.apply(this, config);
39248 this.resizeEl = Roo.get(this.resizeEl, true);
39250 this.resizeEl = this.el;
39252 // handle view.xtype
39260 * Fires when this panel is activated.
39261 * @param {Roo.ContentPanel} this
39265 * @event deactivate
39266 * Fires when this panel is activated.
39267 * @param {Roo.ContentPanel} this
39269 "deactivate" : true,
39273 * Fires when this panel is resized if fitToFrame is true.
39274 * @param {Roo.ContentPanel} this
39275 * @param {Number} width The width after any component adjustments
39276 * @param {Number} height The height after any component adjustments
39282 * Fires when this tab is created
39283 * @param {Roo.ContentPanel} this
39294 if(this.autoScroll){
39295 this.resizeEl.setStyle("overflow", "auto");
39297 // fix randome scrolling
39298 //this.el.on('scroll', function() {
39299 // Roo.log('fix random scolling');
39300 // this.scrollTo('top',0);
39303 content = content || this.content;
39305 this.setContent(content);
39307 if(config && config.url){
39308 this.setUrl(this.url, this.params, this.loadOnce);
39313 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39315 if (this.view && typeof(this.view.xtype) != 'undefined') {
39316 this.view.el = this.el.appendChild(document.createElement("div"));
39317 this.view = Roo.factory(this.view);
39318 this.view.render && this.view.render(false, '');
39322 this.fireEvent('render', this);
39325 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39332 setRegion : function(region){
39333 this.region = region;
39334 this.setActiveClass(region && !this.background);
39338 setActiveClass: function(state)
39341 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39342 this.el.setStyle('position','relative');
39344 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39345 this.el.setStyle('position', 'absolute');
39350 * Returns the toolbar for this Panel if one was configured.
39351 * @return {Roo.Toolbar}
39353 getToolbar : function(){
39354 return this.toolbar;
39357 setActiveState : function(active)
39359 this.active = active;
39360 this.setActiveClass(active);
39362 if(this.fireEvent("deactivate", this) === false){
39367 this.fireEvent("activate", this);
39371 * Updates this panel's element
39372 * @param {String} content The new content
39373 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39375 setContent : function(content, loadScripts){
39376 this.el.update(content, loadScripts);
39379 ignoreResize : function(w, h){
39380 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39383 this.lastSize = {width: w, height: h};
39388 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39389 * @return {Roo.UpdateManager} The UpdateManager
39391 getUpdateManager : function(){
39392 return this.el.getUpdateManager();
39395 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39396 * @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:
39399 url: "your-url.php",
39400 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39401 callback: yourFunction,
39402 scope: yourObject, //(optional scope)
39405 text: "Loading...",
39410 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39411 * 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.
39412 * @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}
39413 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39414 * @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.
39415 * @return {Roo.ContentPanel} this
39418 var um = this.el.getUpdateManager();
39419 um.update.apply(um, arguments);
39425 * 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.
39426 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39427 * @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)
39428 * @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)
39429 * @return {Roo.UpdateManager} The UpdateManager
39431 setUrl : function(url, params, loadOnce){
39432 if(this.refreshDelegate){
39433 this.removeListener("activate", this.refreshDelegate);
39435 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39436 this.on("activate", this.refreshDelegate);
39437 return this.el.getUpdateManager();
39440 _handleRefresh : function(url, params, loadOnce){
39441 if(!loadOnce || !this.loaded){
39442 var updater = this.el.getUpdateManager();
39443 updater.update(url, params, this._setLoaded.createDelegate(this));
39447 _setLoaded : function(){
39448 this.loaded = true;
39452 * Returns this panel's id
39455 getId : function(){
39460 * Returns this panel's element - used by regiosn to add.
39461 * @return {Roo.Element}
39463 getEl : function(){
39464 return this.wrapEl || this.el;
39469 adjustForComponents : function(width, height)
39471 //Roo.log('adjustForComponents ');
39472 if(this.resizeEl != this.el){
39473 width -= this.el.getFrameWidth('lr');
39474 height -= this.el.getFrameWidth('tb');
39477 var te = this.toolbar.getEl();
39478 te.setWidth(width);
39479 height -= te.getHeight();
39482 var te = this.footer.getEl();
39483 te.setWidth(width);
39484 height -= te.getHeight();
39488 if(this.adjustments){
39489 width += this.adjustments[0];
39490 height += this.adjustments[1];
39492 return {"width": width, "height": height};
39495 setSize : function(width, height){
39496 if(this.fitToFrame && !this.ignoreResize(width, height)){
39497 if(this.fitContainer && this.resizeEl != this.el){
39498 this.el.setSize(width, height);
39500 var size = this.adjustForComponents(width, height);
39501 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39502 this.fireEvent('resize', this, size.width, size.height);
39507 * Returns this panel's title
39510 getTitle : function(){
39512 if (typeof(this.title) != 'object') {
39517 for (var k in this.title) {
39518 if (!this.title.hasOwnProperty(k)) {
39522 if (k.indexOf('-') >= 0) {
39523 var s = k.split('-');
39524 for (var i = 0; i<s.length; i++) {
39525 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39528 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39535 * Set this panel's title
39536 * @param {String} title
39538 setTitle : function(title){
39539 this.title = title;
39541 this.region.updatePanelTitle(this, title);
39546 * Returns true is this panel was configured to be closable
39547 * @return {Boolean}
39549 isClosable : function(){
39550 return this.closable;
39553 beforeSlide : function(){
39555 this.resizeEl.clip();
39558 afterSlide : function(){
39560 this.resizeEl.unclip();
39564 * Force a content refresh from the URL specified in the {@link #setUrl} method.
39565 * Will fail silently if the {@link #setUrl} method has not been called.
39566 * This does not activate the panel, just updates its content.
39568 refresh : function(){
39569 if(this.refreshDelegate){
39570 this.loaded = false;
39571 this.refreshDelegate();
39576 * Destroys this panel
39578 destroy : function(){
39579 this.el.removeAllListeners();
39580 var tempEl = document.createElement("span");
39581 tempEl.appendChild(this.el.dom);
39582 tempEl.innerHTML = "";
39588 * form - if the content panel contains a form - this is a reference to it.
39589 * @type {Roo.form.Form}
39593 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39594 * This contains a reference to it.
39600 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39610 * @param {Object} cfg Xtype definition of item to add.
39614 getChildContainer: function () {
39615 return this.getEl();
39620 var ret = new Roo.factory(cfg);
39625 if (cfg.xtype.match(/^Form$/)) {
39628 //if (this.footer) {
39629 // el = this.footer.container.insertSibling(false, 'before');
39631 el = this.el.createChild();
39634 this.form = new Roo.form.Form(cfg);
39637 if ( this.form.allItems.length) {
39638 this.form.render(el.dom);
39642 // should only have one of theses..
39643 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39644 // views.. should not be just added - used named prop 'view''
39646 cfg.el = this.el.appendChild(document.createElement("div"));
39649 var ret = new Roo.factory(cfg);
39651 ret.render && ret.render(false, ''); // render blank..
39661 * @class Roo.bootstrap.panel.Grid
39662 * @extends Roo.bootstrap.panel.Content
39664 * Create a new GridPanel.
39665 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39666 * @param {Object} config A the config object
39672 Roo.bootstrap.panel.Grid = function(config)
39676 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39677 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39679 config.el = this.wrapper;
39680 //this.el = this.wrapper;
39682 if (config.container) {
39683 // ctor'ed from a Border/panel.grid
39686 this.wrapper.setStyle("overflow", "hidden");
39687 this.wrapper.addClass('roo-grid-container');
39692 if(config.toolbar){
39693 var tool_el = this.wrapper.createChild();
39694 this.toolbar = Roo.factory(config.toolbar);
39696 if (config.toolbar.items) {
39697 ti = config.toolbar.items ;
39698 delete config.toolbar.items ;
39702 this.toolbar.render(tool_el);
39703 for(var i =0;i < ti.length;i++) {
39704 // Roo.log(['add child', items[i]]);
39705 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39707 this.toolbar.items = nitems;
39709 delete config.toolbar;
39712 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39713 config.grid.scrollBody = true;;
39714 config.grid.monitorWindowResize = false; // turn off autosizing
39715 config.grid.autoHeight = false;
39716 config.grid.autoWidth = false;
39718 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39720 if (config.background) {
39721 // render grid on panel activation (if panel background)
39722 this.on('activate', function(gp) {
39723 if (!gp.grid.rendered) {
39724 gp.grid.render(this.wrapper);
39725 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39730 this.grid.render(this.wrapper);
39731 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
39734 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39735 // ??? needed ??? config.el = this.wrapper;
39740 // xtype created footer. - not sure if will work as we normally have to render first..
39741 if (this.footer && !this.footer.el && this.footer.xtype) {
39743 var ctr = this.grid.getView().getFooterPanel(true);
39744 this.footer.dataSource = this.grid.dataSource;
39745 this.footer = Roo.factory(this.footer, Roo);
39746 this.footer.render(ctr);
39756 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39757 getId : function(){
39758 return this.grid.id;
39762 * Returns the grid for this panel
39763 * @return {Roo.bootstrap.Table}
39765 getGrid : function(){
39769 setSize : function(width, height){
39770 if(!this.ignoreResize(width, height)){
39771 var grid = this.grid;
39772 var size = this.adjustForComponents(width, height);
39773 // tfoot is not a footer?
39776 var gridel = grid.getGridEl();
39777 gridel.setSize(size.width, size.height);
39779 var tbd = grid.getGridEl().select('tbody', true).first();
39780 var thd = grid.getGridEl().select('thead',true).first();
39781 var tbf= grid.getGridEl().select('tfoot', true).first();
39784 size.height -= thd.getHeight();
39787 size.height -= thd.getHeight();
39790 tbd.setSize(size.width, size.height );
39791 // this is for the account management tab -seems to work there.
39792 var thd = grid.getGridEl().select('thead',true).first();
39794 // tbd.setSize(size.width, size.height - thd.getHeight());
39803 beforeSlide : function(){
39804 this.grid.getView().scroller.clip();
39807 afterSlide : function(){
39808 this.grid.getView().scroller.unclip();
39811 destroy : function(){
39812 this.grid.destroy();
39814 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
39819 * @class Roo.bootstrap.panel.Nest
39820 * @extends Roo.bootstrap.panel.Content
39822 * Create a new Panel, that can contain a layout.Border.
39825 * @param {Roo.BorderLayout} layout The layout for this panel
39826 * @param {String/Object} config A string to set only the title or a config object
39828 Roo.bootstrap.panel.Nest = function(config)
39830 // construct with only one argument..
39831 /* FIXME - implement nicer consturctors
39832 if (layout.layout) {
39834 layout = config.layout;
39835 delete config.layout;
39837 if (layout.xtype && !layout.getEl) {
39838 // then layout needs constructing..
39839 layout = Roo.factory(layout, Roo);
39843 config.el = config.layout.getEl();
39845 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39847 config.layout.monitorWindowResize = false; // turn off autosizing
39848 this.layout = config.layout;
39849 this.layout.getEl().addClass("roo-layout-nested-layout");
39850 this.layout.parent = this;
39857 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39859 setSize : function(width, height){
39860 if(!this.ignoreResize(width, height)){
39861 var size = this.adjustForComponents(width, height);
39862 var el = this.layout.getEl();
39863 if (size.height < 1) {
39864 el.setWidth(size.width);
39866 el.setSize(size.width, size.height);
39868 var touch = el.dom.offsetWidth;
39869 this.layout.layout();
39870 // ie requires a double layout on the first pass
39871 if(Roo.isIE && !this.initialized){
39872 this.initialized = true;
39873 this.layout.layout();
39878 // activate all subpanels if not currently active..
39880 setActiveState : function(active){
39881 this.active = active;
39882 this.setActiveClass(active);
39885 this.fireEvent("deactivate", this);
39889 this.fireEvent("activate", this);
39890 // not sure if this should happen before or after..
39891 if (!this.layout) {
39892 return; // should not happen..
39895 for (var r in this.layout.regions) {
39896 reg = this.layout.getRegion(r);
39897 if (reg.getActivePanel()) {
39898 //reg.showPanel(reg.getActivePanel()); // force it to activate..
39899 reg.setActivePanel(reg.getActivePanel());
39902 if (!reg.panels.length) {
39905 reg.showPanel(reg.getPanel(0));
39914 * Returns the nested BorderLayout for this panel
39915 * @return {Roo.BorderLayout}
39917 getLayout : function(){
39918 return this.layout;
39922 * Adds a xtype elements to the layout of the nested panel
39926 xtype : 'ContentPanel',
39933 xtype : 'NestedLayoutPanel',
39939 items : [ ... list of content panels or nested layout panels.. ]
39943 * @param {Object} cfg Xtype definition of item to add.
39945 addxtype : function(cfg) {
39946 return this.layout.addxtype(cfg);
39951 * Ext JS Library 1.1.1
39952 * Copyright(c) 2006-2007, Ext JS, LLC.
39954 * Originally Released Under LGPL - original licence link has changed is not relivant.
39957 * <script type="text/javascript">
39960 * @class Roo.TabPanel
39961 * @extends Roo.util.Observable
39962 * A lightweight tab container.
39966 // basic tabs 1, built from existing content
39967 var tabs = new Roo.TabPanel("tabs1");
39968 tabs.addTab("script", "View Script");
39969 tabs.addTab("markup", "View Markup");
39970 tabs.activate("script");
39972 // more advanced tabs, built from javascript
39973 var jtabs = new Roo.TabPanel("jtabs");
39974 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39976 // set up the UpdateManager
39977 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39978 var updater = tab2.getUpdateManager();
39979 updater.setDefaultUrl("ajax1.htm");
39980 tab2.on('activate', updater.refresh, updater, true);
39982 // Use setUrl for Ajax loading
39983 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39984 tab3.setUrl("ajax2.htm", null, true);
39987 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39990 jtabs.activate("jtabs-1");
39993 * Create a new TabPanel.
39994 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39995 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39997 Roo.bootstrap.panel.Tabs = function(config){
39999 * The container element for this TabPanel.
40000 * @type Roo.Element
40002 this.el = Roo.get(config.el);
40005 if(typeof config == "boolean"){
40006 this.tabPosition = config ? "bottom" : "top";
40008 Roo.apply(this, config);
40012 if(this.tabPosition == "bottom"){
40013 // if tabs are at the bottom = create the body first.
40014 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40015 this.el.addClass("roo-tabs-bottom");
40017 // next create the tabs holders
40019 if (this.tabPosition == "west"){
40021 var reg = this.region; // fake it..
40023 if (!reg.mgr.parent) {
40026 reg = reg.mgr.parent.region;
40028 Roo.log("got nest?");
40030 if (reg.mgr.getRegion('west')) {
40031 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40032 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40033 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40034 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40035 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40043 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40044 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40045 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40046 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40051 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40054 // finally - if tabs are at the top, then create the body last..
40055 if(this.tabPosition != "bottom"){
40056 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40057 * @type Roo.Element
40059 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40060 this.el.addClass("roo-tabs-top");
40064 this.bodyEl.setStyle("position", "relative");
40066 this.active = null;
40067 this.activateDelegate = this.activate.createDelegate(this);
40072 * Fires when the active tab changes
40073 * @param {Roo.TabPanel} this
40074 * @param {Roo.TabPanelItem} activePanel The new active tab
40078 * @event beforetabchange
40079 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40080 * @param {Roo.TabPanel} this
40081 * @param {Object} e Set cancel to true on this object to cancel the tab change
40082 * @param {Roo.TabPanelItem} tab The tab being changed to
40084 "beforetabchange" : true
40087 Roo.EventManager.onWindowResize(this.onResize, this);
40088 this.cpad = this.el.getPadding("lr");
40089 this.hiddenCount = 0;
40092 // toolbar on the tabbar support...
40093 if (this.toolbar) {
40094 alert("no toolbar support yet");
40095 this.toolbar = false;
40097 var tcfg = this.toolbar;
40098 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40099 this.toolbar = new Roo.Toolbar(tcfg);
40100 if (Roo.isSafari) {
40101 var tbl = tcfg.container.child('table', true);
40102 tbl.setAttribute('width', '100%');
40110 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40113 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40115 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40117 tabPosition : "top",
40119 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40121 currentTabWidth : 0,
40123 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40127 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40131 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40133 preferredTabWidth : 175,
40135 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40137 resizeTabs : false,
40139 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40141 monitorResize : true,
40143 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40145 toolbar : false, // set by caller..
40147 region : false, /// set by caller
40149 disableTooltips : true, // not used yet...
40152 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40153 * @param {String} id The id of the div to use <b>or create</b>
40154 * @param {String} text The text for the tab
40155 * @param {String} content (optional) Content to put in the TabPanelItem body
40156 * @param {Boolean} closable (optional) True to create a close icon on the tab
40157 * @return {Roo.TabPanelItem} The created TabPanelItem
40159 addTab : function(id, text, content, closable, tpl)
40161 var item = new Roo.bootstrap.panel.TabItem({
40165 closable : closable,
40168 this.addTabItem(item);
40170 item.setContent(content);
40176 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40177 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40178 * @return {Roo.TabPanelItem}
40180 getTab : function(id){
40181 return this.items[id];
40185 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40186 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40188 hideTab : function(id){
40189 var t = this.items[id];
40192 this.hiddenCount++;
40193 this.autoSizeTabs();
40198 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40199 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40201 unhideTab : function(id){
40202 var t = this.items[id];
40204 t.setHidden(false);
40205 this.hiddenCount--;
40206 this.autoSizeTabs();
40211 * Adds an existing {@link Roo.TabPanelItem}.
40212 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40214 addTabItem : function(item)
40216 this.items[item.id] = item;
40217 this.items.push(item);
40218 this.autoSizeTabs();
40219 // if(this.resizeTabs){
40220 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40221 // this.autoSizeTabs();
40223 // item.autoSize();
40228 * Removes a {@link Roo.TabPanelItem}.
40229 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40231 removeTab : function(id){
40232 var items = this.items;
40233 var tab = items[id];
40234 if(!tab) { return; }
40235 var index = items.indexOf(tab);
40236 if(this.active == tab && items.length > 1){
40237 var newTab = this.getNextAvailable(index);
40242 this.stripEl.dom.removeChild(tab.pnode.dom);
40243 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40244 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40246 items.splice(index, 1);
40247 delete this.items[tab.id];
40248 tab.fireEvent("close", tab);
40249 tab.purgeListeners();
40250 this.autoSizeTabs();
40253 getNextAvailable : function(start){
40254 var items = this.items;
40256 // look for a next tab that will slide over to
40257 // replace the one being removed
40258 while(index < items.length){
40259 var item = items[++index];
40260 if(item && !item.isHidden()){
40264 // if one isn't found select the previous tab (on the left)
40267 var item = items[--index];
40268 if(item && !item.isHidden()){
40276 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40277 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40279 disableTab : function(id){
40280 var tab = this.items[id];
40281 if(tab && this.active != tab){
40287 * Enables a {@link Roo.TabPanelItem} that is disabled.
40288 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40290 enableTab : function(id){
40291 var tab = this.items[id];
40296 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40297 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40298 * @return {Roo.TabPanelItem} The TabPanelItem.
40300 activate : function(id)
40302 //Roo.log('activite:' + id);
40304 var tab = this.items[id];
40308 if(tab == this.active || tab.disabled){
40312 this.fireEvent("beforetabchange", this, e, tab);
40313 if(e.cancel !== true && !tab.disabled){
40315 this.active.hide();
40317 this.active = this.items[id];
40318 this.active.show();
40319 this.fireEvent("tabchange", this, this.active);
40325 * Gets the active {@link Roo.TabPanelItem}.
40326 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40328 getActiveTab : function(){
40329 return this.active;
40333 * Updates the tab body element to fit the height of the container element
40334 * for overflow scrolling
40335 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40337 syncHeight : function(targetHeight){
40338 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40339 var bm = this.bodyEl.getMargins();
40340 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40341 this.bodyEl.setHeight(newHeight);
40345 onResize : function(){
40346 if(this.monitorResize){
40347 this.autoSizeTabs();
40352 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40354 beginUpdate : function(){
40355 this.updating = true;
40359 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40361 endUpdate : function(){
40362 this.updating = false;
40363 this.autoSizeTabs();
40367 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40369 autoSizeTabs : function()
40371 var count = this.items.length;
40372 var vcount = count - this.hiddenCount;
40375 this.stripEl.hide();
40377 this.stripEl.show();
40380 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40385 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40386 var availWidth = Math.floor(w / vcount);
40387 var b = this.stripBody;
40388 if(b.getWidth() > w){
40389 var tabs = this.items;
40390 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40391 if(availWidth < this.minTabWidth){
40392 /*if(!this.sleft){ // incomplete scrolling code
40393 this.createScrollButtons();
40396 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40399 if(this.currentTabWidth < this.preferredTabWidth){
40400 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40406 * Returns the number of tabs in this TabPanel.
40409 getCount : function(){
40410 return this.items.length;
40414 * Resizes all the tabs to the passed width
40415 * @param {Number} The new width
40417 setTabWidth : function(width){
40418 this.currentTabWidth = width;
40419 for(var i = 0, len = this.items.length; i < len; i++) {
40420 if(!this.items[i].isHidden()) {
40421 this.items[i].setWidth(width);
40427 * Destroys this TabPanel
40428 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40430 destroy : function(removeEl){
40431 Roo.EventManager.removeResizeListener(this.onResize, this);
40432 for(var i = 0, len = this.items.length; i < len; i++){
40433 this.items[i].purgeListeners();
40435 if(removeEl === true){
40436 this.el.update("");
40441 createStrip : function(container)
40443 var strip = document.createElement("nav");
40444 strip.className = Roo.bootstrap.version == 4 ?
40445 "navbar-light bg-light" :
40446 "navbar navbar-default"; //"x-tabs-wrap";
40447 container.appendChild(strip);
40451 createStripList : function(strip)
40453 // div wrapper for retard IE
40454 // returns the "tr" element.
40455 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40456 //'<div class="x-tabs-strip-wrap">'+
40457 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40458 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40459 return strip.firstChild; //.firstChild.firstChild.firstChild;
40461 createBody : function(container)
40463 var body = document.createElement("div");
40464 Roo.id(body, "tab-body");
40465 //Roo.fly(body).addClass("x-tabs-body");
40466 Roo.fly(body).addClass("tab-content");
40467 container.appendChild(body);
40470 createItemBody :function(bodyEl, id){
40471 var body = Roo.getDom(id);
40473 body = document.createElement("div");
40476 //Roo.fly(body).addClass("x-tabs-item-body");
40477 Roo.fly(body).addClass("tab-pane");
40478 bodyEl.insertBefore(body, bodyEl.firstChild);
40482 createStripElements : function(stripEl, text, closable, tpl)
40484 var td = document.createElement("li"); // was td..
40485 td.className = 'nav-item';
40487 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40490 stripEl.appendChild(td);
40492 td.className = "x-tabs-closable";
40493 if(!this.closeTpl){
40494 this.closeTpl = new Roo.Template(
40495 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40496 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40497 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40500 var el = this.closeTpl.overwrite(td, {"text": text});
40501 var close = el.getElementsByTagName("div")[0];
40502 var inner = el.getElementsByTagName("em")[0];
40503 return {"el": el, "close": close, "inner": inner};
40506 // not sure what this is..
40507 // if(!this.tabTpl){
40508 //this.tabTpl = new Roo.Template(
40509 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40510 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40512 // this.tabTpl = new Roo.Template(
40513 // '<a href="#">' +
40514 // '<span unselectable="on"' +
40515 // (this.disableTooltips ? '' : ' title="{text}"') +
40516 // ' >{text}</span></a>'
40522 var template = tpl || this.tabTpl || false;
40525 template = new Roo.Template(
40526 Roo.bootstrap.version == 4 ?
40528 '<a class="nav-link" href="#" unselectable="on"' +
40529 (this.disableTooltips ? '' : ' title="{text}"') +
40532 '<a class="nav-link" href="#">' +
40533 '<span unselectable="on"' +
40534 (this.disableTooltips ? '' : ' title="{text}"') +
40535 ' >{text}</span></a>'
40540 switch (typeof(template)) {
40544 template = new Roo.Template(template);
40550 var el = template.overwrite(td, {"text": text});
40552 var inner = el.getElementsByTagName("span")[0];
40554 return {"el": el, "inner": inner};
40562 * @class Roo.TabPanelItem
40563 * @extends Roo.util.Observable
40564 * Represents an individual item (tab plus body) in a TabPanel.
40565 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40566 * @param {String} id The id of this TabPanelItem
40567 * @param {String} text The text for the tab of this TabPanelItem
40568 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40570 Roo.bootstrap.panel.TabItem = function(config){
40572 * The {@link Roo.TabPanel} this TabPanelItem belongs to
40573 * @type Roo.TabPanel
40575 this.tabPanel = config.panel;
40577 * The id for this TabPanelItem
40580 this.id = config.id;
40582 this.disabled = false;
40584 this.text = config.text;
40586 this.loaded = false;
40587 this.closable = config.closable;
40590 * The body element for this TabPanelItem.
40591 * @type Roo.Element
40593 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40594 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40595 this.bodyEl.setStyle("display", "block");
40596 this.bodyEl.setStyle("zoom", "1");
40597 //this.hideAction();
40599 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40601 this.el = Roo.get(els.el);
40602 this.inner = Roo.get(els.inner, true);
40603 this.textEl = Roo.bootstrap.version == 4 ?
40604 this.el : Roo.get(this.el.dom.firstChild, true);
40606 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40607 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40610 // this.el.on("mousedown", this.onTabMouseDown, this);
40611 this.el.on("click", this.onTabClick, this);
40613 if(config.closable){
40614 var c = Roo.get(els.close, true);
40615 c.dom.title = this.closeText;
40616 c.addClassOnOver("close-over");
40617 c.on("click", this.closeClick, this);
40623 * Fires when this tab becomes the active tab.
40624 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40625 * @param {Roo.TabPanelItem} this
40629 * @event beforeclose
40630 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40631 * @param {Roo.TabPanelItem} this
40632 * @param {Object} e Set cancel to true on this object to cancel the close.
40634 "beforeclose": true,
40637 * Fires when this tab is closed.
40638 * @param {Roo.TabPanelItem} this
40642 * @event deactivate
40643 * Fires when this tab is no longer the active tab.
40644 * @param {Roo.TabPanel} tabPanel The parent TabPanel
40645 * @param {Roo.TabPanelItem} this
40647 "deactivate" : true
40649 this.hidden = false;
40651 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40654 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40656 purgeListeners : function(){
40657 Roo.util.Observable.prototype.purgeListeners.call(this);
40658 this.el.removeAllListeners();
40661 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40664 this.status_node.addClass("active");
40667 this.tabPanel.stripWrap.repaint();
40669 this.fireEvent("activate", this.tabPanel, this);
40673 * Returns true if this tab is the active tab.
40674 * @return {Boolean}
40676 isActive : function(){
40677 return this.tabPanel.getActiveTab() == this;
40681 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40684 this.status_node.removeClass("active");
40686 this.fireEvent("deactivate", this.tabPanel, this);
40689 hideAction : function(){
40690 this.bodyEl.hide();
40691 this.bodyEl.setStyle("position", "absolute");
40692 this.bodyEl.setLeft("-20000px");
40693 this.bodyEl.setTop("-20000px");
40696 showAction : function(){
40697 this.bodyEl.setStyle("position", "relative");
40698 this.bodyEl.setTop("");
40699 this.bodyEl.setLeft("");
40700 this.bodyEl.show();
40704 * Set the tooltip for the tab.
40705 * @param {String} tooltip The tab's tooltip
40707 setTooltip : function(text){
40708 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40709 this.textEl.dom.qtip = text;
40710 this.textEl.dom.removeAttribute('title');
40712 this.textEl.dom.title = text;
40716 onTabClick : function(e){
40717 e.preventDefault();
40718 this.tabPanel.activate(this.id);
40721 onTabMouseDown : function(e){
40722 e.preventDefault();
40723 this.tabPanel.activate(this.id);
40726 getWidth : function(){
40727 return this.inner.getWidth();
40730 setWidth : function(width){
40731 var iwidth = width - this.linode.getPadding("lr");
40732 this.inner.setWidth(iwidth);
40733 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40734 this.linode.setWidth(width);
40738 * Show or hide the tab
40739 * @param {Boolean} hidden True to hide or false to show.
40741 setHidden : function(hidden){
40742 this.hidden = hidden;
40743 this.linode.setStyle("display", hidden ? "none" : "");
40747 * Returns true if this tab is "hidden"
40748 * @return {Boolean}
40750 isHidden : function(){
40751 return this.hidden;
40755 * Returns the text for this tab
40758 getText : function(){
40762 autoSize : function(){
40763 //this.el.beginMeasure();
40764 this.textEl.setWidth(1);
40766 * #2804 [new] Tabs in Roojs
40767 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40769 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40770 //this.el.endMeasure();
40774 * Sets the text for the tab (Note: this also sets the tooltip text)
40775 * @param {String} text The tab's text and tooltip
40777 setText : function(text){
40779 this.textEl.update(text);
40780 this.setTooltip(text);
40781 //if(!this.tabPanel.resizeTabs){
40782 // this.autoSize();
40786 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40788 activate : function(){
40789 this.tabPanel.activate(this.id);
40793 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40795 disable : function(){
40796 if(this.tabPanel.active != this){
40797 this.disabled = true;
40798 this.status_node.addClass("disabled");
40803 * Enables this TabPanelItem if it was previously disabled.
40805 enable : function(){
40806 this.disabled = false;
40807 this.status_node.removeClass("disabled");
40811 * Sets the content for this TabPanelItem.
40812 * @param {String} content The content
40813 * @param {Boolean} loadScripts true to look for and load scripts
40815 setContent : function(content, loadScripts){
40816 this.bodyEl.update(content, loadScripts);
40820 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40821 * @return {Roo.UpdateManager} The UpdateManager
40823 getUpdateManager : function(){
40824 return this.bodyEl.getUpdateManager();
40828 * Set a URL to be used to load the content for this TabPanelItem.
40829 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40830 * @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)
40831 * @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)
40832 * @return {Roo.UpdateManager} The UpdateManager
40834 setUrl : function(url, params, loadOnce){
40835 if(this.refreshDelegate){
40836 this.un('activate', this.refreshDelegate);
40838 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40839 this.on("activate", this.refreshDelegate);
40840 return this.bodyEl.getUpdateManager();
40844 _handleRefresh : function(url, params, loadOnce){
40845 if(!loadOnce || !this.loaded){
40846 var updater = this.bodyEl.getUpdateManager();
40847 updater.update(url, params, this._setLoaded.createDelegate(this));
40852 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
40853 * Will fail silently if the setUrl method has not been called.
40854 * This does not activate the panel, just updates its content.
40856 refresh : function(){
40857 if(this.refreshDelegate){
40858 this.loaded = false;
40859 this.refreshDelegate();
40864 _setLoaded : function(){
40865 this.loaded = true;
40869 closeClick : function(e){
40872 this.fireEvent("beforeclose", this, o);
40873 if(o.cancel !== true){
40874 this.tabPanel.removeTab(this.id);
40878 * The text displayed in the tooltip for the close icon.
40881 closeText : "Close this tab"
40884 * This script refer to:
40885 * Title: International Telephone Input
40886 * Author: Jack O'Connor
40887 * Code version: v12.1.12
40888 * Availability: https://github.com/jackocnr/intl-tel-input.git
40891 Roo.bootstrap.PhoneInputData = function() {
40894 "Afghanistan (افغانستان)",
40899 "Albania (Shqipëri)",
40904 "Algeria (الجزائر)",
40929 "Antigua and Barbuda",
40939 "Armenia (Հայաստան)",
40955 "Austria (Österreich)",
40960 "Azerbaijan (Azərbaycan)",
40970 "Bahrain (البحرين)",
40975 "Bangladesh (বাংলাদেশ)",
40985 "Belarus (Беларусь)",
40990 "Belgium (België)",
41020 "Bosnia and Herzegovina (Босна и Херцеговина)",
41035 "British Indian Ocean Territory",
41040 "British Virgin Islands",
41050 "Bulgaria (България)",
41060 "Burundi (Uburundi)",
41065 "Cambodia (កម្ពុជា)",
41070 "Cameroon (Cameroun)",
41079 ["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"]
41082 "Cape Verde (Kabu Verdi)",
41087 "Caribbean Netherlands",
41098 "Central African Republic (République centrafricaine)",
41118 "Christmas Island",
41124 "Cocos (Keeling) Islands",
41135 "Comoros (جزر القمر)",
41140 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41145 "Congo (Republic) (Congo-Brazzaville)",
41165 "Croatia (Hrvatska)",
41186 "Czech Republic (Česká republika)",
41191 "Denmark (Danmark)",
41206 "Dominican Republic (República Dominicana)",
41210 ["809", "829", "849"]
41228 "Equatorial Guinea (Guinea Ecuatorial)",
41248 "Falkland Islands (Islas Malvinas)",
41253 "Faroe Islands (Føroyar)",
41274 "French Guiana (Guyane française)",
41279 "French Polynesia (Polynésie française)",
41294 "Georgia (საქართველო)",
41299 "Germany (Deutschland)",
41319 "Greenland (Kalaallit Nunaat)",
41356 "Guinea-Bissau (Guiné Bissau)",
41381 "Hungary (Magyarország)",
41386 "Iceland (Ísland)",
41406 "Iraq (العراق)",
41422 "Israel (ישראל)",
41449 "Jordan (الأردن)",
41454 "Kazakhstan (Казахстан)",
41475 "Kuwait (الكويت)",
41480 "Kyrgyzstan (Кыргызстан)",
41490 "Latvia (Latvija)",
41495 "Lebanon (لبنان)",
41510 "Libya (ليبيا)",
41520 "Lithuania (Lietuva)",
41535 "Macedonia (FYROM) (Македонија)",
41540 "Madagascar (Madagasikara)",
41570 "Marshall Islands",
41580 "Mauritania (موريتانيا)",
41585 "Mauritius (Moris)",
41606 "Moldova (Republica Moldova)",
41616 "Mongolia (Монгол)",
41621 "Montenegro (Crna Gora)",
41631 "Morocco (المغرب)",
41637 "Mozambique (Moçambique)",
41642 "Myanmar (Burma) (မြန်မာ)",
41647 "Namibia (Namibië)",
41662 "Netherlands (Nederland)",
41667 "New Caledonia (Nouvelle-Calédonie)",
41702 "North Korea (조선 민주주의 인민 공화국)",
41707 "Northern Mariana Islands",
41723 "Pakistan (پاکستان)",
41733 "Palestine (فلسطين)",
41743 "Papua New Guinea",
41785 "Réunion (La Réunion)",
41791 "Romania (România)",
41807 "Saint Barthélemy",
41818 "Saint Kitts and Nevis",
41828 "Saint Martin (Saint-Martin (partie française))",
41834 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41839 "Saint Vincent and the Grenadines",
41854 "São Tomé and Príncipe (São Tomé e Príncipe)",
41859 "Saudi Arabia (المملكة العربية السعودية)",
41864 "Senegal (Sénégal)",
41894 "Slovakia (Slovensko)",
41899 "Slovenia (Slovenija)",
41909 "Somalia (Soomaaliya)",
41919 "South Korea (대한민국)",
41924 "South Sudan (جنوب السودان)",
41934 "Sri Lanka (ශ්රී ලංකාව)",
41939 "Sudan (السودان)",
41949 "Svalbard and Jan Mayen",
41960 "Sweden (Sverige)",
41965 "Switzerland (Schweiz)",
41970 "Syria (سوريا)",
42015 "Trinidad and Tobago",
42020 "Tunisia (تونس)",
42025 "Turkey (Türkiye)",
42035 "Turks and Caicos Islands",
42045 "U.S. Virgin Islands",
42055 "Ukraine (Україна)",
42060 "United Arab Emirates (الإمارات العربية المتحدة)",
42082 "Uzbekistan (Oʻzbekiston)",
42092 "Vatican City (Città del Vaticano)",
42103 "Vietnam (Việt Nam)",
42108 "Wallis and Futuna (Wallis-et-Futuna)",
42113 "Western Sahara (الصحراء الغربية)",
42119 "Yemen (اليمن)",
42143 * This script refer to:
42144 * Title: International Telephone Input
42145 * Author: Jack O'Connor
42146 * Code version: v12.1.12
42147 * Availability: https://github.com/jackocnr/intl-tel-input.git
42151 * @class Roo.bootstrap.PhoneInput
42152 * @extends Roo.bootstrap.TriggerField
42153 * An input with International dial-code selection
42155 * @cfg {String} defaultDialCode default '+852'
42156 * @cfg {Array} preferedCountries default []
42159 * Create a new PhoneInput.
42160 * @param {Object} config Configuration options
42163 Roo.bootstrap.PhoneInput = function(config) {
42164 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42167 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42169 listWidth: undefined,
42171 selectedClass: 'active',
42173 invalidClass : "has-warning",
42175 validClass: 'has-success',
42177 allowed: '0123456789',
42182 * @cfg {String} defaultDialCode The default dial code when initializing the input
42184 defaultDialCode: '+852',
42187 * @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
42189 preferedCountries: false,
42191 getAutoCreate : function()
42193 var data = Roo.bootstrap.PhoneInputData();
42194 var align = this.labelAlign || this.parentLabelAlign();
42197 this.allCountries = [];
42198 this.dialCodeMapping = [];
42200 for (var i = 0; i < data.length; i++) {
42202 this.allCountries[i] = {
42206 priority: c[3] || 0,
42207 areaCodes: c[4] || null
42209 this.dialCodeMapping[c[2]] = {
42212 priority: c[3] || 0,
42213 areaCodes: c[4] || null
42225 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42226 maxlength: this.max_length,
42227 cls : 'form-control tel-input',
42228 autocomplete: 'new-password'
42231 var hiddenInput = {
42234 cls: 'hidden-tel-input'
42238 hiddenInput.name = this.name;
42241 if (this.disabled) {
42242 input.disabled = true;
42245 var flag_container = {
42262 cls: this.hasFeedback ? 'has-feedback' : '',
42268 cls: 'dial-code-holder',
42275 cls: 'roo-select2-container input-group',
42282 if (this.fieldLabel.length) {
42285 tooltip: 'This field is required'
42291 cls: 'control-label',
42297 html: this.fieldLabel
42300 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42306 if(this.indicatorpos == 'right') {
42307 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42314 if(align == 'left') {
42322 if(this.labelWidth > 12){
42323 label.style = "width: " + this.labelWidth + 'px';
42325 if(this.labelWidth < 13 && this.labelmd == 0){
42326 this.labelmd = this.labelWidth;
42328 if(this.labellg > 0){
42329 label.cls += ' col-lg-' + this.labellg;
42330 input.cls += ' col-lg-' + (12 - this.labellg);
42332 if(this.labelmd > 0){
42333 label.cls += ' col-md-' + this.labelmd;
42334 container.cls += ' col-md-' + (12 - this.labelmd);
42336 if(this.labelsm > 0){
42337 label.cls += ' col-sm-' + this.labelsm;
42338 container.cls += ' col-sm-' + (12 - this.labelsm);
42340 if(this.labelxs > 0){
42341 label.cls += ' col-xs-' + this.labelxs;
42342 container.cls += ' col-xs-' + (12 - this.labelxs);
42352 var settings = this;
42354 ['xs','sm','md','lg'].map(function(size){
42355 if (settings[size]) {
42356 cfg.cls += ' col-' + size + '-' + settings[size];
42360 this.store = new Roo.data.Store({
42361 proxy : new Roo.data.MemoryProxy({}),
42362 reader : new Roo.data.JsonReader({
42373 'name' : 'dialCode',
42377 'name' : 'priority',
42381 'name' : 'areaCodes',
42388 if(!this.preferedCountries) {
42389 this.preferedCountries = [
42396 var p = this.preferedCountries.reverse();
42399 for (var i = 0; i < p.length; i++) {
42400 for (var j = 0; j < this.allCountries.length; j++) {
42401 if(this.allCountries[j].iso2 == p[i]) {
42402 var t = this.allCountries[j];
42403 this.allCountries.splice(j,1);
42404 this.allCountries.unshift(t);
42410 this.store.proxy.data = {
42412 data: this.allCountries
42418 initEvents : function()
42421 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42423 this.indicator = this.indicatorEl();
42424 this.flag = this.flagEl();
42425 this.dialCodeHolder = this.dialCodeHolderEl();
42427 this.trigger = this.el.select('div.flag-box',true).first();
42428 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42433 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42434 _this.list.setWidth(lw);
42437 this.list.on('mouseover', this.onViewOver, this);
42438 this.list.on('mousemove', this.onViewMove, this);
42439 this.inputEl().on("keyup", this.onKeyUp, this);
42440 this.inputEl().on("keypress", this.onKeyPress, this);
42442 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42444 this.view = new Roo.View(this.list, this.tpl, {
42445 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42448 this.view.on('click', this.onViewClick, this);
42449 this.setValue(this.defaultDialCode);
42452 onTriggerClick : function(e)
42454 Roo.log('trigger click');
42459 if(this.isExpanded()){
42461 this.hasFocus = false;
42463 this.store.load({});
42464 this.hasFocus = true;
42469 isExpanded : function()
42471 return this.list.isVisible();
42474 collapse : function()
42476 if(!this.isExpanded()){
42480 Roo.get(document).un('mousedown', this.collapseIf, this);
42481 Roo.get(document).un('mousewheel', this.collapseIf, this);
42482 this.fireEvent('collapse', this);
42486 expand : function()
42490 if(this.isExpanded() || !this.hasFocus){
42494 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42495 this.list.setWidth(lw);
42498 this.restrictHeight();
42500 Roo.get(document).on('mousedown', this.collapseIf, this);
42501 Roo.get(document).on('mousewheel', this.collapseIf, this);
42503 this.fireEvent('expand', this);
42506 restrictHeight : function()
42508 this.list.alignTo(this.inputEl(), this.listAlign);
42509 this.list.alignTo(this.inputEl(), this.listAlign);
42512 onViewOver : function(e, t)
42514 if(this.inKeyMode){
42517 var item = this.view.findItemFromChild(t);
42520 var index = this.view.indexOf(item);
42521 this.select(index, false);
42526 onViewClick : function(view, doFocus, el, e)
42528 var index = this.view.getSelectedIndexes()[0];
42530 var r = this.store.getAt(index);
42533 this.onSelect(r, index);
42535 if(doFocus !== false && !this.blockFocus){
42536 this.inputEl().focus();
42540 onViewMove : function(e, t)
42542 this.inKeyMode = false;
42545 select : function(index, scrollIntoView)
42547 this.selectedIndex = index;
42548 this.view.select(index);
42549 if(scrollIntoView !== false){
42550 var el = this.view.getNode(index);
42552 this.list.scrollChildIntoView(el, false);
42557 createList : function()
42559 this.list = Roo.get(document.body).createChild({
42561 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42562 style: 'display:none'
42565 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42568 collapseIf : function(e)
42570 var in_combo = e.within(this.el);
42571 var in_list = e.within(this.list);
42572 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42574 if (in_combo || in_list || is_list) {
42580 onSelect : function(record, index)
42582 if(this.fireEvent('beforeselect', this, record, index) !== false){
42584 this.setFlagClass(record.data.iso2);
42585 this.setDialCode(record.data.dialCode);
42586 this.hasFocus = false;
42588 this.fireEvent('select', this, record, index);
42592 flagEl : function()
42594 var flag = this.el.select('div.flag',true).first();
42601 dialCodeHolderEl : function()
42603 var d = this.el.select('input.dial-code-holder',true).first();
42610 setDialCode : function(v)
42612 this.dialCodeHolder.dom.value = '+'+v;
42615 setFlagClass : function(n)
42617 this.flag.dom.className = 'flag '+n;
42620 getValue : function()
42622 var v = this.inputEl().getValue();
42623 if(this.dialCodeHolder) {
42624 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42629 setValue : function(v)
42631 var d = this.getDialCode(v);
42633 //invalid dial code
42634 if(v.length == 0 || !d || d.length == 0) {
42636 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42637 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42643 this.setFlagClass(this.dialCodeMapping[d].iso2);
42644 this.setDialCode(d);
42645 this.inputEl().dom.value = v.replace('+'+d,'');
42646 this.hiddenEl().dom.value = this.getValue();
42651 getDialCode : function(v)
42655 if (v.length == 0) {
42656 return this.dialCodeHolder.dom.value;
42660 if (v.charAt(0) != "+") {
42663 var numericChars = "";
42664 for (var i = 1; i < v.length; i++) {
42665 var c = v.charAt(i);
42668 if (this.dialCodeMapping[numericChars]) {
42669 dialCode = v.substr(1, i);
42671 if (numericChars.length == 4) {
42681 this.setValue(this.defaultDialCode);
42685 hiddenEl : function()
42687 return this.el.select('input.hidden-tel-input',true).first();
42690 // after setting val
42691 onKeyUp : function(e){
42692 this.setValue(this.getValue());
42695 onKeyPress : function(e){
42696 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42703 * @class Roo.bootstrap.MoneyField
42704 * @extends Roo.bootstrap.ComboBox
42705 * Bootstrap MoneyField class
42708 * Create a new MoneyField.
42709 * @param {Object} config Configuration options
42712 Roo.bootstrap.MoneyField = function(config) {
42714 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42718 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42721 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42723 allowDecimals : true,
42725 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42727 decimalSeparator : ".",
42729 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42731 decimalPrecision : 0,
42733 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42735 allowNegative : true,
42737 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42741 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42743 minValue : Number.NEGATIVE_INFINITY,
42745 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42747 maxValue : Number.MAX_VALUE,
42749 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42751 minText : "The minimum value for this field is {0}",
42753 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42755 maxText : "The maximum value for this field is {0}",
42757 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
42758 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42760 nanText : "{0} is not a valid number",
42762 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42766 * @cfg {String} defaults currency of the MoneyField
42767 * value should be in lkey
42769 defaultCurrency : false,
42771 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42773 thousandsDelimiter : false,
42775 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42786 getAutoCreate : function()
42788 var align = this.labelAlign || this.parentLabelAlign();
42800 cls : 'form-control roo-money-amount-input',
42801 autocomplete: 'new-password'
42804 var hiddenInput = {
42808 cls: 'hidden-number-input'
42811 if(this.max_length) {
42812 input.maxlength = this.max_length;
42816 hiddenInput.name = this.name;
42819 if (this.disabled) {
42820 input.disabled = true;
42823 var clg = 12 - this.inputlg;
42824 var cmd = 12 - this.inputmd;
42825 var csm = 12 - this.inputsm;
42826 var cxs = 12 - this.inputxs;
42830 cls : 'row roo-money-field',
42834 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42838 cls: 'roo-select2-container input-group',
42842 cls : 'form-control roo-money-currency-input',
42843 autocomplete: 'new-password',
42845 name : this.currencyName
42849 cls : 'input-group-addon',
42863 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42867 cls: this.hasFeedback ? 'has-feedback' : '',
42878 if (this.fieldLabel.length) {
42881 tooltip: 'This field is required'
42887 cls: 'control-label',
42893 html: this.fieldLabel
42896 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42902 if(this.indicatorpos == 'right') {
42903 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42910 if(align == 'left') {
42918 if(this.labelWidth > 12){
42919 label.style = "width: " + this.labelWidth + 'px';
42921 if(this.labelWidth < 13 && this.labelmd == 0){
42922 this.labelmd = this.labelWidth;
42924 if(this.labellg > 0){
42925 label.cls += ' col-lg-' + this.labellg;
42926 input.cls += ' col-lg-' + (12 - this.labellg);
42928 if(this.labelmd > 0){
42929 label.cls += ' col-md-' + this.labelmd;
42930 container.cls += ' col-md-' + (12 - this.labelmd);
42932 if(this.labelsm > 0){
42933 label.cls += ' col-sm-' + this.labelsm;
42934 container.cls += ' col-sm-' + (12 - this.labelsm);
42936 if(this.labelxs > 0){
42937 label.cls += ' col-xs-' + this.labelxs;
42938 container.cls += ' col-xs-' + (12 - this.labelxs);
42949 var settings = this;
42951 ['xs','sm','md','lg'].map(function(size){
42952 if (settings[size]) {
42953 cfg.cls += ' col-' + size + '-' + settings[size];
42960 initEvents : function()
42962 this.indicator = this.indicatorEl();
42964 this.initCurrencyEvent();
42966 this.initNumberEvent();
42969 initCurrencyEvent : function()
42972 throw "can not find store for combo";
42975 this.store = Roo.factory(this.store, Roo.data);
42976 this.store.parent = this;
42980 this.triggerEl = this.el.select('.input-group-addon', true).first();
42982 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42987 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42988 _this.list.setWidth(lw);
42991 this.list.on('mouseover', this.onViewOver, this);
42992 this.list.on('mousemove', this.onViewMove, this);
42993 this.list.on('scroll', this.onViewScroll, this);
42996 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42999 this.view = new Roo.View(this.list, this.tpl, {
43000 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43003 this.view.on('click', this.onViewClick, this);
43005 this.store.on('beforeload', this.onBeforeLoad, this);
43006 this.store.on('load', this.onLoad, this);
43007 this.store.on('loadexception', this.onLoadException, this);
43009 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43010 "up" : function(e){
43011 this.inKeyMode = true;
43015 "down" : function(e){
43016 if(!this.isExpanded()){
43017 this.onTriggerClick();
43019 this.inKeyMode = true;
43024 "enter" : function(e){
43027 if(this.fireEvent("specialkey", this, e)){
43028 this.onViewClick(false);
43034 "esc" : function(e){
43038 "tab" : function(e){
43041 if(this.fireEvent("specialkey", this, e)){
43042 this.onViewClick(false);
43050 doRelay : function(foo, bar, hname){
43051 if(hname == 'down' || this.scope.isExpanded()){
43052 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43060 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43064 initNumberEvent : function(e)
43066 this.inputEl().on("keydown" , this.fireKey, this);
43067 this.inputEl().on("focus", this.onFocus, this);
43068 this.inputEl().on("blur", this.onBlur, this);
43070 this.inputEl().relayEvent('keyup', this);
43072 if(this.indicator){
43073 this.indicator.addClass('invisible');
43076 this.originalValue = this.getValue();
43078 if(this.validationEvent == 'keyup'){
43079 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43080 this.inputEl().on('keyup', this.filterValidation, this);
43082 else if(this.validationEvent !== false){
43083 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43086 if(this.selectOnFocus){
43087 this.on("focus", this.preFocus, this);
43090 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43091 this.inputEl().on("keypress", this.filterKeys, this);
43093 this.inputEl().relayEvent('keypress', this);
43096 var allowed = "0123456789";
43098 if(this.allowDecimals){
43099 allowed += this.decimalSeparator;
43102 if(this.allowNegative){
43106 if(this.thousandsDelimiter) {
43110 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43112 var keyPress = function(e){
43114 var k = e.getKey();
43116 var c = e.getCharCode();
43119 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43120 allowed.indexOf(String.fromCharCode(c)) === -1
43126 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43130 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43135 this.inputEl().on("keypress", keyPress, this);
43139 onTriggerClick : function(e)
43146 this.loadNext = false;
43148 if(this.isExpanded()){
43153 this.hasFocus = true;
43155 if(this.triggerAction == 'all') {
43156 this.doQuery(this.allQuery, true);
43160 this.doQuery(this.getRawValue());
43163 getCurrency : function()
43165 var v = this.currencyEl().getValue();
43170 restrictHeight : function()
43172 this.list.alignTo(this.currencyEl(), this.listAlign);
43173 this.list.alignTo(this.currencyEl(), this.listAlign);
43176 onViewClick : function(view, doFocus, el, e)
43178 var index = this.view.getSelectedIndexes()[0];
43180 var r = this.store.getAt(index);
43183 this.onSelect(r, index);
43187 onSelect : function(record, index){
43189 if(this.fireEvent('beforeselect', this, record, index) !== false){
43191 this.setFromCurrencyData(index > -1 ? record.data : false);
43195 this.fireEvent('select', this, record, index);
43199 setFromCurrencyData : function(o)
43203 this.lastCurrency = o;
43205 if (this.currencyField) {
43206 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43208 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43211 this.lastSelectionText = currency;
43213 //setting default currency
43214 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43215 this.setCurrency(this.defaultCurrency);
43219 this.setCurrency(currency);
43222 setFromData : function(o)
43226 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43228 this.setFromCurrencyData(c);
43233 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43235 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43238 this.setValue(value);
43242 setCurrency : function(v)
43244 this.currencyValue = v;
43247 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43252 setValue : function(v)
43254 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43260 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43262 this.inputEl().dom.value = (v == '') ? '' :
43263 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43265 if(!this.allowZero && v === '0') {
43266 this.hiddenEl().dom.value = '';
43267 this.inputEl().dom.value = '';
43274 getRawValue : function()
43276 var v = this.inputEl().getValue();
43281 getValue : function()
43283 return this.fixPrecision(this.parseValue(this.getRawValue()));
43286 parseValue : function(value)
43288 if(this.thousandsDelimiter) {
43290 r = new RegExp(",", "g");
43291 value = value.replace(r, "");
43294 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43295 return isNaN(value) ? '' : value;
43299 fixPrecision : function(value)
43301 if(this.thousandsDelimiter) {
43303 r = new RegExp(",", "g");
43304 value = value.replace(r, "");
43307 var nan = isNaN(value);
43309 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43310 return nan ? '' : value;
43312 return parseFloat(value).toFixed(this.decimalPrecision);
43315 decimalPrecisionFcn : function(v)
43317 return Math.floor(v);
43320 validateValue : function(value)
43322 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43326 var num = this.parseValue(value);
43329 this.markInvalid(String.format(this.nanText, value));
43333 if(num < this.minValue){
43334 this.markInvalid(String.format(this.minText, this.minValue));
43338 if(num > this.maxValue){
43339 this.markInvalid(String.format(this.maxText, this.maxValue));
43346 validate : function()
43348 if(this.disabled || this.allowBlank){
43353 var currency = this.getCurrency();
43355 if(this.validateValue(this.getRawValue()) && currency.length){
43360 this.markInvalid();
43364 getName: function()
43369 beforeBlur : function()
43375 var v = this.parseValue(this.getRawValue());
43382 onBlur : function()
43386 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43387 //this.el.removeClass(this.focusClass);
43390 this.hasFocus = false;
43392 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43396 var v = this.getValue();
43398 if(String(v) !== String(this.startValue)){
43399 this.fireEvent('change', this, v, this.startValue);
43402 this.fireEvent("blur", this);
43405 inputEl : function()
43407 return this.el.select('.roo-money-amount-input', true).first();
43410 currencyEl : function()
43412 return this.el.select('.roo-money-currency-input', true).first();
43415 hiddenEl : function()
43417 return this.el.select('input.hidden-number-input',true).first();
43421 * @class Roo.bootstrap.BezierSignature
43422 * @extends Roo.bootstrap.Component
43423 * Bootstrap BezierSignature class
43424 * This script refer to:
43425 * Title: Signature Pad
43427 * Availability: https://github.com/szimek/signature_pad
43430 * Create a new BezierSignature
43431 * @param {Object} config The config object
43434 Roo.bootstrap.BezierSignature = function(config){
43435 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43441 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43448 mouse_btn_down: true,
43451 * @cfg {int} canvas height
43453 canvas_height: '200px',
43456 * @cfg {float|function} Radius of a single dot.
43461 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43466 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43471 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43476 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43481 * @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.
43483 bg_color: 'rgba(0, 0, 0, 0)',
43486 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43488 dot_color: 'black',
43491 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43493 velocity_filter_weight: 0.7,
43496 * @cfg {function} Callback when stroke begin.
43501 * @cfg {function} Callback when stroke end.
43505 getAutoCreate : function()
43507 var cls = 'roo-signature column';
43510 cls += ' ' + this.cls;
43520 for(var i = 0; i < col_sizes.length; i++) {
43521 if(this[col_sizes[i]]) {
43522 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43532 cls: 'roo-signature-body',
43536 cls: 'roo-signature-body-canvas',
43537 height: this.canvas_height,
43538 width: this.canvas_width
43545 style: 'display: none'
43553 initEvents: function()
43555 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43557 var canvas = this.canvasEl();
43559 // mouse && touch event swapping...
43560 canvas.dom.style.touchAction = 'none';
43561 canvas.dom.style.msTouchAction = 'none';
43563 this.mouse_btn_down = false;
43564 canvas.on('mousedown', this._handleMouseDown, this);
43565 canvas.on('mousemove', this._handleMouseMove, this);
43566 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43568 if (window.PointerEvent) {
43569 canvas.on('pointerdown', this._handleMouseDown, this);
43570 canvas.on('pointermove', this._handleMouseMove, this);
43571 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43574 if ('ontouchstart' in window) {
43575 canvas.on('touchstart', this._handleTouchStart, this);
43576 canvas.on('touchmove', this._handleTouchMove, this);
43577 canvas.on('touchend', this._handleTouchEnd, this);
43580 Roo.EventManager.onWindowResize(this.resize, this, true);
43582 // file input event
43583 this.fileEl().on('change', this.uploadImage, this);
43590 resize: function(){
43592 var canvas = this.canvasEl().dom;
43593 var ctx = this.canvasElCtx();
43594 var img_data = false;
43596 if(canvas.width > 0) {
43597 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43599 // setting canvas width will clean img data
43602 var style = window.getComputedStyle ?
43603 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43605 var padding_left = parseInt(style.paddingLeft) || 0;
43606 var padding_right = parseInt(style.paddingRight) || 0;
43608 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43611 ctx.putImageData(img_data, 0, 0);
43615 _handleMouseDown: function(e)
43617 if (e.browserEvent.which === 1) {
43618 this.mouse_btn_down = true;
43619 this.strokeBegin(e);
43623 _handleMouseMove: function (e)
43625 if (this.mouse_btn_down) {
43626 this.strokeMoveUpdate(e);
43630 _handleMouseUp: function (e)
43632 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43633 this.mouse_btn_down = false;
43638 _handleTouchStart: function (e) {
43640 e.preventDefault();
43641 if (e.browserEvent.targetTouches.length === 1) {
43642 // var touch = e.browserEvent.changedTouches[0];
43643 // this.strokeBegin(touch);
43645 this.strokeBegin(e); // assume e catching the correct xy...
43649 _handleTouchMove: function (e) {
43650 e.preventDefault();
43651 // var touch = event.targetTouches[0];
43652 // _this._strokeMoveUpdate(touch);
43653 this.strokeMoveUpdate(e);
43656 _handleTouchEnd: function (e) {
43657 var wasCanvasTouched = e.target === this.canvasEl().dom;
43658 if (wasCanvasTouched) {
43659 e.preventDefault();
43660 // var touch = event.changedTouches[0];
43661 // _this._strokeEnd(touch);
43666 reset: function () {
43667 this._lastPoints = [];
43668 this._lastVelocity = 0;
43669 this._lastWidth = (this.min_width + this.max_width) / 2;
43670 this.canvasElCtx().fillStyle = this.dot_color;
43673 strokeMoveUpdate: function(e)
43675 this.strokeUpdate(e);
43677 if (this.throttle) {
43678 this.throttleStroke(this.strokeUpdate, this.throttle);
43681 this.strokeUpdate(e);
43685 strokeBegin: function(e)
43687 var newPointGroup = {
43688 color: this.dot_color,
43692 if (typeof this.onBegin === 'function') {
43696 this.curve_data.push(newPointGroup);
43698 this.strokeUpdate(e);
43701 strokeUpdate: function(e)
43703 var rect = this.canvasEl().dom.getBoundingClientRect();
43704 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43705 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43706 var lastPoints = lastPointGroup.points;
43707 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43708 var isLastPointTooClose = lastPoint
43709 ? point.distanceTo(lastPoint) <= this.min_distance
43711 var color = lastPointGroup.color;
43712 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43713 var curve = this.addPoint(point);
43715 this.drawDot({color: color, point: point});
43718 this.drawCurve({color: color, curve: curve});
43728 strokeEnd: function(e)
43730 this.strokeUpdate(e);
43731 if (typeof this.onEnd === 'function') {
43736 addPoint: function (point) {
43737 var _lastPoints = this._lastPoints;
43738 _lastPoints.push(point);
43739 if (_lastPoints.length > 2) {
43740 if (_lastPoints.length === 3) {
43741 _lastPoints.unshift(_lastPoints[0]);
43743 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43744 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43745 _lastPoints.shift();
43751 calculateCurveWidths: function (startPoint, endPoint) {
43752 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43753 (1 - this.velocity_filter_weight) * this._lastVelocity;
43755 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43758 start: this._lastWidth
43761 this._lastVelocity = velocity;
43762 this._lastWidth = newWidth;
43766 drawDot: function (_a) {
43767 var color = _a.color, point = _a.point;
43768 var ctx = this.canvasElCtx();
43769 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43771 this.drawCurveSegment(point.x, point.y, width);
43773 ctx.fillStyle = color;
43777 drawCurve: function (_a) {
43778 var color = _a.color, curve = _a.curve;
43779 var ctx = this.canvasElCtx();
43780 var widthDelta = curve.endWidth - curve.startWidth;
43781 var drawSteps = Math.floor(curve.length()) * 2;
43783 ctx.fillStyle = color;
43784 for (var i = 0; i < drawSteps; i += 1) {
43785 var t = i / drawSteps;
43791 var x = uuu * curve.startPoint.x;
43792 x += 3 * uu * t * curve.control1.x;
43793 x += 3 * u * tt * curve.control2.x;
43794 x += ttt * curve.endPoint.x;
43795 var y = uuu * curve.startPoint.y;
43796 y += 3 * uu * t * curve.control1.y;
43797 y += 3 * u * tt * curve.control2.y;
43798 y += ttt * curve.endPoint.y;
43799 var width = curve.startWidth + ttt * widthDelta;
43800 this.drawCurveSegment(x, y, width);
43806 drawCurveSegment: function (x, y, width) {
43807 var ctx = this.canvasElCtx();
43809 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43810 this.is_empty = false;
43815 var ctx = this.canvasElCtx();
43816 var canvas = this.canvasEl().dom;
43817 ctx.fillStyle = this.bg_color;
43818 ctx.clearRect(0, 0, canvas.width, canvas.height);
43819 ctx.fillRect(0, 0, canvas.width, canvas.height);
43820 this.curve_data = [];
43822 this.is_empty = true;
43827 return this.el.select('input',true).first();
43830 canvasEl: function()
43832 return this.el.select('canvas',true).first();
43835 canvasElCtx: function()
43837 return this.el.select('canvas',true).first().dom.getContext('2d');
43840 getImage: function(type)
43842 if(this.is_empty) {
43847 return this.canvasEl().dom.toDataURL('image/'+type, 1);
43850 drawFromImage: function(img_src)
43852 var img = new Image();
43854 img.onload = function(){
43855 this.canvasElCtx().drawImage(img, 0, 0);
43860 this.is_empty = false;
43863 selectImage: function()
43865 this.fileEl().dom.click();
43868 uploadImage: function(e)
43870 var reader = new FileReader();
43872 reader.onload = function(e){
43873 var img = new Image();
43874 img.onload = function(){
43876 this.canvasElCtx().drawImage(img, 0, 0);
43878 img.src = e.target.result;
43881 reader.readAsDataURL(e.target.files[0]);
43884 // Bezier Point Constructor
43885 Point: (function () {
43886 function Point(x, y, time) {
43889 this.time = time || Date.now();
43891 Point.prototype.distanceTo = function (start) {
43892 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43894 Point.prototype.equals = function (other) {
43895 return this.x === other.x && this.y === other.y && this.time === other.time;
43897 Point.prototype.velocityFrom = function (start) {
43898 return this.time !== start.time
43899 ? this.distanceTo(start) / (this.time - start.time)
43906 // Bezier Constructor
43907 Bezier: (function () {
43908 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43909 this.startPoint = startPoint;
43910 this.control2 = control2;
43911 this.control1 = control1;
43912 this.endPoint = endPoint;
43913 this.startWidth = startWidth;
43914 this.endWidth = endWidth;
43916 Bezier.fromPoints = function (points, widths, scope) {
43917 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43918 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43919 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43921 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43922 var dx1 = s1.x - s2.x;
43923 var dy1 = s1.y - s2.y;
43924 var dx2 = s2.x - s3.x;
43925 var dy2 = s2.y - s3.y;
43926 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43927 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43928 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43929 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43930 var dxm = m1.x - m2.x;
43931 var dym = m1.y - m2.y;
43932 var k = l2 / (l1 + l2);
43933 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43934 var tx = s2.x - cm.x;
43935 var ty = s2.y - cm.y;
43937 c1: new scope.Point(m1.x + tx, m1.y + ty),
43938 c2: new scope.Point(m2.x + tx, m2.y + ty)
43941 Bezier.prototype.length = function () {
43946 for (var i = 0; i <= steps; i += 1) {
43948 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43949 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43951 var xdiff = cx - px;
43952 var ydiff = cy - py;
43953 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43960 Bezier.prototype.point = function (t, start, c1, c2, end) {
43961 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43962 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43963 + (3.0 * c2 * (1.0 - t) * t * t)
43964 + (end * t * t * t);
43969 throttleStroke: function(fn, wait) {
43970 if (wait === void 0) { wait = 250; }
43972 var timeout = null;
43976 var later = function () {
43977 previous = Date.now();
43979 result = fn.apply(storedContext, storedArgs);
43981 storedContext = null;
43985 return function wrapper() {
43987 for (var _i = 0; _i < arguments.length; _i++) {
43988 args[_i] = arguments[_i];
43990 var now = Date.now();
43991 var remaining = wait - (now - previous);
43992 storedContext = this;
43994 if (remaining <= 0 || remaining > wait) {
43996 clearTimeout(timeout);
44000 result = fn.apply(storedContext, storedArgs);
44002 storedContext = null;
44006 else if (!timeout) {
44007 timeout = window.setTimeout(later, remaining);