2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = (
9 Roo.each(document.styleSheets, function(s) {
10 if (s.href.match(/css-bootstrap4/)) {
18 * base class for bootstrap elements.
22 Roo.bootstrap = Roo.bootstrap || {};
24 * @class Roo.bootstrap.Component
25 * @extends Roo.Component
26 * Bootstrap Component base class
27 * @cfg {String} cls css class
28 * @cfg {String} style any extra css
29 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
31 * @cfg {string} dataId cutomer id
32 * @cfg {string} name Specifies name attribute
33 * @cfg {string} tooltip Text for the tooltip
34 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
35 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
38 * Do not use directly - it does not do anything..
39 * @param {Object} config The config object
44 Roo.bootstrap.Component = function(config){
45 Roo.bootstrap.Component.superclass.constructor.call(this, config);
49 * @event childrenrendered
50 * Fires when the children have been rendered..
51 * @param {Roo.bootstrap.Component} this
53 "childrenrendered" : true
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
65 allowDomMove : false, // to stop relocations in parent onRender...
75 * Initialize Events for the element
77 initEvents : function() { },
83 can_build_overlaid : true,
85 container_method : false,
92 // returns the parent component..
93 return Roo.ComponentMgr.get(this.parentId)
99 onRender : function(ct, position)
101 // Roo.log("Call onRender: " + this.xtype);
103 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
106 if (this.el.attr('xtype')) {
107 this.el.attr('xtypex', this.el.attr('xtype'));
108 this.el.dom.removeAttribute('xtype');
118 var cfg = Roo.apply({}, this.getAutoCreate());
120 cfg.id = this.id || Roo.id();
122 // fill in the extra attributes
123 if (this.xattr && typeof(this.xattr) =='object') {
124 for (var i in this.xattr) {
125 cfg[i] = this.xattr[i];
130 cfg.dataId = this.dataId;
134 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
137 if (this.style) { // fixme needs to support more complex style data.
138 cfg.style = this.style;
142 cfg.name = this.name;
145 this.el = ct.createChild(cfg, position);
148 this.tooltipEl().attr('tooltip', this.tooltip);
151 if(this.tabIndex !== undefined){
152 this.el.dom.setAttribute('tabIndex', this.tabIndex);
159 * Fetch the element to add children to
160 * @return {Roo.Element} defaults to this.el
162 getChildContainer : function()
167 * Fetch the element to display the tooltip on.
168 * @return {Roo.Element} defaults to this.el
170 tooltipEl : function()
175 addxtype : function(tree,cntr)
179 cn = Roo.factory(tree);
180 //Roo.log(['addxtype', cn]);
182 cn.parentType = this.xtype; //??
183 cn.parentId = this.id;
185 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186 if (typeof(cn.container_method) == 'string') {
187 cntr = cn.container_method;
191 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
193 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
195 var build_from_html = Roo.XComponent.build_from_html;
197 var is_body = (tree.xtype == 'Body') ;
199 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
201 var self_cntr_el = Roo.get(this[cntr](false));
203 // do not try and build conditional elements
204 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
208 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210 return this.addxtypeChild(tree,cntr, is_body);
213 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
216 return this.addxtypeChild(Roo.apply({}, tree),cntr);
219 Roo.log('skipping render');
225 if (!build_from_html) {
229 // this i think handles overlaying multiple children of the same type
230 // with the sam eelement.. - which might be buggy..
232 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
238 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
242 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
249 addxtypeChild : function (tree, cntr, is_body)
251 Roo.debug && Roo.log('addxtypeChild:' + cntr);
253 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
256 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257 (typeof(tree['flexy:foreach']) != 'undefined');
261 skip_children = false;
262 // render the element if it's not BODY.
265 // if parent was disabled, then do not try and create the children..
266 if(!this[cntr](true)){
271 cn = Roo.factory(tree);
273 cn.parentType = this.xtype; //??
274 cn.parentId = this.id;
276 var build_from_html = Roo.XComponent.build_from_html;
279 // does the container contain child eleemnts with 'xtype' attributes.
280 // that match this xtype..
281 // note - when we render we create these as well..
282 // so we should check to see if body has xtype set.
283 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
285 var self_cntr_el = Roo.get(this[cntr](false));
286 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
288 //Roo.log(Roo.XComponent.build_from_html);
289 //Roo.log("got echild:");
292 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293 // and are not displayed -this causes this to use up the wrong element when matching.
294 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
297 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
304 //echild.dom.removeAttribute('xtype');
306 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307 Roo.debug && Roo.log(self_cntr_el);
308 Roo.debug && Roo.log(echild);
309 Roo.debug && Roo.log(cn);
315 // if object has flexy:if - then it may or may not be rendered.
316 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
317 // skip a flexy if element.
318 Roo.debug && Roo.log('skipping render');
319 Roo.debug && Roo.log(tree);
321 Roo.debug && Roo.log('skipping all children');
322 skip_children = true;
327 // actually if flexy:foreach is found, we really want to create
328 // multiple copies here...
330 //Roo.log(this[cntr]());
331 // some elements do not have render methods.. like the layouts...
333 if(this[cntr](true) === false){
338 cn.render && cn.render(this[cntr](true));
341 // then add the element..
348 if (typeof (tree.menu) != 'undefined') {
349 tree.menu.parentType = cn.xtype;
350 tree.menu.triggerEl = cn.el;
351 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
355 if (!tree.items || !tree.items.length) {
357 //Roo.log(["no children", this]);
362 var items = tree.items;
365 //Roo.log(items.length);
367 if (!skip_children) {
368 for(var i =0;i < items.length;i++) {
369 // Roo.log(['add child', items[i]]);
370 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
376 //Roo.log("fire childrenrendered");
378 cn.fireEvent('childrenrendered', this);
384 * Set the element that will be used to show or hide
386 setVisibilityEl : function(el)
388 this.visibilityEl = el;
392 * Get the element that will be used to show or hide
394 getVisibilityEl : function()
396 if (typeof(this.visibilityEl) == 'object') {
397 return this.visibilityEl;
400 if (typeof(this.visibilityEl) == 'string') {
401 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
408 * Show a component - removes 'hidden' class
412 if(!this.getVisibilityEl()){
416 this.getVisibilityEl().removeClass(['hidden','d-none']);
418 this.fireEvent('show', this);
423 * Hide a component - adds 'hidden' class
427 if(!this.getVisibilityEl()){
431 this.getVisibilityEl().addClass(['hidden','d-none']);
433 this.fireEvent('hide', this);
446 * @class Roo.bootstrap.Body
447 * @extends Roo.bootstrap.Component
448 * Bootstrap Body class
452 * @param {Object} config The config object
455 Roo.bootstrap.Body = function(config){
457 config = config || {};
459 Roo.bootstrap.Body.superclass.constructor.call(this, config);
460 this.el = Roo.get(config.el ? config.el : document.body );
461 if (this.cls && this.cls.length) {
462 Roo.get(document.body).addClass(this.cls);
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
468 is_body : true,// just to make sure it's constructed?
473 onRender : function(ct, position)
475 /* Roo.log("Roo.bootstrap.Body - onRender");
476 if (this.cls && this.cls.length) {
477 Roo.get(document.body).addClass(this.cls);
496 * @class Roo.bootstrap.ButtonGroup
497 * @extends Roo.bootstrap.Component
498 * Bootstrap ButtonGroup class
499 * @cfg {String} size lg | sm | xs (default empty normal)
500 * @cfg {String} align vertical | justified (default none)
501 * @cfg {String} direction up | down (default down)
502 * @cfg {Boolean} toolbar false | true
503 * @cfg {Boolean} btn true | false
508 * @param {Object} config The config object
511 Roo.bootstrap.ButtonGroup = function(config){
512 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
523 getAutoCreate : function(){
529 cfg.html = this.html || cfg.html;
540 if (['vertical','justified'].indexOf(this.align)!==-1) {
541 cfg.cls = 'btn-group-' + this.align;
543 if (this.align == 'justified') {
544 console.log(this.items);
548 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549 cfg.cls += ' btn-group-' + this.size;
552 if (this.direction == 'up') {
553 cfg.cls += ' dropup' ;
559 * Add a button to the group (similar to NavItem API.)
561 addItem : function(cfg)
563 var cn = new Roo.bootstrap.Button(cfg);
565 cn.parentId = this.id;
566 cn.onRender(this.el, null);
580 * @class Roo.bootstrap.Button
581 * @extends Roo.bootstrap.Component
582 * Bootstrap Button class
583 * @cfg {String} html The button content
584 * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585 * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587 * @cfg {String} size ( lg | sm | xs)
588 * @cfg {String} tag ( a | input | submit)
589 * @cfg {String} href empty or href
590 * @cfg {Boolean} disabled default false;
591 * @cfg {Boolean} isClose default false;
592 * @cfg {String} glyphicon depricated - use fa
593 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
594 * @cfg {String} badge text for badge
595 * @cfg {String} theme (default|glow)
596 * @cfg {Boolean} inverse dark themed version
597 * @cfg {Boolean} toggle is it a slidy toggle button
598 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
599 * @cfg {String} ontext text for on slidy toggle state
600 * @cfg {String} offtext text for off slidy toggle state
601 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
602 * @cfg {Boolean} removeClass remove the standard class..
603 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
606 * Create a new button
607 * @param {Object} config The config object
611 Roo.bootstrap.Button = function(config){
612 Roo.bootstrap.Button.superclass.constructor.call(this, config);
613 this.weightClass = ["btn-default btn-outline-secondary",
625 * When a butotn is pressed
626 * @param {Roo.bootstrap.Button} btn
627 * @param {Roo.EventObject} e
632 * After the button has been toggles
633 * @param {Roo.bootstrap.Button} btn
634 * @param {Roo.EventObject} e
635 * @param {boolean} pressed (also available as button.pressed)
641 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
662 preventDefault: true,
670 getAutoCreate : function(){
678 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
679 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
684 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
686 if (this.toggle == true) {
689 cls: 'slider-frame roo-button',
694 'data-off-text':'OFF',
695 cls: 'slider-button',
701 if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
702 cfg.cls += ' '+this.weight;
711 cfg["aria-hidden"] = true;
713 cfg.html = "×";
719 if (this.theme==='default') {
720 cfg.cls = 'btn roo-button';
722 //if (this.parentType != 'Navbar') {
723 this.weight = this.weight.length ? this.weight : 'default';
725 if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
727 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
728 var weight = this.weight == 'default' ? 'secondary' : this.weight;
729 cfg.cls += ' btn-' + outline + weight;
730 if (this.weight == 'default') {
732 cfg.cls += ' btn-' + this.weight;
735 } else if (this.theme==='glow') {
738 cfg.cls = 'btn-glow roo-button';
740 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
742 cfg.cls += ' ' + this.weight;
748 this.cls += ' inverse';
752 if (this.active || this.pressed === true) {
753 cfg.cls += ' active';
757 cfg.disabled = 'disabled';
761 Roo.log('changing to ul' );
763 this.glyphicon = 'caret';
764 if (Roo.bootstrap.version == 4) {
765 this.fa = 'caret-down';
770 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
772 //gsRoo.log(this.parentType);
773 if (this.parentType === 'Navbar' && !this.parent().bar) {
774 Roo.log('changing to li?');
783 href : this.href || '#'
786 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
787 cfg.cls += ' dropdown';
794 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
796 if (this.glyphicon) {
797 cfg.html = ' ' + cfg.html;
802 cls: 'glyphicon glyphicon-' + this.glyphicon
807 cfg.html = ' ' + cfg.html;
812 cls: 'fa fas fa-' + this.fa
822 // cfg.cls='btn roo-button';
826 var value = cfg.html;
831 cls: 'glyphicon glyphicon-' + this.glyphicon,
838 cls: 'fa fas fa-' + this.fa,
843 var bw = this.badge_weight.length ? this.badge_weight :
844 (this.weight.length ? this.weight : 'secondary');
845 bw = bw == 'default' ? 'secondary' : bw;
851 cls: 'badge badge-' + bw,
860 cfg.cls += ' dropdown';
861 cfg.html = typeof(cfg.html) != 'undefined' ?
862 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
865 if (cfg.tag !== 'a' && this.href !== '') {
866 throw "Tag must be a to set href.";
867 } else if (this.href.length > 0) {
868 cfg.href = this.href;
871 if(this.removeClass){
876 cfg.target = this.target;
881 initEvents: function() {
882 // Roo.log('init events?');
883 // Roo.log(this.el.dom);
886 if (typeof (this.menu) != 'undefined') {
887 this.menu.parentType = this.xtype;
888 this.menu.triggerEl = this.el;
889 this.addxtype(Roo.apply({}, this.menu));
893 if (this.el.hasClass('roo-button')) {
894 this.el.on('click', this.onClick, this);
896 this.el.select('.roo-button').on('click', this.onClick, this);
899 if(this.removeClass){
900 this.el.on('click', this.onClick, this);
903 this.el.enableDisplayMode();
906 onClick : function(e)
912 Roo.log('button on click ');
913 if(this.preventDefault){
917 if (this.pressed === true || this.pressed === false) {
918 this.toggleActive(e);
922 this.fireEvent('click', this, e);
926 * Enables this button
930 this.disabled = false;
931 this.el.removeClass('disabled');
935 * Disable this button
939 this.disabled = true;
940 this.el.addClass('disabled');
943 * sets the active state on/off,
944 * @param {Boolean} state (optional) Force a particular state
946 setActive : function(v) {
948 this.el[v ? 'addClass' : 'removeClass']('active');
952 * toggles the current active state
954 toggleActive : function(e)
956 this.setActive(!this.pressed);
957 this.fireEvent('toggle', this, e, !this.pressed);
960 * get the current active state
961 * @return {boolean} true if it's active
963 isActive : function()
965 return this.el.hasClass('active');
968 * set the text of the first selected button
970 setText : function(str)
972 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
975 * get the text of the first selected button
979 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
982 setWeight : function(str)
984 this.el.removeClass(this.weightClass);
986 var outline = this.outline ? 'outline-' : '';
987 if (str == 'default') {
988 this.el.addClass('btn-default btn-outline-secondary');
991 this.el.addClass('btn-' + outline + str);
1005 * @class Roo.bootstrap.Column
1006 * @extends Roo.bootstrap.Component
1007 * Bootstrap Column class
1008 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1009 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1010 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1011 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1012 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1013 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1014 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1015 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1018 * @cfg {Boolean} hidden (true|false) hide the element
1019 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1020 * @cfg {String} fa (ban|check|...) font awesome icon
1021 * @cfg {Number} fasize (1|2|....) font awsome size
1023 * @cfg {String} icon (info-sign|check|...) glyphicon name
1025 * @cfg {String} html content of column.
1028 * Create a new Column
1029 * @param {Object} config The config object
1032 Roo.bootstrap.Column = function(config){
1033 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1036 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1054 getAutoCreate : function(){
1055 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1063 ['xs','sm','md','lg'].map(function(size){
1064 //Roo.log( size + ':' + settings[size]);
1066 if (settings[size+'off'] !== false) {
1067 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1070 if (settings[size] === false) {
1074 if (!settings[size]) { // 0 = hidden
1075 cfg.cls += ' hidden-' + size;
1078 cfg.cls += ' col-' + size + '-' + settings[size];
1083 cfg.cls += ' hidden';
1086 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1087 cfg.cls +=' alert alert-' + this.alert;
1091 if (this.html.length) {
1092 cfg.html = this.html;
1096 if (this.fasize > 1) {
1097 fasize = ' fa-' + this.fasize + 'x';
1099 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1104 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1123 * @class Roo.bootstrap.Container
1124 * @extends Roo.bootstrap.Component
1125 * Bootstrap Container class
1126 * @cfg {Boolean} jumbotron is it a jumbotron element
1127 * @cfg {String} html content of element
1128 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1129 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1130 * @cfg {String} header content of header (for panel)
1131 * @cfg {String} footer content of footer (for panel)
1132 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1133 * @cfg {String} tag (header|aside|section) type of HTML tag.
1134 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1135 * @cfg {String} fa font awesome icon
1136 * @cfg {String} icon (info-sign|check|...) glyphicon name
1137 * @cfg {Boolean} hidden (true|false) hide the element
1138 * @cfg {Boolean} expandable (true|false) default false
1139 * @cfg {Boolean} expanded (true|false) default true
1140 * @cfg {String} rheader contet on the right of header
1141 * @cfg {Boolean} clickable (true|false) default false
1145 * Create a new Container
1146 * @param {Object} config The config object
1149 Roo.bootstrap.Container = function(config){
1150 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1156 * After the panel has been expand
1158 * @param {Roo.bootstrap.Container} this
1163 * After the panel has been collapsed
1165 * @param {Roo.bootstrap.Container} this
1170 * When a element is chick
1171 * @param {Roo.bootstrap.Container} this
1172 * @param {Roo.EventObject} e
1178 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1196 getChildContainer : function() {
1202 if (this.panel.length) {
1203 return this.el.select('.panel-body',true).first();
1210 getAutoCreate : function(){
1213 tag : this.tag || 'div',
1217 if (this.jumbotron) {
1218 cfg.cls = 'jumbotron';
1223 // - this is applied by the parent..
1225 // cfg.cls = this.cls + '';
1228 if (this.sticky.length) {
1230 var bd = Roo.get(document.body);
1231 if (!bd.hasClass('bootstrap-sticky')) {
1232 bd.addClass('bootstrap-sticky');
1233 Roo.select('html',true).setStyle('height', '100%');
1236 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1240 if (this.well.length) {
1241 switch (this.well) {
1244 cfg.cls +=' well well-' +this.well;
1253 cfg.cls += ' hidden';
1257 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1258 cfg.cls +=' alert alert-' + this.alert;
1263 if (this.panel.length) {
1264 cfg.cls += ' panel panel-' + this.panel;
1266 if (this.header.length) {
1270 if(this.expandable){
1272 cfg.cls = cfg.cls + ' expandable';
1276 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1284 cls : 'panel-title',
1285 html : (this.expandable ? ' ' : '') + this.header
1289 cls: 'panel-header-right',
1295 cls : 'panel-heading',
1296 style : this.expandable ? 'cursor: pointer' : '',
1304 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1309 if (this.footer.length) {
1311 cls : 'panel-footer',
1320 body.html = this.html || cfg.html;
1321 // prefix with the icons..
1323 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1326 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1331 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1332 cfg.cls = 'container';
1338 initEvents: function()
1340 if(this.expandable){
1341 var headerEl = this.headerEl();
1344 headerEl.on('click', this.onToggleClick, this);
1349 this.el.on('click', this.onClick, this);
1354 onToggleClick : function()
1356 var headerEl = this.headerEl();
1372 if(this.fireEvent('expand', this)) {
1374 this.expanded = true;
1376 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1378 this.el.select('.panel-body',true).first().removeClass('hide');
1380 var toggleEl = this.toggleEl();
1386 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1391 collapse : function()
1393 if(this.fireEvent('collapse', this)) {
1395 this.expanded = false;
1397 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1398 this.el.select('.panel-body',true).first().addClass('hide');
1400 var toggleEl = this.toggleEl();
1406 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1410 toggleEl : function()
1412 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1416 return this.el.select('.panel-heading .fa',true).first();
1419 headerEl : function()
1421 if(!this.el || !this.panel.length || !this.header.length){
1425 return this.el.select('.panel-heading',true).first()
1430 if(!this.el || !this.panel.length){
1434 return this.el.select('.panel-body',true).first()
1437 titleEl : function()
1439 if(!this.el || !this.panel.length || !this.header.length){
1443 return this.el.select('.panel-title',true).first();
1446 setTitle : function(v)
1448 var titleEl = this.titleEl();
1454 titleEl.dom.innerHTML = v;
1457 getTitle : function()
1460 var titleEl = this.titleEl();
1466 return titleEl.dom.innerHTML;
1469 setRightTitle : function(v)
1471 var t = this.el.select('.panel-header-right',true).first();
1477 t.dom.innerHTML = v;
1480 onClick : function(e)
1484 this.fireEvent('click', this, e);
1497 * @class Roo.bootstrap.Img
1498 * @extends Roo.bootstrap.Component
1499 * Bootstrap Img class
1500 * @cfg {Boolean} imgResponsive false | true
1501 * @cfg {String} border rounded | circle | thumbnail
1502 * @cfg {String} src image source
1503 * @cfg {String} alt image alternative text
1504 * @cfg {String} href a tag href
1505 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1506 * @cfg {String} xsUrl xs image source
1507 * @cfg {String} smUrl sm image source
1508 * @cfg {String} mdUrl md image source
1509 * @cfg {String} lgUrl lg image source
1512 * Create a new Input
1513 * @param {Object} config The config object
1516 Roo.bootstrap.Img = function(config){
1517 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1523 * The img click event for the img.
1524 * @param {Roo.EventObject} e
1530 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1532 imgResponsive: true,
1542 getAutoCreate : function()
1544 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1545 return this.createSingleImg();
1550 cls: 'roo-image-responsive-group',
1555 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1557 if(!_this[size + 'Url']){
1563 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1564 html: _this.html || cfg.html,
1565 src: _this[size + 'Url']
1568 img.cls += ' roo-image-responsive-' + size;
1570 var s = ['xs', 'sm', 'md', 'lg'];
1572 s.splice(s.indexOf(size), 1);
1574 Roo.each(s, function(ss){
1575 img.cls += ' hidden-' + ss;
1578 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1579 cfg.cls += ' img-' + _this.border;
1583 cfg.alt = _this.alt;
1596 a.target = _this.target;
1600 cfg.cn.push((_this.href) ? a : img);
1607 createSingleImg : function()
1611 cls: (this.imgResponsive) ? 'img-responsive' : '',
1613 src : 'about:blank' // just incase src get's set to undefined?!?
1616 cfg.html = this.html || cfg.html;
1618 cfg.src = this.src || cfg.src;
1620 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1621 cfg.cls += ' img-' + this.border;
1638 a.target = this.target;
1643 return (this.href) ? a : cfg;
1646 initEvents: function()
1649 this.el.on('click', this.onClick, this);
1654 onClick : function(e)
1656 Roo.log('img onclick');
1657 this.fireEvent('click', this, e);
1660 * Sets the url of the image - used to update it
1661 * @param {String} url the url of the image
1664 setSrc : function(url)
1668 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1669 this.el.dom.src = url;
1673 this.el.select('img', true).first().dom.src = url;
1689 * @class Roo.bootstrap.Link
1690 * @extends Roo.bootstrap.Component
1691 * Bootstrap Link Class
1692 * @cfg {String} alt image alternative text
1693 * @cfg {String} href a tag href
1694 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1695 * @cfg {String} html the content of the link.
1696 * @cfg {String} anchor name for the anchor link
1697 * @cfg {String} fa - favicon
1699 * @cfg {Boolean} preventDefault (true | false) default false
1703 * Create a new Input
1704 * @param {Object} config The config object
1707 Roo.bootstrap.Link = function(config){
1708 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1714 * The img click event for the img.
1715 * @param {Roo.EventObject} e
1721 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1725 preventDefault: false,
1731 getAutoCreate : function()
1733 var html = this.html || '';
1735 if (this.fa !== false) {
1736 html = '<i class="fa fa-' + this.fa + '"></i>';
1741 // anchor's do not require html/href...
1742 if (this.anchor === false) {
1744 cfg.href = this.href || '#';
1746 cfg.name = this.anchor;
1747 if (this.html !== false || this.fa !== false) {
1750 if (this.href !== false) {
1751 cfg.href = this.href;
1755 if(this.alt !== false){
1760 if(this.target !== false) {
1761 cfg.target = this.target;
1767 initEvents: function() {
1769 if(!this.href || this.preventDefault){
1770 this.el.on('click', this.onClick, this);
1774 onClick : function(e)
1776 if(this.preventDefault){
1779 //Roo.log('img onclick');
1780 this.fireEvent('click', this, e);
1793 * @class Roo.bootstrap.Header
1794 * @extends Roo.bootstrap.Component
1795 * Bootstrap Header class
1796 * @cfg {String} html content of header
1797 * @cfg {Number} level (1|2|3|4|5|6) default 1
1800 * Create a new Header
1801 * @param {Object} config The config object
1805 Roo.bootstrap.Header = function(config){
1806 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1809 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1817 getAutoCreate : function(){
1822 tag: 'h' + (1 *this.level),
1823 html: this.html || ''
1835 * Ext JS Library 1.1.1
1836 * Copyright(c) 2006-2007, Ext JS, LLC.
1838 * Originally Released Under LGPL - original licence link has changed is not relivant.
1841 * <script type="text/javascript">
1845 * @class Roo.bootstrap.MenuMgr
1846 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1849 Roo.bootstrap.MenuMgr = function(){
1850 var menus, active, groups = {}, attached = false, lastShow = new Date();
1852 // private - called when first menu is created
1855 active = new Roo.util.MixedCollection();
1856 Roo.get(document).addKeyListener(27, function(){
1857 if(active.length > 0){
1865 if(active && active.length > 0){
1866 var c = active.clone();
1876 if(active.length < 1){
1877 Roo.get(document).un("mouseup", onMouseDown);
1885 var last = active.last();
1886 lastShow = new Date();
1889 Roo.get(document).on("mouseup", onMouseDown);
1894 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1895 m.parentMenu.activeChild = m;
1896 }else if(last && last.isVisible()){
1897 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1902 function onBeforeHide(m){
1904 m.activeChild.hide();
1906 if(m.autoHideTimer){
1907 clearTimeout(m.autoHideTimer);
1908 delete m.autoHideTimer;
1913 function onBeforeShow(m){
1914 var pm = m.parentMenu;
1915 if(!pm && !m.allowOtherMenus){
1917 }else if(pm && pm.activeChild && active != m){
1918 pm.activeChild.hide();
1922 // private this should really trigger on mouseup..
1923 function onMouseDown(e){
1924 Roo.log("on Mouse Up");
1926 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1927 Roo.log("MenuManager hideAll");
1936 function onBeforeCheck(mi, state){
1938 var g = groups[mi.group];
1939 for(var i = 0, l = g.length; i < l; i++){
1941 g[i].setChecked(false);
1950 * Hides all menus that are currently visible
1952 hideAll : function(){
1957 register : function(menu){
1961 menus[menu.id] = menu;
1962 menu.on("beforehide", onBeforeHide);
1963 menu.on("hide", onHide);
1964 menu.on("beforeshow", onBeforeShow);
1965 menu.on("show", onShow);
1967 if(g && menu.events["checkchange"]){
1971 groups[g].push(menu);
1972 menu.on("checkchange", onCheck);
1977 * Returns a {@link Roo.menu.Menu} object
1978 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1979 * be used to generate and return a new Menu instance.
1981 get : function(menu){
1982 if(typeof menu == "string"){ // menu id
1984 }else if(menu.events){ // menu instance
1987 /*else if(typeof menu.length == 'number'){ // array of menu items?
1988 return new Roo.bootstrap.Menu({items:menu});
1989 }else{ // otherwise, must be a config
1990 return new Roo.bootstrap.Menu(menu);
1997 unregister : function(menu){
1998 delete menus[menu.id];
1999 menu.un("beforehide", onBeforeHide);
2000 menu.un("hide", onHide);
2001 menu.un("beforeshow", onBeforeShow);
2002 menu.un("show", onShow);
2004 if(g && menu.events["checkchange"]){
2005 groups[g].remove(menu);
2006 menu.un("checkchange", onCheck);
2011 registerCheckable : function(menuItem){
2012 var g = menuItem.group;
2017 groups[g].push(menuItem);
2018 menuItem.on("beforecheckchange", onBeforeCheck);
2023 unregisterCheckable : function(menuItem){
2024 var g = menuItem.group;
2026 groups[g].remove(menuItem);
2027 menuItem.un("beforecheckchange", onBeforeCheck);
2039 * @class Roo.bootstrap.Menu
2040 * @extends Roo.bootstrap.Component
2041 * Bootstrap Menu class - container for MenuItems
2042 * @cfg {String} type (dropdown|treeview|submenu) type of menu
2043 * @cfg {bool} hidden if the menu should be hidden when rendered.
2044 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
2045 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
2049 * @param {Object} config The config object
2053 Roo.bootstrap.Menu = function(config){
2054 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2055 if (this.registerMenu && this.type != 'treeview') {
2056 Roo.bootstrap.MenuMgr.register(this);
2063 * Fires before this menu is displayed (return false to block)
2064 * @param {Roo.menu.Menu} this
2069 * Fires before this menu is hidden (return false to block)
2070 * @param {Roo.menu.Menu} this
2075 * Fires after this menu is displayed
2076 * @param {Roo.menu.Menu} this
2081 * Fires after this menu is hidden
2082 * @param {Roo.menu.Menu} this
2087 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2088 * @param {Roo.menu.Menu} this
2089 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2090 * @param {Roo.EventObject} e
2095 * Fires when the mouse is hovering over this menu
2096 * @param {Roo.menu.Menu} this
2097 * @param {Roo.EventObject} e
2098 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2103 * Fires when the mouse exits this menu
2104 * @param {Roo.menu.Menu} this
2105 * @param {Roo.EventObject} e
2106 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2111 * Fires when a menu item contained in this menu is clicked
2112 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2113 * @param {Roo.EventObject} e
2117 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2120 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2124 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2127 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2129 registerMenu : true,
2131 menuItems :false, // stores the menu items..
2141 getChildContainer : function() {
2145 getAutoCreate : function(){
2147 //if (['right'].indexOf(this.align)!==-1) {
2148 // cfg.cn[1].cls += ' pull-right'
2154 cls : 'dropdown-menu' ,
2155 style : 'z-index:1000'
2159 if (this.type === 'submenu') {
2160 cfg.cls = 'submenu active';
2162 if (this.type === 'treeview') {
2163 cfg.cls = 'treeview-menu';
2168 initEvents : function() {
2170 // Roo.log("ADD event");
2171 // Roo.log(this.triggerEl.dom);
2173 this.triggerEl.on('click', this.onTriggerClick, this);
2175 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2178 if (this.triggerEl.hasClass('nav-item')) {
2179 // dropdown toggle on the 'a' in BS4?
2180 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2182 this.triggerEl.addClass('dropdown-toggle');
2185 this.el.on('touchstart' , this.onTouch, this);
2187 this.el.on('click' , this.onClick, this);
2189 this.el.on("mouseover", this.onMouseOver, this);
2190 this.el.on("mouseout", this.onMouseOut, this);
2194 findTargetItem : function(e)
2196 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2200 //Roo.log(t); Roo.log(t.id);
2202 //Roo.log(this.menuitems);
2203 return this.menuitems.get(t.id);
2205 //return this.items.get(t.menuItemId);
2211 onTouch : function(e)
2213 Roo.log("menu.onTouch");
2214 //e.stopEvent(); this make the user popdown broken
2218 onClick : function(e)
2220 Roo.log("menu.onClick");
2222 var t = this.findTargetItem(e);
2223 if(!t || t.isContainer){
2228 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2229 if(t == this.activeItem && t.shouldDeactivate(e)){
2230 this.activeItem.deactivate();
2231 delete this.activeItem;
2235 this.setActiveItem(t, true);
2243 Roo.log('pass click event');
2247 this.fireEvent("click", this, t, e);
2251 if(!t.href.length || t.href == '#'){
2252 (function() { _this.hide(); }).defer(100);
2257 onMouseOver : function(e){
2258 var t = this.findTargetItem(e);
2261 // if(t.canActivate && !t.disabled){
2262 // this.setActiveItem(t, true);
2266 this.fireEvent("mouseover", this, e, t);
2268 isVisible : function(){
2269 return !this.hidden;
2271 onMouseOut : function(e){
2272 var t = this.findTargetItem(e);
2275 // if(t == this.activeItem && t.shouldDeactivate(e)){
2276 // this.activeItem.deactivate();
2277 // delete this.activeItem;
2280 this.fireEvent("mouseout", this, e, t);
2285 * Displays this menu relative to another element
2286 * @param {String/HTMLElement/Roo.Element} element The element to align to
2287 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2288 * the element (defaults to this.defaultAlign)
2289 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2291 show : function(el, pos, parentMenu)
2293 if (false === this.fireEvent("beforeshow", this)) {
2294 Roo.log("show canceled");
2297 this.parentMenu = parentMenu;
2302 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2305 * Displays this menu at a specific xy position
2306 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2307 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2309 showAt : function(xy, parentMenu, /* private: */_e){
2310 this.parentMenu = parentMenu;
2315 this.fireEvent("beforeshow", this);
2316 //xy = this.el.adjustForConstraints(xy);
2320 this.hideMenuItems();
2321 this.hidden = false;
2322 this.triggerEl.addClass('open');
2323 this.el.addClass('show');
2325 // reassign x when hitting right
2326 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2327 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2330 // reassign y when hitting bottom
2331 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2332 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2335 // but the list may align on trigger left or trigger top... should it be a properity?
2337 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2342 this.fireEvent("show", this);
2348 this.doFocus.defer(50, this);
2352 doFocus : function(){
2354 this.focusEl.focus();
2359 * Hides this menu and optionally all parent menus
2360 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2362 hide : function(deep)
2364 if (false === this.fireEvent("beforehide", this)) {
2365 Roo.log("hide canceled");
2368 this.hideMenuItems();
2369 if(this.el && this.isVisible()){
2371 if(this.activeItem){
2372 this.activeItem.deactivate();
2373 this.activeItem = null;
2375 this.triggerEl.removeClass('open');;
2376 this.el.removeClass('show');
2378 this.fireEvent("hide", this);
2380 if(deep === true && this.parentMenu){
2381 this.parentMenu.hide(true);
2385 onTriggerClick : function(e)
2387 Roo.log('trigger click');
2389 var target = e.getTarget();
2391 Roo.log(target.nodeName.toLowerCase());
2393 if(target.nodeName.toLowerCase() === 'i'){
2399 onTriggerPress : function(e)
2401 Roo.log('trigger press');
2402 //Roo.log(e.getTarget());
2403 // Roo.log(this.triggerEl.dom);
2405 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2406 var pel = Roo.get(e.getTarget());
2407 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2408 Roo.log('is treeview or dropdown?');
2412 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2416 if (this.isVisible()) {
2421 this.show(this.triggerEl, '?', false);
2424 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2431 hideMenuItems : function()
2433 Roo.log("hide Menu Items");
2438 this.el.select('.open',true).each(function(aa) {
2440 aa.removeClass('open');
2444 addxtypeChild : function (tree, cntr) {
2445 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2447 this.menuitems.add(comp);
2459 this.getEl().dom.innerHTML = '';
2460 this.menuitems.clear();
2474 * @class Roo.bootstrap.MenuItem
2475 * @extends Roo.bootstrap.Component
2476 * Bootstrap MenuItem class
2477 * @cfg {String} html the menu label
2478 * @cfg {String} href the link
2479 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2480 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2481 * @cfg {Boolean} active used on sidebars to highlight active itesm
2482 * @cfg {String} fa favicon to show on left of menu item.
2483 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2487 * Create a new MenuItem
2488 * @param {Object} config The config object
2492 Roo.bootstrap.MenuItem = function(config){
2493 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2498 * The raw click event for the entire grid.
2499 * @param {Roo.bootstrap.MenuItem} this
2500 * @param {Roo.EventObject} e
2506 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2510 preventDefault: false,
2511 isContainer : false,
2515 getAutoCreate : function(){
2517 if(this.isContainer){
2520 cls: 'dropdown-menu-item '
2530 cls : 'dropdown-item',
2535 if (this.fa !== false) {
2538 cls : 'fa fa-' + this.fa
2547 cls: 'dropdown-menu-item',
2550 if (this.parent().type == 'treeview') {
2551 cfg.cls = 'treeview-menu';
2554 cfg.cls += ' active';
2559 anc.href = this.href || cfg.cn[0].href ;
2560 ctag.html = this.html || cfg.cn[0].html ;
2564 initEvents: function()
2566 if (this.parent().type == 'treeview') {
2567 this.el.select('a').on('click', this.onClick, this);
2571 this.menu.parentType = this.xtype;
2572 this.menu.triggerEl = this.el;
2573 this.menu = this.addxtype(Roo.apply({}, this.menu));
2577 onClick : function(e)
2579 Roo.log('item on click ');
2581 if(this.preventDefault){
2584 //this.parent().hideMenuItems();
2586 this.fireEvent('click', this, e);
2605 * @class Roo.bootstrap.MenuSeparator
2606 * @extends Roo.bootstrap.Component
2607 * Bootstrap MenuSeparator class
2610 * Create a new MenuItem
2611 * @param {Object} config The config object
2615 Roo.bootstrap.MenuSeparator = function(config){
2616 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2619 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2621 getAutoCreate : function(){
2640 * @class Roo.bootstrap.Modal
2641 * @extends Roo.bootstrap.Component
2642 * Bootstrap Modal class
2643 * @cfg {String} title Title of dialog
2644 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2645 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2646 * @cfg {Boolean} specificTitle default false
2647 * @cfg {Array} buttons Array of buttons or standard button set..
2648 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2649 * @cfg {Boolean} animate default true
2650 * @cfg {Boolean} allow_close default true
2651 * @cfg {Boolean} fitwindow default false
2652 * @cfg {String} size (sm|lg) default empty
2653 * @cfg {Number} max_width set the max width of modal
2657 * Create a new Modal Dialog
2658 * @param {Object} config The config object
2661 Roo.bootstrap.Modal = function(config){
2662 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2667 * The raw btnclick event for the button
2668 * @param {Roo.EventObject} e
2673 * Fire when dialog resize
2674 * @param {Roo.bootstrap.Modal} this
2675 * @param {Roo.EventObject} e
2679 this.buttons = this.buttons || [];
2682 this.tmpl = Roo.factory(this.tmpl);
2687 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2689 title : 'test dialog',
2699 specificTitle: false,
2701 buttonPosition: 'right',
2724 onRender : function(ct, position)
2726 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2729 var cfg = Roo.apply({}, this.getAutoCreate());
2732 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2734 //if (!cfg.name.length) {
2738 cfg.cls += ' ' + this.cls;
2741 cfg.style = this.style;
2743 this.el = Roo.get(document.body).createChild(cfg, position);
2745 //var type = this.el.dom.type;
2748 if(this.tabIndex !== undefined){
2749 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2752 this.dialogEl = this.el.select('.modal-dialog',true).first();
2753 this.bodyEl = this.el.select('.modal-body',true).first();
2754 this.closeEl = this.el.select('.modal-header .close', true).first();
2755 this.headerEl = this.el.select('.modal-header',true).first();
2756 this.titleEl = this.el.select('.modal-title',true).first();
2757 this.footerEl = this.el.select('.modal-footer',true).first();
2759 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2761 //this.el.addClass("x-dlg-modal");
2763 if (this.buttons.length) {
2764 Roo.each(this.buttons, function(bb) {
2765 var b = Roo.apply({}, bb);
2766 b.xns = b.xns || Roo.bootstrap;
2767 b.xtype = b.xtype || 'Button';
2768 if (typeof(b.listeners) == 'undefined') {
2769 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2772 var btn = Roo.factory(b);
2774 btn.render(this.getButtonContainer());
2778 // render the children.
2781 if(typeof(this.items) != 'undefined'){
2782 var items = this.items;
2785 for(var i =0;i < items.length;i++) {
2786 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2790 this.items = nitems;
2792 // where are these used - they used to be body/close/footer
2796 //this.el.addClass([this.fieldClass, this.cls]);
2800 getAutoCreate : function()
2804 html : this.html || ''
2809 cls : 'modal-title',
2813 if(this.specificTitle){
2819 if (this.allow_close && Roo.bootstrap.version == 3) {
2829 if (this.allow_close && Roo.bootstrap.version == 4) {
2839 if(this.size.length){
2840 size = 'modal-' + this.size;
2843 var footer = Roo.bootstrap.version == 3 ?
2845 cls : 'modal-footer',
2849 cls: 'btn-' + this.buttonPosition
2854 { // BS4 uses mr-auto on left buttons....
2855 cls : 'modal-footer'
2866 cls: "modal-dialog " + size,
2869 cls : "modal-content",
2872 cls : 'modal-header',
2887 modal.cls += ' fade';
2893 getChildContainer : function() {
2898 getButtonContainer : function() {
2900 return Roo.bootstrap.version == 4 ?
2901 this.el.select('.modal-footer',true).first()
2902 : this.el.select('.modal-footer div',true).first();
2905 initEvents : function()
2907 if (this.allow_close) {
2908 this.closeEl.on('click', this.hide, this);
2910 Roo.EventManager.onWindowResize(this.resize, this, true);
2918 this.maskEl.setSize(
2919 Roo.lib.Dom.getViewWidth(true),
2920 Roo.lib.Dom.getViewHeight(true)
2923 if (this.fitwindow) {
2927 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2928 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
2933 if(this.max_width !== 0) {
2935 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2938 this.setSize(w, this.height);
2942 if(this.max_height) {
2943 this.setSize(w,Math.min(
2945 Roo.lib.Dom.getViewportHeight(true) - 60
2951 if(!this.fit_content) {
2952 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2956 this.setSize(w, Math.min(
2958 this.headerEl.getHeight() +
2959 this.footerEl.getHeight() +
2960 this.getChildHeight(this.bodyEl.dom.childNodes),
2961 Roo.lib.Dom.getViewportHeight(true) - 60)
2967 setSize : function(w,h)
2978 if (!this.rendered) {
2982 //this.el.setStyle('display', 'block');
2983 this.el.removeClass('hideing');
2984 this.el.dom.style.display='block';
2986 Roo.get(document.body).addClass('modal-open');
2988 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2991 this.el.addClass('show');
2992 this.el.addClass('in');
2995 this.el.addClass('show');
2996 this.el.addClass('in');
2999 // not sure how we can show data in here..
3001 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3004 Roo.get(document.body).addClass("x-body-masked");
3006 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3007 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3008 this.maskEl.dom.style.display = 'block';
3009 this.maskEl.addClass('show');
3014 this.fireEvent('show', this);
3016 // set zindex here - otherwise it appears to be ignored...
3017 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3020 this.items.forEach( function(e) {
3021 e.layout ? e.layout() : false;
3029 if(this.fireEvent("beforehide", this) !== false){
3031 this.maskEl.removeClass('show');
3033 this.maskEl.dom.style.display = '';
3034 Roo.get(document.body).removeClass("x-body-masked");
3035 this.el.removeClass('in');
3036 this.el.select('.modal-dialog', true).first().setStyle('transform','');
3038 if(this.animate){ // why
3039 this.el.addClass('hideing');
3040 this.el.removeClass('show');
3042 if (!this.el.hasClass('hideing')) {
3043 return; // it's been shown again...
3046 this.el.dom.style.display='';
3048 Roo.get(document.body).removeClass('modal-open');
3049 this.el.removeClass('hideing');
3053 this.el.removeClass('show');
3054 this.el.dom.style.display='';
3055 Roo.get(document.body).removeClass('modal-open');
3058 this.fireEvent('hide', this);
3061 isVisible : function()
3064 return this.el.hasClass('show') && !this.el.hasClass('hideing');
3068 addButton : function(str, cb)
3072 var b = Roo.apply({}, { html : str } );
3073 b.xns = b.xns || Roo.bootstrap;
3074 b.xtype = b.xtype || 'Button';
3075 if (typeof(b.listeners) == 'undefined') {
3076 b.listeners = { click : cb.createDelegate(this) };
3079 var btn = Roo.factory(b);
3081 btn.render(this.getButtonContainer());
3087 setDefaultButton : function(btn)
3089 //this.el.select('.modal-footer').()
3092 resizeTo: function(w,h)
3094 this.dialogEl.setWidth(w);
3096 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
3098 this.bodyEl.setHeight(h - diff);
3100 this.fireEvent('resize', this);
3103 setContentSize : function(w, h)
3107 onButtonClick: function(btn,e)
3110 this.fireEvent('btnclick', btn.name, e);
3113 * Set the title of the Dialog
3114 * @param {String} str new Title
3116 setTitle: function(str) {
3117 this.titleEl.dom.innerHTML = str;
3120 * Set the body of the Dialog
3121 * @param {String} str new Title
3123 setBody: function(str) {
3124 this.bodyEl.dom.innerHTML = str;
3127 * Set the body of the Dialog using the template
3128 * @param {Obj} data - apply this data to the template and replace the body contents.
3130 applyBody: function(obj)
3133 Roo.log("Error - using apply Body without a template");
3136 this.tmpl.overwrite(this.bodyEl, obj);
3139 getChildHeight : function(child_nodes)
3143 child_nodes.length == 0
3148 var child_height = 0;
3150 for(var i = 0; i < child_nodes.length; i++) {
3153 * for modal with tabs...
3154 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3156 var layout_childs = child_nodes[i].childNodes;
3158 for(var j = 0; j < layout_childs.length; j++) {
3160 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3162 var layout_body_childs = layout_childs[j].childNodes;
3164 for(var k = 0; k < layout_body_childs.length; k++) {
3166 if(layout_body_childs[k].classList.contains('navbar')) {
3167 child_height += layout_body_childs[k].offsetHeight;
3171 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3173 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3175 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3177 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3178 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3193 child_height += child_nodes[i].offsetHeight;
3194 // Roo.log(child_nodes[i].offsetHeight);
3197 return child_height;
3203 Roo.apply(Roo.bootstrap.Modal, {
3205 * Button config that displays a single OK button
3214 * Button config that displays Yes and No buttons
3230 * Button config that displays OK and Cancel buttons
3245 * Button config that displays Yes, No and Cancel buttons
3269 * messagebox - can be used as a replace
3273 * @class Roo.MessageBox
3274 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3278 Roo.Msg.alert('Status', 'Changes saved successfully.');
3280 // Prompt for user data:
3281 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3283 // process text value...
3287 // Show a dialog using config options:
3289 title:'Save Changes?',
3290 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3291 buttons: Roo.Msg.YESNOCANCEL,
3298 Roo.bootstrap.MessageBox = function(){
3299 var dlg, opt, mask, waitTimer;
3300 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3301 var buttons, activeTextEl, bwidth;
3305 var handleButton = function(button){
3307 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3311 var handleHide = function(){
3313 dlg.el.removeClass(opt.cls);
3316 // Roo.TaskMgr.stop(waitTimer);
3317 // waitTimer = null;
3322 var updateButtons = function(b){
3325 buttons["ok"].hide();
3326 buttons["cancel"].hide();
3327 buttons["yes"].hide();
3328 buttons["no"].hide();
3329 dlg.footerEl.hide();
3333 dlg.footerEl.show();
3334 for(var k in buttons){
3335 if(typeof buttons[k] != "function"){
3338 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3339 width += buttons[k].el.getWidth()+15;
3349 var handleEsc = function(d, k, e){
3350 if(opt && opt.closable !== false){
3360 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3361 * @return {Roo.BasicDialog} The BasicDialog element
3363 getDialog : function(){
3365 dlg = new Roo.bootstrap.Modal( {
3368 //constraintoviewport:false,
3370 //collapsible : false,
3375 //buttonAlign:"center",
3376 closeClick : function(){
3377 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3380 handleButton("cancel");
3385 dlg.on("hide", handleHide);
3387 //dlg.addKeyListener(27, handleEsc);
3389 this.buttons = buttons;
3390 var bt = this.buttonText;
3391 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3392 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3393 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3394 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3396 bodyEl = dlg.bodyEl.createChild({
3398 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3399 '<textarea class="roo-mb-textarea"></textarea>' +
3400 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3402 msgEl = bodyEl.dom.firstChild;
3403 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3404 textboxEl.enableDisplayMode();
3405 textboxEl.addKeyListener([10,13], function(){
3406 if(dlg.isVisible() && opt && opt.buttons){
3409 }else if(opt.buttons.yes){
3410 handleButton("yes");
3414 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3415 textareaEl.enableDisplayMode();
3416 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3417 progressEl.enableDisplayMode();
3419 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3420 var pf = progressEl.dom.firstChild;
3422 pp = Roo.get(pf.firstChild);
3423 pp.setHeight(pf.offsetHeight);
3431 * Updates the message box body text
3432 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3433 * the XHTML-compliant non-breaking space character '&#160;')
3434 * @return {Roo.MessageBox} This message box
3436 updateText : function(text)
3438 if(!dlg.isVisible() && !opt.width){
3439 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3440 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3442 msgEl.innerHTML = text || ' ';
3444 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3445 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3447 Math.min(opt.width || cw , this.maxWidth),
3448 Math.max(opt.minWidth || this.minWidth, bwidth)
3451 activeTextEl.setWidth(w);
3453 if(dlg.isVisible()){
3454 dlg.fixedcenter = false;
3456 // to big, make it scroll. = But as usual stupid IE does not support
3459 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3460 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3461 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3463 bodyEl.dom.style.height = '';
3464 bodyEl.dom.style.overflowY = '';
3467 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3469 bodyEl.dom.style.overflowX = '';
3472 dlg.setContentSize(w, bodyEl.getHeight());
3473 if(dlg.isVisible()){
3474 dlg.fixedcenter = true;
3480 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3481 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3482 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3483 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3484 * @return {Roo.MessageBox} This message box
3486 updateProgress : function(value, text){
3488 this.updateText(text);
3491 if (pp) { // weird bug on my firefox - for some reason this is not defined
3492 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3493 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3499 * Returns true if the message box is currently displayed
3500 * @return {Boolean} True if the message box is visible, else false
3502 isVisible : function(){
3503 return dlg && dlg.isVisible();
3507 * Hides the message box if it is displayed
3510 if(this.isVisible()){
3516 * Displays a new message box, or reinitializes an existing message box, based on the config options
3517 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3518 * The following config object properties are supported:
3520 Property Type Description
3521 ---------- --------------- ------------------------------------------------------------------------------------
3522 animEl String/Element An id or Element from which the message box should animate as it opens and
3523 closes (defaults to undefined)
3524 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3525 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3526 closable Boolean False to hide the top-right close button (defaults to true). Note that
3527 progress and wait dialogs will ignore this property and always hide the
3528 close button as they can only be closed programmatically.
3529 cls String A custom CSS class to apply to the message box element
3530 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3531 displayed (defaults to 75)
3532 fn Function A callback function to execute after closing the dialog. The arguments to the
3533 function will be btn (the name of the button that was clicked, if applicable,
3534 e.g. "ok"), and text (the value of the active text field, if applicable).
3535 Progress and wait dialogs will ignore this option since they do not respond to
3536 user actions and can only be closed programmatically, so any required function
3537 should be called by the same code after it closes the dialog.
3538 icon String A CSS class that provides a background image to be used as an icon for
3539 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3540 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3541 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3542 modal Boolean False to allow user interaction with the page while the message box is
3543 displayed (defaults to true)
3544 msg String A string that will replace the existing message box body text (defaults
3545 to the XHTML-compliant non-breaking space character ' ')
3546 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3547 progress Boolean True to display a progress bar (defaults to false)
3548 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3549 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3550 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3551 title String The title text
3552 value String The string value to set into the active textbox element if displayed
3553 wait Boolean True to display a progress bar (defaults to false)
3554 width Number The width of the dialog in pixels
3561 msg: 'Please enter your address:',
3563 buttons: Roo.MessageBox.OKCANCEL,
3566 animEl: 'addAddressBtn'
3569 * @param {Object} config Configuration options
3570 * @return {Roo.MessageBox} This message box
3572 show : function(options)
3575 // this causes nightmares if you show one dialog after another
3576 // especially on callbacks..
3578 if(this.isVisible()){
3581 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3582 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3583 Roo.log("New Dialog Message:" + options.msg )
3584 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3585 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3588 var d = this.getDialog();
3590 d.setTitle(opt.title || " ");
3591 d.closeEl.setDisplayed(opt.closable !== false);
3592 activeTextEl = textboxEl;
3593 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3598 textareaEl.setHeight(typeof opt.multiline == "number" ?
3599 opt.multiline : this.defaultTextHeight);
3600 activeTextEl = textareaEl;
3609 progressEl.setDisplayed(opt.progress === true);
3611 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3613 this.updateProgress(0);
3614 activeTextEl.dom.value = opt.value || "";
3616 dlg.setDefaultButton(activeTextEl);
3618 var bs = opt.buttons;
3622 }else if(bs && bs.yes){
3623 db = buttons["yes"];
3625 dlg.setDefaultButton(db);
3627 bwidth = updateButtons(opt.buttons);
3628 this.updateText(opt.msg);
3630 d.el.addClass(opt.cls);
3632 d.proxyDrag = opt.proxyDrag === true;
3633 d.modal = opt.modal !== false;
3634 d.mask = opt.modal !== false ? mask : false;
3636 // force it to the end of the z-index stack so it gets a cursor in FF
3637 document.body.appendChild(dlg.el.dom);
3638 d.animateTarget = null;
3639 d.show(options.animEl);
3645 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3646 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3647 * and closing the message box when the process is complete.
3648 * @param {String} title The title bar text
3649 * @param {String} msg The message box body text
3650 * @return {Roo.MessageBox} This message box
3652 progress : function(title, msg){
3659 minWidth: this.minProgressWidth,
3666 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3667 * If a callback function is passed it will be called after the user clicks the button, and the
3668 * id of the button that was clicked will be passed as the only parameter to the callback
3669 * (could also be the top-right close button).
3670 * @param {String} title The title bar text
3671 * @param {String} msg The message box body text
3672 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3673 * @param {Object} scope (optional) The scope of the callback function
3674 * @return {Roo.MessageBox} This message box
3676 alert : function(title, msg, fn, scope)
3691 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3692 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3693 * You are responsible for closing the message box when the process is complete.
3694 * @param {String} msg The message box body text
3695 * @param {String} title (optional) The title bar text
3696 * @return {Roo.MessageBox} This message box
3698 wait : function(msg, title){
3709 waitTimer = Roo.TaskMgr.start({
3711 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3719 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3720 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3721 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3722 * @param {String} title The title bar text
3723 * @param {String} msg The message box body text
3724 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3725 * @param {Object} scope (optional) The scope of the callback function
3726 * @return {Roo.MessageBox} This message box
3728 confirm : function(title, msg, fn, scope){
3732 buttons: this.YESNO,
3741 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3742 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3743 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3744 * (could also be the top-right close button) and the text that was entered will be passed as the two
3745 * parameters to the callback.
3746 * @param {String} title The title bar text
3747 * @param {String} msg The message box body text
3748 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3749 * @param {Object} scope (optional) The scope of the callback function
3750 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3751 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3752 * @return {Roo.MessageBox} This message box
3754 prompt : function(title, msg, fn, scope, multiline){
3758 buttons: this.OKCANCEL,
3763 multiline: multiline,
3770 * Button config that displays a single OK button
3775 * Button config that displays Yes and No buttons
3778 YESNO : {yes:true, no:true},
3780 * Button config that displays OK and Cancel buttons
3783 OKCANCEL : {ok:true, cancel:true},
3785 * Button config that displays Yes, No and Cancel buttons
3788 YESNOCANCEL : {yes:true, no:true, cancel:true},
3791 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3794 defaultTextHeight : 75,
3796 * The maximum width in pixels of the message box (defaults to 600)
3801 * The minimum width in pixels of the message box (defaults to 100)
3806 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3807 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3810 minProgressWidth : 250,
3812 * An object containing the default button text strings that can be overriden for localized language support.
3813 * Supported properties are: ok, cancel, yes and no.
3814 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3827 * Shorthand for {@link Roo.MessageBox}
3829 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3830 Roo.Msg = Roo.Msg || Roo.MessageBox;
3839 * @class Roo.bootstrap.Navbar
3840 * @extends Roo.bootstrap.Component
3841 * Bootstrap Navbar class
3844 * Create a new Navbar
3845 * @param {Object} config The config object
3849 Roo.bootstrap.Navbar = function(config){
3850 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3854 * @event beforetoggle
3855 * Fire before toggle the menu
3856 * @param {Roo.EventObject} e
3858 "beforetoggle" : true
3862 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3871 getAutoCreate : function(){
3874 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3878 initEvents :function ()
3880 //Roo.log(this.el.select('.navbar-toggle',true));
3881 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
3888 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3890 var size = this.el.getSize();
3891 this.maskEl.setSize(size.width, size.height);
3892 this.maskEl.enableDisplayMode("block");
3901 getChildContainer : function()
3903 if (this.el && this.el.select('.collapse').getCount()) {
3904 return this.el.select('.collapse',true).first();
3919 onToggle : function()
3922 if(this.fireEvent('beforetoggle', this) === false){
3925 var ce = this.el.select('.navbar-collapse',true).first();
3927 if (!ce.hasClass('show')) {
3937 * Expand the navbar pulldown
3939 expand : function ()
3942 var ce = this.el.select('.navbar-collapse',true).first();
3943 if (ce.hasClass('collapsing')) {
3946 ce.dom.style.height = '';
3948 ce.addClass('in'); // old...
3949 ce.removeClass('collapse');
3950 ce.addClass('show');
3951 var h = ce.getHeight();
3953 ce.removeClass('show');
3954 // at this point we should be able to see it..
3955 ce.addClass('collapsing');
3957 ce.setHeight(0); // resize it ...
3958 ce.on('transitionend', function() {
3959 //Roo.log('done transition');
3960 ce.removeClass('collapsing');
3961 ce.addClass('show');
3962 ce.removeClass('collapse');
3964 ce.dom.style.height = '';
3965 }, this, { single: true} );
3967 ce.dom.scrollTop = 0;
3970 * Collapse the navbar pulldown
3972 collapse : function()
3974 var ce = this.el.select('.navbar-collapse',true).first();
3976 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
3977 // it's collapsed or collapsing..
3980 ce.removeClass('in'); // old...
3981 ce.setHeight(ce.getHeight());
3982 ce.removeClass('show');
3983 ce.addClass('collapsing');
3985 ce.on('transitionend', function() {
3986 ce.dom.style.height = '';
3987 ce.removeClass('collapsing');
3988 ce.addClass('collapse');
3989 }, this, { single: true} );
4009 * @class Roo.bootstrap.NavSimplebar
4010 * @extends Roo.bootstrap.Navbar
4011 * Bootstrap Sidebar class
4013 * @cfg {Boolean} inverse is inverted color
4015 * @cfg {String} type (nav | pills | tabs)
4016 * @cfg {Boolean} arrangement stacked | justified
4017 * @cfg {String} align (left | right) alignment
4019 * @cfg {Boolean} main (true|false) main nav bar? default false
4020 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4022 * @cfg {String} tag (header|footer|nav|div) default is nav
4024 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4028 * Create a new Sidebar
4029 * @param {Object} config The config object
4033 Roo.bootstrap.NavSimplebar = function(config){
4034 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4037 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
4053 getAutoCreate : function(){
4057 tag : this.tag || 'div',
4058 cls : 'navbar navbar-expand-lg roo-navbar-simple'
4060 if (['light','white'].indexOf(this.weight) > -1) {
4061 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4063 cfg.cls += ' bg-' + this.weight;
4066 cfg.cls += ' navbar-inverse';
4070 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4072 if (Roo.bootstrap.version == 4) {
4087 this.type = this.type || 'nav';
4088 if (['tabs','pills'].indexOf(this.type) != -1) {
4089 cfg.cn[0].cls += ' nav-' + this.type
4093 if (this.type!=='nav') {
4094 Roo.log('nav type must be nav/tabs/pills')
4096 cfg.cn[0].cls += ' navbar-nav'
4102 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4103 cfg.cn[0].cls += ' nav-' + this.arrangement;
4107 if (this.align === 'right') {
4108 cfg.cn[0].cls += ' navbar-right';
4133 * navbar-expand-md fixed-top
4137 * @class Roo.bootstrap.NavHeaderbar
4138 * @extends Roo.bootstrap.NavSimplebar
4139 * Bootstrap Sidebar class
4141 * @cfg {String} brand what is brand
4142 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4143 * @cfg {String} brand_href href of the brand
4144 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4145 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4146 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4147 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4150 * Create a new Sidebar
4151 * @param {Object} config The config object
4155 Roo.bootstrap.NavHeaderbar = function(config){
4156 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4160 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4167 desktopCenter : false,
4170 getAutoCreate : function(){
4173 tag: this.nav || 'nav',
4174 cls: 'navbar navbar-expand-md',
4180 if (this.desktopCenter) {
4181 cn.push({cls : 'container', cn : []});
4189 cls: 'navbar-toggle navbar-toggler',
4190 'data-toggle': 'collapse',
4195 html: 'Toggle navigation'
4199 cls: 'icon-bar navbar-toggler-icon'
4212 cn.push( Roo.bootstrap.version == 4 ? btn : {
4214 cls: 'navbar-header',
4223 cls: 'collapse navbar-collapse',
4227 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4229 if (['light','white'].indexOf(this.weight) > -1) {
4230 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4232 cfg.cls += ' bg-' + this.weight;
4235 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4236 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4238 // tag can override this..
4240 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4243 if (this.brand !== '') {
4244 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4245 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4247 href: this.brand_href ? this.brand_href : '#',
4248 cls: 'navbar-brand',
4256 cfg.cls += ' main-nav';
4264 getHeaderChildContainer : function()
4266 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4267 return this.el.select('.navbar-header',true).first();
4270 return this.getChildContainer();
4274 initEvents : function()
4276 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4278 if (this.autohide) {
4283 Roo.get(document).on('scroll',function(e) {
4284 var ns = Roo.get(document).getScroll().top;
4285 var os = prevScroll;
4289 ft.removeClass('slideDown');
4290 ft.addClass('slideUp');
4293 ft.removeClass('slideUp');
4294 ft.addClass('slideDown');
4315 * @class Roo.bootstrap.NavSidebar
4316 * @extends Roo.bootstrap.Navbar
4317 * Bootstrap Sidebar class
4320 * Create a new Sidebar
4321 * @param {Object} config The config object
4325 Roo.bootstrap.NavSidebar = function(config){
4326 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4329 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4331 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4333 getAutoCreate : function(){
4338 cls: 'sidebar sidebar-nav'
4360 * @class Roo.bootstrap.NavGroup
4361 * @extends Roo.bootstrap.Component
4362 * Bootstrap NavGroup class
4363 * @cfg {String} align (left|right)
4364 * @cfg {Boolean} inverse
4365 * @cfg {String} type (nav|pills|tab) default nav
4366 * @cfg {String} navId - reference Id for navbar.
4370 * Create a new nav group
4371 * @param {Object} config The config object
4374 Roo.bootstrap.NavGroup = function(config){
4375 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4378 Roo.bootstrap.NavGroup.register(this);
4382 * Fires when the active item changes
4383 * @param {Roo.bootstrap.NavGroup} this
4384 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4385 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4392 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4403 getAutoCreate : function()
4405 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4411 if (Roo.bootstrap.version == 4) {
4412 if (['tabs','pills'].indexOf(this.type) != -1) {
4413 cfg.cls += ' nav-' + this.type;
4415 cfg.cls += ' navbar-nav';
4418 if (['tabs','pills'].indexOf(this.type) != -1) {
4419 cfg.cls += ' nav-' + this.type
4421 if (this.type !== 'nav') {
4422 Roo.log('nav type must be nav/tabs/pills')
4424 cfg.cls += ' navbar-nav'
4428 if (this.parent() && this.parent().sidebar) {
4431 cls: 'dashboard-menu sidebar-menu'
4437 if (this.form === true) {
4440 cls: 'navbar-form form-inline'
4443 if (this.align === 'right') {
4444 cfg.cls += ' navbar-right ml-md-auto';
4446 cfg.cls += ' navbar-left';
4450 if (this.align === 'right') {
4451 cfg.cls += ' navbar-right ml-md-auto';
4453 cfg.cls += ' mr-auto';
4457 cfg.cls += ' navbar-inverse';
4465 * sets the active Navigation item
4466 * @param {Roo.bootstrap.NavItem} the new current navitem
4468 setActiveItem : function(item)
4471 Roo.each(this.navItems, function(v){
4476 v.setActive(false, true);
4483 item.setActive(true, true);
4484 this.fireEvent('changed', this, item, prev);
4489 * gets the active Navigation item
4490 * @return {Roo.bootstrap.NavItem} the current navitem
4492 getActive : function()
4496 Roo.each(this.navItems, function(v){
4507 indexOfNav : function()
4511 Roo.each(this.navItems, function(v,i){
4522 * adds a Navigation item
4523 * @param {Roo.bootstrap.NavItem} the navitem to add
4525 addItem : function(cfg)
4527 if (this.form && Roo.bootstrap.version == 4) {
4530 var cn = new Roo.bootstrap.NavItem(cfg);
4532 cn.parentId = this.id;
4533 cn.onRender(this.el, null);
4537 * register a Navigation item
4538 * @param {Roo.bootstrap.NavItem} the navitem to add
4540 register : function(item)
4542 this.navItems.push( item);
4543 item.navId = this.navId;
4548 * clear all the Navigation item
4551 clearAll : function()
4554 this.el.dom.innerHTML = '';
4557 getNavItem: function(tabId)
4560 Roo.each(this.navItems, function(e) {
4561 if (e.tabId == tabId) {
4571 setActiveNext : function()
4573 var i = this.indexOfNav(this.getActive());
4574 if (i > this.navItems.length) {
4577 this.setActiveItem(this.navItems[i+1]);
4579 setActivePrev : function()
4581 var i = this.indexOfNav(this.getActive());
4585 this.setActiveItem(this.navItems[i-1]);
4587 clearWasActive : function(except) {
4588 Roo.each(this.navItems, function(e) {
4589 if (e.tabId != except.tabId && e.was_active) {
4590 e.was_active = false;
4597 getWasActive : function ()
4600 Roo.each(this.navItems, function(e) {
4615 Roo.apply(Roo.bootstrap.NavGroup, {
4619 * register a Navigation Group
4620 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4622 register : function(navgrp)
4624 this.groups[navgrp.navId] = navgrp;
4628 * fetch a Navigation Group based on the navigation ID
4629 * @param {string} the navgroup to add
4630 * @returns {Roo.bootstrap.NavGroup} the navgroup
4632 get: function(navId) {
4633 if (typeof(this.groups[navId]) == 'undefined') {
4635 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4637 return this.groups[navId] ;
4652 * @class Roo.bootstrap.NavItem
4653 * @extends Roo.bootstrap.Component
4654 * Bootstrap Navbar.NavItem class
4655 * @cfg {String} href link to
4656 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4658 * @cfg {String} html content of button
4659 * @cfg {String} badge text inside badge
4660 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4661 * @cfg {String} glyphicon DEPRICATED - use fa
4662 * @cfg {String} icon DEPRICATED - use fa
4663 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4664 * @cfg {Boolean} active Is item active
4665 * @cfg {Boolean} disabled Is item disabled
4667 * @cfg {Boolean} preventDefault (true | false) default false
4668 * @cfg {String} tabId the tab that this item activates.
4669 * @cfg {String} tagtype (a|span) render as a href or span?
4670 * @cfg {Boolean} animateRef (true|false) link to element default false
4673 * Create a new Navbar Item
4674 * @param {Object} config The config object
4676 Roo.bootstrap.NavItem = function(config){
4677 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4682 * The raw click event for the entire grid.
4683 * @param {Roo.EventObject} e
4688 * Fires when the active item active state changes
4689 * @param {Roo.bootstrap.NavItem} this
4690 * @param {boolean} state the new state
4696 * Fires when scroll to element
4697 * @param {Roo.bootstrap.NavItem} this
4698 * @param {Object} options
4699 * @param {Roo.EventObject} e
4707 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4716 preventDefault : false,
4724 button_outline : false,
4728 getAutoCreate : function(){
4736 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4738 if (this.disabled) {
4739 cfg.cls += ' disabled';
4743 if (this.button_weight.length) {
4744 cfg.tag = this.href ? 'a' : 'button';
4745 cfg.html = this.html || '';
4746 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4748 cfg.href = this.href;
4751 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4754 // menu .. should add dropdown-menu class - so no need for carat..
4756 if (this.badge !== '') {
4758 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4763 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4767 href : this.href || "#",
4768 html: this.html || ''
4771 if (this.tagtype == 'a') {
4772 cfg.cn[0].cls = 'nav-link';
4775 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4778 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4780 if(this.glyphicon) {
4781 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4786 cfg.cn[0].html += " <span class='caret'></span>";
4790 if (this.badge !== '') {
4792 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4800 onRender : function(ct, position)
4802 // Roo.log("Call onRender: " + this.xtype);
4803 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4807 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4808 this.navLink = this.el.select('.nav-link',true).first();
4813 initEvents: function()
4815 if (typeof (this.menu) != 'undefined') {
4816 this.menu.parentType = this.xtype;
4817 this.menu.triggerEl = this.el;
4818 this.menu = this.addxtype(Roo.apply({}, this.menu));
4821 this.el.select('a',true).on('click', this.onClick, this);
4823 if(this.tagtype == 'span'){
4824 this.el.select('span',true).on('click', this.onClick, this);
4827 // at this point parent should be available..
4828 this.parent().register(this);
4831 onClick : function(e)
4833 if (e.getTarget('.dropdown-menu-item')) {
4834 // did you click on a menu itemm.... - then don't trigger onclick..
4839 this.preventDefault ||
4842 Roo.log("NavItem - prevent Default?");
4846 if (this.disabled) {
4850 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4851 if (tg && tg.transition) {
4852 Roo.log("waiting for the transitionend");
4858 //Roo.log("fire event clicked");
4859 if(this.fireEvent('click', this, e) === false){
4863 if(this.tagtype == 'span'){
4867 //Roo.log(this.href);
4868 var ael = this.el.select('a',true).first();
4871 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4872 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4873 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4874 return; // ignore... - it's a 'hash' to another page.
4876 Roo.log("NavItem - prevent Default?");
4878 this.scrollToElement(e);
4882 var p = this.parent();
4884 if (['tabs','pills'].indexOf(p.type)!==-1) {
4885 if (typeof(p.setActiveItem) !== 'undefined') {
4886 p.setActiveItem(this);
4890 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4891 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4892 // remove the collapsed menu expand...
4893 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4897 isActive: function () {
4900 setActive : function(state, fire, is_was_active)
4902 if (this.active && !state && this.navId) {
4903 this.was_active = true;
4904 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4906 nv.clearWasActive(this);
4910 this.active = state;
4913 this.el.removeClass('active');
4914 this.navLink ? this.navLink.removeClass('active') : false;
4915 } else if (!this.el.hasClass('active')) {
4917 this.el.addClass('active');
4918 if (Roo.bootstrap.version == 4 && this.navLink ) {
4919 this.navLink.addClass('active');
4924 this.fireEvent('changed', this, state);
4927 // show a panel if it's registered and related..
4929 if (!this.navId || !this.tabId || !state || is_was_active) {
4933 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4937 var pan = tg.getPanelByName(this.tabId);
4941 // if we can not flip to new panel - go back to old nav highlight..
4942 if (false == tg.showPanel(pan)) {
4943 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4945 var onav = nv.getWasActive();
4947 onav.setActive(true, false, true);
4956 // this should not be here...
4957 setDisabled : function(state)
4959 this.disabled = state;
4961 this.el.removeClass('disabled');
4962 } else if (!this.el.hasClass('disabled')) {
4963 this.el.addClass('disabled');
4969 * Fetch the element to display the tooltip on.
4970 * @return {Roo.Element} defaults to this.el
4972 tooltipEl : function()
4974 return this.el.select('' + this.tagtype + '', true).first();
4977 scrollToElement : function(e)
4979 var c = document.body;
4982 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4984 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4985 c = document.documentElement;
4988 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4994 var o = target.calcOffsetsTo(c);
5001 this.fireEvent('scrollto', this, options, e);
5003 Roo.get(c).scrollTo('top', options.value, true);
5016 * <span> icon </span>
5017 * <span> text </span>
5018 * <span>badge </span>
5022 * @class Roo.bootstrap.NavSidebarItem
5023 * @extends Roo.bootstrap.NavItem
5024 * Bootstrap Navbar.NavSidebarItem class
5025 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5026 * {Boolean} open is the menu open
5027 * {Boolean} buttonView use button as the tigger el rather that a (default false)
5028 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5029 * {String} buttonSize (sm|md|lg)the extra classes for the button
5030 * {Boolean} showArrow show arrow next to the text (default true)
5032 * Create a new Navbar Button
5033 * @param {Object} config The config object
5035 Roo.bootstrap.NavSidebarItem = function(config){
5036 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5041 * The raw click event for the entire grid.
5042 * @param {Roo.EventObject} e
5047 * Fires when the active item active state changes
5048 * @param {Roo.bootstrap.NavSidebarItem} this
5049 * @param {boolean} state the new state
5057 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
5059 badgeWeight : 'default',
5065 buttonWeight : 'default',
5071 getAutoCreate : function(){
5076 href : this.href || '#',
5082 if(this.buttonView){
5085 href : this.href || '#',
5086 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5099 cfg.cls += ' active';
5102 if (this.disabled) {
5103 cfg.cls += ' disabled';
5106 cfg.cls += ' open x-open';
5109 if (this.glyphicon || this.icon) {
5110 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
5111 a.cn.push({ tag : 'i', cls : c }) ;
5114 if(!this.buttonView){
5117 html : this.html || ''
5124 if (this.badge !== '') {
5125 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
5131 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5134 a.cls += ' dropdown-toggle treeview' ;
5140 initEvents : function()
5142 if (typeof (this.menu) != 'undefined') {
5143 this.menu.parentType = this.xtype;
5144 this.menu.triggerEl = this.el;
5145 this.menu = this.addxtype(Roo.apply({}, this.menu));
5148 this.el.on('click', this.onClick, this);
5150 if(this.badge !== ''){
5151 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5156 onClick : function(e)
5163 if(this.preventDefault){
5167 this.fireEvent('click', this, e);
5170 disable : function()
5172 this.setDisabled(true);
5177 this.setDisabled(false);
5180 setDisabled : function(state)
5182 if(this.disabled == state){
5186 this.disabled = state;
5189 this.el.addClass('disabled');
5193 this.el.removeClass('disabled');
5198 setActive : function(state)
5200 if(this.active == state){
5204 this.active = state;
5207 this.el.addClass('active');
5211 this.el.removeClass('active');
5216 isActive: function ()
5221 setBadge : function(str)
5227 this.badgeEl.dom.innerHTML = str;
5244 * @class Roo.bootstrap.Row
5245 * @extends Roo.bootstrap.Component
5246 * Bootstrap Row class (contains columns...)
5250 * @param {Object} config The config object
5253 Roo.bootstrap.Row = function(config){
5254 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5257 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5259 getAutoCreate : function(){
5278 * @class Roo.bootstrap.Element
5279 * @extends Roo.bootstrap.Component
5280 * Bootstrap Element class
5281 * @cfg {String} html contents of the element
5282 * @cfg {String} tag tag of the element
5283 * @cfg {String} cls class of the element
5284 * @cfg {Boolean} preventDefault (true|false) default false
5285 * @cfg {Boolean} clickable (true|false) default false
5288 * Create a new Element
5289 * @param {Object} config The config object
5292 Roo.bootstrap.Element = function(config){
5293 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5299 * When a element is chick
5300 * @param {Roo.bootstrap.Element} this
5301 * @param {Roo.EventObject} e
5307 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5312 preventDefault: false,
5315 getAutoCreate : function(){
5319 // cls: this.cls, double assign in parent class Component.js :: onRender
5326 initEvents: function()
5328 Roo.bootstrap.Element.superclass.initEvents.call(this);
5331 this.el.on('click', this.onClick, this);
5336 onClick : function(e)
5338 if(this.preventDefault){
5342 this.fireEvent('click', this, e);
5345 getValue : function()
5347 return this.el.dom.innerHTML;
5350 setValue : function(value)
5352 this.el.dom.innerHTML = value;
5367 * @class Roo.bootstrap.Pagination
5368 * @extends Roo.bootstrap.Component
5369 * Bootstrap Pagination class
5370 * @cfg {String} size xs | sm | md | lg
5371 * @cfg {Boolean} inverse false | true
5374 * Create a new Pagination
5375 * @param {Object} config The config object
5378 Roo.bootstrap.Pagination = function(config){
5379 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5382 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5388 getAutoCreate : function(){
5394 cfg.cls += ' inverse';
5400 cfg.cls += " " + this.cls;
5418 * @class Roo.bootstrap.PaginationItem
5419 * @extends Roo.bootstrap.Component
5420 * Bootstrap PaginationItem class
5421 * @cfg {String} html text
5422 * @cfg {String} href the link
5423 * @cfg {Boolean} preventDefault (true | false) default true
5424 * @cfg {Boolean} active (true | false) default false
5425 * @cfg {Boolean} disabled default false
5429 * Create a new PaginationItem
5430 * @param {Object} config The config object
5434 Roo.bootstrap.PaginationItem = function(config){
5435 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5440 * The raw click event for the entire grid.
5441 * @param {Roo.EventObject} e
5447 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5451 preventDefault: true,
5456 getAutoCreate : function(){
5462 href : this.href ? this.href : '#',
5463 html : this.html ? this.html : ''
5473 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5477 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5483 initEvents: function() {
5485 this.el.on('click', this.onClick, this);
5488 onClick : function(e)
5490 Roo.log('PaginationItem on click ');
5491 if(this.preventDefault){
5499 this.fireEvent('click', this, e);
5515 * @class Roo.bootstrap.Slider
5516 * @extends Roo.bootstrap.Component
5517 * Bootstrap Slider class
5520 * Create a new Slider
5521 * @param {Object} config The config object
5524 Roo.bootstrap.Slider = function(config){
5525 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5528 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5530 getAutoCreate : function(){
5534 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5538 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5550 * Ext JS Library 1.1.1
5551 * Copyright(c) 2006-2007, Ext JS, LLC.
5553 * Originally Released Under LGPL - original licence link has changed is not relivant.
5556 * <script type="text/javascript">
5561 * @class Roo.grid.ColumnModel
5562 * @extends Roo.util.Observable
5563 * This is the default implementation of a ColumnModel used by the Grid. It defines
5564 * the columns in the grid.
5567 var colModel = new Roo.grid.ColumnModel([
5568 {header: "Ticker", width: 60, sortable: true, locked: true},
5569 {header: "Company Name", width: 150, sortable: true},
5570 {header: "Market Cap.", width: 100, sortable: true},
5571 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5572 {header: "Employees", width: 100, sortable: true, resizable: false}
5577 * The config options listed for this class are options which may appear in each
5578 * individual column definition.
5579 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5581 * @param {Object} config An Array of column config objects. See this class's
5582 * config objects for details.
5584 Roo.grid.ColumnModel = function(config){
5586 * The config passed into the constructor
5588 this.config = config;
5591 // if no id, create one
5592 // if the column does not have a dataIndex mapping,
5593 // map it to the order it is in the config
5594 for(var i = 0, len = config.length; i < len; i++){
5596 if(typeof c.dataIndex == "undefined"){
5599 if(typeof c.renderer == "string"){
5600 c.renderer = Roo.util.Format[c.renderer];
5602 if(typeof c.id == "undefined"){
5605 if(c.editor && c.editor.xtype){
5606 c.editor = Roo.factory(c.editor, Roo.grid);
5608 if(c.editor && c.editor.isFormField){
5609 c.editor = new Roo.grid.GridEditor(c.editor);
5611 this.lookup[c.id] = c;
5615 * The width of columns which have no width specified (defaults to 100)
5618 this.defaultWidth = 100;
5621 * Default sortable of columns which have no sortable specified (defaults to false)
5624 this.defaultSortable = false;
5628 * @event widthchange
5629 * Fires when the width of a column changes.
5630 * @param {ColumnModel} this
5631 * @param {Number} columnIndex The column index
5632 * @param {Number} newWidth The new width
5634 "widthchange": true,
5636 * @event headerchange
5637 * Fires when the text of a header changes.
5638 * @param {ColumnModel} this
5639 * @param {Number} columnIndex The column index
5640 * @param {Number} newText The new header text
5642 "headerchange": true,
5644 * @event hiddenchange
5645 * Fires when a column is hidden or "unhidden".
5646 * @param {ColumnModel} this
5647 * @param {Number} columnIndex The column index
5648 * @param {Boolean} hidden true if hidden, false otherwise
5650 "hiddenchange": true,
5652 * @event columnmoved
5653 * Fires when a column is moved.
5654 * @param {ColumnModel} this
5655 * @param {Number} oldIndex
5656 * @param {Number} newIndex
5658 "columnmoved" : true,
5660 * @event columlockchange
5661 * Fires when a column's locked state is changed
5662 * @param {ColumnModel} this
5663 * @param {Number} colIndex
5664 * @param {Boolean} locked true if locked
5666 "columnlockchange" : true
5668 Roo.grid.ColumnModel.superclass.constructor.call(this);
5670 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5672 * @cfg {String} header The header text to display in the Grid view.
5675 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5676 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5677 * specified, the column's index is used as an index into the Record's data Array.
5680 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5681 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5684 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5685 * Defaults to the value of the {@link #defaultSortable} property.
5686 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5689 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5692 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5695 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5698 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5701 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5702 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5703 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5704 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5707 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5710 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5713 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5716 * @cfg {String} cursor (Optional)
5719 * @cfg {String} tooltip (Optional)
5722 * @cfg {Number} xs (Optional)
5725 * @cfg {Number} sm (Optional)
5728 * @cfg {Number} md (Optional)
5731 * @cfg {Number} lg (Optional)
5734 * Returns the id of the column at the specified index.
5735 * @param {Number} index The column index
5736 * @return {String} the id
5738 getColumnId : function(index){
5739 return this.config[index].id;
5743 * Returns the column for a specified id.
5744 * @param {String} id The column id
5745 * @return {Object} the column
5747 getColumnById : function(id){
5748 return this.lookup[id];
5753 * Returns the column for a specified dataIndex.
5754 * @param {String} dataIndex The column dataIndex
5755 * @return {Object|Boolean} the column or false if not found
5757 getColumnByDataIndex: function(dataIndex){
5758 var index = this.findColumnIndex(dataIndex);
5759 return index > -1 ? this.config[index] : false;
5763 * Returns the index for a specified column id.
5764 * @param {String} id The column id
5765 * @return {Number} the index, or -1 if not found
5767 getIndexById : function(id){
5768 for(var i = 0, len = this.config.length; i < len; i++){
5769 if(this.config[i].id == id){
5777 * Returns the index for a specified column dataIndex.
5778 * @param {String} dataIndex The column dataIndex
5779 * @return {Number} the index, or -1 if not found
5782 findColumnIndex : function(dataIndex){
5783 for(var i = 0, len = this.config.length; i < len; i++){
5784 if(this.config[i].dataIndex == dataIndex){
5792 moveColumn : function(oldIndex, newIndex){
5793 var c = this.config[oldIndex];
5794 this.config.splice(oldIndex, 1);
5795 this.config.splice(newIndex, 0, c);
5796 this.dataMap = null;
5797 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5800 isLocked : function(colIndex){
5801 return this.config[colIndex].locked === true;
5804 setLocked : function(colIndex, value, suppressEvent){
5805 if(this.isLocked(colIndex) == value){
5808 this.config[colIndex].locked = value;
5810 this.fireEvent("columnlockchange", this, colIndex, value);
5814 getTotalLockedWidth : function(){
5816 for(var i = 0; i < this.config.length; i++){
5817 if(this.isLocked(i) && !this.isHidden(i)){
5818 this.totalWidth += this.getColumnWidth(i);
5824 getLockedCount : function(){
5825 for(var i = 0, len = this.config.length; i < len; i++){
5826 if(!this.isLocked(i)){
5831 return this.config.length;
5835 * Returns the number of columns.
5838 getColumnCount : function(visibleOnly){
5839 if(visibleOnly === true){
5841 for(var i = 0, len = this.config.length; i < len; i++){
5842 if(!this.isHidden(i)){
5848 return this.config.length;
5852 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5853 * @param {Function} fn
5854 * @param {Object} scope (optional)
5855 * @return {Array} result
5857 getColumnsBy : function(fn, scope){
5859 for(var i = 0, len = this.config.length; i < len; i++){
5860 var c = this.config[i];
5861 if(fn.call(scope||this, c, i) === true){
5869 * Returns true if the specified column is sortable.
5870 * @param {Number} col The column index
5873 isSortable : function(col){
5874 if(typeof this.config[col].sortable == "undefined"){
5875 return this.defaultSortable;
5877 return this.config[col].sortable;
5881 * Returns the rendering (formatting) function defined for the column.
5882 * @param {Number} col The column index.
5883 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5885 getRenderer : function(col){
5886 if(!this.config[col].renderer){
5887 return Roo.grid.ColumnModel.defaultRenderer;
5889 return this.config[col].renderer;
5893 * Sets the rendering (formatting) function for a column.
5894 * @param {Number} col The column index
5895 * @param {Function} fn The function to use to process the cell's raw data
5896 * to return HTML markup for the grid view. The render function is called with
5897 * the following parameters:<ul>
5898 * <li>Data value.</li>
5899 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5900 * <li>css A CSS style string to apply to the table cell.</li>
5901 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5902 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5903 * <li>Row index</li>
5904 * <li>Column index</li>
5905 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5907 setRenderer : function(col, fn){
5908 this.config[col].renderer = fn;
5912 * Returns the width for the specified column.
5913 * @param {Number} col The column index
5916 getColumnWidth : function(col){
5917 return this.config[col].width * 1 || this.defaultWidth;
5921 * Sets the width for a column.
5922 * @param {Number} col The column index
5923 * @param {Number} width The new width
5925 setColumnWidth : function(col, width, suppressEvent){
5926 this.config[col].width = width;
5927 this.totalWidth = null;
5929 this.fireEvent("widthchange", this, col, width);
5934 * Returns the total width of all columns.
5935 * @param {Boolean} includeHidden True to include hidden column widths
5938 getTotalWidth : function(includeHidden){
5939 if(!this.totalWidth){
5940 this.totalWidth = 0;
5941 for(var i = 0, len = this.config.length; i < len; i++){
5942 if(includeHidden || !this.isHidden(i)){
5943 this.totalWidth += this.getColumnWidth(i);
5947 return this.totalWidth;
5951 * Returns the header for the specified column.
5952 * @param {Number} col The column index
5955 getColumnHeader : function(col){
5956 return this.config[col].header;
5960 * Sets the header for a column.
5961 * @param {Number} col The column index
5962 * @param {String} header The new header
5964 setColumnHeader : function(col, header){
5965 this.config[col].header = header;
5966 this.fireEvent("headerchange", this, col, header);
5970 * Returns the tooltip for the specified column.
5971 * @param {Number} col The column index
5974 getColumnTooltip : function(col){
5975 return this.config[col].tooltip;
5978 * Sets the tooltip for a column.
5979 * @param {Number} col The column index
5980 * @param {String} tooltip The new tooltip
5982 setColumnTooltip : function(col, tooltip){
5983 this.config[col].tooltip = tooltip;
5987 * Returns the dataIndex for the specified column.
5988 * @param {Number} col The column index
5991 getDataIndex : function(col){
5992 return this.config[col].dataIndex;
5996 * Sets the dataIndex for a column.
5997 * @param {Number} col The column index
5998 * @param {Number} dataIndex The new dataIndex
6000 setDataIndex : function(col, dataIndex){
6001 this.config[col].dataIndex = dataIndex;
6007 * Returns true if the cell is editable.
6008 * @param {Number} colIndex The column index
6009 * @param {Number} rowIndex The row index - this is nto actually used..?
6012 isCellEditable : function(colIndex, rowIndex){
6013 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6017 * Returns the editor defined for the cell/column.
6018 * return false or null to disable editing.
6019 * @param {Number} colIndex The column index
6020 * @param {Number} rowIndex The row index
6023 getCellEditor : function(colIndex, rowIndex){
6024 return this.config[colIndex].editor;
6028 * Sets if a column is editable.
6029 * @param {Number} col The column index
6030 * @param {Boolean} editable True if the column is editable
6032 setEditable : function(col, editable){
6033 this.config[col].editable = editable;
6038 * Returns true if the column is hidden.
6039 * @param {Number} colIndex The column index
6042 isHidden : function(colIndex){
6043 return this.config[colIndex].hidden;
6048 * Returns true if the column width cannot be changed
6050 isFixed : function(colIndex){
6051 return this.config[colIndex].fixed;
6055 * Returns true if the column can be resized
6058 isResizable : function(colIndex){
6059 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6062 * Sets if a column is hidden.
6063 * @param {Number} colIndex The column index
6064 * @param {Boolean} hidden True if the column is hidden
6066 setHidden : function(colIndex, hidden){
6067 this.config[colIndex].hidden = hidden;
6068 this.totalWidth = null;
6069 this.fireEvent("hiddenchange", this, colIndex, hidden);
6073 * Sets the editor for a column.
6074 * @param {Number} col The column index
6075 * @param {Object} editor The editor object
6077 setEditor : function(col, editor){
6078 this.config[col].editor = editor;
6082 Roo.grid.ColumnModel.defaultRenderer = function(value)
6084 if(typeof value == "object") {
6087 if(typeof value == "string" && value.length < 1){
6091 return String.format("{0}", value);
6094 // Alias for backwards compatibility
6095 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6098 * Ext JS Library 1.1.1
6099 * Copyright(c) 2006-2007, Ext JS, LLC.
6101 * Originally Released Under LGPL - original licence link has changed is not relivant.
6104 * <script type="text/javascript">
6108 * @class Roo.LoadMask
6109 * A simple utility class for generically masking elements while loading data. If the element being masked has
6110 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6111 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
6112 * element's UpdateManager load indicator and will be destroyed after the initial load.
6114 * Create a new LoadMask
6115 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6116 * @param {Object} config The config object
6118 Roo.LoadMask = function(el, config){
6119 this.el = Roo.get(el);
6120 Roo.apply(this, config);
6122 this.store.on('beforeload', this.onBeforeLoad, this);
6123 this.store.on('load', this.onLoad, this);
6124 this.store.on('loadexception', this.onLoadException, this);
6125 this.removeMask = false;
6127 var um = this.el.getUpdateManager();
6128 um.showLoadIndicator = false; // disable the default indicator
6129 um.on('beforeupdate', this.onBeforeLoad, this);
6130 um.on('update', this.onLoad, this);
6131 um.on('failure', this.onLoad, this);
6132 this.removeMask = true;
6136 Roo.LoadMask.prototype = {
6138 * @cfg {Boolean} removeMask
6139 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6140 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6144 * The text to display in a centered loading message box (defaults to 'Loading...')
6148 * @cfg {String} msgCls
6149 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6151 msgCls : 'x-mask-loading',
6154 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6160 * Disables the mask to prevent it from being displayed
6162 disable : function(){
6163 this.disabled = true;
6167 * Enables the mask so that it can be displayed
6169 enable : function(){
6170 this.disabled = false;
6173 onLoadException : function()
6177 if (typeof(arguments[3]) != 'undefined') {
6178 Roo.MessageBox.alert("Error loading",arguments[3]);
6182 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6183 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6190 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6195 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6199 onBeforeLoad : function(){
6201 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6206 destroy : function(){
6208 this.store.un('beforeload', this.onBeforeLoad, this);
6209 this.store.un('load', this.onLoad, this);
6210 this.store.un('loadexception', this.onLoadException, this);
6212 var um = this.el.getUpdateManager();
6213 um.un('beforeupdate', this.onBeforeLoad, this);
6214 um.un('update', this.onLoad, this);
6215 um.un('failure', this.onLoad, this);
6226 * @class Roo.bootstrap.Table
6227 * @extends Roo.bootstrap.Component
6228 * Bootstrap Table class
6229 * @cfg {String} cls table class
6230 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6231 * @cfg {String} bgcolor Specifies the background color for a table
6232 * @cfg {Number} border Specifies whether the table cells should have borders or not
6233 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6234 * @cfg {Number} cellspacing Specifies the space between cells
6235 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6236 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6237 * @cfg {String} sortable Specifies that the table should be sortable
6238 * @cfg {String} summary Specifies a summary of the content of a table
6239 * @cfg {Number} width Specifies the width of a table
6240 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6242 * @cfg {boolean} striped Should the rows be alternative striped
6243 * @cfg {boolean} bordered Add borders to the table
6244 * @cfg {boolean} hover Add hover highlighting
6245 * @cfg {boolean} condensed Format condensed
6246 * @cfg {boolean} responsive Format condensed
6247 * @cfg {Boolean} loadMask (true|false) default false
6248 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6249 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6250 * @cfg {Boolean} rowSelection (true|false) default false
6251 * @cfg {Boolean} cellSelection (true|false) default false
6252 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6253 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6254 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6255 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6259 * Create a new Table
6260 * @param {Object} config The config object
6263 Roo.bootstrap.Table = function(config){
6264 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6269 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6270 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6271 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6272 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6274 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6276 this.sm.grid = this;
6277 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6278 this.sm = this.selModel;
6279 this.sm.xmodule = this.xmodule || false;
6282 if (this.cm && typeof(this.cm.config) == 'undefined') {
6283 this.colModel = new Roo.grid.ColumnModel(this.cm);
6284 this.cm = this.colModel;
6285 this.cm.xmodule = this.xmodule || false;
6288 this.store= Roo.factory(this.store, Roo.data);
6289 this.ds = this.store;
6290 this.ds.xmodule = this.xmodule || false;
6293 if (this.footer && this.store) {
6294 this.footer.dataSource = this.ds;
6295 this.footer = Roo.factory(this.footer);
6302 * Fires when a cell is clicked
6303 * @param {Roo.bootstrap.Table} this
6304 * @param {Roo.Element} el
6305 * @param {Number} rowIndex
6306 * @param {Number} columnIndex
6307 * @param {Roo.EventObject} e
6311 * @event celldblclick
6312 * Fires when a cell is double clicked
6313 * @param {Roo.bootstrap.Table} this
6314 * @param {Roo.Element} el
6315 * @param {Number} rowIndex
6316 * @param {Number} columnIndex
6317 * @param {Roo.EventObject} e
6319 "celldblclick" : true,
6322 * Fires when a row is clicked
6323 * @param {Roo.bootstrap.Table} this
6324 * @param {Roo.Element} el
6325 * @param {Number} rowIndex
6326 * @param {Roo.EventObject} e
6330 * @event rowdblclick
6331 * Fires when a row is double clicked
6332 * @param {Roo.bootstrap.Table} this
6333 * @param {Roo.Element} el
6334 * @param {Number} rowIndex
6335 * @param {Roo.EventObject} e
6337 "rowdblclick" : true,
6340 * Fires when a mouseover occur
6341 * @param {Roo.bootstrap.Table} this
6342 * @param {Roo.Element} el
6343 * @param {Number} rowIndex
6344 * @param {Number} columnIndex
6345 * @param {Roo.EventObject} e
6350 * Fires when a mouseout occur
6351 * @param {Roo.bootstrap.Table} this
6352 * @param {Roo.Element} el
6353 * @param {Number} rowIndex
6354 * @param {Number} columnIndex
6355 * @param {Roo.EventObject} e
6360 * Fires when a row is rendered, so you can change add a style to it.
6361 * @param {Roo.bootstrap.Table} this
6362 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6366 * @event rowsrendered
6367 * Fires when all the rows have been rendered
6368 * @param {Roo.bootstrap.Table} this
6370 'rowsrendered' : true,
6372 * @event contextmenu
6373 * The raw contextmenu event for the entire grid.
6374 * @param {Roo.EventObject} e
6376 "contextmenu" : true,
6378 * @event rowcontextmenu
6379 * Fires when a row is right clicked
6380 * @param {Roo.bootstrap.Table} this
6381 * @param {Number} rowIndex
6382 * @param {Roo.EventObject} e
6384 "rowcontextmenu" : true,
6386 * @event cellcontextmenu
6387 * Fires when a cell is right clicked
6388 * @param {Roo.bootstrap.Table} this
6389 * @param {Number} rowIndex
6390 * @param {Number} cellIndex
6391 * @param {Roo.EventObject} e
6393 "cellcontextmenu" : true,
6395 * @event headercontextmenu
6396 * Fires when a header is right clicked
6397 * @param {Roo.bootstrap.Table} this
6398 * @param {Number} columnIndex
6399 * @param {Roo.EventObject} e
6401 "headercontextmenu" : true
6405 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6431 rowSelection : false,
6432 cellSelection : false,
6435 // Roo.Element - the tbody
6437 // Roo.Element - thead element
6440 container: false, // used by gridpanel...
6446 auto_hide_footer : false,
6448 getAutoCreate : function()
6450 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6457 if (this.scrollBody) {
6458 cfg.cls += ' table-body-fixed';
6461 cfg.cls += ' table-striped';
6465 cfg.cls += ' table-hover';
6467 if (this.bordered) {
6468 cfg.cls += ' table-bordered';
6470 if (this.condensed) {
6471 cfg.cls += ' table-condensed';
6473 if (this.responsive) {
6474 cfg.cls += ' table-responsive';
6478 cfg.cls+= ' ' +this.cls;
6481 // this lot should be simplifed...
6494 ].forEach(function(k) {
6502 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6505 if(this.store || this.cm){
6506 if(this.headerShow){
6507 cfg.cn.push(this.renderHeader());
6510 cfg.cn.push(this.renderBody());
6512 if(this.footerShow){
6513 cfg.cn.push(this.renderFooter());
6515 // where does this come from?
6516 //cfg.cls+= ' TableGrid';
6519 return { cn : [ cfg ] };
6522 initEvents : function()
6524 if(!this.store || !this.cm){
6527 if (this.selModel) {
6528 this.selModel.initEvents();
6532 //Roo.log('initEvents with ds!!!!');
6534 this.mainBody = this.el.select('tbody', true).first();
6535 this.mainHead = this.el.select('thead', true).first();
6536 this.mainFoot = this.el.select('tfoot', true).first();
6542 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6543 e.on('click', _this.sort, _this);
6546 this.mainBody.on("click", this.onClick, this);
6547 this.mainBody.on("dblclick", this.onDblClick, this);
6549 // why is this done????? = it breaks dialogs??
6550 //this.parent().el.setStyle('position', 'relative');
6554 this.footer.parentId = this.id;
6555 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6558 this.el.select('tfoot tr td').first().addClass('hide');
6563 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6566 this.store.on('load', this.onLoad, this);
6567 this.store.on('beforeload', this.onBeforeLoad, this);
6568 this.store.on('update', this.onUpdate, this);
6569 this.store.on('add', this.onAdd, this);
6570 this.store.on("clear", this.clear, this);
6572 this.el.on("contextmenu", this.onContextMenu, this);
6574 this.mainBody.on('scroll', this.onBodyScroll, this);
6576 this.cm.on("headerchange", this.onHeaderChange, this);
6578 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6582 onContextMenu : function(e, t)
6584 this.processEvent("contextmenu", e);
6587 processEvent : function(name, e)
6589 if (name != 'touchstart' ) {
6590 this.fireEvent(name, e);
6593 var t = e.getTarget();
6595 var cell = Roo.get(t);
6601 if(cell.findParent('tfoot', false, true)){
6605 if(cell.findParent('thead', false, true)){
6607 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6608 cell = Roo.get(t).findParent('th', false, true);
6610 Roo.log("failed to find th in thead?");
6611 Roo.log(e.getTarget());
6616 var cellIndex = cell.dom.cellIndex;
6618 var ename = name == 'touchstart' ? 'click' : name;
6619 this.fireEvent("header" + ename, this, cellIndex, e);
6624 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6625 cell = Roo.get(t).findParent('td', false, true);
6627 Roo.log("failed to find th in tbody?");
6628 Roo.log(e.getTarget());
6633 var row = cell.findParent('tr', false, true);
6634 var cellIndex = cell.dom.cellIndex;
6635 var rowIndex = row.dom.rowIndex - 1;
6639 this.fireEvent("row" + name, this, rowIndex, e);
6643 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6649 onMouseover : function(e, el)
6651 var cell = Roo.get(el);
6657 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6658 cell = cell.findParent('td', false, true);
6661 var row = cell.findParent('tr', false, true);
6662 var cellIndex = cell.dom.cellIndex;
6663 var rowIndex = row.dom.rowIndex - 1; // start from 0
6665 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6669 onMouseout : function(e, el)
6671 var cell = Roo.get(el);
6677 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6678 cell = cell.findParent('td', false, true);
6681 var row = cell.findParent('tr', false, true);
6682 var cellIndex = cell.dom.cellIndex;
6683 var rowIndex = row.dom.rowIndex - 1; // start from 0
6685 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6689 onClick : function(e, el)
6691 var cell = Roo.get(el);
6693 if(!cell || (!this.cellSelection && !this.rowSelection)){
6697 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6698 cell = cell.findParent('td', false, true);
6701 if(!cell || typeof(cell) == 'undefined'){
6705 var row = cell.findParent('tr', false, true);
6707 if(!row || typeof(row) == 'undefined'){
6711 var cellIndex = cell.dom.cellIndex;
6712 var rowIndex = this.getRowIndex(row);
6714 // why??? - should these not be based on SelectionModel?
6715 if(this.cellSelection){
6716 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6719 if(this.rowSelection){
6720 this.fireEvent('rowclick', this, row, rowIndex, e);
6726 onDblClick : function(e,el)
6728 var cell = Roo.get(el);
6730 if(!cell || (!this.cellSelection && !this.rowSelection)){
6734 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6735 cell = cell.findParent('td', false, true);
6738 if(!cell || typeof(cell) == 'undefined'){
6742 var row = cell.findParent('tr', false, true);
6744 if(!row || typeof(row) == 'undefined'){
6748 var cellIndex = cell.dom.cellIndex;
6749 var rowIndex = this.getRowIndex(row);
6751 if(this.cellSelection){
6752 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6755 if(this.rowSelection){
6756 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6760 sort : function(e,el)
6762 var col = Roo.get(el);
6764 if(!col.hasClass('sortable')){
6768 var sort = col.attr('sort');
6771 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6775 this.store.sortInfo = {field : sort, direction : dir};
6778 Roo.log("calling footer first");
6779 this.footer.onClick('first');
6782 this.store.load({ params : { start : 0 } });
6786 renderHeader : function()
6794 this.totalWidth = 0;
6796 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6798 var config = cm.config[i];
6802 cls : 'x-hcol-' + i,
6804 html: cm.getColumnHeader(i)
6809 if(typeof(config.sortable) != 'undefined' && config.sortable){
6811 c.html = '<i class="glyphicon"></i>' + c.html;
6814 // could use BS4 hidden-..-down
6816 if(typeof(config.lgHeader) != 'undefined'){
6817 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
6820 if(typeof(config.mdHeader) != 'undefined'){
6821 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6824 if(typeof(config.smHeader) != 'undefined'){
6825 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6828 if(typeof(config.xsHeader) != 'undefined'){
6829 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6836 if(typeof(config.tooltip) != 'undefined'){
6837 c.tooltip = config.tooltip;
6840 if(typeof(config.colspan) != 'undefined'){
6841 c.colspan = config.colspan;
6844 if(typeof(config.hidden) != 'undefined' && config.hidden){
6845 c.style += ' display:none;';
6848 if(typeof(config.dataIndex) != 'undefined'){
6849 c.sort = config.dataIndex;
6854 if(typeof(config.align) != 'undefined' && config.align.length){
6855 c.style += ' text-align:' + config.align + ';';
6858 if(typeof(config.width) != 'undefined'){
6859 c.style += ' width:' + config.width + 'px;';
6860 this.totalWidth += config.width;
6862 this.totalWidth += 100; // assume minimum of 100 per column?
6865 if(typeof(config.cls) != 'undefined'){
6866 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6869 ['xs','sm','md','lg'].map(function(size){
6871 if(typeof(config[size]) == 'undefined'){
6875 if (!config[size]) { // 0 = hidden
6876 // BS 4 '0' is treated as hide that column and below.
6877 c.cls += ' hidden-' + size + ' hidden' + size + 'down';
6881 c.cls += ' col-' + size + '-' + config[size] + (
6882 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
6894 renderBody : function()
6904 colspan : this.cm.getColumnCount()
6914 renderFooter : function()
6924 colspan : this.cm.getColumnCount()
6938 // Roo.log('ds onload');
6943 var ds = this.store;
6945 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6946 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6947 if (_this.store.sortInfo) {
6949 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6950 e.select('i', true).addClass(['glyphicon-arrow-up']);
6953 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6954 e.select('i', true).addClass(['glyphicon-arrow-down']);
6959 var tbody = this.mainBody;
6961 if(ds.getCount() > 0){
6962 ds.data.each(function(d,rowIndex){
6963 var row = this.renderRow(cm, ds, rowIndex);
6965 tbody.createChild(row);
6969 if(row.cellObjects.length){
6970 Roo.each(row.cellObjects, function(r){
6971 _this.renderCellObject(r);
6978 var tfoot = this.el.select('tfoot', true).first();
6980 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6982 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6984 var total = this.ds.getTotalCount();
6986 if(this.footer.pageSize < total){
6987 this.mainFoot.show();
6991 Roo.each(this.el.select('tbody td', true).elements, function(e){
6992 e.on('mouseover', _this.onMouseover, _this);
6995 Roo.each(this.el.select('tbody td', true).elements, function(e){
6996 e.on('mouseout', _this.onMouseout, _this);
6998 this.fireEvent('rowsrendered', this);
7004 onUpdate : function(ds,record)
7006 this.refreshRow(record);
7010 onRemove : function(ds, record, index, isUpdate){
7011 if(isUpdate !== true){
7012 this.fireEvent("beforerowremoved", this, index, record);
7014 var bt = this.mainBody.dom;
7016 var rows = this.el.select('tbody > tr', true).elements;
7018 if(typeof(rows[index]) != 'undefined'){
7019 bt.removeChild(rows[index].dom);
7022 // if(bt.rows[index]){
7023 // bt.removeChild(bt.rows[index]);
7026 if(isUpdate !== true){
7027 //this.stripeRows(index);
7028 //this.syncRowHeights(index, index);
7030 this.fireEvent("rowremoved", this, index, record);
7034 onAdd : function(ds, records, rowIndex)
7036 //Roo.log('on Add called');
7037 // - note this does not handle multiple adding very well..
7038 var bt = this.mainBody.dom;
7039 for (var i =0 ; i < records.length;i++) {
7040 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7041 //Roo.log(records[i]);
7042 //Roo.log(this.store.getAt(rowIndex+i));
7043 this.insertRow(this.store, rowIndex + i, false);
7050 refreshRow : function(record){
7051 var ds = this.store, index;
7052 if(typeof record == 'number'){
7054 record = ds.getAt(index);
7056 index = ds.indexOf(record);
7058 this.insertRow(ds, index, true);
7060 this.onRemove(ds, record, index+1, true);
7062 //this.syncRowHeights(index, index);
7064 this.fireEvent("rowupdated", this, index, record);
7067 insertRow : function(dm, rowIndex, isUpdate){
7070 this.fireEvent("beforerowsinserted", this, rowIndex);
7072 //var s = this.getScrollState();
7073 var row = this.renderRow(this.cm, this.store, rowIndex);
7074 // insert before rowIndex..
7075 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7079 if(row.cellObjects.length){
7080 Roo.each(row.cellObjects, function(r){
7081 _this.renderCellObject(r);
7086 this.fireEvent("rowsinserted", this, rowIndex);
7087 //this.syncRowHeights(firstRow, lastRow);
7088 //this.stripeRows(firstRow);
7095 getRowDom : function(rowIndex)
7097 var rows = this.el.select('tbody > tr', true).elements;
7099 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7102 // returns the object tree for a tr..
7105 renderRow : function(cm, ds, rowIndex)
7107 var d = ds.getAt(rowIndex);
7111 cls : 'x-row-' + rowIndex,
7115 var cellObjects = [];
7117 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7118 var config = cm.config[i];
7120 var renderer = cm.getRenderer(i);
7124 if(typeof(renderer) !== 'undefined'){
7125 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7127 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7128 // and are rendered into the cells after the row is rendered - using the id for the element.
7130 if(typeof(value) === 'object'){
7140 rowIndex : rowIndex,
7145 this.fireEvent('rowclass', this, rowcfg);
7149 cls : rowcfg.rowClass + ' x-col-' + i,
7151 html: (typeof(value) === 'object') ? '' : value
7158 if(typeof(config.colspan) != 'undefined'){
7159 td.colspan = config.colspan;
7162 if(typeof(config.hidden) != 'undefined' && config.hidden){
7163 td.style += ' display:none;';
7166 if(typeof(config.align) != 'undefined' && config.align.length){
7167 td.style += ' text-align:' + config.align + ';';
7169 if(typeof(config.valign) != 'undefined' && config.valign.length){
7170 td.style += ' vertical-align:' + config.valign + ';';
7173 if(typeof(config.width) != 'undefined'){
7174 td.style += ' width:' + config.width + 'px;';
7177 if(typeof(config.cursor) != 'undefined'){
7178 td.style += ' cursor:' + config.cursor + ';';
7181 if(typeof(config.cls) != 'undefined'){
7182 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7185 ['xs','sm','md','lg'].map(function(size){
7187 if(typeof(config[size]) == 'undefined'){
7193 if (!config[size]) { // 0 = hidden
7194 // BS 4 '0' is treated as hide that column and below.
7195 td.cls += ' hidden-' + size + ' hidden' + size + 'down';
7199 td.cls += ' col-' + size + '-' + config[size] + (
7200 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7210 row.cellObjects = cellObjects;
7218 onBeforeLoad : function()
7227 this.el.select('tbody', true).first().dom.innerHTML = '';
7230 * Show or hide a row.
7231 * @param {Number} rowIndex to show or hide
7232 * @param {Boolean} state hide
7234 setRowVisibility : function(rowIndex, state)
7236 var bt = this.mainBody.dom;
7238 var rows = this.el.select('tbody > tr', true).elements;
7240 if(typeof(rows[rowIndex]) == 'undefined'){
7243 rows[rowIndex].dom.style.display = state ? '' : 'none';
7247 getSelectionModel : function(){
7249 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7251 return this.selModel;
7254 * Render the Roo.bootstrap object from renderder
7256 renderCellObject : function(r)
7260 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7262 var t = r.cfg.render(r.container);
7265 Roo.each(r.cfg.cn, function(c){
7267 container: t.getChildContainer(),
7270 _this.renderCellObject(child);
7275 getRowIndex : function(row)
7279 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7290 * Returns the grid's underlying element = used by panel.Grid
7291 * @return {Element} The element
7293 getGridEl : function(){
7297 * Forces a resize - used by panel.Grid
7298 * @return {Element} The element
7300 autoSize : function()
7302 //var ctr = Roo.get(this.container.dom.parentElement);
7303 var ctr = Roo.get(this.el.dom);
7305 var thd = this.getGridEl().select('thead',true).first();
7306 var tbd = this.getGridEl().select('tbody', true).first();
7307 var tfd = this.getGridEl().select('tfoot', true).first();
7309 var cw = ctr.getWidth();
7313 tbd.setSize(ctr.getWidth(),
7314 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7316 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7319 cw = Math.max(cw, this.totalWidth);
7320 this.getGridEl().select('tr',true).setWidth(cw);
7321 // resize 'expandable coloumn?
7323 return; // we doe not have a view in this design..
7326 onBodyScroll: function()
7328 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7330 this.mainHead.setStyle({
7331 'position' : 'relative',
7332 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7338 var scrollHeight = this.mainBody.dom.scrollHeight;
7340 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7342 var height = this.mainBody.getHeight();
7344 if(scrollHeight - height == scrollTop) {
7346 var total = this.ds.getTotalCount();
7348 if(this.footer.cursor + this.footer.pageSize < total){
7350 this.footer.ds.load({
7352 start : this.footer.cursor + this.footer.pageSize,
7353 limit : this.footer.pageSize
7363 onHeaderChange : function()
7365 var header = this.renderHeader();
7366 var table = this.el.select('table', true).first();
7368 this.mainHead.remove();
7369 this.mainHead = table.createChild(header, this.mainBody, false);
7372 onHiddenChange : function(colModel, colIndex, hidden)
7374 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7375 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7377 this.CSS.updateRule(thSelector, "display", "");
7378 this.CSS.updateRule(tdSelector, "display", "");
7381 this.CSS.updateRule(thSelector, "display", "none");
7382 this.CSS.updateRule(tdSelector, "display", "none");
7385 this.onHeaderChange();
7389 setColumnWidth: function(col_index, width)
7391 // width = "md-2 xs-2..."
7392 if(!this.colModel.config[col_index]) {
7396 var w = width.split(" ");
7398 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7400 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7403 for(var j = 0; j < w.length; j++) {
7409 var size_cls = w[j].split("-");
7411 if(!Number.isInteger(size_cls[1] * 1)) {
7415 if(!this.colModel.config[col_index][size_cls[0]]) {
7419 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7423 h_row[0].classList.replace(
7424 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7425 "col-"+size_cls[0]+"-"+size_cls[1]
7428 for(var i = 0; i < rows.length; i++) {
7430 var size_cls = w[j].split("-");
7432 if(!Number.isInteger(size_cls[1] * 1)) {
7436 if(!this.colModel.config[col_index][size_cls[0]]) {
7440 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7444 rows[i].classList.replace(
7445 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7446 "col-"+size_cls[0]+"-"+size_cls[1]
7450 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7465 * @class Roo.bootstrap.TableCell
7466 * @extends Roo.bootstrap.Component
7467 * Bootstrap TableCell class
7468 * @cfg {String} html cell contain text
7469 * @cfg {String} cls cell class
7470 * @cfg {String} tag cell tag (td|th) default td
7471 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7472 * @cfg {String} align Aligns the content in a cell
7473 * @cfg {String} axis Categorizes cells
7474 * @cfg {String} bgcolor Specifies the background color of a cell
7475 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7476 * @cfg {Number} colspan Specifies the number of columns a cell should span
7477 * @cfg {String} headers Specifies one or more header cells a cell is related to
7478 * @cfg {Number} height Sets the height of a cell
7479 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7480 * @cfg {Number} rowspan Sets the number of rows a cell should span
7481 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7482 * @cfg {String} valign Vertical aligns the content in a cell
7483 * @cfg {Number} width Specifies the width of a cell
7486 * Create a new TableCell
7487 * @param {Object} config The config object
7490 Roo.bootstrap.TableCell = function(config){
7491 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7494 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7514 getAutoCreate : function(){
7515 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7535 cfg.align=this.align
7541 cfg.bgcolor=this.bgcolor
7544 cfg.charoff=this.charoff
7547 cfg.colspan=this.colspan
7550 cfg.headers=this.headers
7553 cfg.height=this.height
7556 cfg.nowrap=this.nowrap
7559 cfg.rowspan=this.rowspan
7562 cfg.scope=this.scope
7565 cfg.valign=this.valign
7568 cfg.width=this.width
7587 * @class Roo.bootstrap.TableRow
7588 * @extends Roo.bootstrap.Component
7589 * Bootstrap TableRow class
7590 * @cfg {String} cls row class
7591 * @cfg {String} align Aligns the content in a table row
7592 * @cfg {String} bgcolor Specifies a background color for a table row
7593 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7594 * @cfg {String} valign Vertical aligns the content in a table row
7597 * Create a new TableRow
7598 * @param {Object} config The config object
7601 Roo.bootstrap.TableRow = function(config){
7602 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7605 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7613 getAutoCreate : function(){
7614 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7624 cfg.align = this.align;
7627 cfg.bgcolor = this.bgcolor;
7630 cfg.charoff = this.charoff;
7633 cfg.valign = this.valign;
7651 * @class Roo.bootstrap.TableBody
7652 * @extends Roo.bootstrap.Component
7653 * Bootstrap TableBody class
7654 * @cfg {String} cls element class
7655 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7656 * @cfg {String} align Aligns the content inside the element
7657 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7658 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7661 * Create a new TableBody
7662 * @param {Object} config The config object
7665 Roo.bootstrap.TableBody = function(config){
7666 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7669 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7677 getAutoCreate : function(){
7678 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7692 cfg.align = this.align;
7695 cfg.charoff = this.charoff;
7698 cfg.valign = this.valign;
7705 // initEvents : function()
7712 // this.store = Roo.factory(this.store, Roo.data);
7713 // this.store.on('load', this.onLoad, this);
7715 // this.store.load();
7719 // onLoad: function ()
7721 // this.fireEvent('load', this);
7731 * Ext JS Library 1.1.1
7732 * Copyright(c) 2006-2007, Ext JS, LLC.
7734 * Originally Released Under LGPL - original licence link has changed is not relivant.
7737 * <script type="text/javascript">
7740 // as we use this in bootstrap.
7741 Roo.namespace('Roo.form');
7743 * @class Roo.form.Action
7744 * Internal Class used to handle form actions
7746 * @param {Roo.form.BasicForm} el The form element or its id
7747 * @param {Object} config Configuration options
7752 // define the action interface
7753 Roo.form.Action = function(form, options){
7755 this.options = options || {};
7758 * Client Validation Failed
7761 Roo.form.Action.CLIENT_INVALID = 'client';
7763 * Server Validation Failed
7766 Roo.form.Action.SERVER_INVALID = 'server';
7768 * Connect to Server Failed
7771 Roo.form.Action.CONNECT_FAILURE = 'connect';
7773 * Reading Data from Server Failed
7776 Roo.form.Action.LOAD_FAILURE = 'load';
7778 Roo.form.Action.prototype = {
7780 failureType : undefined,
7781 response : undefined,
7785 run : function(options){
7790 success : function(response){
7795 handleResponse : function(response){
7799 // default connection failure
7800 failure : function(response){
7802 this.response = response;
7803 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7804 this.form.afterAction(this, false);
7807 processResponse : function(response){
7808 this.response = response;
7809 if(!response.responseText){
7812 this.result = this.handleResponse(response);
7816 // utility functions used internally
7817 getUrl : function(appendParams){
7818 var url = this.options.url || this.form.url || this.form.el.dom.action;
7820 var p = this.getParams();
7822 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7828 getMethod : function(){
7829 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7832 getParams : function(){
7833 var bp = this.form.baseParams;
7834 var p = this.options.params;
7836 if(typeof p == "object"){
7837 p = Roo.urlEncode(Roo.applyIf(p, bp));
7838 }else if(typeof p == 'string' && bp){
7839 p += '&' + Roo.urlEncode(bp);
7842 p = Roo.urlEncode(bp);
7847 createCallback : function(){
7849 success: this.success,
7850 failure: this.failure,
7852 timeout: (this.form.timeout*1000),
7853 upload: this.form.fileUpload ? this.success : undefined
7858 Roo.form.Action.Submit = function(form, options){
7859 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7862 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7865 haveProgress : false,
7866 uploadComplete : false,
7868 // uploadProgress indicator.
7869 uploadProgress : function()
7871 if (!this.form.progressUrl) {
7875 if (!this.haveProgress) {
7876 Roo.MessageBox.progress("Uploading", "Uploading");
7878 if (this.uploadComplete) {
7879 Roo.MessageBox.hide();
7883 this.haveProgress = true;
7885 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7887 var c = new Roo.data.Connection();
7889 url : this.form.progressUrl,
7894 success : function(req){
7895 //console.log(data);
7899 rdata = Roo.decode(req.responseText)
7901 Roo.log("Invalid data from server..");
7905 if (!rdata || !rdata.success) {
7907 Roo.MessageBox.alert(Roo.encode(rdata));
7910 var data = rdata.data;
7912 if (this.uploadComplete) {
7913 Roo.MessageBox.hide();
7918 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7919 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7922 this.uploadProgress.defer(2000,this);
7925 failure: function(data) {
7926 Roo.log('progress url failed ');
7937 // run get Values on the form, so it syncs any secondary forms.
7938 this.form.getValues();
7940 var o = this.options;
7941 var method = this.getMethod();
7942 var isPost = method == 'POST';
7943 if(o.clientValidation === false || this.form.isValid()){
7945 if (this.form.progressUrl) {
7946 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7947 (new Date() * 1) + '' + Math.random());
7952 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7953 form:this.form.el.dom,
7954 url:this.getUrl(!isPost),
7956 params:isPost ? this.getParams() : null,
7957 isUpload: this.form.fileUpload,
7958 formData : this.form.formData
7961 this.uploadProgress();
7963 }else if (o.clientValidation !== false){ // client validation failed
7964 this.failureType = Roo.form.Action.CLIENT_INVALID;
7965 this.form.afterAction(this, false);
7969 success : function(response)
7971 this.uploadComplete= true;
7972 if (this.haveProgress) {
7973 Roo.MessageBox.hide();
7977 var result = this.processResponse(response);
7978 if(result === true || result.success){
7979 this.form.afterAction(this, true);
7983 this.form.markInvalid(result.errors);
7984 this.failureType = Roo.form.Action.SERVER_INVALID;
7986 this.form.afterAction(this, false);
7988 failure : function(response)
7990 this.uploadComplete= true;
7991 if (this.haveProgress) {
7992 Roo.MessageBox.hide();
7995 this.response = response;
7996 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7997 this.form.afterAction(this, false);
8000 handleResponse : function(response){
8001 if(this.form.errorReader){
8002 var rs = this.form.errorReader.read(response);
8005 for(var i = 0, len = rs.records.length; i < len; i++) {
8006 var r = rs.records[i];
8010 if(errors.length < 1){
8014 success : rs.success,
8020 ret = Roo.decode(response.responseText);
8024 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8034 Roo.form.Action.Load = function(form, options){
8035 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8036 this.reader = this.form.reader;
8039 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8044 Roo.Ajax.request(Roo.apply(
8045 this.createCallback(), {
8046 method:this.getMethod(),
8047 url:this.getUrl(false),
8048 params:this.getParams()
8052 success : function(response){
8054 var result = this.processResponse(response);
8055 if(result === true || !result.success || !result.data){
8056 this.failureType = Roo.form.Action.LOAD_FAILURE;
8057 this.form.afterAction(this, false);
8060 this.form.clearInvalid();
8061 this.form.setValues(result.data);
8062 this.form.afterAction(this, true);
8065 handleResponse : function(response){
8066 if(this.form.reader){
8067 var rs = this.form.reader.read(response);
8068 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8070 success : rs.success,
8074 return Roo.decode(response.responseText);
8078 Roo.form.Action.ACTION_TYPES = {
8079 'load' : Roo.form.Action.Load,
8080 'submit' : Roo.form.Action.Submit
8089 * @class Roo.bootstrap.Form
8090 * @extends Roo.bootstrap.Component
8091 * Bootstrap Form class
8092 * @cfg {String} method GET | POST (default POST)
8093 * @cfg {String} labelAlign top | left (default top)
8094 * @cfg {String} align left | right - for navbars
8095 * @cfg {Boolean} loadMask load mask when submit (default true)
8100 * @param {Object} config The config object
8104 Roo.bootstrap.Form = function(config){
8106 Roo.bootstrap.Form.superclass.constructor.call(this, config);
8108 Roo.bootstrap.Form.popover.apply();
8112 * @event clientvalidation
8113 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8114 * @param {Form} this
8115 * @param {Boolean} valid true if the form has passed client-side validation
8117 clientvalidation: true,
8119 * @event beforeaction
8120 * Fires before any action is performed. Return false to cancel the action.
8121 * @param {Form} this
8122 * @param {Action} action The action to be performed
8126 * @event actionfailed
8127 * Fires when an action fails.
8128 * @param {Form} this
8129 * @param {Action} action The action that failed
8131 actionfailed : true,
8133 * @event actioncomplete
8134 * Fires when an action is completed.
8135 * @param {Form} this
8136 * @param {Action} action The action that completed
8138 actioncomplete : true
8142 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
8145 * @cfg {String} method
8146 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8151 * The URL to use for form actions if one isn't supplied in the action options.
8154 * @cfg {Boolean} fileUpload
8155 * Set to true if this form is a file upload.
8159 * @cfg {Object} baseParams
8160 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8164 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8168 * @cfg {Sting} align (left|right) for navbar forms
8173 activeAction : null,
8176 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8177 * element by passing it or its id or mask the form itself by passing in true.
8180 waitMsgTarget : false,
8185 * @cfg {Boolean} errorMask (true|false) default false
8190 * @cfg {Number} maskOffset Default 100
8195 * @cfg {Boolean} maskBody
8199 getAutoCreate : function(){
8203 method : this.method || 'POST',
8204 id : this.id || Roo.id(),
8207 if (this.parent().xtype.match(/^Nav/)) {
8208 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8212 if (this.labelAlign == 'left' ) {
8213 cfg.cls += ' form-horizontal';
8219 initEvents : function()
8221 this.el.on('submit', this.onSubmit, this);
8222 // this was added as random key presses on the form where triggering form submit.
8223 this.el.on('keypress', function(e) {
8224 if (e.getCharCode() != 13) {
8227 // we might need to allow it for textareas.. and some other items.
8228 // check e.getTarget().
8230 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8234 Roo.log("keypress blocked");
8242 onSubmit : function(e){
8247 * Returns true if client-side validation on the form is successful.
8250 isValid : function(){
8251 var items = this.getItems();
8255 items.each(function(f){
8261 Roo.log('invalid field: ' + f.name);
8265 if(!target && f.el.isVisible(true)){
8271 if(this.errorMask && !valid){
8272 Roo.bootstrap.Form.popover.mask(this, target);
8279 * Returns true if any fields in this form have changed since their original load.
8282 isDirty : function(){
8284 var items = this.getItems();
8285 items.each(function(f){
8295 * Performs a predefined action (submit or load) or custom actions you define on this form.
8296 * @param {String} actionName The name of the action type
8297 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8298 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8299 * accept other config options):
8301 Property Type Description
8302 ---------------- --------------- ----------------------------------------------------------------------------------
8303 url String The url for the action (defaults to the form's url)
8304 method String The form method to use (defaults to the form's method, or POST if not defined)
8305 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8306 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8307 validate the form on the client (defaults to false)
8309 * @return {BasicForm} this
8311 doAction : function(action, options){
8312 if(typeof action == 'string'){
8313 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8315 if(this.fireEvent('beforeaction', this, action) !== false){
8316 this.beforeAction(action);
8317 action.run.defer(100, action);
8323 beforeAction : function(action){
8324 var o = action.options;
8329 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8331 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8334 // not really supported yet.. ??
8336 //if(this.waitMsgTarget === true){
8337 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8338 //}else if(this.waitMsgTarget){
8339 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8340 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8342 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8348 afterAction : function(action, success){
8349 this.activeAction = null;
8350 var o = action.options;
8355 Roo.get(document.body).unmask();
8361 //if(this.waitMsgTarget === true){
8362 // this.el.unmask();
8363 //}else if(this.waitMsgTarget){
8364 // this.waitMsgTarget.unmask();
8366 // Roo.MessageBox.updateProgress(1);
8367 // Roo.MessageBox.hide();
8374 Roo.callback(o.success, o.scope, [this, action]);
8375 this.fireEvent('actioncomplete', this, action);
8379 // failure condition..
8380 // we have a scenario where updates need confirming.
8381 // eg. if a locking scenario exists..
8382 // we look for { errors : { needs_confirm : true }} in the response.
8384 (typeof(action.result) != 'undefined') &&
8385 (typeof(action.result.errors) != 'undefined') &&
8386 (typeof(action.result.errors.needs_confirm) != 'undefined')
8389 Roo.log("not supported yet");
8392 Roo.MessageBox.confirm(
8393 "Change requires confirmation",
8394 action.result.errorMsg,
8399 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8409 Roo.callback(o.failure, o.scope, [this, action]);
8410 // show an error message if no failed handler is set..
8411 if (!this.hasListener('actionfailed')) {
8412 Roo.log("need to add dialog support");
8414 Roo.MessageBox.alert("Error",
8415 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8416 action.result.errorMsg :
8417 "Saving Failed, please check your entries or try again"
8422 this.fireEvent('actionfailed', this, action);
8427 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8428 * @param {String} id The value to search for
8431 findField : function(id){
8432 var items = this.getItems();
8433 var field = items.get(id);
8435 items.each(function(f){
8436 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8443 return field || null;
8446 * Mark fields in this form invalid in bulk.
8447 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8448 * @return {BasicForm} this
8450 markInvalid : function(errors){
8451 if(errors instanceof Array){
8452 for(var i = 0, len = errors.length; i < len; i++){
8453 var fieldError = errors[i];
8454 var f = this.findField(fieldError.id);
8456 f.markInvalid(fieldError.msg);
8462 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8463 field.markInvalid(errors[id]);
8467 //Roo.each(this.childForms || [], function (f) {
8468 // f.markInvalid(errors);
8475 * Set values for fields in this form in bulk.
8476 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8477 * @return {BasicForm} this
8479 setValues : function(values){
8480 if(values instanceof Array){ // array of objects
8481 for(var i = 0, len = values.length; i < len; i++){
8483 var f = this.findField(v.id);
8485 f.setValue(v.value);
8486 if(this.trackResetOnLoad){
8487 f.originalValue = f.getValue();
8491 }else{ // object hash
8494 if(typeof values[id] != 'function' && (field = this.findField(id))){
8496 if (field.setFromData &&
8498 field.displayField &&
8499 // combos' with local stores can
8500 // be queried via setValue()
8501 // to set their value..
8502 (field.store && !field.store.isLocal)
8506 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8507 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8508 field.setFromData(sd);
8510 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8512 field.setFromData(values);
8515 field.setValue(values[id]);
8519 if(this.trackResetOnLoad){
8520 field.originalValue = field.getValue();
8526 //Roo.each(this.childForms || [], function (f) {
8527 // f.setValues(values);
8534 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8535 * they are returned as an array.
8536 * @param {Boolean} asString
8539 getValues : function(asString){
8540 //if (this.childForms) {
8541 // copy values from the child forms
8542 // Roo.each(this.childForms, function (f) {
8543 // this.setValues(f.getValues());
8549 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8550 if(asString === true){
8553 return Roo.urlDecode(fs);
8557 * Returns the fields in this form as an object with key/value pairs.
8558 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8561 getFieldValues : function(with_hidden)
8563 var items = this.getItems();
8565 items.each(function(f){
8571 var v = f.getValue();
8573 if (f.inputType =='radio') {
8574 if (typeof(ret[f.getName()]) == 'undefined') {
8575 ret[f.getName()] = ''; // empty..
8578 if (!f.el.dom.checked) {
8586 if(f.xtype == 'MoneyField'){
8587 ret[f.currencyName] = f.getCurrency();
8590 // not sure if this supported any more..
8591 if ((typeof(v) == 'object') && f.getRawValue) {
8592 v = f.getRawValue() ; // dates..
8594 // combo boxes where name != hiddenName...
8595 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8596 ret[f.name] = f.getRawValue();
8598 ret[f.getName()] = v;
8605 * Clears all invalid messages in this form.
8606 * @return {BasicForm} this
8608 clearInvalid : function(){
8609 var items = this.getItems();
8611 items.each(function(f){
8620 * @return {BasicForm} this
8623 var items = this.getItems();
8624 items.each(function(f){
8628 Roo.each(this.childForms || [], function (f) {
8636 getItems : function()
8638 var r=new Roo.util.MixedCollection(false, function(o){
8639 return o.id || (o.id = Roo.id());
8641 var iter = function(el) {
8648 Roo.each(el.items,function(e) {
8657 hideFields : function(items)
8659 Roo.each(items, function(i){
8661 var f = this.findField(i);
8672 showFields : function(items)
8674 Roo.each(items, function(i){
8676 var f = this.findField(i);
8689 Roo.apply(Roo.bootstrap.Form, {
8716 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8717 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8718 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8719 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8722 this.maskEl.top.enableDisplayMode("block");
8723 this.maskEl.left.enableDisplayMode("block");
8724 this.maskEl.bottom.enableDisplayMode("block");
8725 this.maskEl.right.enableDisplayMode("block");
8727 this.toolTip = new Roo.bootstrap.Tooltip({
8728 cls : 'roo-form-error-popover',
8730 'left' : ['r-l', [-2,0], 'right'],
8731 'right' : ['l-r', [2,0], 'left'],
8732 'bottom' : ['tl-bl', [0,2], 'top'],
8733 'top' : [ 'bl-tl', [0,-2], 'bottom']
8737 this.toolTip.render(Roo.get(document.body));
8739 this.toolTip.el.enableDisplayMode("block");
8741 Roo.get(document.body).on('click', function(){
8745 Roo.get(document.body).on('touchstart', function(){
8749 this.isApplied = true
8752 mask : function(form, target)
8756 this.target = target;
8758 if(!this.form.errorMask || !target.el){
8762 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8764 Roo.log(scrollable);
8766 var ot = this.target.el.calcOffsetsTo(scrollable);
8768 var scrollTo = ot[1] - this.form.maskOffset;
8770 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8772 scrollable.scrollTo('top', scrollTo);
8774 var box = this.target.el.getBox();
8776 var zIndex = Roo.bootstrap.Modal.zIndex++;
8779 this.maskEl.top.setStyle('position', 'absolute');
8780 this.maskEl.top.setStyle('z-index', zIndex);
8781 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8782 this.maskEl.top.setLeft(0);
8783 this.maskEl.top.setTop(0);
8784 this.maskEl.top.show();
8786 this.maskEl.left.setStyle('position', 'absolute');
8787 this.maskEl.left.setStyle('z-index', zIndex);
8788 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8789 this.maskEl.left.setLeft(0);
8790 this.maskEl.left.setTop(box.y - this.padding);
8791 this.maskEl.left.show();
8793 this.maskEl.bottom.setStyle('position', 'absolute');
8794 this.maskEl.bottom.setStyle('z-index', zIndex);
8795 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8796 this.maskEl.bottom.setLeft(0);
8797 this.maskEl.bottom.setTop(box.bottom + this.padding);
8798 this.maskEl.bottom.show();
8800 this.maskEl.right.setStyle('position', 'absolute');
8801 this.maskEl.right.setStyle('z-index', zIndex);
8802 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8803 this.maskEl.right.setLeft(box.right + this.padding);
8804 this.maskEl.right.setTop(box.y - this.padding);
8805 this.maskEl.right.show();
8807 this.toolTip.bindEl = this.target.el;
8809 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8811 var tip = this.target.blankText;
8813 if(this.target.getValue() !== '' ) {
8815 if (this.target.invalidText.length) {
8816 tip = this.target.invalidText;
8817 } else if (this.target.regexText.length){
8818 tip = this.target.regexText;
8822 this.toolTip.show(tip);
8824 this.intervalID = window.setInterval(function() {
8825 Roo.bootstrap.Form.popover.unmask();
8828 window.onwheel = function(){ return false;};
8830 (function(){ this.isMasked = true; }).defer(500, this);
8836 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8840 this.maskEl.top.setStyle('position', 'absolute');
8841 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8842 this.maskEl.top.hide();
8844 this.maskEl.left.setStyle('position', 'absolute');
8845 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8846 this.maskEl.left.hide();
8848 this.maskEl.bottom.setStyle('position', 'absolute');
8849 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8850 this.maskEl.bottom.hide();
8852 this.maskEl.right.setStyle('position', 'absolute');
8853 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8854 this.maskEl.right.hide();
8856 this.toolTip.hide();
8858 this.toolTip.el.hide();
8860 window.onwheel = function(){ return true;};
8862 if(this.intervalID){
8863 window.clearInterval(this.intervalID);
8864 this.intervalID = false;
8867 this.isMasked = false;
8877 * Ext JS Library 1.1.1
8878 * Copyright(c) 2006-2007, Ext JS, LLC.
8880 * Originally Released Under LGPL - original licence link has changed is not relivant.
8883 * <script type="text/javascript">
8886 * @class Roo.form.VTypes
8887 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8890 Roo.form.VTypes = function(){
8891 // closure these in so they are only created once.
8892 var alpha = /^[a-zA-Z_]+$/;
8893 var alphanum = /^[a-zA-Z0-9_]+$/;
8894 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8895 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8897 // All these messages and functions are configurable
8900 * The function used to validate email addresses
8901 * @param {String} value The email address
8903 'email' : function(v){
8904 return email.test(v);
8907 * The error text to display when the email validation function returns false
8910 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8912 * The keystroke filter mask to be applied on email input
8915 'emailMask' : /[a-z0-9_\.\-@]/i,
8918 * The function used to validate URLs
8919 * @param {String} value The URL
8921 'url' : function(v){
8925 * The error text to display when the url validation function returns false
8928 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8931 * The function used to validate alpha values
8932 * @param {String} value The value
8934 'alpha' : function(v){
8935 return alpha.test(v);
8938 * The error text to display when the alpha validation function returns false
8941 'alphaText' : 'This field should only contain letters and _',
8943 * The keystroke filter mask to be applied on alpha input
8946 'alphaMask' : /[a-z_]/i,
8949 * The function used to validate alphanumeric values
8950 * @param {String} value The value
8952 'alphanum' : function(v){
8953 return alphanum.test(v);
8956 * The error text to display when the alphanumeric validation function returns false
8959 'alphanumText' : 'This field should only contain letters, numbers and _',
8961 * The keystroke filter mask to be applied on alphanumeric input
8964 'alphanumMask' : /[a-z0-9_]/i
8974 * @class Roo.bootstrap.Input
8975 * @extends Roo.bootstrap.Component
8976 * Bootstrap Input class
8977 * @cfg {Boolean} disabled is it disabled
8978 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8979 * @cfg {String} name name of the input
8980 * @cfg {string} fieldLabel - the label associated
8981 * @cfg {string} placeholder - placeholder to put in text.
8982 * @cfg {string} before - input group add on before
8983 * @cfg {string} after - input group add on after
8984 * @cfg {string} size - (lg|sm) or leave empty..
8985 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8986 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8987 * @cfg {Number} md colspan out of 12 for computer-sized screens
8988 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8989 * @cfg {string} value default value of the input
8990 * @cfg {Number} labelWidth set the width of label
8991 * @cfg {Number} labellg set the width of label (1-12)
8992 * @cfg {Number} labelmd set the width of label (1-12)
8993 * @cfg {Number} labelsm set the width of label (1-12)
8994 * @cfg {Number} labelxs set the width of label (1-12)
8995 * @cfg {String} labelAlign (top|left)
8996 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8997 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8998 * @cfg {String} indicatorpos (left|right) default left
8999 * @cfg {String} capture (user|camera) use for file input only. (default empty)
9000 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9002 * @cfg {String} align (left|center|right) Default left
9003 * @cfg {Boolean} forceFeedback (true|false) Default false
9006 * Create a new Input
9007 * @param {Object} config The config object
9010 Roo.bootstrap.Input = function(config){
9012 Roo.bootstrap.Input.superclass.constructor.call(this, config);
9017 * Fires when this field receives input focus.
9018 * @param {Roo.form.Field} this
9023 * Fires when this field loses input focus.
9024 * @param {Roo.form.Field} this
9029 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9030 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9031 * @param {Roo.form.Field} this
9032 * @param {Roo.EventObject} e The event object
9037 * Fires just before the field blurs if the field value has changed.
9038 * @param {Roo.form.Field} this
9039 * @param {Mixed} newValue The new value
9040 * @param {Mixed} oldValue The original value
9045 * Fires after the field has been marked as invalid.
9046 * @param {Roo.form.Field} this
9047 * @param {String} msg The validation message
9052 * Fires after the field has been validated with no errors.
9053 * @param {Roo.form.Field} this
9058 * Fires after the key up
9059 * @param {Roo.form.Field} this
9060 * @param {Roo.EventObject} e The event Object
9066 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
9068 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9069 automatic validation (defaults to "keyup").
9071 validationEvent : "keyup",
9073 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9075 validateOnBlur : true,
9077 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9079 validationDelay : 250,
9081 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9083 focusClass : "x-form-focus", // not needed???
9087 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9089 invalidClass : "has-warning",
9092 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9094 validClass : "has-success",
9097 * @cfg {Boolean} hasFeedback (true|false) default true
9102 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9104 invalidFeedbackClass : "glyphicon-warning-sign",
9107 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9109 validFeedbackClass : "glyphicon-ok",
9112 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9114 selectOnFocus : false,
9117 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9121 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9126 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9128 disableKeyFilter : false,
9131 * @cfg {Boolean} disabled True to disable the field (defaults to false).
9135 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9139 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9141 blankText : "Please complete this mandatory field",
9144 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9148 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9150 maxLength : Number.MAX_VALUE,
9152 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9154 minLengthText : "The minimum length for this field is {0}",
9156 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9158 maxLengthText : "The maximum length for this field is {0}",
9162 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9163 * If available, this function will be called only after the basic validators all return true, and will be passed the
9164 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9168 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9169 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9170 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9174 * @cfg {String} regexText -- Depricated - use Invalid Text
9179 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9185 autocomplete: false,
9204 formatedValue : false,
9205 forceFeedback : false,
9207 indicatorpos : 'left',
9217 parentLabelAlign : function()
9220 while (parent.parent()) {
9221 parent = parent.parent();
9222 if (typeof(parent.labelAlign) !='undefined') {
9223 return parent.labelAlign;
9230 getAutoCreate : function()
9232 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9238 if(this.inputType != 'hidden'){
9239 cfg.cls = 'form-group' //input-group
9245 type : this.inputType,
9247 cls : 'form-control',
9248 placeholder : this.placeholder || '',
9249 autocomplete : this.autocomplete || 'new-password'
9252 if(this.capture.length){
9253 input.capture = this.capture;
9256 if(this.accept.length){
9257 input.accept = this.accept + "/*";
9261 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9264 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9265 input.maxLength = this.maxLength;
9268 if (this.disabled) {
9269 input.disabled=true;
9272 if (this.readOnly) {
9273 input.readonly=true;
9277 input.name = this.name;
9281 input.cls += ' input-' + this.size;
9285 ['xs','sm','md','lg'].map(function(size){
9286 if (settings[size]) {
9287 cfg.cls += ' col-' + size + '-' + settings[size];
9291 var inputblock = input;
9295 cls: 'glyphicon form-control-feedback'
9298 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9301 cls : 'has-feedback',
9309 if (this.before || this.after) {
9312 cls : 'input-group',
9316 if (this.before && typeof(this.before) == 'string') {
9318 inputblock.cn.push({
9320 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9324 if (this.before && typeof(this.before) == 'object') {
9325 this.before = Roo.factory(this.before);
9327 inputblock.cn.push({
9329 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9330 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9334 inputblock.cn.push(input);
9336 if (this.after && typeof(this.after) == 'string') {
9337 inputblock.cn.push({
9339 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9343 if (this.after && typeof(this.after) == 'object') {
9344 this.after = Roo.factory(this.after);
9346 inputblock.cn.push({
9348 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9349 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9353 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9354 inputblock.cls += ' has-feedback';
9355 inputblock.cn.push(feedback);
9360 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9361 tooltip : 'This field is required'
9363 if (Roo.bootstrap.version == 4) {
9366 style : 'display-none'
9369 if (align ==='left' && this.fieldLabel.length) {
9371 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
9378 cls : 'control-label col-form-label',
9379 html : this.fieldLabel
9390 var labelCfg = cfg.cn[1];
9391 var contentCfg = cfg.cn[2];
9393 if(this.indicatorpos == 'right'){
9398 cls : 'control-label col-form-label',
9402 html : this.fieldLabel
9416 labelCfg = cfg.cn[0];
9417 contentCfg = cfg.cn[1];
9421 if(this.labelWidth > 12){
9422 labelCfg.style = "width: " + this.labelWidth + 'px';
9425 if(this.labelWidth < 13 && this.labelmd == 0){
9426 this.labelmd = this.labelWidth;
9429 if(this.labellg > 0){
9430 labelCfg.cls += ' col-lg-' + this.labellg;
9431 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9434 if(this.labelmd > 0){
9435 labelCfg.cls += ' col-md-' + this.labelmd;
9436 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9439 if(this.labelsm > 0){
9440 labelCfg.cls += ' col-sm-' + this.labelsm;
9441 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9444 if(this.labelxs > 0){
9445 labelCfg.cls += ' col-xs-' + this.labelxs;
9446 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9450 } else if ( this.fieldLabel.length) {
9455 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9456 tooltip : 'This field is required'
9460 //cls : 'input-group-addon',
9461 html : this.fieldLabel
9469 if(this.indicatorpos == 'right'){
9474 //cls : 'input-group-addon',
9475 html : this.fieldLabel
9480 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9481 tooltip : 'This field is required'
9501 if (this.parentType === 'Navbar' && this.parent().bar) {
9502 cfg.cls += ' navbar-form';
9505 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9506 // on BS4 we do this only if not form
9507 cfg.cls += ' navbar-form';
9515 * return the real input element.
9517 inputEl: function ()
9519 return this.el.select('input.form-control',true).first();
9522 tooltipEl : function()
9524 return this.inputEl();
9527 indicatorEl : function()
9529 if (Roo.bootstrap.version == 4) {
9530 return false; // not enabled in v4 yet.
9533 var indicator = this.el.select('i.roo-required-indicator',true).first();
9543 setDisabled : function(v)
9545 var i = this.inputEl().dom;
9547 i.removeAttribute('disabled');
9551 i.setAttribute('disabled','true');
9553 initEvents : function()
9556 this.inputEl().on("keydown" , this.fireKey, this);
9557 this.inputEl().on("focus", this.onFocus, this);
9558 this.inputEl().on("blur", this.onBlur, this);
9560 this.inputEl().relayEvent('keyup', this);
9562 this.indicator = this.indicatorEl();
9565 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9568 // reference to original value for reset
9569 this.originalValue = this.getValue();
9570 //Roo.form.TextField.superclass.initEvents.call(this);
9571 if(this.validationEvent == 'keyup'){
9572 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9573 this.inputEl().on('keyup', this.filterValidation, this);
9575 else if(this.validationEvent !== false){
9576 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9579 if(this.selectOnFocus){
9580 this.on("focus", this.preFocus, this);
9583 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9584 this.inputEl().on("keypress", this.filterKeys, this);
9586 this.inputEl().relayEvent('keypress', this);
9589 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9590 this.el.on("click", this.autoSize, this);
9593 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9594 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9597 if (typeof(this.before) == 'object') {
9598 this.before.render(this.el.select('.roo-input-before',true).first());
9600 if (typeof(this.after) == 'object') {
9601 this.after.render(this.el.select('.roo-input-after',true).first());
9604 this.inputEl().on('change', this.onChange, this);
9607 filterValidation : function(e){
9608 if(!e.isNavKeyPress()){
9609 this.validationTask.delay(this.validationDelay);
9613 * Validates the field value
9614 * @return {Boolean} True if the value is valid, else false
9616 validate : function(){
9617 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9618 if(this.disabled || this.validateValue(this.getRawValue())){
9629 * Validates a value according to the field's validation rules and marks the field as invalid
9630 * if the validation fails
9631 * @param {Mixed} value The value to validate
9632 * @return {Boolean} True if the value is valid, else false
9634 validateValue : function(value)
9636 if(this.getVisibilityEl().hasClass('hidden')){
9640 if(value.length < 1) { // if it's blank
9641 if(this.allowBlank){
9647 if(value.length < this.minLength){
9650 if(value.length > this.maxLength){
9654 var vt = Roo.form.VTypes;
9655 if(!vt[this.vtype](value, this)){
9659 if(typeof this.validator == "function"){
9660 var msg = this.validator(value);
9664 if (typeof(msg) == 'string') {
9665 this.invalidText = msg;
9669 if(this.regex && !this.regex.test(value)){
9677 fireKey : function(e){
9678 //Roo.log('field ' + e.getKey());
9679 if(e.isNavKeyPress()){
9680 this.fireEvent("specialkey", this, e);
9683 focus : function (selectText){
9685 this.inputEl().focus();
9686 if(selectText === true){
9687 this.inputEl().dom.select();
9693 onFocus : function(){
9694 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9695 // this.el.addClass(this.focusClass);
9698 this.hasFocus = true;
9699 this.startValue = this.getValue();
9700 this.fireEvent("focus", this);
9704 beforeBlur : Roo.emptyFn,
9708 onBlur : function(){
9710 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9711 //this.el.removeClass(this.focusClass);
9713 this.hasFocus = false;
9714 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9717 var v = this.getValue();
9718 if(String(v) !== String(this.startValue)){
9719 this.fireEvent('change', this, v, this.startValue);
9721 this.fireEvent("blur", this);
9724 onChange : function(e)
9726 var v = this.getValue();
9727 if(String(v) !== String(this.startValue)){
9728 this.fireEvent('change', this, v, this.startValue);
9734 * Resets the current field value to the originally loaded value and clears any validation messages
9737 this.setValue(this.originalValue);
9741 * Returns the name of the field
9742 * @return {Mixed} name The name field
9744 getName: function(){
9748 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9749 * @return {Mixed} value The field value
9751 getValue : function(){
9753 var v = this.inputEl().getValue();
9758 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9759 * @return {Mixed} value The field value
9761 getRawValue : function(){
9762 var v = this.inputEl().getValue();
9768 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9769 * @param {Mixed} value The value to set
9771 setRawValue : function(v){
9772 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9775 selectText : function(start, end){
9776 var v = this.getRawValue();
9778 start = start === undefined ? 0 : start;
9779 end = end === undefined ? v.length : end;
9780 var d = this.inputEl().dom;
9781 if(d.setSelectionRange){
9782 d.setSelectionRange(start, end);
9783 }else if(d.createTextRange){
9784 var range = d.createTextRange();
9785 range.moveStart("character", start);
9786 range.moveEnd("character", v.length-end);
9793 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9794 * @param {Mixed} value The value to set
9796 setValue : function(v){
9799 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9805 processValue : function(value){
9806 if(this.stripCharsRe){
9807 var newValue = value.replace(this.stripCharsRe, '');
9808 if(newValue !== value){
9809 this.setRawValue(newValue);
9816 preFocus : function(){
9818 if(this.selectOnFocus){
9819 this.inputEl().dom.select();
9822 filterKeys : function(e){
9824 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9827 var c = e.getCharCode(), cc = String.fromCharCode(c);
9828 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9831 if(!this.maskRe.test(cc)){
9836 * Clear any invalid styles/messages for this field
9838 clearInvalid : function(){
9840 if(!this.el || this.preventMark){ // not rendered
9845 this.el.removeClass([this.invalidClass, 'is-invalid']);
9847 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9849 var feedback = this.el.select('.form-control-feedback', true).first();
9852 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9858 this.indicator.removeClass('visible');
9859 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9862 this.fireEvent('valid', this);
9866 * Mark this field as valid
9868 markValid : function()
9870 if(!this.el || this.preventMark){ // not rendered...
9874 this.el.removeClass([this.invalidClass, this.validClass]);
9875 this.inputEl().removeClass(['is-valid', 'is-invalid']);
9877 var feedback = this.el.select('.form-control-feedback', true).first();
9880 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9884 this.indicator.removeClass('visible');
9885 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9892 if(this.allowBlank && !this.getRawValue().length){
9895 if (Roo.bootstrap.version == 3) {
9896 this.el.addClass(this.validClass);
9898 this.inputEl().addClass('is-valid');
9901 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9903 var feedback = this.el.select('.form-control-feedback', true).first();
9906 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9907 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9912 this.fireEvent('valid', this);
9916 * Mark this field as invalid
9917 * @param {String} msg The validation message
9919 markInvalid : function(msg)
9921 if(!this.el || this.preventMark){ // not rendered
9925 this.el.removeClass([this.invalidClass, this.validClass]);
9926 this.inputEl().removeClass(['is-valid', 'is-invalid']);
9928 var feedback = this.el.select('.form-control-feedback', true).first();
9931 this.el.select('.form-control-feedback', true).first().removeClass(
9932 [this.invalidFeedbackClass, this.validFeedbackClass]);
9939 if(this.allowBlank && !this.getRawValue().length){
9944 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9945 this.indicator.addClass('visible');
9947 if (Roo.bootstrap.version == 3) {
9948 this.el.addClass(this.invalidClass);
9950 this.inputEl().addClass('is-invalid');
9955 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9957 var feedback = this.el.select('.form-control-feedback', true).first();
9960 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9962 if(this.getValue().length || this.forceFeedback){
9963 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9970 this.fireEvent('invalid', this, msg);
9973 SafariOnKeyDown : function(event)
9975 // this is a workaround for a password hang bug on chrome/ webkit.
9976 if (this.inputEl().dom.type != 'password') {
9980 var isSelectAll = false;
9982 if(this.inputEl().dom.selectionEnd > 0){
9983 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9985 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9986 event.preventDefault();
9991 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9993 event.preventDefault();
9994 // this is very hacky as keydown always get's upper case.
9996 var cc = String.fromCharCode(event.getCharCode());
9997 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
10001 adjustWidth : function(tag, w){
10002 tag = tag.toLowerCase();
10003 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10004 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10005 if(tag == 'input'){
10008 if(tag == 'textarea'){
10011 }else if(Roo.isOpera){
10012 if(tag == 'input'){
10015 if(tag == 'textarea'){
10023 setFieldLabel : function(v)
10025 if(!this.rendered){
10029 if(this.indicatorEl()){
10030 var ar = this.el.select('label > span',true);
10032 if (ar.elements.length) {
10033 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10034 this.fieldLabel = v;
10038 var br = this.el.select('label',true);
10040 if(br.elements.length) {
10041 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10042 this.fieldLabel = v;
10046 Roo.log('Cannot Found any of label > span || label in input');
10050 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10051 this.fieldLabel = v;
10066 * @class Roo.bootstrap.TextArea
10067 * @extends Roo.bootstrap.Input
10068 * Bootstrap TextArea class
10069 * @cfg {Number} cols Specifies the visible width of a text area
10070 * @cfg {Number} rows Specifies the visible number of lines in a text area
10071 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10072 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10073 * @cfg {string} html text
10076 * Create a new TextArea
10077 * @param {Object} config The config object
10080 Roo.bootstrap.TextArea = function(config){
10081 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10085 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
10095 getAutoCreate : function(){
10097 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10103 if(this.inputType != 'hidden'){
10104 cfg.cls = 'form-group' //input-group
10112 value : this.value || '',
10113 html: this.html || '',
10114 cls : 'form-control',
10115 placeholder : this.placeholder || ''
10119 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10120 input.maxLength = this.maxLength;
10124 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10128 input.cols = this.cols;
10131 if (this.readOnly) {
10132 input.readonly = true;
10136 input.name = this.name;
10140 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10144 ['xs','sm','md','lg'].map(function(size){
10145 if (settings[size]) {
10146 cfg.cls += ' col-' + size + '-' + settings[size];
10150 var inputblock = input;
10152 if(this.hasFeedback && !this.allowBlank){
10156 cls: 'glyphicon form-control-feedback'
10160 cls : 'has-feedback',
10169 if (this.before || this.after) {
10172 cls : 'input-group',
10176 inputblock.cn.push({
10178 cls : 'input-group-addon',
10183 inputblock.cn.push(input);
10185 if(this.hasFeedback && !this.allowBlank){
10186 inputblock.cls += ' has-feedback';
10187 inputblock.cn.push(feedback);
10191 inputblock.cn.push({
10193 cls : 'input-group-addon',
10200 if (align ==='left' && this.fieldLabel.length) {
10205 cls : 'control-label',
10206 html : this.fieldLabel
10217 if(this.labelWidth > 12){
10218 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10221 if(this.labelWidth < 13 && this.labelmd == 0){
10222 this.labelmd = this.labelWidth;
10225 if(this.labellg > 0){
10226 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10227 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10230 if(this.labelmd > 0){
10231 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10232 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10235 if(this.labelsm > 0){
10236 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10237 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10240 if(this.labelxs > 0){
10241 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10242 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10245 } else if ( this.fieldLabel.length) {
10250 //cls : 'input-group-addon',
10251 html : this.fieldLabel
10269 if (this.disabled) {
10270 input.disabled=true;
10277 * return the real textarea element.
10279 inputEl: function ()
10281 return this.el.select('textarea.form-control',true).first();
10285 * Clear any invalid styles/messages for this field
10287 clearInvalid : function()
10290 if(!this.el || this.preventMark){ // not rendered
10294 var label = this.el.select('label', true).first();
10295 var icon = this.el.select('i.fa-star', true).first();
10300 this.el.removeClass( this.validClass);
10301 this.inputEl().removeClass('is-invalid');
10303 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10305 var feedback = this.el.select('.form-control-feedback', true).first();
10308 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10313 this.fireEvent('valid', this);
10317 * Mark this field as valid
10319 markValid : function()
10321 if(!this.el || this.preventMark){ // not rendered
10325 this.el.removeClass([this.invalidClass, this.validClass]);
10326 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10328 var feedback = this.el.select('.form-control-feedback', true).first();
10331 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10334 if(this.disabled || this.allowBlank){
10338 var label = this.el.select('label', true).first();
10339 var icon = this.el.select('i.fa-star', true).first();
10344 if (Roo.bootstrap.version == 3) {
10345 this.el.addClass(this.validClass);
10347 this.inputEl().addClass('is-valid');
10351 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10353 var feedback = this.el.select('.form-control-feedback', true).first();
10356 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10357 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10362 this.fireEvent('valid', this);
10366 * Mark this field as invalid
10367 * @param {String} msg The validation message
10369 markInvalid : function(msg)
10371 if(!this.el || this.preventMark){ // not rendered
10375 this.el.removeClass([this.invalidClass, this.validClass]);
10376 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10378 var feedback = this.el.select('.form-control-feedback', true).first();
10381 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10384 if(this.disabled || this.allowBlank){
10388 var label = this.el.select('label', true).first();
10389 var icon = this.el.select('i.fa-star', true).first();
10391 if(!this.getValue().length && label && !icon){
10392 this.el.createChild({
10394 cls : 'text-danger fa fa-lg fa-star',
10395 tooltip : 'This field is required',
10396 style : 'margin-right:5px;'
10400 if (Roo.bootstrap.version == 3) {
10401 this.el.addClass(this.invalidClass);
10403 this.inputEl().addClass('is-invalid');
10406 // fixme ... this may be depricated need to test..
10407 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10409 var feedback = this.el.select('.form-control-feedback', true).first();
10412 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10414 if(this.getValue().length || this.forceFeedback){
10415 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10422 this.fireEvent('invalid', this, msg);
10430 * trigger field - base class for combo..
10435 * @class Roo.bootstrap.TriggerField
10436 * @extends Roo.bootstrap.Input
10437 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10438 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10439 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10440 * for which you can provide a custom implementation. For example:
10442 var trigger = new Roo.bootstrap.TriggerField();
10443 trigger.onTriggerClick = myTriggerFn;
10444 trigger.applyTo('my-field');
10447 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10448 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10449 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10450 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10451 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10454 * Create a new TriggerField.
10455 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10456 * to the base TextField)
10458 Roo.bootstrap.TriggerField = function(config){
10459 this.mimicing = false;
10460 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10463 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10465 * @cfg {String} triggerClass A CSS class to apply to the trigger
10468 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10473 * @cfg {Boolean} removable (true|false) special filter default false
10477 /** @cfg {Boolean} grow @hide */
10478 /** @cfg {Number} growMin @hide */
10479 /** @cfg {Number} growMax @hide */
10485 autoSize: Roo.emptyFn,
10489 deferHeight : true,
10492 actionMode : 'wrap',
10497 getAutoCreate : function(){
10499 var align = this.labelAlign || this.parentLabelAlign();
10504 cls: 'form-group' //input-group
10511 type : this.inputType,
10512 cls : 'form-control',
10513 autocomplete: 'new-password',
10514 placeholder : this.placeholder || ''
10518 input.name = this.name;
10521 input.cls += ' input-' + this.size;
10524 if (this.disabled) {
10525 input.disabled=true;
10528 var inputblock = input;
10530 if(this.hasFeedback && !this.allowBlank){
10534 cls: 'glyphicon form-control-feedback'
10537 if(this.removable && !this.editable && !this.tickable){
10539 cls : 'has-feedback',
10545 cls : 'roo-combo-removable-btn close'
10552 cls : 'has-feedback',
10561 if(this.removable && !this.editable && !this.tickable){
10563 cls : 'roo-removable',
10569 cls : 'roo-combo-removable-btn close'
10576 if (this.before || this.after) {
10579 cls : 'input-group',
10583 inputblock.cn.push({
10585 cls : 'input-group-addon input-group-prepend input-group-text',
10590 inputblock.cn.push(input);
10592 if(this.hasFeedback && !this.allowBlank){
10593 inputblock.cls += ' has-feedback';
10594 inputblock.cn.push(feedback);
10598 inputblock.cn.push({
10600 cls : 'input-group-addon input-group-append input-group-text',
10609 var ibwrap = inputblock;
10614 cls: 'roo-select2-choices',
10618 cls: 'roo-select2-search-field',
10630 cls: 'roo-select2-container input-group',
10635 cls: 'form-hidden-field'
10641 if(!this.multiple && this.showToggleBtn){
10647 if (this.caret != false) {
10650 cls: 'fa fa-' + this.caret
10657 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10662 cls: 'combobox-clear',
10676 combobox.cls += ' roo-select2-container-multi';
10680 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10681 tooltip : 'This field is required'
10683 if (Roo.bootstrap.version == 4) {
10686 style : 'display:none'
10691 if (align ==='left' && this.fieldLabel.length) {
10693 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10700 cls : 'control-label',
10701 html : this.fieldLabel
10713 var labelCfg = cfg.cn[1];
10714 var contentCfg = cfg.cn[2];
10716 if(this.indicatorpos == 'right'){
10721 cls : 'control-label',
10725 html : this.fieldLabel
10739 labelCfg = cfg.cn[0];
10740 contentCfg = cfg.cn[1];
10743 if(this.labelWidth > 12){
10744 labelCfg.style = "width: " + this.labelWidth + 'px';
10747 if(this.labelWidth < 13 && this.labelmd == 0){
10748 this.labelmd = this.labelWidth;
10751 if(this.labellg > 0){
10752 labelCfg.cls += ' col-lg-' + this.labellg;
10753 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10756 if(this.labelmd > 0){
10757 labelCfg.cls += ' col-md-' + this.labelmd;
10758 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10761 if(this.labelsm > 0){
10762 labelCfg.cls += ' col-sm-' + this.labelsm;
10763 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10766 if(this.labelxs > 0){
10767 labelCfg.cls += ' col-xs-' + this.labelxs;
10768 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10771 } else if ( this.fieldLabel.length) {
10772 // Roo.log(" label");
10777 //cls : 'input-group-addon',
10778 html : this.fieldLabel
10786 if(this.indicatorpos == 'right'){
10794 html : this.fieldLabel
10808 // Roo.log(" no label && no align");
10815 ['xs','sm','md','lg'].map(function(size){
10816 if (settings[size]) {
10817 cfg.cls += ' col-' + size + '-' + settings[size];
10828 onResize : function(w, h){
10829 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10830 // if(typeof w == 'number'){
10831 // var x = w - this.trigger.getWidth();
10832 // this.inputEl().setWidth(this.adjustWidth('input', x));
10833 // this.trigger.setStyle('left', x+'px');
10838 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10841 getResizeEl : function(){
10842 return this.inputEl();
10846 getPositionEl : function(){
10847 return this.inputEl();
10851 alignErrorIcon : function(){
10852 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10856 initEvents : function(){
10860 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10861 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10862 if(!this.multiple && this.showToggleBtn){
10863 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10864 if(this.hideTrigger){
10865 this.trigger.setDisplayed(false);
10867 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10871 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10874 if(this.removable && !this.editable && !this.tickable){
10875 var close = this.closeTriggerEl();
10878 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10879 close.on('click', this.removeBtnClick, this, close);
10883 //this.trigger.addClassOnOver('x-form-trigger-over');
10884 //this.trigger.addClassOnClick('x-form-trigger-click');
10887 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10891 closeTriggerEl : function()
10893 var close = this.el.select('.roo-combo-removable-btn', true).first();
10894 return close ? close : false;
10897 removeBtnClick : function(e, h, el)
10899 e.preventDefault();
10901 if(this.fireEvent("remove", this) !== false){
10903 this.fireEvent("afterremove", this)
10907 createList : function()
10909 this.list = Roo.get(document.body).createChild({
10910 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10911 cls: 'typeahead typeahead-long dropdown-menu',
10912 style: 'display:none'
10915 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10920 initTrigger : function(){
10925 onDestroy : function(){
10927 this.trigger.removeAllListeners();
10928 // this.trigger.remove();
10931 // this.wrap.remove();
10933 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10937 onFocus : function(){
10938 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10940 if(!this.mimicing){
10941 this.wrap.addClass('x-trigger-wrap-focus');
10942 this.mimicing = true;
10943 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10944 if(this.monitorTab){
10945 this.el.on("keydown", this.checkTab, this);
10952 checkTab : function(e){
10953 if(e.getKey() == e.TAB){
10954 this.triggerBlur();
10959 onBlur : function(){
10964 mimicBlur : function(e, t){
10966 if(!this.wrap.contains(t) && this.validateBlur()){
10967 this.triggerBlur();
10973 triggerBlur : function(){
10974 this.mimicing = false;
10975 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10976 if(this.monitorTab){
10977 this.el.un("keydown", this.checkTab, this);
10979 //this.wrap.removeClass('x-trigger-wrap-focus');
10980 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10984 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10985 validateBlur : function(e, t){
10990 onDisable : function(){
10991 this.inputEl().dom.disabled = true;
10992 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10994 // this.wrap.addClass('x-item-disabled');
10999 onEnable : function(){
11000 this.inputEl().dom.disabled = false;
11001 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11003 // this.el.removeClass('x-item-disabled');
11008 onShow : function(){
11009 var ae = this.getActionEl();
11012 ae.dom.style.display = '';
11013 ae.dom.style.visibility = 'visible';
11019 onHide : function(){
11020 var ae = this.getActionEl();
11021 ae.dom.style.display = 'none';
11025 * The function that should handle the trigger's click event. This method does nothing by default until overridden
11026 * by an implementing function.
11028 * @param {EventObject} e
11030 onTriggerClick : Roo.emptyFn
11034 * Ext JS Library 1.1.1
11035 * Copyright(c) 2006-2007, Ext JS, LLC.
11037 * Originally Released Under LGPL - original licence link has changed is not relivant.
11040 * <script type="text/javascript">
11045 * @class Roo.data.SortTypes
11047 * Defines the default sorting (casting?) comparison functions used when sorting data.
11049 Roo.data.SortTypes = {
11051 * Default sort that does nothing
11052 * @param {Mixed} s The value being converted
11053 * @return {Mixed} The comparison value
11055 none : function(s){
11060 * The regular expression used to strip tags
11064 stripTagsRE : /<\/?[^>]+>/gi,
11067 * Strips all HTML tags to sort on text only
11068 * @param {Mixed} s The value being converted
11069 * @return {String} The comparison value
11071 asText : function(s){
11072 return String(s).replace(this.stripTagsRE, "");
11076 * Strips all HTML tags to sort on text only - Case insensitive
11077 * @param {Mixed} s The value being converted
11078 * @return {String} The comparison value
11080 asUCText : function(s){
11081 return String(s).toUpperCase().replace(this.stripTagsRE, "");
11085 * Case insensitive string
11086 * @param {Mixed} s The value being converted
11087 * @return {String} The comparison value
11089 asUCString : function(s) {
11090 return String(s).toUpperCase();
11095 * @param {Mixed} s The value being converted
11096 * @return {Number} The comparison value
11098 asDate : function(s) {
11102 if(s instanceof Date){
11103 return s.getTime();
11105 return Date.parse(String(s));
11110 * @param {Mixed} s The value being converted
11111 * @return {Float} The comparison value
11113 asFloat : function(s) {
11114 var val = parseFloat(String(s).replace(/,/g, ""));
11123 * @param {Mixed} s The value being converted
11124 * @return {Number} The comparison value
11126 asInt : function(s) {
11127 var val = parseInt(String(s).replace(/,/g, ""));
11135 * Ext JS Library 1.1.1
11136 * Copyright(c) 2006-2007, Ext JS, LLC.
11138 * Originally Released Under LGPL - original licence link has changed is not relivant.
11141 * <script type="text/javascript">
11145 * @class Roo.data.Record
11146 * Instances of this class encapsulate both record <em>definition</em> information, and record
11147 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11148 * to access Records cached in an {@link Roo.data.Store} object.<br>
11150 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11151 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11154 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11156 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11157 * {@link #create}. The parameters are the same.
11158 * @param {Array} data An associative Array of data values keyed by the field name.
11159 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11160 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11161 * not specified an integer id is generated.
11163 Roo.data.Record = function(data, id){
11164 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11169 * Generate a constructor for a specific record layout.
11170 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11171 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11172 * Each field definition object may contain the following properties: <ul>
11173 * <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,
11174 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11175 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11176 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11177 * is being used, then this is a string containing the javascript expression to reference the data relative to
11178 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11179 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11180 * this may be omitted.</p></li>
11181 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11182 * <ul><li>auto (Default, implies no conversion)</li>
11187 * <li>date</li></ul></p></li>
11188 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11189 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11190 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11191 * by the Reader into an object that will be stored in the Record. It is passed the
11192 * following parameters:<ul>
11193 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11195 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11197 * <br>usage:<br><pre><code>
11198 var TopicRecord = Roo.data.Record.create(
11199 {name: 'title', mapping: 'topic_title'},
11200 {name: 'author', mapping: 'username'},
11201 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11202 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11203 {name: 'lastPoster', mapping: 'user2'},
11204 {name: 'excerpt', mapping: 'post_text'}
11207 var myNewRecord = new TopicRecord({
11208 title: 'Do my job please',
11211 lastPost: new Date(),
11212 lastPoster: 'Animal',
11213 excerpt: 'No way dude!'
11215 myStore.add(myNewRecord);
11220 Roo.data.Record.create = function(o){
11221 var f = function(){
11222 f.superclass.constructor.apply(this, arguments);
11224 Roo.extend(f, Roo.data.Record);
11225 var p = f.prototype;
11226 p.fields = new Roo.util.MixedCollection(false, function(field){
11229 for(var i = 0, len = o.length; i < len; i++){
11230 p.fields.add(new Roo.data.Field(o[i]));
11232 f.getField = function(name){
11233 return p.fields.get(name);
11238 Roo.data.Record.AUTO_ID = 1000;
11239 Roo.data.Record.EDIT = 'edit';
11240 Roo.data.Record.REJECT = 'reject';
11241 Roo.data.Record.COMMIT = 'commit';
11243 Roo.data.Record.prototype = {
11245 * Readonly flag - true if this record has been modified.
11254 join : function(store){
11255 this.store = store;
11259 * Set the named field to the specified value.
11260 * @param {String} name The name of the field to set.
11261 * @param {Object} value The value to set the field to.
11263 set : function(name, value){
11264 if(this.data[name] == value){
11268 if(!this.modified){
11269 this.modified = {};
11271 if(typeof this.modified[name] == 'undefined'){
11272 this.modified[name] = this.data[name];
11274 this.data[name] = value;
11275 if(!this.editing && this.store){
11276 this.store.afterEdit(this);
11281 * Get the value of the named field.
11282 * @param {String} name The name of the field to get the value of.
11283 * @return {Object} The value of the field.
11285 get : function(name){
11286 return this.data[name];
11290 beginEdit : function(){
11291 this.editing = true;
11292 this.modified = {};
11296 cancelEdit : function(){
11297 this.editing = false;
11298 delete this.modified;
11302 endEdit : function(){
11303 this.editing = false;
11304 if(this.dirty && this.store){
11305 this.store.afterEdit(this);
11310 * Usually called by the {@link Roo.data.Store} which owns the Record.
11311 * Rejects all changes made to the Record since either creation, or the last commit operation.
11312 * Modified fields are reverted to their original values.
11314 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11315 * of reject operations.
11317 reject : function(){
11318 var m = this.modified;
11320 if(typeof m[n] != "function"){
11321 this.data[n] = m[n];
11324 this.dirty = false;
11325 delete this.modified;
11326 this.editing = false;
11328 this.store.afterReject(this);
11333 * Usually called by the {@link Roo.data.Store} which owns the Record.
11334 * Commits all changes made to the Record since either creation, or the last commit operation.
11336 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11337 * of commit operations.
11339 commit : function(){
11340 this.dirty = false;
11341 delete this.modified;
11342 this.editing = false;
11344 this.store.afterCommit(this);
11349 hasError : function(){
11350 return this.error != null;
11354 clearError : function(){
11359 * Creates a copy of this record.
11360 * @param {String} id (optional) A new record id if you don't want to use this record's id
11363 copy : function(newId) {
11364 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11368 * Ext JS Library 1.1.1
11369 * Copyright(c) 2006-2007, Ext JS, LLC.
11371 * Originally Released Under LGPL - original licence link has changed is not relivant.
11374 * <script type="text/javascript">
11380 * @class Roo.data.Store
11381 * @extends Roo.util.Observable
11382 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11383 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11385 * 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
11386 * has no knowledge of the format of the data returned by the Proxy.<br>
11388 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11389 * instances from the data object. These records are cached and made available through accessor functions.
11391 * Creates a new Store.
11392 * @param {Object} config A config object containing the objects needed for the Store to access data,
11393 * and read the data into Records.
11395 Roo.data.Store = function(config){
11396 this.data = new Roo.util.MixedCollection(false);
11397 this.data.getKey = function(o){
11400 this.baseParams = {};
11402 this.paramNames = {
11407 "multisort" : "_multisort"
11410 if(config && config.data){
11411 this.inlineData = config.data;
11412 delete config.data;
11415 Roo.apply(this, config);
11417 if(this.reader){ // reader passed
11418 this.reader = Roo.factory(this.reader, Roo.data);
11419 this.reader.xmodule = this.xmodule || false;
11420 if(!this.recordType){
11421 this.recordType = this.reader.recordType;
11423 if(this.reader.onMetaChange){
11424 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11428 if(this.recordType){
11429 this.fields = this.recordType.prototype.fields;
11431 this.modified = [];
11435 * @event datachanged
11436 * Fires when the data cache has changed, and a widget which is using this Store
11437 * as a Record cache should refresh its view.
11438 * @param {Store} this
11440 datachanged : true,
11442 * @event metachange
11443 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11444 * @param {Store} this
11445 * @param {Object} meta The JSON metadata
11450 * Fires when Records have been added to the Store
11451 * @param {Store} this
11452 * @param {Roo.data.Record[]} records The array of Records added
11453 * @param {Number} index The index at which the record(s) were added
11458 * Fires when a Record has been removed from the Store
11459 * @param {Store} this
11460 * @param {Roo.data.Record} record The Record that was removed
11461 * @param {Number} index The index at which the record was removed
11466 * Fires when a Record has been updated
11467 * @param {Store} this
11468 * @param {Roo.data.Record} record The Record that was updated
11469 * @param {String} operation The update operation being performed. Value may be one of:
11471 Roo.data.Record.EDIT
11472 Roo.data.Record.REJECT
11473 Roo.data.Record.COMMIT
11479 * Fires when the data cache has been cleared.
11480 * @param {Store} this
11484 * @event beforeload
11485 * Fires before a request is made for a new data object. If the beforeload handler returns false
11486 * the load action will be canceled.
11487 * @param {Store} this
11488 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11492 * @event beforeloadadd
11493 * Fires after a new set of Records has been loaded.
11494 * @param {Store} this
11495 * @param {Roo.data.Record[]} records The Records that were loaded
11496 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11498 beforeloadadd : true,
11501 * Fires after a new set of Records has been loaded, before they are added to the store.
11502 * @param {Store} this
11503 * @param {Roo.data.Record[]} records The Records that were loaded
11504 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11505 * @params {Object} return from reader
11509 * @event loadexception
11510 * Fires if an exception occurs in the Proxy during loading.
11511 * Called with the signature of the Proxy's "loadexception" event.
11512 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11515 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11516 * @param {Object} load options
11517 * @param {Object} jsonData from your request (normally this contains the Exception)
11519 loadexception : true
11523 this.proxy = Roo.factory(this.proxy, Roo.data);
11524 this.proxy.xmodule = this.xmodule || false;
11525 this.relayEvents(this.proxy, ["loadexception"]);
11527 this.sortToggle = {};
11528 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11530 Roo.data.Store.superclass.constructor.call(this);
11532 if(this.inlineData){
11533 this.loadData(this.inlineData);
11534 delete this.inlineData;
11538 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11540 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11541 * without a remote query - used by combo/forms at present.
11545 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11548 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11551 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11552 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11555 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11556 * on any HTTP request
11559 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11562 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11566 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11567 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11569 remoteSort : false,
11572 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11573 * loaded or when a record is removed. (defaults to false).
11575 pruneModifiedRecords : false,
11578 lastOptions : null,
11581 * Add Records to the Store and fires the add event.
11582 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11584 add : function(records){
11585 records = [].concat(records);
11586 for(var i = 0, len = records.length; i < len; i++){
11587 records[i].join(this);
11589 var index = this.data.length;
11590 this.data.addAll(records);
11591 this.fireEvent("add", this, records, index);
11595 * Remove a Record from the Store and fires the remove event.
11596 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11598 remove : function(record){
11599 var index = this.data.indexOf(record);
11600 this.data.removeAt(index);
11602 if(this.pruneModifiedRecords){
11603 this.modified.remove(record);
11605 this.fireEvent("remove", this, record, index);
11609 * Remove all Records from the Store and fires the clear event.
11611 removeAll : function(){
11613 if(this.pruneModifiedRecords){
11614 this.modified = [];
11616 this.fireEvent("clear", this);
11620 * Inserts Records to the Store at the given index and fires the add event.
11621 * @param {Number} index The start index at which to insert the passed Records.
11622 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11624 insert : function(index, records){
11625 records = [].concat(records);
11626 for(var i = 0, len = records.length; i < len; i++){
11627 this.data.insert(index, records[i]);
11628 records[i].join(this);
11630 this.fireEvent("add", this, records, index);
11634 * Get the index within the cache of the passed Record.
11635 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11636 * @return {Number} The index of the passed Record. Returns -1 if not found.
11638 indexOf : function(record){
11639 return this.data.indexOf(record);
11643 * Get the index within the cache of the Record with the passed id.
11644 * @param {String} id The id of the Record to find.
11645 * @return {Number} The index of the Record. Returns -1 if not found.
11647 indexOfId : function(id){
11648 return this.data.indexOfKey(id);
11652 * Get the Record with the specified id.
11653 * @param {String} id The id of the Record to find.
11654 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11656 getById : function(id){
11657 return this.data.key(id);
11661 * Get the Record at the specified index.
11662 * @param {Number} index The index of the Record to find.
11663 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11665 getAt : function(index){
11666 return this.data.itemAt(index);
11670 * Returns a range of Records between specified indices.
11671 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11672 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11673 * @return {Roo.data.Record[]} An array of Records
11675 getRange : function(start, end){
11676 return this.data.getRange(start, end);
11680 storeOptions : function(o){
11681 o = Roo.apply({}, o);
11684 this.lastOptions = o;
11688 * Loads the Record cache from the configured Proxy using the configured Reader.
11690 * If using remote paging, then the first load call must specify the <em>start</em>
11691 * and <em>limit</em> properties in the options.params property to establish the initial
11692 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11694 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11695 * and this call will return before the new data has been loaded. Perform any post-processing
11696 * in a callback function, or in a "load" event handler.</strong>
11698 * @param {Object} options An object containing properties which control loading options:<ul>
11699 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11700 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11701 * passed the following arguments:<ul>
11702 * <li>r : Roo.data.Record[]</li>
11703 * <li>options: Options object from the load call</li>
11704 * <li>success: Boolean success indicator</li></ul></li>
11705 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11706 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11709 load : function(options){
11710 options = options || {};
11711 if(this.fireEvent("beforeload", this, options) !== false){
11712 this.storeOptions(options);
11713 var p = Roo.apply(options.params || {}, this.baseParams);
11714 // if meta was not loaded from remote source.. try requesting it.
11715 if (!this.reader.metaFromRemote) {
11716 p._requestMeta = 1;
11718 if(this.sortInfo && this.remoteSort){
11719 var pn = this.paramNames;
11720 p[pn["sort"]] = this.sortInfo.field;
11721 p[pn["dir"]] = this.sortInfo.direction;
11723 if (this.multiSort) {
11724 var pn = this.paramNames;
11725 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11728 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11733 * Reloads the Record cache from the configured Proxy using the configured Reader and
11734 * the options from the last load operation performed.
11735 * @param {Object} options (optional) An object containing properties which may override the options
11736 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11737 * the most recently used options are reused).
11739 reload : function(options){
11740 this.load(Roo.applyIf(options||{}, this.lastOptions));
11744 // Called as a callback by the Reader during a load operation.
11745 loadRecords : function(o, options, success){
11746 if(!o || success === false){
11747 if(success !== false){
11748 this.fireEvent("load", this, [], options, o);
11750 if(options.callback){
11751 options.callback.call(options.scope || this, [], options, false);
11755 // if data returned failure - throw an exception.
11756 if (o.success === false) {
11757 // show a message if no listener is registered.
11758 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11759 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11761 // loadmask wil be hooked into this..
11762 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11765 var r = o.records, t = o.totalRecords || r.length;
11767 this.fireEvent("beforeloadadd", this, r, options, o);
11769 if(!options || options.add !== true){
11770 if(this.pruneModifiedRecords){
11771 this.modified = [];
11773 for(var i = 0, len = r.length; i < len; i++){
11777 this.data = this.snapshot;
11778 delete this.snapshot;
11781 this.data.addAll(r);
11782 this.totalLength = t;
11784 this.fireEvent("datachanged", this);
11786 this.totalLength = Math.max(t, this.data.length+r.length);
11790 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11792 var e = new Roo.data.Record({});
11794 e.set(this.parent.displayField, this.parent.emptyTitle);
11795 e.set(this.parent.valueField, '');
11800 this.fireEvent("load", this, r, options, o);
11801 if(options.callback){
11802 options.callback.call(options.scope || this, r, options, true);
11808 * Loads data from a passed data block. A Reader which understands the format of the data
11809 * must have been configured in the constructor.
11810 * @param {Object} data The data block from which to read the Records. The format of the data expected
11811 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11812 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11814 loadData : function(o, append){
11815 var r = this.reader.readRecords(o);
11816 this.loadRecords(r, {add: append}, true);
11820 * Gets the number of cached records.
11822 * <em>If using paging, this may not be the total size of the dataset. If the data object
11823 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11824 * the data set size</em>
11826 getCount : function(){
11827 return this.data.length || 0;
11831 * Gets the total number of records in the dataset as returned by the server.
11833 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11834 * the dataset size</em>
11836 getTotalCount : function(){
11837 return this.totalLength || 0;
11841 * Returns the sort state of the Store as an object with two properties:
11843 field {String} The name of the field by which the Records are sorted
11844 direction {String} The sort order, "ASC" or "DESC"
11847 getSortState : function(){
11848 return this.sortInfo;
11852 applySort : function(){
11853 if(this.sortInfo && !this.remoteSort){
11854 var s = this.sortInfo, f = s.field;
11855 var st = this.fields.get(f).sortType;
11856 var fn = function(r1, r2){
11857 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11858 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11860 this.data.sort(s.direction, fn);
11861 if(this.snapshot && this.snapshot != this.data){
11862 this.snapshot.sort(s.direction, fn);
11868 * Sets the default sort column and order to be used by the next load operation.
11869 * @param {String} fieldName The name of the field to sort by.
11870 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11872 setDefaultSort : function(field, dir){
11873 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11877 * Sort the Records.
11878 * If remote sorting is used, the sort is performed on the server, and the cache is
11879 * reloaded. If local sorting is used, the cache is sorted internally.
11880 * @param {String} fieldName The name of the field to sort by.
11881 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11883 sort : function(fieldName, dir){
11884 var f = this.fields.get(fieldName);
11886 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11888 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11889 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11894 this.sortToggle[f.name] = dir;
11895 this.sortInfo = {field: f.name, direction: dir};
11896 if(!this.remoteSort){
11898 this.fireEvent("datachanged", this);
11900 this.load(this.lastOptions);
11905 * Calls the specified function for each of the Records in the cache.
11906 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11907 * Returning <em>false</em> aborts and exits the iteration.
11908 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11910 each : function(fn, scope){
11911 this.data.each(fn, scope);
11915 * Gets all records modified since the last commit. Modified records are persisted across load operations
11916 * (e.g., during paging).
11917 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11919 getModifiedRecords : function(){
11920 return this.modified;
11924 createFilterFn : function(property, value, anyMatch){
11925 if(!value.exec){ // not a regex
11926 value = String(value);
11927 if(value.length == 0){
11930 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11932 return function(r){
11933 return value.test(r.data[property]);
11938 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11939 * @param {String} property A field on your records
11940 * @param {Number} start The record index to start at (defaults to 0)
11941 * @param {Number} end The last record index to include (defaults to length - 1)
11942 * @return {Number} The sum
11944 sum : function(property, start, end){
11945 var rs = this.data.items, v = 0;
11946 start = start || 0;
11947 end = (end || end === 0) ? end : rs.length-1;
11949 for(var i = start; i <= end; i++){
11950 v += (rs[i].data[property] || 0);
11956 * Filter the records by a specified property.
11957 * @param {String} field A field on your records
11958 * @param {String/RegExp} value Either a string that the field
11959 * should start with or a RegExp to test against the field
11960 * @param {Boolean} anyMatch True to match any part not just the beginning
11962 filter : function(property, value, anyMatch){
11963 var fn = this.createFilterFn(property, value, anyMatch);
11964 return fn ? this.filterBy(fn) : this.clearFilter();
11968 * Filter by a function. The specified function will be called with each
11969 * record in this data source. If the function returns true the record is included,
11970 * otherwise it is filtered.
11971 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11972 * @param {Object} scope (optional) The scope of the function (defaults to this)
11974 filterBy : function(fn, scope){
11975 this.snapshot = this.snapshot || this.data;
11976 this.data = this.queryBy(fn, scope||this);
11977 this.fireEvent("datachanged", this);
11981 * Query the records by a specified property.
11982 * @param {String} field A field on your records
11983 * @param {String/RegExp} value Either a string that the field
11984 * should start with or a RegExp to test against the field
11985 * @param {Boolean} anyMatch True to match any part not just the beginning
11986 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11988 query : function(property, value, anyMatch){
11989 var fn = this.createFilterFn(property, value, anyMatch);
11990 return fn ? this.queryBy(fn) : this.data.clone();
11994 * Query by a function. The specified function will be called with each
11995 * record in this data source. If the function returns true the record is included
11997 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11998 * @param {Object} scope (optional) The scope of the function (defaults to this)
11999 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12001 queryBy : function(fn, scope){
12002 var data = this.snapshot || this.data;
12003 return data.filterBy(fn, scope||this);
12007 * Collects unique values for a particular dataIndex from this store.
12008 * @param {String} dataIndex The property to collect
12009 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12010 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12011 * @return {Array} An array of the unique values
12013 collect : function(dataIndex, allowNull, bypassFilter){
12014 var d = (bypassFilter === true && this.snapshot) ?
12015 this.snapshot.items : this.data.items;
12016 var v, sv, r = [], l = {};
12017 for(var i = 0, len = d.length; i < len; i++){
12018 v = d[i].data[dataIndex];
12020 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12029 * Revert to a view of the Record cache with no filtering applied.
12030 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12032 clearFilter : function(suppressEvent){
12033 if(this.snapshot && this.snapshot != this.data){
12034 this.data = this.snapshot;
12035 delete this.snapshot;
12036 if(suppressEvent !== true){
12037 this.fireEvent("datachanged", this);
12043 afterEdit : function(record){
12044 if(this.modified.indexOf(record) == -1){
12045 this.modified.push(record);
12047 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12051 afterReject : function(record){
12052 this.modified.remove(record);
12053 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12057 afterCommit : function(record){
12058 this.modified.remove(record);
12059 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12063 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12064 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12066 commitChanges : function(){
12067 var m = this.modified.slice(0);
12068 this.modified = [];
12069 for(var i = 0, len = m.length; i < len; i++){
12075 * Cancel outstanding changes on all changed records.
12077 rejectChanges : function(){
12078 var m = this.modified.slice(0);
12079 this.modified = [];
12080 for(var i = 0, len = m.length; i < len; i++){
12085 onMetaChange : function(meta, rtype, o){
12086 this.recordType = rtype;
12087 this.fields = rtype.prototype.fields;
12088 delete this.snapshot;
12089 this.sortInfo = meta.sortInfo || this.sortInfo;
12090 this.modified = [];
12091 this.fireEvent('metachange', this, this.reader.meta);
12094 moveIndex : function(data, type)
12096 var index = this.indexOf(data);
12098 var newIndex = index + type;
12102 this.insert(newIndex, data);
12107 * Ext JS Library 1.1.1
12108 * Copyright(c) 2006-2007, Ext JS, LLC.
12110 * Originally Released Under LGPL - original licence link has changed is not relivant.
12113 * <script type="text/javascript">
12117 * @class Roo.data.SimpleStore
12118 * @extends Roo.data.Store
12119 * Small helper class to make creating Stores from Array data easier.
12120 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12121 * @cfg {Array} fields An array of field definition objects, or field name strings.
12122 * @cfg {Array} data The multi-dimensional array of data
12124 * @param {Object} config
12126 Roo.data.SimpleStore = function(config){
12127 Roo.data.SimpleStore.superclass.constructor.call(this, {
12129 reader: new Roo.data.ArrayReader({
12132 Roo.data.Record.create(config.fields)
12134 proxy : new Roo.data.MemoryProxy(config.data)
12138 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12140 * Ext JS Library 1.1.1
12141 * Copyright(c) 2006-2007, Ext JS, LLC.
12143 * Originally Released Under LGPL - original licence link has changed is not relivant.
12146 * <script type="text/javascript">
12151 * @extends Roo.data.Store
12152 * @class Roo.data.JsonStore
12153 * Small helper class to make creating Stores for JSON data easier. <br/>
12155 var store = new Roo.data.JsonStore({
12156 url: 'get-images.php',
12158 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12161 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12162 * JsonReader and HttpProxy (unless inline data is provided).</b>
12163 * @cfg {Array} fields An array of field definition objects, or field name strings.
12165 * @param {Object} config
12167 Roo.data.JsonStore = function(c){
12168 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12169 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12170 reader: new Roo.data.JsonReader(c, c.fields)
12173 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12175 * Ext JS Library 1.1.1
12176 * Copyright(c) 2006-2007, Ext JS, LLC.
12178 * Originally Released Under LGPL - original licence link has changed is not relivant.
12181 * <script type="text/javascript">
12185 Roo.data.Field = function(config){
12186 if(typeof config == "string"){
12187 config = {name: config};
12189 Roo.apply(this, config);
12192 this.type = "auto";
12195 var st = Roo.data.SortTypes;
12196 // named sortTypes are supported, here we look them up
12197 if(typeof this.sortType == "string"){
12198 this.sortType = st[this.sortType];
12201 // set default sortType for strings and dates
12202 if(!this.sortType){
12205 this.sortType = st.asUCString;
12208 this.sortType = st.asDate;
12211 this.sortType = st.none;
12216 var stripRe = /[\$,%]/g;
12218 // prebuilt conversion function for this field, instead of
12219 // switching every time we're reading a value
12221 var cv, dateFormat = this.dateFormat;
12226 cv = function(v){ return v; };
12229 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12233 return v !== undefined && v !== null && v !== '' ?
12234 parseInt(String(v).replace(stripRe, ""), 10) : '';
12239 return v !== undefined && v !== null && v !== '' ?
12240 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12245 cv = function(v){ return v === true || v === "true" || v == 1; };
12252 if(v instanceof Date){
12256 if(dateFormat == "timestamp"){
12257 return new Date(v*1000);
12259 return Date.parseDate(v, dateFormat);
12261 var parsed = Date.parse(v);
12262 return parsed ? new Date(parsed) : null;
12271 Roo.data.Field.prototype = {
12279 * Ext JS Library 1.1.1
12280 * Copyright(c) 2006-2007, Ext JS, LLC.
12282 * Originally Released Under LGPL - original licence link has changed is not relivant.
12285 * <script type="text/javascript">
12288 // Base class for reading structured data from a data source. This class is intended to be
12289 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12292 * @class Roo.data.DataReader
12293 * Base class for reading structured data from a data source. This class is intended to be
12294 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12297 Roo.data.DataReader = function(meta, recordType){
12301 this.recordType = recordType instanceof Array ?
12302 Roo.data.Record.create(recordType) : recordType;
12305 Roo.data.DataReader.prototype = {
12307 * Create an empty record
12308 * @param {Object} data (optional) - overlay some values
12309 * @return {Roo.data.Record} record created.
12311 newRow : function(d) {
12313 this.recordType.prototype.fields.each(function(c) {
12315 case 'int' : da[c.name] = 0; break;
12316 case 'date' : da[c.name] = new Date(); break;
12317 case 'float' : da[c.name] = 0.0; break;
12318 case 'boolean' : da[c.name] = false; break;
12319 default : da[c.name] = ""; break;
12323 return new this.recordType(Roo.apply(da, d));
12328 * Ext JS Library 1.1.1
12329 * Copyright(c) 2006-2007, Ext JS, LLC.
12331 * Originally Released Under LGPL - original licence link has changed is not relivant.
12334 * <script type="text/javascript">
12338 * @class Roo.data.DataProxy
12339 * @extends Roo.data.Observable
12340 * This class is an abstract base class for implementations which provide retrieval of
12341 * unformatted data objects.<br>
12343 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12344 * (of the appropriate type which knows how to parse the data object) to provide a block of
12345 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12347 * Custom implementations must implement the load method as described in
12348 * {@link Roo.data.HttpProxy#load}.
12350 Roo.data.DataProxy = function(){
12353 * @event beforeload
12354 * Fires before a network request is made to retrieve a data object.
12355 * @param {Object} This DataProxy object.
12356 * @param {Object} params The params parameter to the load function.
12361 * Fires before the load method's callback is called.
12362 * @param {Object} This DataProxy object.
12363 * @param {Object} o The data object.
12364 * @param {Object} arg The callback argument object passed to the load function.
12368 * @event loadexception
12369 * Fires if an Exception occurs during data retrieval.
12370 * @param {Object} This DataProxy object.
12371 * @param {Object} o The data object.
12372 * @param {Object} arg The callback argument object passed to the load function.
12373 * @param {Object} e The Exception.
12375 loadexception : true
12377 Roo.data.DataProxy.superclass.constructor.call(this);
12380 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12383 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12387 * Ext JS Library 1.1.1
12388 * Copyright(c) 2006-2007, Ext JS, LLC.
12390 * Originally Released Under LGPL - original licence link has changed is not relivant.
12393 * <script type="text/javascript">
12396 * @class Roo.data.MemoryProxy
12397 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12398 * to the Reader when its load method is called.
12400 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12402 Roo.data.MemoryProxy = function(data){
12406 Roo.data.MemoryProxy.superclass.constructor.call(this);
12410 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12413 * Load data from the requested source (in this case an in-memory
12414 * data object passed to the constructor), read the data object into
12415 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12416 * process that block using the passed callback.
12417 * @param {Object} params This parameter is not used by the MemoryProxy class.
12418 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12419 * object into a block of Roo.data.Records.
12420 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12421 * The function must be passed <ul>
12422 * <li>The Record block object</li>
12423 * <li>The "arg" argument from the load function</li>
12424 * <li>A boolean success indicator</li>
12426 * @param {Object} scope The scope in which to call the callback
12427 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12429 load : function(params, reader, callback, scope, arg){
12430 params = params || {};
12433 result = reader.readRecords(params.data ? params.data :this.data);
12435 this.fireEvent("loadexception", this, arg, null, e);
12436 callback.call(scope, null, arg, false);
12439 callback.call(scope, result, arg, true);
12443 update : function(params, records){
12448 * Ext JS Library 1.1.1
12449 * Copyright(c) 2006-2007, Ext JS, LLC.
12451 * Originally Released Under LGPL - original licence link has changed is not relivant.
12454 * <script type="text/javascript">
12457 * @class Roo.data.HttpProxy
12458 * @extends Roo.data.DataProxy
12459 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12460 * configured to reference a certain URL.<br><br>
12462 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12463 * from which the running page was served.<br><br>
12465 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12467 * Be aware that to enable the browser to parse an XML document, the server must set
12468 * the Content-Type header in the HTTP response to "text/xml".
12470 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12471 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12472 * will be used to make the request.
12474 Roo.data.HttpProxy = function(conn){
12475 Roo.data.HttpProxy.superclass.constructor.call(this);
12476 // is conn a conn config or a real conn?
12478 this.useAjax = !conn || !conn.events;
12482 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12483 // thse are take from connection...
12486 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12489 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12490 * extra parameters to each request made by this object. (defaults to undefined)
12493 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12494 * to each request made by this object. (defaults to undefined)
12497 * @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)
12500 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12503 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12509 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12513 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12514 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12515 * a finer-grained basis than the DataProxy events.
12517 getConnection : function(){
12518 return this.useAjax ? Roo.Ajax : this.conn;
12522 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12523 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12524 * process that block using the passed callback.
12525 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12526 * for the request to the remote server.
12527 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12528 * object into a block of Roo.data.Records.
12529 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12530 * The function must be passed <ul>
12531 * <li>The Record block object</li>
12532 * <li>The "arg" argument from the load function</li>
12533 * <li>A boolean success indicator</li>
12535 * @param {Object} scope The scope in which to call the callback
12536 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12538 load : function(params, reader, callback, scope, arg){
12539 if(this.fireEvent("beforeload", this, params) !== false){
12541 params : params || {},
12543 callback : callback,
12548 callback : this.loadResponse,
12552 Roo.applyIf(o, this.conn);
12553 if(this.activeRequest){
12554 Roo.Ajax.abort(this.activeRequest);
12556 this.activeRequest = Roo.Ajax.request(o);
12558 this.conn.request(o);
12561 callback.call(scope||this, null, arg, false);
12566 loadResponse : function(o, success, response){
12567 delete this.activeRequest;
12569 this.fireEvent("loadexception", this, o, response);
12570 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12575 result = o.reader.read(response);
12577 this.fireEvent("loadexception", this, o, response, e);
12578 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12582 this.fireEvent("load", this, o, o.request.arg);
12583 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12587 update : function(dataSet){
12592 updateResponse : function(dataSet){
12597 * Ext JS Library 1.1.1
12598 * Copyright(c) 2006-2007, Ext JS, LLC.
12600 * Originally Released Under LGPL - original licence link has changed is not relivant.
12603 * <script type="text/javascript">
12607 * @class Roo.data.ScriptTagProxy
12608 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12609 * other than the originating domain of the running page.<br><br>
12611 * <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
12612 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12614 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12615 * source code that is used as the source inside a <script> tag.<br><br>
12617 * In order for the browser to process the returned data, the server must wrap the data object
12618 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12619 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12620 * depending on whether the callback name was passed:
12623 boolean scriptTag = false;
12624 String cb = request.getParameter("callback");
12627 response.setContentType("text/javascript");
12629 response.setContentType("application/x-json");
12631 Writer out = response.getWriter();
12633 out.write(cb + "(");
12635 out.print(dataBlock.toJsonString());
12642 * @param {Object} config A configuration object.
12644 Roo.data.ScriptTagProxy = function(config){
12645 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12646 Roo.apply(this, config);
12647 this.head = document.getElementsByTagName("head")[0];
12650 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12652 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12654 * @cfg {String} url The URL from which to request the data object.
12657 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12661 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12662 * the server the name of the callback function set up by the load call to process the returned data object.
12663 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12664 * javascript output which calls this named function passing the data object as its only parameter.
12666 callbackParam : "callback",
12668 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12669 * name to the request.
12674 * Load data from the configured URL, read the data object into
12675 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12676 * process that block using the passed callback.
12677 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12678 * for the request to the remote server.
12679 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12680 * object into a block of Roo.data.Records.
12681 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12682 * The function must be passed <ul>
12683 * <li>The Record block object</li>
12684 * <li>The "arg" argument from the load function</li>
12685 * <li>A boolean success indicator</li>
12687 * @param {Object} scope The scope in which to call the callback
12688 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12690 load : function(params, reader, callback, scope, arg){
12691 if(this.fireEvent("beforeload", this, params) !== false){
12693 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12695 var url = this.url;
12696 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12698 url += "&_dc=" + (new Date().getTime());
12700 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12703 cb : "stcCallback"+transId,
12704 scriptId : "stcScript"+transId,
12708 callback : callback,
12714 window[trans.cb] = function(o){
12715 conn.handleResponse(o, trans);
12718 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12720 if(this.autoAbort !== false){
12724 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12726 var script = document.createElement("script");
12727 script.setAttribute("src", url);
12728 script.setAttribute("type", "text/javascript");
12729 script.setAttribute("id", trans.scriptId);
12730 this.head.appendChild(script);
12732 this.trans = trans;
12734 callback.call(scope||this, null, arg, false);
12739 isLoading : function(){
12740 return this.trans ? true : false;
12744 * Abort the current server request.
12746 abort : function(){
12747 if(this.isLoading()){
12748 this.destroyTrans(this.trans);
12753 destroyTrans : function(trans, isLoaded){
12754 this.head.removeChild(document.getElementById(trans.scriptId));
12755 clearTimeout(trans.timeoutId);
12757 window[trans.cb] = undefined;
12759 delete window[trans.cb];
12762 // if hasn't been loaded, wait for load to remove it to prevent script error
12763 window[trans.cb] = function(){
12764 window[trans.cb] = undefined;
12766 delete window[trans.cb];
12773 handleResponse : function(o, trans){
12774 this.trans = false;
12775 this.destroyTrans(trans, true);
12778 result = trans.reader.readRecords(o);
12780 this.fireEvent("loadexception", this, o, trans.arg, e);
12781 trans.callback.call(trans.scope||window, null, trans.arg, false);
12784 this.fireEvent("load", this, o, trans.arg);
12785 trans.callback.call(trans.scope||window, result, trans.arg, true);
12789 handleFailure : function(trans){
12790 this.trans = false;
12791 this.destroyTrans(trans, false);
12792 this.fireEvent("loadexception", this, null, trans.arg);
12793 trans.callback.call(trans.scope||window, null, trans.arg, false);
12797 * Ext JS Library 1.1.1
12798 * Copyright(c) 2006-2007, Ext JS, LLC.
12800 * Originally Released Under LGPL - original licence link has changed is not relivant.
12803 * <script type="text/javascript">
12807 * @class Roo.data.JsonReader
12808 * @extends Roo.data.DataReader
12809 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12810 * based on mappings in a provided Roo.data.Record constructor.
12812 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12813 * in the reply previously.
12818 var RecordDef = Roo.data.Record.create([
12819 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12820 {name: 'occupation'} // This field will use "occupation" as the mapping.
12822 var myReader = new Roo.data.JsonReader({
12823 totalProperty: "results", // The property which contains the total dataset size (optional)
12824 root: "rows", // The property which contains an Array of row objects
12825 id: "id" // The property within each row object that provides an ID for the record (optional)
12829 * This would consume a JSON file like this:
12831 { 'results': 2, 'rows': [
12832 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12833 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12836 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12837 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12838 * paged from the remote server.
12839 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12840 * @cfg {String} root name of the property which contains the Array of row objects.
12841 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12842 * @cfg {Array} fields Array of field definition objects
12844 * Create a new JsonReader
12845 * @param {Object} meta Metadata configuration options
12846 * @param {Object} recordType Either an Array of field definition objects,
12847 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12849 Roo.data.JsonReader = function(meta, recordType){
12852 // set some defaults:
12853 Roo.applyIf(meta, {
12854 totalProperty: 'total',
12855 successProperty : 'success',
12860 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12862 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12865 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12866 * Used by Store query builder to append _requestMeta to params.
12869 metaFromRemote : false,
12871 * This method is only used by a DataProxy which has retrieved data from a remote server.
12872 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12873 * @return {Object} data A data block which is used by an Roo.data.Store object as
12874 * a cache of Roo.data.Records.
12876 read : function(response){
12877 var json = response.responseText;
12879 var o = /* eval:var:o */ eval("("+json+")");
12881 throw {message: "JsonReader.read: Json object not found"};
12887 this.metaFromRemote = true;
12888 this.meta = o.metaData;
12889 this.recordType = Roo.data.Record.create(o.metaData.fields);
12890 this.onMetaChange(this.meta, this.recordType, o);
12892 return this.readRecords(o);
12895 // private function a store will implement
12896 onMetaChange : function(meta, recordType, o){
12903 simpleAccess: function(obj, subsc) {
12910 getJsonAccessor: function(){
12912 return function(expr) {
12914 return(re.test(expr))
12915 ? new Function("obj", "return obj." + expr)
12920 return Roo.emptyFn;
12925 * Create a data block containing Roo.data.Records from an XML document.
12926 * @param {Object} o An object which contains an Array of row objects in the property specified
12927 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12928 * which contains the total size of the dataset.
12929 * @return {Object} data A data block which is used by an Roo.data.Store object as
12930 * a cache of Roo.data.Records.
12932 readRecords : function(o){
12934 * After any data loads, the raw JSON data is available for further custom processing.
12938 var s = this.meta, Record = this.recordType,
12939 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12941 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12943 if(s.totalProperty) {
12944 this.getTotal = this.getJsonAccessor(s.totalProperty);
12946 if(s.successProperty) {
12947 this.getSuccess = this.getJsonAccessor(s.successProperty);
12949 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12951 var g = this.getJsonAccessor(s.id);
12952 this.getId = function(rec) {
12954 return (r === undefined || r === "") ? null : r;
12957 this.getId = function(){return null;};
12960 for(var jj = 0; jj < fl; jj++){
12962 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12963 this.ef[jj] = this.getJsonAccessor(map);
12967 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12968 if(s.totalProperty){
12969 var vt = parseInt(this.getTotal(o), 10);
12974 if(s.successProperty){
12975 var vs = this.getSuccess(o);
12976 if(vs === false || vs === 'false'){
12981 for(var i = 0; i < c; i++){
12984 var id = this.getId(n);
12985 for(var j = 0; j < fl; j++){
12987 var v = this.ef[j](n);
12989 Roo.log('missing convert for ' + f.name);
12993 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12995 var record = new Record(values, id);
12997 records[i] = record;
13003 totalRecords : totalRecords
13008 * Ext JS Library 1.1.1
13009 * Copyright(c) 2006-2007, Ext JS, LLC.
13011 * Originally Released Under LGPL - original licence link has changed is not relivant.
13014 * <script type="text/javascript">
13018 * @class Roo.data.ArrayReader
13019 * @extends Roo.data.DataReader
13020 * Data reader class to create an Array of Roo.data.Record objects from an Array.
13021 * Each element of that Array represents a row of data fields. The
13022 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13023 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13027 var RecordDef = Roo.data.Record.create([
13028 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
13029 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
13031 var myReader = new Roo.data.ArrayReader({
13032 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
13036 * This would consume an Array like this:
13038 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13042 * Create a new JsonReader
13043 * @param {Object} meta Metadata configuration options.
13044 * @param {Object|Array} recordType Either an Array of field definition objects
13046 * @cfg {Array} fields Array of field definition objects
13047 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13048 * as specified to {@link Roo.data.Record#create},
13049 * or an {@link Roo.data.Record} object
13052 * created using {@link Roo.data.Record#create}.
13054 Roo.data.ArrayReader = function(meta, recordType){
13057 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13060 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13062 * Create a data block containing Roo.data.Records from an XML document.
13063 * @param {Object} o An Array of row objects which represents the dataset.
13064 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13065 * a cache of Roo.data.Records.
13067 readRecords : function(o){
13068 var sid = this.meta ? this.meta.id : null;
13069 var recordType = this.recordType, fields = recordType.prototype.fields;
13072 for(var i = 0; i < root.length; i++){
13075 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13076 for(var j = 0, jlen = fields.length; j < jlen; j++){
13077 var f = fields.items[j];
13078 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13079 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13081 values[f.name] = v;
13083 var record = new recordType(values, id);
13085 records[records.length] = record;
13089 totalRecords : records.length
13098 * @class Roo.bootstrap.ComboBox
13099 * @extends Roo.bootstrap.TriggerField
13100 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13101 * @cfg {Boolean} append (true|false) default false
13102 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13103 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13104 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13105 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13106 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13107 * @cfg {Boolean} animate default true
13108 * @cfg {Boolean} emptyResultText only for touch device
13109 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13110 * @cfg {String} emptyTitle default ''
13112 * Create a new ComboBox.
13113 * @param {Object} config Configuration options
13115 Roo.bootstrap.ComboBox = function(config){
13116 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13120 * Fires when the dropdown list is expanded
13121 * @param {Roo.bootstrap.ComboBox} combo This combo box
13126 * Fires when the dropdown list is collapsed
13127 * @param {Roo.bootstrap.ComboBox} combo This combo box
13131 * @event beforeselect
13132 * Fires before a list item is selected. Return false to cancel the selection.
13133 * @param {Roo.bootstrap.ComboBox} combo This combo box
13134 * @param {Roo.data.Record} record The data record returned from the underlying store
13135 * @param {Number} index The index of the selected item in the dropdown list
13137 'beforeselect' : true,
13140 * Fires when a list item is selected
13141 * @param {Roo.bootstrap.ComboBox} combo This combo box
13142 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13143 * @param {Number} index The index of the selected item in the dropdown list
13147 * @event beforequery
13148 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13149 * The event object passed has these properties:
13150 * @param {Roo.bootstrap.ComboBox} combo This combo box
13151 * @param {String} query The query
13152 * @param {Boolean} forceAll true to force "all" query
13153 * @param {Boolean} cancel true to cancel the query
13154 * @param {Object} e The query event object
13156 'beforequery': true,
13159 * Fires when the 'add' icon is pressed (add a listener to enable add button)
13160 * @param {Roo.bootstrap.ComboBox} combo This combo box
13165 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13166 * @param {Roo.bootstrap.ComboBox} combo This combo box
13167 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13172 * Fires when the remove value from the combobox array
13173 * @param {Roo.bootstrap.ComboBox} combo This combo box
13177 * @event afterremove
13178 * Fires when the remove value from the combobox array
13179 * @param {Roo.bootstrap.ComboBox} combo This combo box
13181 'afterremove' : true,
13183 * @event specialfilter
13184 * Fires when specialfilter
13185 * @param {Roo.bootstrap.ComboBox} combo This combo box
13187 'specialfilter' : true,
13190 * Fires when tick the element
13191 * @param {Roo.bootstrap.ComboBox} combo This combo box
13195 * @event touchviewdisplay
13196 * Fires when touch view require special display (default is using displayField)
13197 * @param {Roo.bootstrap.ComboBox} combo This combo box
13198 * @param {Object} cfg set html .
13200 'touchviewdisplay' : true
13205 this.tickItems = [];
13207 this.selectedIndex = -1;
13208 if(this.mode == 'local'){
13209 if(config.queryDelay === undefined){
13210 this.queryDelay = 10;
13212 if(config.minChars === undefined){
13218 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13221 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13222 * rendering into an Roo.Editor, defaults to false)
13225 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13226 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13229 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13232 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13233 * the dropdown list (defaults to undefined, with no header element)
13237 * @cfg {String/Roo.Template} tpl The template to use to render the output
13241 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13243 listWidth: undefined,
13245 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13246 * mode = 'remote' or 'text' if mode = 'local')
13248 displayField: undefined,
13251 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13252 * mode = 'remote' or 'value' if mode = 'local').
13253 * Note: use of a valueField requires the user make a selection
13254 * in order for a value to be mapped.
13256 valueField: undefined,
13258 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13263 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13264 * field's data value (defaults to the underlying DOM element's name)
13266 hiddenName: undefined,
13268 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13272 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13274 selectedClass: 'active',
13277 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13281 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13282 * anchor positions (defaults to 'tl-bl')
13284 listAlign: 'tl-bl?',
13286 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13290 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13291 * query specified by the allQuery config option (defaults to 'query')
13293 triggerAction: 'query',
13295 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13296 * (defaults to 4, does not apply if editable = false)
13300 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13301 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13305 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13306 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13310 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13311 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13315 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13316 * when editable = true (defaults to false)
13318 selectOnFocus:false,
13320 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13322 queryParam: 'query',
13324 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13325 * when mode = 'remote' (defaults to 'Loading...')
13327 loadingText: 'Loading...',
13329 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13333 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13337 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13338 * traditional select (defaults to true)
13342 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13346 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13350 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13351 * listWidth has a higher value)
13355 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13356 * allow the user to set arbitrary text into the field (defaults to false)
13358 forceSelection:false,
13360 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13361 * if typeAhead = true (defaults to 250)
13363 typeAheadDelay : 250,
13365 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13366 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13368 valueNotFoundText : undefined,
13370 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13372 blockFocus : false,
13375 * @cfg {Boolean} disableClear Disable showing of clear button.
13377 disableClear : false,
13379 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13381 alwaysQuery : false,
13384 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13389 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13391 invalidClass : "has-warning",
13394 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13396 validClass : "has-success",
13399 * @cfg {Boolean} specialFilter (true|false) special filter default false
13401 specialFilter : false,
13404 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13406 mobileTouchView : true,
13409 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13411 useNativeIOS : false,
13414 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13416 mobile_restrict_height : false,
13418 ios_options : false,
13430 btnPosition : 'right',
13431 triggerList : true,
13432 showToggleBtn : true,
13434 emptyResultText: 'Empty',
13435 triggerText : 'Select',
13438 // element that contains real text value.. (when hidden is used..)
13440 getAutoCreate : function()
13445 * Render classic select for iso
13448 if(Roo.isIOS && this.useNativeIOS){
13449 cfg = this.getAutoCreateNativeIOS();
13457 if(Roo.isTouch && this.mobileTouchView){
13458 cfg = this.getAutoCreateTouchView();
13465 if(!this.tickable){
13466 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13471 * ComboBox with tickable selections
13474 var align = this.labelAlign || this.parentLabelAlign();
13477 cls : 'form-group roo-combobox-tickable' //input-group
13480 var btn_text_select = '';
13481 var btn_text_done = '';
13482 var btn_text_cancel = '';
13484 if (this.btn_text_show) {
13485 btn_text_select = 'Select';
13486 btn_text_done = 'Done';
13487 btn_text_cancel = 'Cancel';
13492 cls : 'tickable-buttons',
13497 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13498 //html : this.triggerText
13499 html: btn_text_select
13505 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13507 html: btn_text_done
13513 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13515 html: btn_text_cancel
13521 buttons.cn.unshift({
13523 cls: 'roo-select2-search-field-input'
13529 Roo.each(buttons.cn, function(c){
13531 c.cls += ' btn-' + _this.size;
13534 if (_this.disabled) {
13541 style : 'display: contents',
13546 cls: 'form-hidden-field'
13550 cls: 'roo-select2-choices',
13554 cls: 'roo-select2-search-field',
13565 cls: 'roo-select2-container input-group roo-select2-container-multi',
13571 // cls: 'typeahead typeahead-long dropdown-menu',
13572 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13577 if(this.hasFeedback && !this.allowBlank){
13581 cls: 'glyphicon form-control-feedback'
13584 combobox.cn.push(feedback);
13589 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13590 tooltip : 'This field is required'
13592 if (Roo.bootstrap.version == 4) {
13595 style : 'display:none'
13598 if (align ==='left' && this.fieldLabel.length) {
13600 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13607 cls : 'control-label col-form-label',
13608 html : this.fieldLabel
13620 var labelCfg = cfg.cn[1];
13621 var contentCfg = cfg.cn[2];
13624 if(this.indicatorpos == 'right'){
13630 cls : 'control-label col-form-label',
13634 html : this.fieldLabel
13650 labelCfg = cfg.cn[0];
13651 contentCfg = cfg.cn[1];
13655 if(this.labelWidth > 12){
13656 labelCfg.style = "width: " + this.labelWidth + 'px';
13659 if(this.labelWidth < 13 && this.labelmd == 0){
13660 this.labelmd = this.labelWidth;
13663 if(this.labellg > 0){
13664 labelCfg.cls += ' col-lg-' + this.labellg;
13665 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13668 if(this.labelmd > 0){
13669 labelCfg.cls += ' col-md-' + this.labelmd;
13670 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13673 if(this.labelsm > 0){
13674 labelCfg.cls += ' col-sm-' + this.labelsm;
13675 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13678 if(this.labelxs > 0){
13679 labelCfg.cls += ' col-xs-' + this.labelxs;
13680 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13684 } else if ( this.fieldLabel.length) {
13685 // Roo.log(" label");
13690 //cls : 'input-group-addon',
13691 html : this.fieldLabel
13696 if(this.indicatorpos == 'right'){
13700 //cls : 'input-group-addon',
13701 html : this.fieldLabel
13711 // Roo.log(" no label && no align");
13718 ['xs','sm','md','lg'].map(function(size){
13719 if (settings[size]) {
13720 cfg.cls += ' col-' + size + '-' + settings[size];
13728 _initEventsCalled : false,
13731 initEvents: function()
13733 if (this._initEventsCalled) { // as we call render... prevent looping...
13736 this._initEventsCalled = true;
13739 throw "can not find store for combo";
13742 this.indicator = this.indicatorEl();
13744 this.store = Roo.factory(this.store, Roo.data);
13745 this.store.parent = this;
13747 // if we are building from html. then this element is so complex, that we can not really
13748 // use the rendered HTML.
13749 // so we have to trash and replace the previous code.
13750 if (Roo.XComponent.build_from_html) {
13751 // remove this element....
13752 var e = this.el.dom, k=0;
13753 while (e ) { e = e.previousSibling; ++k;}
13758 this.rendered = false;
13760 this.render(this.parent().getChildContainer(true), k);
13763 if(Roo.isIOS && this.useNativeIOS){
13764 this.initIOSView();
13772 if(Roo.isTouch && this.mobileTouchView){
13773 this.initTouchView();
13778 this.initTickableEvents();
13782 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13784 if(this.hiddenName){
13786 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13788 this.hiddenField.dom.value =
13789 this.hiddenValue !== undefined ? this.hiddenValue :
13790 this.value !== undefined ? this.value : '';
13792 // prevent input submission
13793 this.el.dom.removeAttribute('name');
13794 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13799 // this.el.dom.setAttribute('autocomplete', 'off');
13802 var cls = 'x-combo-list';
13804 //this.list = new Roo.Layer({
13805 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13811 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13812 _this.list.setWidth(lw);
13815 this.list.on('mouseover', this.onViewOver, this);
13816 this.list.on('mousemove', this.onViewMove, this);
13817 this.list.on('scroll', this.onViewScroll, this);
13820 this.list.swallowEvent('mousewheel');
13821 this.assetHeight = 0;
13824 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13825 this.assetHeight += this.header.getHeight();
13828 this.innerList = this.list.createChild({cls:cls+'-inner'});
13829 this.innerList.on('mouseover', this.onViewOver, this);
13830 this.innerList.on('mousemove', this.onViewMove, this);
13831 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13833 if(this.allowBlank && !this.pageSize && !this.disableClear){
13834 this.footer = this.list.createChild({cls:cls+'-ft'});
13835 this.pageTb = new Roo.Toolbar(this.footer);
13839 this.footer = this.list.createChild({cls:cls+'-ft'});
13840 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13841 {pageSize: this.pageSize});
13845 if (this.pageTb && this.allowBlank && !this.disableClear) {
13847 this.pageTb.add(new Roo.Toolbar.Fill(), {
13848 cls: 'x-btn-icon x-btn-clear',
13850 handler: function()
13853 _this.clearValue();
13854 _this.onSelect(false, -1);
13859 this.assetHeight += this.footer.getHeight();
13864 this.tpl = Roo.bootstrap.version == 4 ?
13865 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
13866 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13869 this.view = new Roo.View(this.list, this.tpl, {
13870 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13872 //this.view.wrapEl.setDisplayed(false);
13873 this.view.on('click', this.onViewClick, this);
13876 this.store.on('beforeload', this.onBeforeLoad, this);
13877 this.store.on('load', this.onLoad, this);
13878 this.store.on('loadexception', this.onLoadException, this);
13880 if(this.resizable){
13881 this.resizer = new Roo.Resizable(this.list, {
13882 pinned:true, handles:'se'
13884 this.resizer.on('resize', function(r, w, h){
13885 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13886 this.listWidth = w;
13887 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13888 this.restrictHeight();
13890 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13893 if(!this.editable){
13894 this.editable = true;
13895 this.setEditable(false);
13900 if (typeof(this.events.add.listeners) != 'undefined') {
13902 this.addicon = this.wrap.createChild(
13903 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13905 this.addicon.on('click', function(e) {
13906 this.fireEvent('add', this);
13909 if (typeof(this.events.edit.listeners) != 'undefined') {
13911 this.editicon = this.wrap.createChild(
13912 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13913 if (this.addicon) {
13914 this.editicon.setStyle('margin-left', '40px');
13916 this.editicon.on('click', function(e) {
13918 // we fire even if inothing is selected..
13919 this.fireEvent('edit', this, this.lastData );
13925 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13926 "up" : function(e){
13927 this.inKeyMode = true;
13931 "down" : function(e){
13932 if(!this.isExpanded()){
13933 this.onTriggerClick();
13935 this.inKeyMode = true;
13940 "enter" : function(e){
13941 // this.onViewClick();
13945 if(this.fireEvent("specialkey", this, e)){
13946 this.onViewClick(false);
13952 "esc" : function(e){
13956 "tab" : function(e){
13959 if(this.fireEvent("specialkey", this, e)){
13960 this.onViewClick(false);
13968 doRelay : function(foo, bar, hname){
13969 if(hname == 'down' || this.scope.isExpanded()){
13970 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13979 this.queryDelay = Math.max(this.queryDelay || 10,
13980 this.mode == 'local' ? 10 : 250);
13983 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13985 if(this.typeAhead){
13986 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13988 if(this.editable !== false){
13989 this.inputEl().on("keyup", this.onKeyUp, this);
13991 if(this.forceSelection){
13992 this.inputEl().on('blur', this.doForce, this);
13996 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13997 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14001 initTickableEvents: function()
14005 if(this.hiddenName){
14007 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14009 this.hiddenField.dom.value =
14010 this.hiddenValue !== undefined ? this.hiddenValue :
14011 this.value !== undefined ? this.value : '';
14013 // prevent input submission
14014 this.el.dom.removeAttribute('name');
14015 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14020 // this.list = this.el.select('ul.dropdown-menu',true).first();
14022 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14023 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14024 if(this.triggerList){
14025 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14028 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14029 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14031 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14032 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14034 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14035 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14037 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14038 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14039 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14042 this.cancelBtn.hide();
14047 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14048 _this.list.setWidth(lw);
14051 this.list.on('mouseover', this.onViewOver, this);
14052 this.list.on('mousemove', this.onViewMove, this);
14054 this.list.on('scroll', this.onViewScroll, this);
14057 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
14058 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14061 this.view = new Roo.View(this.list, this.tpl, {
14066 selectedClass: this.selectedClass
14069 //this.view.wrapEl.setDisplayed(false);
14070 this.view.on('click', this.onViewClick, this);
14074 this.store.on('beforeload', this.onBeforeLoad, this);
14075 this.store.on('load', this.onLoad, this);
14076 this.store.on('loadexception', this.onLoadException, this);
14079 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14080 "up" : function(e){
14081 this.inKeyMode = true;
14085 "down" : function(e){
14086 this.inKeyMode = true;
14090 "enter" : function(e){
14091 if(this.fireEvent("specialkey", this, e)){
14092 this.onViewClick(false);
14098 "esc" : function(e){
14099 this.onTickableFooterButtonClick(e, false, false);
14102 "tab" : function(e){
14103 this.fireEvent("specialkey", this, e);
14105 this.onTickableFooterButtonClick(e, false, false);
14112 doRelay : function(e, fn, key){
14113 if(this.scope.isExpanded()){
14114 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14123 this.queryDelay = Math.max(this.queryDelay || 10,
14124 this.mode == 'local' ? 10 : 250);
14127 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14129 if(this.typeAhead){
14130 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14133 if(this.editable !== false){
14134 this.tickableInputEl().on("keyup", this.onKeyUp, this);
14137 this.indicator = this.indicatorEl();
14139 if(this.indicator){
14140 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14141 this.indicator.hide();
14146 onDestroy : function(){
14148 this.view.setStore(null);
14149 this.view.el.removeAllListeners();
14150 this.view.el.remove();
14151 this.view.purgeListeners();
14154 this.list.dom.innerHTML = '';
14158 this.store.un('beforeload', this.onBeforeLoad, this);
14159 this.store.un('load', this.onLoad, this);
14160 this.store.un('loadexception', this.onLoadException, this);
14162 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14166 fireKey : function(e){
14167 if(e.isNavKeyPress() && !this.list.isVisible()){
14168 this.fireEvent("specialkey", this, e);
14173 onResize: function(w, h){
14174 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14176 // if(typeof w != 'number'){
14177 // // we do not handle it!?!?
14180 // var tw = this.trigger.getWidth();
14181 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14182 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14184 // this.inputEl().setWidth( this.adjustWidth('input', x));
14186 // //this.trigger.setStyle('left', x+'px');
14188 // if(this.list && this.listWidth === undefined){
14189 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14190 // this.list.setWidth(lw);
14191 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14199 * Allow or prevent the user from directly editing the field text. If false is passed,
14200 * the user will only be able to select from the items defined in the dropdown list. This method
14201 * is the runtime equivalent of setting the 'editable' config option at config time.
14202 * @param {Boolean} value True to allow the user to directly edit the field text
14204 setEditable : function(value){
14205 if(value == this.editable){
14208 this.editable = value;
14210 this.inputEl().dom.setAttribute('readOnly', true);
14211 this.inputEl().on('mousedown', this.onTriggerClick, this);
14212 this.inputEl().addClass('x-combo-noedit');
14214 this.inputEl().dom.setAttribute('readOnly', false);
14215 this.inputEl().un('mousedown', this.onTriggerClick, this);
14216 this.inputEl().removeClass('x-combo-noedit');
14222 onBeforeLoad : function(combo,opts){
14223 if(!this.hasFocus){
14227 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14229 this.restrictHeight();
14230 this.selectedIndex = -1;
14234 onLoad : function(){
14236 this.hasQuery = false;
14238 if(!this.hasFocus){
14242 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14243 this.loading.hide();
14246 if(this.store.getCount() > 0){
14249 this.restrictHeight();
14250 if(this.lastQuery == this.allQuery){
14251 if(this.editable && !this.tickable){
14252 this.inputEl().dom.select();
14256 !this.selectByValue(this.value, true) &&
14259 !this.store.lastOptions ||
14260 typeof(this.store.lastOptions.add) == 'undefined' ||
14261 this.store.lastOptions.add != true
14264 this.select(0, true);
14267 if(this.autoFocus){
14270 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14271 this.taTask.delay(this.typeAheadDelay);
14275 this.onEmptyResults();
14281 onLoadException : function()
14283 this.hasQuery = false;
14285 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14286 this.loading.hide();
14289 if(this.tickable && this.editable){
14294 // only causes errors at present
14295 //Roo.log(this.store.reader.jsonData);
14296 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14298 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14304 onTypeAhead : function(){
14305 if(this.store.getCount() > 0){
14306 var r = this.store.getAt(0);
14307 var newValue = r.data[this.displayField];
14308 var len = newValue.length;
14309 var selStart = this.getRawValue().length;
14311 if(selStart != len){
14312 this.setRawValue(newValue);
14313 this.selectText(selStart, newValue.length);
14319 onSelect : function(record, index){
14321 if(this.fireEvent('beforeselect', this, record, index) !== false){
14323 this.setFromData(index > -1 ? record.data : false);
14326 this.fireEvent('select', this, record, index);
14331 * Returns the currently selected field value or empty string if no value is set.
14332 * @return {String} value The selected value
14334 getValue : function()
14336 if(Roo.isIOS && this.useNativeIOS){
14337 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14341 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14344 if(this.valueField){
14345 return typeof this.value != 'undefined' ? this.value : '';
14347 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14351 getRawValue : function()
14353 if(Roo.isIOS && this.useNativeIOS){
14354 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14357 var v = this.inputEl().getValue();
14363 * Clears any text/value currently set in the field
14365 clearValue : function(){
14367 if(this.hiddenField){
14368 this.hiddenField.dom.value = '';
14371 this.setRawValue('');
14372 this.lastSelectionText = '';
14373 this.lastData = false;
14375 var close = this.closeTriggerEl();
14386 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14387 * will be displayed in the field. If the value does not match the data value of an existing item,
14388 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14389 * Otherwise the field will be blank (although the value will still be set).
14390 * @param {String} value The value to match
14392 setValue : function(v)
14394 if(Roo.isIOS && this.useNativeIOS){
14395 this.setIOSValue(v);
14405 if(this.valueField){
14406 var r = this.findRecord(this.valueField, v);
14408 text = r.data[this.displayField];
14409 }else if(this.valueNotFoundText !== undefined){
14410 text = this.valueNotFoundText;
14413 this.lastSelectionText = text;
14414 if(this.hiddenField){
14415 this.hiddenField.dom.value = v;
14417 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14420 var close = this.closeTriggerEl();
14423 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14429 * @property {Object} the last set data for the element
14434 * Sets the value of the field based on a object which is related to the record format for the store.
14435 * @param {Object} value the value to set as. or false on reset?
14437 setFromData : function(o){
14444 var dv = ''; // display value
14445 var vv = ''; // value value..
14447 if (this.displayField) {
14448 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14450 // this is an error condition!!!
14451 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14454 if(this.valueField){
14455 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14458 var close = this.closeTriggerEl();
14461 if(dv.length || vv * 1 > 0){
14463 this.blockFocus=true;
14469 if(this.hiddenField){
14470 this.hiddenField.dom.value = vv;
14472 this.lastSelectionText = dv;
14473 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14477 // no hidden field.. - we store the value in 'value', but still display
14478 // display field!!!!
14479 this.lastSelectionText = dv;
14480 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14487 reset : function(){
14488 // overridden so that last data is reset..
14495 this.setValue(this.originalValue);
14496 //this.clearInvalid();
14497 this.lastData = false;
14499 this.view.clearSelections();
14505 findRecord : function(prop, value){
14507 if(this.store.getCount() > 0){
14508 this.store.each(function(r){
14509 if(r.data[prop] == value){
14519 getName: function()
14521 // returns hidden if it's set..
14522 if (!this.rendered) {return ''};
14523 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14527 onViewMove : function(e, t){
14528 this.inKeyMode = false;
14532 onViewOver : function(e, t){
14533 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14536 var item = this.view.findItemFromChild(t);
14539 var index = this.view.indexOf(item);
14540 this.select(index, false);
14545 onViewClick : function(view, doFocus, el, e)
14547 var index = this.view.getSelectedIndexes()[0];
14549 var r = this.store.getAt(index);
14553 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14560 Roo.each(this.tickItems, function(v,k){
14562 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14564 _this.tickItems.splice(k, 1);
14566 if(typeof(e) == 'undefined' && view == false){
14567 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14579 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14580 this.tickItems.push(r.data);
14583 if(typeof(e) == 'undefined' && view == false){
14584 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14591 this.onSelect(r, index);
14593 if(doFocus !== false && !this.blockFocus){
14594 this.inputEl().focus();
14599 restrictHeight : function(){
14600 //this.innerList.dom.style.height = '';
14601 //var inner = this.innerList.dom;
14602 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14603 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14604 //this.list.beginUpdate();
14605 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14606 this.list.alignTo(this.inputEl(), this.listAlign);
14607 this.list.alignTo(this.inputEl(), this.listAlign);
14608 //this.list.endUpdate();
14612 onEmptyResults : function(){
14614 if(this.tickable && this.editable){
14615 this.hasFocus = false;
14616 this.restrictHeight();
14624 * Returns true if the dropdown list is expanded, else false.
14626 isExpanded : function(){
14627 return this.list.isVisible();
14631 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14632 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14633 * @param {String} value The data value of the item to select
14634 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14635 * selected item if it is not currently in view (defaults to true)
14636 * @return {Boolean} True if the value matched an item in the list, else false
14638 selectByValue : function(v, scrollIntoView){
14639 if(v !== undefined && v !== null){
14640 var r = this.findRecord(this.valueField || this.displayField, v);
14642 this.select(this.store.indexOf(r), scrollIntoView);
14650 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14651 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14652 * @param {Number} index The zero-based index of the list item to select
14653 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14654 * selected item if it is not currently in view (defaults to true)
14656 select : function(index, scrollIntoView){
14657 this.selectedIndex = index;
14658 this.view.select(index);
14659 if(scrollIntoView !== false){
14660 var el = this.view.getNode(index);
14662 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14665 this.list.scrollChildIntoView(el, false);
14671 selectNext : function(){
14672 var ct = this.store.getCount();
14674 if(this.selectedIndex == -1){
14676 }else if(this.selectedIndex < ct-1){
14677 this.select(this.selectedIndex+1);
14683 selectPrev : function(){
14684 var ct = this.store.getCount();
14686 if(this.selectedIndex == -1){
14688 }else if(this.selectedIndex != 0){
14689 this.select(this.selectedIndex-1);
14695 onKeyUp : function(e){
14696 if(this.editable !== false && !e.isSpecialKey()){
14697 this.lastKey = e.getKey();
14698 this.dqTask.delay(this.queryDelay);
14703 validateBlur : function(){
14704 return !this.list || !this.list.isVisible();
14708 initQuery : function(){
14710 var v = this.getRawValue();
14712 if(this.tickable && this.editable){
14713 v = this.tickableInputEl().getValue();
14720 doForce : function(){
14721 if(this.inputEl().dom.value.length > 0){
14722 this.inputEl().dom.value =
14723 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14729 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14730 * query allowing the query action to be canceled if needed.
14731 * @param {String} query The SQL query to execute
14732 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14733 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14734 * saved in the current store (defaults to false)
14736 doQuery : function(q, forceAll){
14738 if(q === undefined || q === null){
14743 forceAll: forceAll,
14747 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14752 forceAll = qe.forceAll;
14753 if(forceAll === true || (q.length >= this.minChars)){
14755 this.hasQuery = true;
14757 if(this.lastQuery != q || this.alwaysQuery){
14758 this.lastQuery = q;
14759 if(this.mode == 'local'){
14760 this.selectedIndex = -1;
14762 this.store.clearFilter();
14765 if(this.specialFilter){
14766 this.fireEvent('specialfilter', this);
14771 this.store.filter(this.displayField, q);
14774 this.store.fireEvent("datachanged", this.store);
14781 this.store.baseParams[this.queryParam] = q;
14783 var options = {params : this.getParams(q)};
14786 options.add = true;
14787 options.params.start = this.page * this.pageSize;
14790 this.store.load(options);
14793 * this code will make the page width larger, at the beginning, the list not align correctly,
14794 * we should expand the list on onLoad
14795 * so command out it
14800 this.selectedIndex = -1;
14805 this.loadNext = false;
14809 getParams : function(q){
14811 //p[this.queryParam] = q;
14815 p.limit = this.pageSize;
14821 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14823 collapse : function(){
14824 if(!this.isExpanded()){
14830 this.hasFocus = false;
14834 this.cancelBtn.hide();
14835 this.trigger.show();
14838 this.tickableInputEl().dom.value = '';
14839 this.tickableInputEl().blur();
14844 Roo.get(document).un('mousedown', this.collapseIf, this);
14845 Roo.get(document).un('mousewheel', this.collapseIf, this);
14846 if (!this.editable) {
14847 Roo.get(document).un('keydown', this.listKeyPress, this);
14849 this.fireEvent('collapse', this);
14855 collapseIf : function(e){
14856 var in_combo = e.within(this.el);
14857 var in_list = e.within(this.list);
14858 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14860 if (in_combo || in_list || is_list) {
14861 //e.stopPropagation();
14866 this.onTickableFooterButtonClick(e, false, false);
14874 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14876 expand : function(){
14878 if(this.isExpanded() || !this.hasFocus){
14882 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14883 this.list.setWidth(lw);
14889 this.restrictHeight();
14893 this.tickItems = Roo.apply([], this.item);
14896 this.cancelBtn.show();
14897 this.trigger.hide();
14900 this.tickableInputEl().focus();
14905 Roo.get(document).on('mousedown', this.collapseIf, this);
14906 Roo.get(document).on('mousewheel', this.collapseIf, this);
14907 if (!this.editable) {
14908 Roo.get(document).on('keydown', this.listKeyPress, this);
14911 this.fireEvent('expand', this);
14915 // Implements the default empty TriggerField.onTriggerClick function
14916 onTriggerClick : function(e)
14918 Roo.log('trigger click');
14920 if(this.disabled || !this.triggerList){
14925 this.loadNext = false;
14927 if(this.isExpanded()){
14929 if (!this.blockFocus) {
14930 this.inputEl().focus();
14934 this.hasFocus = true;
14935 if(this.triggerAction == 'all') {
14936 this.doQuery(this.allQuery, true);
14938 this.doQuery(this.getRawValue());
14940 if (!this.blockFocus) {
14941 this.inputEl().focus();
14946 onTickableTriggerClick : function(e)
14953 this.loadNext = false;
14954 this.hasFocus = true;
14956 if(this.triggerAction == 'all') {
14957 this.doQuery(this.allQuery, true);
14959 this.doQuery(this.getRawValue());
14963 onSearchFieldClick : function(e)
14965 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14966 this.onTickableFooterButtonClick(e, false, false);
14970 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14975 this.loadNext = false;
14976 this.hasFocus = true;
14978 if(this.triggerAction == 'all') {
14979 this.doQuery(this.allQuery, true);
14981 this.doQuery(this.getRawValue());
14985 listKeyPress : function(e)
14987 //Roo.log('listkeypress');
14988 // scroll to first matching element based on key pres..
14989 if (e.isSpecialKey()) {
14992 var k = String.fromCharCode(e.getKey()).toUpperCase();
14995 var csel = this.view.getSelectedNodes();
14996 var cselitem = false;
14998 var ix = this.view.indexOf(csel[0]);
14999 cselitem = this.store.getAt(ix);
15000 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15006 this.store.each(function(v) {
15008 // start at existing selection.
15009 if (cselitem.id == v.id) {
15015 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15016 match = this.store.indexOf(v);
15022 if (match === false) {
15023 return true; // no more action?
15026 this.view.select(match);
15027 var sn = Roo.get(this.view.getSelectedNodes()[0]);
15028 sn.scrollIntoView(sn.dom.parentNode, false);
15031 onViewScroll : function(e, t){
15033 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){
15037 this.hasQuery = true;
15039 this.loading = this.list.select('.loading', true).first();
15041 if(this.loading === null){
15042 this.list.createChild({
15044 cls: 'loading roo-select2-more-results roo-select2-active',
15045 html: 'Loading more results...'
15048 this.loading = this.list.select('.loading', true).first();
15050 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15052 this.loading.hide();
15055 this.loading.show();
15060 this.loadNext = true;
15062 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15067 addItem : function(o)
15069 var dv = ''; // display value
15071 if (this.displayField) {
15072 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15074 // this is an error condition!!!
15075 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
15082 var choice = this.choices.createChild({
15084 cls: 'roo-select2-search-choice',
15093 cls: 'roo-select2-search-choice-close fa fa-times',
15098 }, this.searchField);
15100 var close = choice.select('a.roo-select2-search-choice-close', true).first();
15102 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15110 this.inputEl().dom.value = '';
15115 onRemoveItem : function(e, _self, o)
15117 e.preventDefault();
15119 this.lastItem = Roo.apply([], this.item);
15121 var index = this.item.indexOf(o.data) * 1;
15124 Roo.log('not this item?!');
15128 this.item.splice(index, 1);
15133 this.fireEvent('remove', this, e);
15139 syncValue : function()
15141 if(!this.item.length){
15148 Roo.each(this.item, function(i){
15149 if(_this.valueField){
15150 value.push(i[_this.valueField]);
15157 this.value = value.join(',');
15159 if(this.hiddenField){
15160 this.hiddenField.dom.value = this.value;
15163 this.store.fireEvent("datachanged", this.store);
15168 clearItem : function()
15170 if(!this.multiple){
15176 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15184 if(this.tickable && !Roo.isTouch){
15185 this.view.refresh();
15189 inputEl: function ()
15191 if(Roo.isIOS && this.useNativeIOS){
15192 return this.el.select('select.roo-ios-select', true).first();
15195 if(Roo.isTouch && this.mobileTouchView){
15196 return this.el.select('input.form-control',true).first();
15200 return this.searchField;
15203 return this.el.select('input.form-control',true).first();
15206 onTickableFooterButtonClick : function(e, btn, el)
15208 e.preventDefault();
15210 this.lastItem = Roo.apply([], this.item);
15212 if(btn && btn.name == 'cancel'){
15213 this.tickItems = Roo.apply([], this.item);
15222 Roo.each(this.tickItems, function(o){
15230 validate : function()
15232 if(this.getVisibilityEl().hasClass('hidden')){
15236 var v = this.getRawValue();
15239 v = this.getValue();
15242 if(this.disabled || this.allowBlank || v.length){
15247 this.markInvalid();
15251 tickableInputEl : function()
15253 if(!this.tickable || !this.editable){
15254 return this.inputEl();
15257 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15261 getAutoCreateTouchView : function()
15266 cls: 'form-group' //input-group
15272 type : this.inputType,
15273 cls : 'form-control x-combo-noedit',
15274 autocomplete: 'new-password',
15275 placeholder : this.placeholder || '',
15280 input.name = this.name;
15284 input.cls += ' input-' + this.size;
15287 if (this.disabled) {
15288 input.disabled = true;
15299 inputblock.cls += ' input-group';
15301 inputblock.cn.unshift({
15303 cls : 'input-group-addon input-group-prepend input-group-text',
15308 if(this.removable && !this.multiple){
15309 inputblock.cls += ' roo-removable';
15311 inputblock.cn.push({
15314 cls : 'roo-combo-removable-btn close'
15318 if(this.hasFeedback && !this.allowBlank){
15320 inputblock.cls += ' has-feedback';
15322 inputblock.cn.push({
15324 cls: 'glyphicon form-control-feedback'
15331 inputblock.cls += (this.before) ? '' : ' input-group';
15333 inputblock.cn.push({
15335 cls : 'input-group-addon input-group-append input-group-text',
15341 var ibwrap = inputblock;
15346 cls: 'roo-select2-choices',
15350 cls: 'roo-select2-search-field',
15363 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15368 cls: 'form-hidden-field'
15374 if(!this.multiple && this.showToggleBtn){
15381 if (this.caret != false) {
15384 cls: 'fa fa-' + this.caret
15391 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15396 cls: 'combobox-clear',
15410 combobox.cls += ' roo-select2-container-multi';
15413 var align = this.labelAlign || this.parentLabelAlign();
15415 if (align ==='left' && this.fieldLabel.length) {
15420 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15421 tooltip : 'This field is required'
15425 cls : 'control-label col-form-label',
15426 html : this.fieldLabel
15437 var labelCfg = cfg.cn[1];
15438 var contentCfg = cfg.cn[2];
15441 if(this.indicatorpos == 'right'){
15446 cls : 'control-label col-form-label',
15450 html : this.fieldLabel
15454 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15455 tooltip : 'This field is required'
15468 labelCfg = cfg.cn[0];
15469 contentCfg = cfg.cn[1];
15474 if(this.labelWidth > 12){
15475 labelCfg.style = "width: " + this.labelWidth + 'px';
15478 if(this.labelWidth < 13 && this.labelmd == 0){
15479 this.labelmd = this.labelWidth;
15482 if(this.labellg > 0){
15483 labelCfg.cls += ' col-lg-' + this.labellg;
15484 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15487 if(this.labelmd > 0){
15488 labelCfg.cls += ' col-md-' + this.labelmd;
15489 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15492 if(this.labelsm > 0){
15493 labelCfg.cls += ' col-sm-' + this.labelsm;
15494 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15497 if(this.labelxs > 0){
15498 labelCfg.cls += ' col-xs-' + this.labelxs;
15499 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15503 } else if ( this.fieldLabel.length) {
15507 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15508 tooltip : 'This field is required'
15512 cls : 'control-label',
15513 html : this.fieldLabel
15524 if(this.indicatorpos == 'right'){
15528 cls : 'control-label',
15529 html : this.fieldLabel,
15533 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15534 tooltip : 'This field is required'
15551 var settings = this;
15553 ['xs','sm','md','lg'].map(function(size){
15554 if (settings[size]) {
15555 cfg.cls += ' col-' + size + '-' + settings[size];
15562 initTouchView : function()
15564 this.renderTouchView();
15566 this.touchViewEl.on('scroll', function(){
15567 this.el.dom.scrollTop = 0;
15570 this.originalValue = this.getValue();
15572 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15574 this.inputEl().on("click", this.showTouchView, this);
15575 if (this.triggerEl) {
15576 this.triggerEl.on("click", this.showTouchView, this);
15580 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15581 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15583 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15585 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15586 this.store.on('load', this.onTouchViewLoad, this);
15587 this.store.on('loadexception', this.onTouchViewLoadException, this);
15589 if(this.hiddenName){
15591 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15593 this.hiddenField.dom.value =
15594 this.hiddenValue !== undefined ? this.hiddenValue :
15595 this.value !== undefined ? this.value : '';
15597 this.el.dom.removeAttribute('name');
15598 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15602 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15603 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15606 if(this.removable && !this.multiple){
15607 var close = this.closeTriggerEl();
15609 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15610 close.on('click', this.removeBtnClick, this, close);
15614 * fix the bug in Safari iOS8
15616 this.inputEl().on("focus", function(e){
15617 document.activeElement.blur();
15620 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15627 renderTouchView : function()
15629 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15630 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15632 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15633 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15635 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15636 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15637 this.touchViewBodyEl.setStyle('overflow', 'auto');
15639 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15640 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15642 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15643 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15647 showTouchView : function()
15653 this.touchViewHeaderEl.hide();
15655 if(this.modalTitle.length){
15656 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15657 this.touchViewHeaderEl.show();
15660 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15661 this.touchViewEl.show();
15663 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15665 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15666 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15668 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15670 if(this.modalTitle.length){
15671 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15674 this.touchViewBodyEl.setHeight(bodyHeight);
15678 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15680 this.touchViewEl.addClass('in');
15683 if(this._touchViewMask){
15684 Roo.get(document.body).addClass("x-body-masked");
15685 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15686 this._touchViewMask.setStyle('z-index', 10000);
15687 this._touchViewMask.addClass('show');
15690 this.doTouchViewQuery();
15694 hideTouchView : function()
15696 this.touchViewEl.removeClass('in');
15700 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15702 this.touchViewEl.setStyle('display', 'none');
15705 if(this._touchViewMask){
15706 this._touchViewMask.removeClass('show');
15707 Roo.get(document.body).removeClass("x-body-masked");
15711 setTouchViewValue : function()
15718 Roo.each(this.tickItems, function(o){
15723 this.hideTouchView();
15726 doTouchViewQuery : function()
15735 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15739 if(!this.alwaysQuery || this.mode == 'local'){
15740 this.onTouchViewLoad();
15747 onTouchViewBeforeLoad : function(combo,opts)
15753 onTouchViewLoad : function()
15755 if(this.store.getCount() < 1){
15756 this.onTouchViewEmptyResults();
15760 this.clearTouchView();
15762 var rawValue = this.getRawValue();
15764 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15766 this.tickItems = [];
15768 this.store.data.each(function(d, rowIndex){
15769 var row = this.touchViewListGroup.createChild(template);
15771 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15772 row.addClass(d.data.cls);
15775 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15778 html : d.data[this.displayField]
15781 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15782 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15785 row.removeClass('selected');
15786 if(!this.multiple && this.valueField &&
15787 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15790 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15791 row.addClass('selected');
15794 if(this.multiple && this.valueField &&
15795 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15799 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15800 this.tickItems.push(d.data);
15803 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15807 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15809 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15811 if(this.modalTitle.length){
15812 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15815 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15817 if(this.mobile_restrict_height && listHeight < bodyHeight){
15818 this.touchViewBodyEl.setHeight(listHeight);
15823 if(firstChecked && listHeight > bodyHeight){
15824 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15829 onTouchViewLoadException : function()
15831 this.hideTouchView();
15834 onTouchViewEmptyResults : function()
15836 this.clearTouchView();
15838 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15840 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15844 clearTouchView : function()
15846 this.touchViewListGroup.dom.innerHTML = '';
15849 onTouchViewClick : function(e, el, o)
15851 e.preventDefault();
15854 var rowIndex = o.rowIndex;
15856 var r = this.store.getAt(rowIndex);
15858 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15860 if(!this.multiple){
15861 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15862 c.dom.removeAttribute('checked');
15865 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15867 this.setFromData(r.data);
15869 var close = this.closeTriggerEl();
15875 this.hideTouchView();
15877 this.fireEvent('select', this, r, rowIndex);
15882 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15883 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15884 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15888 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15889 this.addItem(r.data);
15890 this.tickItems.push(r.data);
15894 getAutoCreateNativeIOS : function()
15897 cls: 'form-group' //input-group,
15902 cls : 'roo-ios-select'
15906 combobox.name = this.name;
15909 if (this.disabled) {
15910 combobox.disabled = true;
15913 var settings = this;
15915 ['xs','sm','md','lg'].map(function(size){
15916 if (settings[size]) {
15917 cfg.cls += ' col-' + size + '-' + settings[size];
15927 initIOSView : function()
15929 this.store.on('load', this.onIOSViewLoad, this);
15934 onIOSViewLoad : function()
15936 if(this.store.getCount() < 1){
15940 this.clearIOSView();
15942 if(this.allowBlank) {
15944 var default_text = '-- SELECT --';
15946 if(this.placeholder.length){
15947 default_text = this.placeholder;
15950 if(this.emptyTitle.length){
15951 default_text += ' - ' + this.emptyTitle + ' -';
15954 var opt = this.inputEl().createChild({
15957 html : default_text
15961 o[this.valueField] = 0;
15962 o[this.displayField] = default_text;
15964 this.ios_options.push({
15971 this.store.data.each(function(d, rowIndex){
15975 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15976 html = d.data[this.displayField];
15981 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15982 value = d.data[this.valueField];
15991 if(this.value == d.data[this.valueField]){
15992 option['selected'] = true;
15995 var opt = this.inputEl().createChild(option);
15997 this.ios_options.push({
16004 this.inputEl().on('change', function(){
16005 this.fireEvent('select', this);
16010 clearIOSView: function()
16012 this.inputEl().dom.innerHTML = '';
16014 this.ios_options = [];
16017 setIOSValue: function(v)
16021 if(!this.ios_options){
16025 Roo.each(this.ios_options, function(opts){
16027 opts.el.dom.removeAttribute('selected');
16029 if(opts.data[this.valueField] != v){
16033 opts.el.dom.setAttribute('selected', true);
16039 * @cfg {Boolean} grow
16043 * @cfg {Number} growMin
16047 * @cfg {Number} growMax
16056 Roo.apply(Roo.bootstrap.ComboBox, {
16060 cls: 'modal-header',
16082 cls: 'list-group-item',
16086 cls: 'roo-combobox-list-group-item-value'
16090 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16104 listItemCheckbox : {
16106 cls: 'list-group-item',
16110 cls: 'roo-combobox-list-group-item-value'
16114 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16130 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16135 cls: 'modal-footer',
16143 cls: 'col-xs-6 text-left',
16146 cls: 'btn btn-danger roo-touch-view-cancel',
16152 cls: 'col-xs-6 text-right',
16155 cls: 'btn btn-success roo-touch-view-ok',
16166 Roo.apply(Roo.bootstrap.ComboBox, {
16168 touchViewTemplate : {
16170 cls: 'modal fade roo-combobox-touch-view',
16174 cls: 'modal-dialog',
16175 style : 'position:fixed', // we have to fix position....
16179 cls: 'modal-content',
16181 Roo.bootstrap.ComboBox.header,
16182 Roo.bootstrap.ComboBox.body,
16183 Roo.bootstrap.ComboBox.footer
16192 * Ext JS Library 1.1.1
16193 * Copyright(c) 2006-2007, Ext JS, LLC.
16195 * Originally Released Under LGPL - original licence link has changed is not relivant.
16198 * <script type="text/javascript">
16203 * @extends Roo.util.Observable
16204 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16205 * This class also supports single and multi selection modes. <br>
16206 * Create a data model bound view:
16208 var store = new Roo.data.Store(...);
16210 var view = new Roo.View({
16212 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16214 singleSelect: true,
16215 selectedClass: "ydataview-selected",
16219 // listen for node click?
16220 view.on("click", function(vw, index, node, e){
16221 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16225 dataModel.load("foobar.xml");
16227 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16229 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16230 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16232 * Note: old style constructor is still suported (container, template, config)
16235 * Create a new View
16236 * @param {Object} config The config object
16239 Roo.View = function(config, depreciated_tpl, depreciated_config){
16241 this.parent = false;
16243 if (typeof(depreciated_tpl) == 'undefined') {
16244 // new way.. - universal constructor.
16245 Roo.apply(this, config);
16246 this.el = Roo.get(this.el);
16249 this.el = Roo.get(config);
16250 this.tpl = depreciated_tpl;
16251 Roo.apply(this, depreciated_config);
16253 this.wrapEl = this.el.wrap().wrap();
16254 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16257 if(typeof(this.tpl) == "string"){
16258 this.tpl = new Roo.Template(this.tpl);
16260 // support xtype ctors..
16261 this.tpl = new Roo.factory(this.tpl, Roo);
16265 this.tpl.compile();
16270 * @event beforeclick
16271 * Fires before a click is processed. Returns false to cancel the default action.
16272 * @param {Roo.View} this
16273 * @param {Number} index The index of the target node
16274 * @param {HTMLElement} node The target node
16275 * @param {Roo.EventObject} e The raw event object
16277 "beforeclick" : true,
16280 * Fires when a template node is clicked.
16281 * @param {Roo.View} this
16282 * @param {Number} index The index of the target node
16283 * @param {HTMLElement} node The target node
16284 * @param {Roo.EventObject} e The raw event object
16289 * Fires when a template node is double clicked.
16290 * @param {Roo.View} this
16291 * @param {Number} index The index of the target node
16292 * @param {HTMLElement} node The target node
16293 * @param {Roo.EventObject} e The raw event object
16297 * @event contextmenu
16298 * Fires when a template node is right clicked.
16299 * @param {Roo.View} this
16300 * @param {Number} index The index of the target node
16301 * @param {HTMLElement} node The target node
16302 * @param {Roo.EventObject} e The raw event object
16304 "contextmenu" : true,
16306 * @event selectionchange
16307 * Fires when the selected nodes change.
16308 * @param {Roo.View} this
16309 * @param {Array} selections Array of the selected nodes
16311 "selectionchange" : true,
16314 * @event beforeselect
16315 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16316 * @param {Roo.View} this
16317 * @param {HTMLElement} node The node to be selected
16318 * @param {Array} selections Array of currently selected nodes
16320 "beforeselect" : true,
16322 * @event preparedata
16323 * Fires on every row to render, to allow you to change the data.
16324 * @param {Roo.View} this
16325 * @param {Object} data to be rendered (change this)
16327 "preparedata" : true
16335 "click": this.onClick,
16336 "dblclick": this.onDblClick,
16337 "contextmenu": this.onContextMenu,
16341 this.selections = [];
16343 this.cmp = new Roo.CompositeElementLite([]);
16345 this.store = Roo.factory(this.store, Roo.data);
16346 this.setStore(this.store, true);
16349 if ( this.footer && this.footer.xtype) {
16351 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16353 this.footer.dataSource = this.store;
16354 this.footer.container = fctr;
16355 this.footer = Roo.factory(this.footer, Roo);
16356 fctr.insertFirst(this.el);
16358 // this is a bit insane - as the paging toolbar seems to detach the el..
16359 // dom.parentNode.parentNode.parentNode
16360 // they get detached?
16364 Roo.View.superclass.constructor.call(this);
16369 Roo.extend(Roo.View, Roo.util.Observable, {
16372 * @cfg {Roo.data.Store} store Data store to load data from.
16377 * @cfg {String|Roo.Element} el The container element.
16382 * @cfg {String|Roo.Template} tpl The template used by this View
16386 * @cfg {String} dataName the named area of the template to use as the data area
16387 * Works with domtemplates roo-name="name"
16391 * @cfg {String} selectedClass The css class to add to selected nodes
16393 selectedClass : "x-view-selected",
16395 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16400 * @cfg {String} text to display on mask (default Loading)
16404 * @cfg {Boolean} multiSelect Allow multiple selection
16406 multiSelect : false,
16408 * @cfg {Boolean} singleSelect Allow single selection
16410 singleSelect: false,
16413 * @cfg {Boolean} toggleSelect - selecting
16415 toggleSelect : false,
16418 * @cfg {Boolean} tickable - selecting
16423 * Returns the element this view is bound to.
16424 * @return {Roo.Element}
16426 getEl : function(){
16427 return this.wrapEl;
16433 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16435 refresh : function(){
16436 //Roo.log('refresh');
16439 // if we are using something like 'domtemplate', then
16440 // the what gets used is:
16441 // t.applySubtemplate(NAME, data, wrapping data..)
16442 // the outer template then get' applied with
16443 // the store 'extra data'
16444 // and the body get's added to the
16445 // roo-name="data" node?
16446 // <span class='roo-tpl-{name}'></span> ?????
16450 this.clearSelections();
16451 this.el.update("");
16453 var records = this.store.getRange();
16454 if(records.length < 1) {
16456 // is this valid?? = should it render a template??
16458 this.el.update(this.emptyText);
16462 if (this.dataName) {
16463 this.el.update(t.apply(this.store.meta)); //????
16464 el = this.el.child('.roo-tpl-' + this.dataName);
16467 for(var i = 0, len = records.length; i < len; i++){
16468 var data = this.prepareData(records[i].data, i, records[i]);
16469 this.fireEvent("preparedata", this, data, i, records[i]);
16471 var d = Roo.apply({}, data);
16474 Roo.apply(d, {'roo-id' : Roo.id()});
16478 Roo.each(this.parent.item, function(item){
16479 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16482 Roo.apply(d, {'roo-data-checked' : 'checked'});
16486 html[html.length] = Roo.util.Format.trim(
16488 t.applySubtemplate(this.dataName, d, this.store.meta) :
16495 el.update(html.join(""));
16496 this.nodes = el.dom.childNodes;
16497 this.updateIndexes(0);
16502 * Function to override to reformat the data that is sent to
16503 * the template for each node.
16504 * DEPRICATED - use the preparedata event handler.
16505 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16506 * a JSON object for an UpdateManager bound view).
16508 prepareData : function(data, index, record)
16510 this.fireEvent("preparedata", this, data, index, record);
16514 onUpdate : function(ds, record){
16515 // Roo.log('on update');
16516 this.clearSelections();
16517 var index = this.store.indexOf(record);
16518 var n = this.nodes[index];
16519 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16520 n.parentNode.removeChild(n);
16521 this.updateIndexes(index, index);
16527 onAdd : function(ds, records, index)
16529 //Roo.log(['on Add', ds, records, index] );
16530 this.clearSelections();
16531 if(this.nodes.length == 0){
16535 var n = this.nodes[index];
16536 for(var i = 0, len = records.length; i < len; i++){
16537 var d = this.prepareData(records[i].data, i, records[i]);
16539 this.tpl.insertBefore(n, d);
16542 this.tpl.append(this.el, d);
16545 this.updateIndexes(index);
16548 onRemove : function(ds, record, index){
16549 // Roo.log('onRemove');
16550 this.clearSelections();
16551 var el = this.dataName ?
16552 this.el.child('.roo-tpl-' + this.dataName) :
16555 el.dom.removeChild(this.nodes[index]);
16556 this.updateIndexes(index);
16560 * Refresh an individual node.
16561 * @param {Number} index
16563 refreshNode : function(index){
16564 this.onUpdate(this.store, this.store.getAt(index));
16567 updateIndexes : function(startIndex, endIndex){
16568 var ns = this.nodes;
16569 startIndex = startIndex || 0;
16570 endIndex = endIndex || ns.length - 1;
16571 for(var i = startIndex; i <= endIndex; i++){
16572 ns[i].nodeIndex = i;
16577 * Changes the data store this view uses and refresh the view.
16578 * @param {Store} store
16580 setStore : function(store, initial){
16581 if(!initial && this.store){
16582 this.store.un("datachanged", this.refresh);
16583 this.store.un("add", this.onAdd);
16584 this.store.un("remove", this.onRemove);
16585 this.store.un("update", this.onUpdate);
16586 this.store.un("clear", this.refresh);
16587 this.store.un("beforeload", this.onBeforeLoad);
16588 this.store.un("load", this.onLoad);
16589 this.store.un("loadexception", this.onLoad);
16593 store.on("datachanged", this.refresh, this);
16594 store.on("add", this.onAdd, this);
16595 store.on("remove", this.onRemove, this);
16596 store.on("update", this.onUpdate, this);
16597 store.on("clear", this.refresh, this);
16598 store.on("beforeload", this.onBeforeLoad, this);
16599 store.on("load", this.onLoad, this);
16600 store.on("loadexception", this.onLoad, this);
16608 * onbeforeLoad - masks the loading area.
16611 onBeforeLoad : function(store,opts)
16613 //Roo.log('onBeforeLoad');
16615 this.el.update("");
16617 this.el.mask(this.mask ? this.mask : "Loading" );
16619 onLoad : function ()
16626 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16627 * @param {HTMLElement} node
16628 * @return {HTMLElement} The template node
16630 findItemFromChild : function(node){
16631 var el = this.dataName ?
16632 this.el.child('.roo-tpl-' + this.dataName,true) :
16635 if(!node || node.parentNode == el){
16638 var p = node.parentNode;
16639 while(p && p != el){
16640 if(p.parentNode == el){
16649 onClick : function(e){
16650 var item = this.findItemFromChild(e.getTarget());
16652 var index = this.indexOf(item);
16653 if(this.onItemClick(item, index, e) !== false){
16654 this.fireEvent("click", this, index, item, e);
16657 this.clearSelections();
16662 onContextMenu : function(e){
16663 var item = this.findItemFromChild(e.getTarget());
16665 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16670 onDblClick : function(e){
16671 var item = this.findItemFromChild(e.getTarget());
16673 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16677 onItemClick : function(item, index, e)
16679 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16682 if (this.toggleSelect) {
16683 var m = this.isSelected(item) ? 'unselect' : 'select';
16686 _t[m](item, true, false);
16689 if(this.multiSelect || this.singleSelect){
16690 if(this.multiSelect && e.shiftKey && this.lastSelection){
16691 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16693 this.select(item, this.multiSelect && e.ctrlKey);
16694 this.lastSelection = item;
16697 if(!this.tickable){
16698 e.preventDefault();
16706 * Get the number of selected nodes.
16709 getSelectionCount : function(){
16710 return this.selections.length;
16714 * Get the currently selected nodes.
16715 * @return {Array} An array of HTMLElements
16717 getSelectedNodes : function(){
16718 return this.selections;
16722 * Get the indexes of the selected nodes.
16725 getSelectedIndexes : function(){
16726 var indexes = [], s = this.selections;
16727 for(var i = 0, len = s.length; i < len; i++){
16728 indexes.push(s[i].nodeIndex);
16734 * Clear all selections
16735 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16737 clearSelections : function(suppressEvent){
16738 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16739 this.cmp.elements = this.selections;
16740 this.cmp.removeClass(this.selectedClass);
16741 this.selections = [];
16742 if(!suppressEvent){
16743 this.fireEvent("selectionchange", this, this.selections);
16749 * Returns true if the passed node is selected
16750 * @param {HTMLElement/Number} node The node or node index
16751 * @return {Boolean}
16753 isSelected : function(node){
16754 var s = this.selections;
16758 node = this.getNode(node);
16759 return s.indexOf(node) !== -1;
16764 * @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
16765 * @param {Boolean} keepExisting (optional) true to keep existing selections
16766 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16768 select : function(nodeInfo, keepExisting, suppressEvent){
16769 if(nodeInfo instanceof Array){
16771 this.clearSelections(true);
16773 for(var i = 0, len = nodeInfo.length; i < len; i++){
16774 this.select(nodeInfo[i], true, true);
16778 var node = this.getNode(nodeInfo);
16779 if(!node || this.isSelected(node)){
16780 return; // already selected.
16783 this.clearSelections(true);
16786 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16787 Roo.fly(node).addClass(this.selectedClass);
16788 this.selections.push(node);
16789 if(!suppressEvent){
16790 this.fireEvent("selectionchange", this, this.selections);
16798 * @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
16799 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16800 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16802 unselect : function(nodeInfo, keepExisting, suppressEvent)
16804 if(nodeInfo instanceof Array){
16805 Roo.each(this.selections, function(s) {
16806 this.unselect(s, nodeInfo);
16810 var node = this.getNode(nodeInfo);
16811 if(!node || !this.isSelected(node)){
16812 //Roo.log("not selected");
16813 return; // not selected.
16817 Roo.each(this.selections, function(s) {
16819 Roo.fly(node).removeClass(this.selectedClass);
16826 this.selections= ns;
16827 this.fireEvent("selectionchange", this, this.selections);
16831 * Gets a template node.
16832 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16833 * @return {HTMLElement} The node or null if it wasn't found
16835 getNode : function(nodeInfo){
16836 if(typeof nodeInfo == "string"){
16837 return document.getElementById(nodeInfo);
16838 }else if(typeof nodeInfo == "number"){
16839 return this.nodes[nodeInfo];
16845 * Gets a range template nodes.
16846 * @param {Number} startIndex
16847 * @param {Number} endIndex
16848 * @return {Array} An array of nodes
16850 getNodes : function(start, end){
16851 var ns = this.nodes;
16852 start = start || 0;
16853 end = typeof end == "undefined" ? ns.length - 1 : end;
16856 for(var i = start; i <= end; i++){
16860 for(var i = start; i >= end; i--){
16868 * Finds the index of the passed node
16869 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16870 * @return {Number} The index of the node or -1
16872 indexOf : function(node){
16873 node = this.getNode(node);
16874 if(typeof node.nodeIndex == "number"){
16875 return node.nodeIndex;
16877 var ns = this.nodes;
16878 for(var i = 0, len = ns.length; i < len; i++){
16889 * based on jquery fullcalendar
16893 Roo.bootstrap = Roo.bootstrap || {};
16895 * @class Roo.bootstrap.Calendar
16896 * @extends Roo.bootstrap.Component
16897 * Bootstrap Calendar class
16898 * @cfg {Boolean} loadMask (true|false) default false
16899 * @cfg {Object} header generate the user specific header of the calendar, default false
16902 * Create a new Container
16903 * @param {Object} config The config object
16908 Roo.bootstrap.Calendar = function(config){
16909 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16913 * Fires when a date is selected
16914 * @param {DatePicker} this
16915 * @param {Date} date The selected date
16919 * @event monthchange
16920 * Fires when the displayed month changes
16921 * @param {DatePicker} this
16922 * @param {Date} date The selected month
16924 'monthchange': true,
16926 * @event evententer
16927 * Fires when mouse over an event
16928 * @param {Calendar} this
16929 * @param {event} Event
16931 'evententer': true,
16933 * @event eventleave
16934 * Fires when the mouse leaves an
16935 * @param {Calendar} this
16938 'eventleave': true,
16940 * @event eventclick
16941 * Fires when the mouse click an
16942 * @param {Calendar} this
16951 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16954 * @cfg {Number} startDay
16955 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16963 getAutoCreate : function(){
16966 var fc_button = function(name, corner, style, content ) {
16967 return Roo.apply({},{
16969 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16971 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16974 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16985 style : 'width:100%',
16992 cls : 'fc-header-left',
16994 fc_button('prev', 'left', 'arrow', '‹' ),
16995 fc_button('next', 'right', 'arrow', '›' ),
16996 { tag: 'span', cls: 'fc-header-space' },
16997 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
17005 cls : 'fc-header-center',
17009 cls: 'fc-header-title',
17012 html : 'month / year'
17020 cls : 'fc-header-right',
17022 /* fc_button('month', 'left', '', 'month' ),
17023 fc_button('week', '', '', 'week' ),
17024 fc_button('day', 'right', '', 'day' )
17036 header = this.header;
17039 var cal_heads = function() {
17041 // fixme - handle this.
17043 for (var i =0; i < Date.dayNames.length; i++) {
17044 var d = Date.dayNames[i];
17047 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17048 html : d.substring(0,3)
17052 ret[0].cls += ' fc-first';
17053 ret[6].cls += ' fc-last';
17056 var cal_cell = function(n) {
17059 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17064 cls: 'fc-day-number',
17068 cls: 'fc-day-content',
17072 style: 'position: relative;' // height: 17px;
17084 var cal_rows = function() {
17087 for (var r = 0; r < 6; r++) {
17094 for (var i =0; i < Date.dayNames.length; i++) {
17095 var d = Date.dayNames[i];
17096 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17099 row.cn[0].cls+=' fc-first';
17100 row.cn[0].cn[0].style = 'min-height:90px';
17101 row.cn[6].cls+=' fc-last';
17105 ret[0].cls += ' fc-first';
17106 ret[4].cls += ' fc-prev-last';
17107 ret[5].cls += ' fc-last';
17114 cls: 'fc-border-separate',
17115 style : 'width:100%',
17123 cls : 'fc-first fc-last',
17141 cls : 'fc-content',
17142 style : "position: relative;",
17145 cls : 'fc-view fc-view-month fc-grid',
17146 style : 'position: relative',
17147 unselectable : 'on',
17150 cls : 'fc-event-container',
17151 style : 'position:absolute;z-index:8;top:0;left:0;'
17169 initEvents : function()
17172 throw "can not find store for calendar";
17178 style: "text-align:center",
17182 style: "background-color:white;width:50%;margin:250 auto",
17186 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17197 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17199 var size = this.el.select('.fc-content', true).first().getSize();
17200 this.maskEl.setSize(size.width, size.height);
17201 this.maskEl.enableDisplayMode("block");
17202 if(!this.loadMask){
17203 this.maskEl.hide();
17206 this.store = Roo.factory(this.store, Roo.data);
17207 this.store.on('load', this.onLoad, this);
17208 this.store.on('beforeload', this.onBeforeLoad, this);
17212 this.cells = this.el.select('.fc-day',true);
17213 //Roo.log(this.cells);
17214 this.textNodes = this.el.query('.fc-day-number');
17215 this.cells.addClassOnOver('fc-state-hover');
17217 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17218 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17219 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17220 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17222 this.on('monthchange', this.onMonthChange, this);
17224 this.update(new Date().clearTime());
17227 resize : function() {
17228 var sz = this.el.getSize();
17230 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17231 this.el.select('.fc-day-content div',true).setHeight(34);
17236 showPrevMonth : function(e){
17237 this.update(this.activeDate.add("mo", -1));
17239 showToday : function(e){
17240 this.update(new Date().clearTime());
17243 showNextMonth : function(e){
17244 this.update(this.activeDate.add("mo", 1));
17248 showPrevYear : function(){
17249 this.update(this.activeDate.add("y", -1));
17253 showNextYear : function(){
17254 this.update(this.activeDate.add("y", 1));
17259 update : function(date)
17261 var vd = this.activeDate;
17262 this.activeDate = date;
17263 // if(vd && this.el){
17264 // var t = date.getTime();
17265 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17266 // Roo.log('using add remove');
17268 // this.fireEvent('monthchange', this, date);
17270 // this.cells.removeClass("fc-state-highlight");
17271 // this.cells.each(function(c){
17272 // if(c.dateValue == t){
17273 // c.addClass("fc-state-highlight");
17274 // setTimeout(function(){
17275 // try{c.dom.firstChild.focus();}catch(e){}
17285 var days = date.getDaysInMonth();
17287 var firstOfMonth = date.getFirstDateOfMonth();
17288 var startingPos = firstOfMonth.getDay()-this.startDay;
17290 if(startingPos < this.startDay){
17294 var pm = date.add(Date.MONTH, -1);
17295 var prevStart = pm.getDaysInMonth()-startingPos;
17297 this.cells = this.el.select('.fc-day',true);
17298 this.textNodes = this.el.query('.fc-day-number');
17299 this.cells.addClassOnOver('fc-state-hover');
17301 var cells = this.cells.elements;
17302 var textEls = this.textNodes;
17304 Roo.each(cells, function(cell){
17305 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17308 days += startingPos;
17310 // convert everything to numbers so it's fast
17311 var day = 86400000;
17312 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17315 //Roo.log(prevStart);
17317 var today = new Date().clearTime().getTime();
17318 var sel = date.clearTime().getTime();
17319 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17320 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17321 var ddMatch = this.disabledDatesRE;
17322 var ddText = this.disabledDatesText;
17323 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17324 var ddaysText = this.disabledDaysText;
17325 var format = this.format;
17327 var setCellClass = function(cal, cell){
17331 //Roo.log('set Cell Class');
17333 var t = d.getTime();
17337 cell.dateValue = t;
17339 cell.className += " fc-today";
17340 cell.className += " fc-state-highlight";
17341 cell.title = cal.todayText;
17344 // disable highlight in other month..
17345 //cell.className += " fc-state-highlight";
17350 cell.className = " fc-state-disabled";
17351 cell.title = cal.minText;
17355 cell.className = " fc-state-disabled";
17356 cell.title = cal.maxText;
17360 if(ddays.indexOf(d.getDay()) != -1){
17361 cell.title = ddaysText;
17362 cell.className = " fc-state-disabled";
17365 if(ddMatch && format){
17366 var fvalue = d.dateFormat(format);
17367 if(ddMatch.test(fvalue)){
17368 cell.title = ddText.replace("%0", fvalue);
17369 cell.className = " fc-state-disabled";
17373 if (!cell.initialClassName) {
17374 cell.initialClassName = cell.dom.className;
17377 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17382 for(; i < startingPos; i++) {
17383 textEls[i].innerHTML = (++prevStart);
17384 d.setDate(d.getDate()+1);
17386 cells[i].className = "fc-past fc-other-month";
17387 setCellClass(this, cells[i]);
17392 for(; i < days; i++){
17393 intDay = i - startingPos + 1;
17394 textEls[i].innerHTML = (intDay);
17395 d.setDate(d.getDate()+1);
17397 cells[i].className = ''; // "x-date-active";
17398 setCellClass(this, cells[i]);
17402 for(; i < 42; i++) {
17403 textEls[i].innerHTML = (++extraDays);
17404 d.setDate(d.getDate()+1);
17406 cells[i].className = "fc-future fc-other-month";
17407 setCellClass(this, cells[i]);
17410 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17412 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17414 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17415 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17417 if(totalRows != 6){
17418 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17419 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17422 this.fireEvent('monthchange', this, date);
17426 if(!this.internalRender){
17427 var main = this.el.dom.firstChild;
17428 var w = main.offsetWidth;
17429 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17430 Roo.fly(main).setWidth(w);
17431 this.internalRender = true;
17432 // opera does not respect the auto grow header center column
17433 // then, after it gets a width opera refuses to recalculate
17434 // without a second pass
17435 if(Roo.isOpera && !this.secondPass){
17436 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17437 this.secondPass = true;
17438 this.update.defer(10, this, [date]);
17445 findCell : function(dt) {
17446 dt = dt.clearTime().getTime();
17448 this.cells.each(function(c){
17449 //Roo.log("check " +c.dateValue + '?=' + dt);
17450 if(c.dateValue == dt){
17460 findCells : function(ev) {
17461 var s = ev.start.clone().clearTime().getTime();
17463 var e= ev.end.clone().clearTime().getTime();
17466 this.cells.each(function(c){
17467 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17469 if(c.dateValue > e){
17472 if(c.dateValue < s){
17481 // findBestRow: function(cells)
17485 // for (var i =0 ; i < cells.length;i++) {
17486 // ret = Math.max(cells[i].rows || 0,ret);
17493 addItem : function(ev)
17495 // look for vertical location slot in
17496 var cells = this.findCells(ev);
17498 // ev.row = this.findBestRow(cells);
17500 // work out the location.
17504 for(var i =0; i < cells.length; i++) {
17506 cells[i].row = cells[0].row;
17509 cells[i].row = cells[i].row + 1;
17519 if (crow.start.getY() == cells[i].getY()) {
17521 crow.end = cells[i];
17538 cells[0].events.push(ev);
17540 this.calevents.push(ev);
17543 clearEvents: function() {
17545 if(!this.calevents){
17549 Roo.each(this.cells.elements, function(c){
17555 Roo.each(this.calevents, function(e) {
17556 Roo.each(e.els, function(el) {
17557 el.un('mouseenter' ,this.onEventEnter, this);
17558 el.un('mouseleave' ,this.onEventLeave, this);
17563 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17569 renderEvents: function()
17573 this.cells.each(function(c) {
17582 if(c.row != c.events.length){
17583 r = 4 - (4 - (c.row - c.events.length));
17586 c.events = ev.slice(0, r);
17587 c.more = ev.slice(r);
17589 if(c.more.length && c.more.length == 1){
17590 c.events.push(c.more.pop());
17593 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17597 this.cells.each(function(c) {
17599 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17602 for (var e = 0; e < c.events.length; e++){
17603 var ev = c.events[e];
17604 var rows = ev.rows;
17606 for(var i = 0; i < rows.length; i++) {
17608 // how many rows should it span..
17611 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17612 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17614 unselectable : "on",
17617 cls: 'fc-event-inner',
17621 // cls: 'fc-event-time',
17622 // html : cells.length > 1 ? '' : ev.time
17626 cls: 'fc-event-title',
17627 html : String.format('{0}', ev.title)
17634 cls: 'ui-resizable-handle ui-resizable-e',
17635 html : '  '
17642 cfg.cls += ' fc-event-start';
17644 if ((i+1) == rows.length) {
17645 cfg.cls += ' fc-event-end';
17648 var ctr = _this.el.select('.fc-event-container',true).first();
17649 var cg = ctr.createChild(cfg);
17651 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17652 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17654 var r = (c.more.length) ? 1 : 0;
17655 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17656 cg.setWidth(ebox.right - sbox.x -2);
17658 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17659 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17660 cg.on('click', _this.onEventClick, _this, ev);
17671 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17672 style : 'position: absolute',
17673 unselectable : "on",
17676 cls: 'fc-event-inner',
17680 cls: 'fc-event-title',
17688 cls: 'ui-resizable-handle ui-resizable-e',
17689 html : '  '
17695 var ctr = _this.el.select('.fc-event-container',true).first();
17696 var cg = ctr.createChild(cfg);
17698 var sbox = c.select('.fc-day-content',true).first().getBox();
17699 var ebox = c.select('.fc-day-content',true).first().getBox();
17701 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17702 cg.setWidth(ebox.right - sbox.x -2);
17704 cg.on('click', _this.onMoreEventClick, _this, c.more);
17714 onEventEnter: function (e, el,event,d) {
17715 this.fireEvent('evententer', this, el, event);
17718 onEventLeave: function (e, el,event,d) {
17719 this.fireEvent('eventleave', this, el, event);
17722 onEventClick: function (e, el,event,d) {
17723 this.fireEvent('eventclick', this, el, event);
17726 onMonthChange: function () {
17730 onMoreEventClick: function(e, el, more)
17734 this.calpopover.placement = 'right';
17735 this.calpopover.setTitle('More');
17737 this.calpopover.setContent('');
17739 var ctr = this.calpopover.el.select('.popover-content', true).first();
17741 Roo.each(more, function(m){
17743 cls : 'fc-event-hori fc-event-draggable',
17746 var cg = ctr.createChild(cfg);
17748 cg.on('click', _this.onEventClick, _this, m);
17751 this.calpopover.show(el);
17756 onLoad: function ()
17758 this.calevents = [];
17761 if(this.store.getCount() > 0){
17762 this.store.data.each(function(d){
17765 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17766 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17767 time : d.data.start_time,
17768 title : d.data.title,
17769 description : d.data.description,
17770 venue : d.data.venue
17775 this.renderEvents();
17777 if(this.calevents.length && this.loadMask){
17778 this.maskEl.hide();
17782 onBeforeLoad: function()
17784 this.clearEvents();
17786 this.maskEl.show();
17800 * @class Roo.bootstrap.Popover
17801 * @extends Roo.bootstrap.Component
17802 * Bootstrap Popover class
17803 * @cfg {String} html contents of the popover (or false to use children..)
17804 * @cfg {String} title of popover (or false to hide)
17805 * @cfg {String} placement how it is placed
17806 * @cfg {String} trigger click || hover (or false to trigger manually)
17807 * @cfg {String} over what (parent or false to trigger manually.)
17808 * @cfg {Number} delay - delay before showing
17811 * Create a new Popover
17812 * @param {Object} config The config object
17815 Roo.bootstrap.Popover = function(config){
17816 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17822 * After the popover show
17824 * @param {Roo.bootstrap.Popover} this
17829 * After the popover hide
17831 * @param {Roo.bootstrap.Popover} this
17837 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17839 title: 'Fill in a title',
17842 placement : 'right',
17843 trigger : 'hover', // hover
17849 can_build_overlaid : false,
17851 getChildContainer : function()
17853 return this.el.select('.popover-content',true).first();
17856 getAutoCreate : function(){
17859 cls : 'popover roo-dynamic',
17860 style: 'display:block',
17866 cls : 'popover-inner',
17870 cls: 'popover-title popover-header',
17874 cls : 'popover-content popover-body',
17885 setTitle: function(str)
17888 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17890 setContent: function(str)
17893 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17895 // as it get's added to the bottom of the page.
17896 onRender : function(ct, position)
17898 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17900 var cfg = Roo.apply({}, this.getAutoCreate());
17904 cfg.cls += ' ' + this.cls;
17907 cfg.style = this.style;
17909 //Roo.log("adding to ");
17910 this.el = Roo.get(document.body).createChild(cfg, position);
17911 // Roo.log(this.el);
17916 initEvents : function()
17918 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17919 this.el.enableDisplayMode('block');
17921 if (this.over === false) {
17924 if (this.triggers === false) {
17927 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17928 var triggers = this.trigger ? this.trigger.split(' ') : [];
17929 Roo.each(triggers, function(trigger) {
17931 if (trigger == 'click') {
17932 on_el.on('click', this.toggle, this);
17933 } else if (trigger != 'manual') {
17934 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17935 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17937 on_el.on(eventIn ,this.enter, this);
17938 on_el.on(eventOut, this.leave, this);
17949 toggle : function () {
17950 this.hoverState == 'in' ? this.leave() : this.enter();
17953 enter : function () {
17955 clearTimeout(this.timeout);
17957 this.hoverState = 'in';
17959 if (!this.delay || !this.delay.show) {
17964 this.timeout = setTimeout(function () {
17965 if (_t.hoverState == 'in') {
17968 }, this.delay.show)
17971 leave : function() {
17972 clearTimeout(this.timeout);
17974 this.hoverState = 'out';
17976 if (!this.delay || !this.delay.hide) {
17981 this.timeout = setTimeout(function () {
17982 if (_t.hoverState == 'out') {
17985 }, this.delay.hide)
17988 show : function (on_el)
17991 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17995 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17996 if (this.html !== false) {
17997 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17999 this.el.removeClass([
18000 'fade','top','bottom', 'left', 'right','in',
18001 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18003 if (!this.title.length) {
18004 this.el.select('.popover-title',true).hide();
18007 var placement = typeof this.placement == 'function' ?
18008 this.placement.call(this, this.el, on_el) :
18011 var autoToken = /\s?auto?\s?/i;
18012 var autoPlace = autoToken.test(placement);
18014 placement = placement.replace(autoToken, '') || 'top';
18018 //this.el.setXY([0,0]);
18020 this.el.dom.style.display='block';
18021 this.el.addClass(placement);
18023 //this.el.appendTo(on_el);
18025 var p = this.getPosition();
18026 var box = this.el.getBox();
18031 var align = Roo.bootstrap.Popover.alignment[placement];
18034 this.el.alignTo(on_el, align[0],align[1]);
18035 //var arrow = this.el.select('.arrow',true).first();
18036 //arrow.set(align[2],
18038 this.el.addClass('in');
18041 if (this.el.hasClass('fade')) {
18045 this.hoverState = 'in';
18047 this.fireEvent('show', this);
18052 this.el.setXY([0,0]);
18053 this.el.removeClass('in');
18055 this.hoverState = null;
18057 this.fireEvent('hide', this);
18062 Roo.bootstrap.Popover.alignment = {
18063 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18064 'right' : ['l-r', [10,0], 'left bs-popover-left'],
18065 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18066 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18077 * @class Roo.bootstrap.Progress
18078 * @extends Roo.bootstrap.Component
18079 * Bootstrap Progress class
18080 * @cfg {Boolean} striped striped of the progress bar
18081 * @cfg {Boolean} active animated of the progress bar
18085 * Create a new Progress
18086 * @param {Object} config The config object
18089 Roo.bootstrap.Progress = function(config){
18090 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18093 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
18098 getAutoCreate : function(){
18106 cfg.cls += ' progress-striped';
18110 cfg.cls += ' active';
18129 * @class Roo.bootstrap.ProgressBar
18130 * @extends Roo.bootstrap.Component
18131 * Bootstrap ProgressBar class
18132 * @cfg {Number} aria_valuenow aria-value now
18133 * @cfg {Number} aria_valuemin aria-value min
18134 * @cfg {Number} aria_valuemax aria-value max
18135 * @cfg {String} label label for the progress bar
18136 * @cfg {String} panel (success | info | warning | danger )
18137 * @cfg {String} role role of the progress bar
18138 * @cfg {String} sr_only text
18142 * Create a new ProgressBar
18143 * @param {Object} config The config object
18146 Roo.bootstrap.ProgressBar = function(config){
18147 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18150 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
18154 aria_valuemax : 100,
18160 getAutoCreate : function()
18165 cls: 'progress-bar',
18166 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18178 cfg.role = this.role;
18181 if(this.aria_valuenow){
18182 cfg['aria-valuenow'] = this.aria_valuenow;
18185 if(this.aria_valuemin){
18186 cfg['aria-valuemin'] = this.aria_valuemin;
18189 if(this.aria_valuemax){
18190 cfg['aria-valuemax'] = this.aria_valuemax;
18193 if(this.label && !this.sr_only){
18194 cfg.html = this.label;
18198 cfg.cls += ' progress-bar-' + this.panel;
18204 update : function(aria_valuenow)
18206 this.aria_valuenow = aria_valuenow;
18208 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18223 * @class Roo.bootstrap.TabGroup
18224 * @extends Roo.bootstrap.Column
18225 * Bootstrap Column class
18226 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18227 * @cfg {Boolean} carousel true to make the group behave like a carousel
18228 * @cfg {Boolean} bullets show bullets for the panels
18229 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18230 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18231 * @cfg {Boolean} showarrow (true|false) show arrow default true
18234 * Create a new TabGroup
18235 * @param {Object} config The config object
18238 Roo.bootstrap.TabGroup = function(config){
18239 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18241 this.navId = Roo.id();
18244 Roo.bootstrap.TabGroup.register(this);
18248 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18251 transition : false,
18256 slideOnTouch : false,
18259 getAutoCreate : function()
18261 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18263 cfg.cls += ' tab-content';
18265 if (this.carousel) {
18266 cfg.cls += ' carousel slide';
18269 cls : 'carousel-inner',
18273 if(this.bullets && !Roo.isTouch){
18276 cls : 'carousel-bullets',
18280 if(this.bullets_cls){
18281 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18288 cfg.cn[0].cn.push(bullets);
18291 if(this.showarrow){
18292 cfg.cn[0].cn.push({
18294 class : 'carousel-arrow',
18298 class : 'carousel-prev',
18302 class : 'fa fa-chevron-left'
18308 class : 'carousel-next',
18312 class : 'fa fa-chevron-right'
18325 initEvents: function()
18327 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18328 // this.el.on("touchstart", this.onTouchStart, this);
18331 if(this.autoslide){
18334 this.slideFn = window.setInterval(function() {
18335 _this.showPanelNext();
18339 if(this.showarrow){
18340 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18341 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18347 // onTouchStart : function(e, el, o)
18349 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18353 // this.showPanelNext();
18357 getChildContainer : function()
18359 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18363 * register a Navigation item
18364 * @param {Roo.bootstrap.NavItem} the navitem to add
18366 register : function(item)
18368 this.tabs.push( item);
18369 item.navId = this.navId; // not really needed..
18374 getActivePanel : function()
18377 Roo.each(this.tabs, function(t) {
18387 getPanelByName : function(n)
18390 Roo.each(this.tabs, function(t) {
18391 if (t.tabId == n) {
18399 indexOfPanel : function(p)
18402 Roo.each(this.tabs, function(t,i) {
18403 if (t.tabId == p.tabId) {
18412 * show a specific panel
18413 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18414 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18416 showPanel : function (pan)
18418 if(this.transition || typeof(pan) == 'undefined'){
18419 Roo.log("waiting for the transitionend");
18423 if (typeof(pan) == 'number') {
18424 pan = this.tabs[pan];
18427 if (typeof(pan) == 'string') {
18428 pan = this.getPanelByName(pan);
18431 var cur = this.getActivePanel();
18434 Roo.log('pan or acitve pan is undefined');
18438 if (pan.tabId == this.getActivePanel().tabId) {
18442 if (false === cur.fireEvent('beforedeactivate')) {
18446 if(this.bullets > 0 && !Roo.isTouch){
18447 this.setActiveBullet(this.indexOfPanel(pan));
18450 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18452 //class="carousel-item carousel-item-next carousel-item-left"
18454 this.transition = true;
18455 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18456 var lr = dir == 'next' ? 'left' : 'right';
18457 pan.el.addClass(dir); // or prev
18458 pan.el.addClass('carousel-item-' + dir); // or prev
18459 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18460 cur.el.addClass(lr); // or right
18461 pan.el.addClass(lr);
18462 cur.el.addClass('carousel-item-' +lr); // or right
18463 pan.el.addClass('carousel-item-' +lr);
18467 cur.el.on('transitionend', function() {
18468 Roo.log("trans end?");
18470 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18471 pan.setActive(true);
18473 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18474 cur.setActive(false);
18476 _this.transition = false;
18478 }, this, { single: true } );
18483 cur.setActive(false);
18484 pan.setActive(true);
18489 showPanelNext : function()
18491 var i = this.indexOfPanel(this.getActivePanel());
18493 if (i >= this.tabs.length - 1 && !this.autoslide) {
18497 if (i >= this.tabs.length - 1 && this.autoslide) {
18501 this.showPanel(this.tabs[i+1]);
18504 showPanelPrev : function()
18506 var i = this.indexOfPanel(this.getActivePanel());
18508 if (i < 1 && !this.autoslide) {
18512 if (i < 1 && this.autoslide) {
18513 i = this.tabs.length;
18516 this.showPanel(this.tabs[i-1]);
18520 addBullet: function()
18522 if(!this.bullets || Roo.isTouch){
18525 var ctr = this.el.select('.carousel-bullets',true).first();
18526 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18527 var bullet = ctr.createChild({
18528 cls : 'bullet bullet-' + i
18529 },ctr.dom.lastChild);
18534 bullet.on('click', (function(e, el, o, ii, t){
18536 e.preventDefault();
18538 this.showPanel(ii);
18540 if(this.autoslide && this.slideFn){
18541 clearInterval(this.slideFn);
18542 this.slideFn = window.setInterval(function() {
18543 _this.showPanelNext();
18547 }).createDelegate(this, [i, bullet], true));
18552 setActiveBullet : function(i)
18558 Roo.each(this.el.select('.bullet', true).elements, function(el){
18559 el.removeClass('selected');
18562 var bullet = this.el.select('.bullet-' + i, true).first();
18568 bullet.addClass('selected');
18579 Roo.apply(Roo.bootstrap.TabGroup, {
18583 * register a Navigation Group
18584 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18586 register : function(navgrp)
18588 this.groups[navgrp.navId] = navgrp;
18592 * fetch a Navigation Group based on the navigation ID
18593 * if one does not exist , it will get created.
18594 * @param {string} the navgroup to add
18595 * @returns {Roo.bootstrap.NavGroup} the navgroup
18597 get: function(navId) {
18598 if (typeof(this.groups[navId]) == 'undefined') {
18599 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18601 return this.groups[navId] ;
18616 * @class Roo.bootstrap.TabPanel
18617 * @extends Roo.bootstrap.Component
18618 * Bootstrap TabPanel class
18619 * @cfg {Boolean} active panel active
18620 * @cfg {String} html panel content
18621 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18622 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18623 * @cfg {String} href click to link..
18627 * Create a new TabPanel
18628 * @param {Object} config The config object
18631 Roo.bootstrap.TabPanel = function(config){
18632 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18636 * Fires when the active status changes
18637 * @param {Roo.bootstrap.TabPanel} this
18638 * @param {Boolean} state the new state
18643 * @event beforedeactivate
18644 * Fires before a tab is de-activated - can be used to do validation on a form.
18645 * @param {Roo.bootstrap.TabPanel} this
18646 * @return {Boolean} false if there is an error
18649 'beforedeactivate': true
18652 this.tabId = this.tabId || Roo.id();
18656 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18664 getAutoCreate : function(){
18669 // item is needed for carousel - not sure if it has any effect otherwise
18670 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18671 html: this.html || ''
18675 cfg.cls += ' active';
18679 cfg.tabId = this.tabId;
18687 initEvents: function()
18689 var p = this.parent();
18691 this.navId = this.navId || p.navId;
18693 if (typeof(this.navId) != 'undefined') {
18694 // not really needed.. but just in case.. parent should be a NavGroup.
18695 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18699 var i = tg.tabs.length - 1;
18701 if(this.active && tg.bullets > 0 && i < tg.bullets){
18702 tg.setActiveBullet(i);
18706 this.el.on('click', this.onClick, this);
18709 this.el.on("touchstart", this.onTouchStart, this);
18710 this.el.on("touchmove", this.onTouchMove, this);
18711 this.el.on("touchend", this.onTouchEnd, this);
18716 onRender : function(ct, position)
18718 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18721 setActive : function(state)
18723 Roo.log("panel - set active " + this.tabId + "=" + state);
18725 this.active = state;
18727 this.el.removeClass('active');
18729 } else if (!this.el.hasClass('active')) {
18730 this.el.addClass('active');
18733 this.fireEvent('changed', this, state);
18736 onClick : function(e)
18738 e.preventDefault();
18740 if(!this.href.length){
18744 window.location.href = this.href;
18753 onTouchStart : function(e)
18755 this.swiping = false;
18757 this.startX = e.browserEvent.touches[0].clientX;
18758 this.startY = e.browserEvent.touches[0].clientY;
18761 onTouchMove : function(e)
18763 this.swiping = true;
18765 this.endX = e.browserEvent.touches[0].clientX;
18766 this.endY = e.browserEvent.touches[0].clientY;
18769 onTouchEnd : function(e)
18776 var tabGroup = this.parent();
18778 if(this.endX > this.startX){ // swiping right
18779 tabGroup.showPanelPrev();
18783 if(this.startX > this.endX){ // swiping left
18784 tabGroup.showPanelNext();
18803 * @class Roo.bootstrap.DateField
18804 * @extends Roo.bootstrap.Input
18805 * Bootstrap DateField class
18806 * @cfg {Number} weekStart default 0
18807 * @cfg {String} viewMode default empty, (months|years)
18808 * @cfg {String} minViewMode default empty, (months|years)
18809 * @cfg {Number} startDate default -Infinity
18810 * @cfg {Number} endDate default Infinity
18811 * @cfg {Boolean} todayHighlight default false
18812 * @cfg {Boolean} todayBtn default false
18813 * @cfg {Boolean} calendarWeeks default false
18814 * @cfg {Object} daysOfWeekDisabled default empty
18815 * @cfg {Boolean} singleMode default false (true | false)
18817 * @cfg {Boolean} keyboardNavigation default true
18818 * @cfg {String} language default en
18821 * Create a new DateField
18822 * @param {Object} config The config object
18825 Roo.bootstrap.DateField = function(config){
18826 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18830 * Fires when this field show.
18831 * @param {Roo.bootstrap.DateField} this
18832 * @param {Mixed} date The date value
18837 * Fires when this field hide.
18838 * @param {Roo.bootstrap.DateField} this
18839 * @param {Mixed} date The date value
18844 * Fires when select a date.
18845 * @param {Roo.bootstrap.DateField} this
18846 * @param {Mixed} date The date value
18850 * @event beforeselect
18851 * Fires when before select a date.
18852 * @param {Roo.bootstrap.DateField} this
18853 * @param {Mixed} date The date value
18855 beforeselect : true
18859 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18862 * @cfg {String} format
18863 * The default date format string which can be overriden for localization support. The format must be
18864 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18868 * @cfg {String} altFormats
18869 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18870 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18872 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18880 todayHighlight : false,
18886 keyboardNavigation: true,
18888 calendarWeeks: false,
18890 startDate: -Infinity,
18894 daysOfWeekDisabled: [],
18898 singleMode : false,
18900 UTCDate: function()
18902 return new Date(Date.UTC.apply(Date, arguments));
18905 UTCToday: function()
18907 var today = new Date();
18908 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18911 getDate: function() {
18912 var d = this.getUTCDate();
18913 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18916 getUTCDate: function() {
18920 setDate: function(d) {
18921 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18924 setUTCDate: function(d) {
18926 this.setValue(this.formatDate(this.date));
18929 onRender: function(ct, position)
18932 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18934 this.language = this.language || 'en';
18935 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18936 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18938 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18939 this.format = this.format || 'm/d/y';
18940 this.isInline = false;
18941 this.isInput = true;
18942 this.component = this.el.select('.add-on', true).first() || false;
18943 this.component = (this.component && this.component.length === 0) ? false : this.component;
18944 this.hasInput = this.component && this.inputEl().length;
18946 if (typeof(this.minViewMode === 'string')) {
18947 switch (this.minViewMode) {
18949 this.minViewMode = 1;
18952 this.minViewMode = 2;
18955 this.minViewMode = 0;
18960 if (typeof(this.viewMode === 'string')) {
18961 switch (this.viewMode) {
18974 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18976 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18978 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18980 this.picker().on('mousedown', this.onMousedown, this);
18981 this.picker().on('click', this.onClick, this);
18983 this.picker().addClass('datepicker-dropdown');
18985 this.startViewMode = this.viewMode;
18987 if(this.singleMode){
18988 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18989 v.setVisibilityMode(Roo.Element.DISPLAY);
18993 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18994 v.setStyle('width', '189px');
18998 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18999 if(!this.calendarWeeks){
19004 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19005 v.attr('colspan', function(i, val){
19006 return parseInt(val) + 1;
19011 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19013 this.setStartDate(this.startDate);
19014 this.setEndDate(this.endDate);
19016 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19023 if(this.isInline) {
19028 picker : function()
19030 return this.pickerEl;
19031 // return this.el.select('.datepicker', true).first();
19034 fillDow: function()
19036 var dowCnt = this.weekStart;
19045 if(this.calendarWeeks){
19053 while (dowCnt < this.weekStart + 7) {
19057 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19061 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19064 fillMonths: function()
19067 var months = this.picker().select('>.datepicker-months td', true).first();
19069 months.dom.innerHTML = '';
19075 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19078 months.createChild(month);
19085 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;
19087 if (this.date < this.startDate) {
19088 this.viewDate = new Date(this.startDate);
19089 } else if (this.date > this.endDate) {
19090 this.viewDate = new Date(this.endDate);
19092 this.viewDate = new Date(this.date);
19100 var d = new Date(this.viewDate),
19101 year = d.getUTCFullYear(),
19102 month = d.getUTCMonth(),
19103 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19104 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19105 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19106 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19107 currentDate = this.date && this.date.valueOf(),
19108 today = this.UTCToday();
19110 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19112 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19114 // this.picker.select('>tfoot th.today').
19115 // .text(dates[this.language].today)
19116 // .toggle(this.todayBtn !== false);
19118 this.updateNavArrows();
19121 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19123 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19125 prevMonth.setUTCDate(day);
19127 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19129 var nextMonth = new Date(prevMonth);
19131 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19133 nextMonth = nextMonth.valueOf();
19135 var fillMonths = false;
19137 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19139 while(prevMonth.valueOf() <= nextMonth) {
19142 if (prevMonth.getUTCDay() === this.weekStart) {
19144 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19152 if(this.calendarWeeks){
19153 // ISO 8601: First week contains first thursday.
19154 // ISO also states week starts on Monday, but we can be more abstract here.
19156 // Start of current week: based on weekstart/current date
19157 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19158 // Thursday of this week
19159 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19160 // First Thursday of year, year from thursday
19161 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19162 // Calendar week: ms between thursdays, div ms per day, div 7 days
19163 calWeek = (th - yth) / 864e5 / 7 + 1;
19165 fillMonths.cn.push({
19173 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19175 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19178 if (this.todayHighlight &&
19179 prevMonth.getUTCFullYear() == today.getFullYear() &&
19180 prevMonth.getUTCMonth() == today.getMonth() &&
19181 prevMonth.getUTCDate() == today.getDate()) {
19182 clsName += ' today';
19185 if (currentDate && prevMonth.valueOf() === currentDate) {
19186 clsName += ' active';
19189 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19190 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19191 clsName += ' disabled';
19194 fillMonths.cn.push({
19196 cls: 'day ' + clsName,
19197 html: prevMonth.getDate()
19200 prevMonth.setDate(prevMonth.getDate()+1);
19203 var currentYear = this.date && this.date.getUTCFullYear();
19204 var currentMonth = this.date && this.date.getUTCMonth();
19206 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19208 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19209 v.removeClass('active');
19211 if(currentYear === year && k === currentMonth){
19212 v.addClass('active');
19215 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19216 v.addClass('disabled');
19222 year = parseInt(year/10, 10) * 10;
19224 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19226 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19229 for (var i = -1; i < 11; i++) {
19230 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19232 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19240 showMode: function(dir)
19243 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19246 Roo.each(this.picker().select('>div',true).elements, function(v){
19247 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19250 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19255 if(this.isInline) {
19259 this.picker().removeClass(['bottom', 'top']);
19261 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19263 * place to the top of element!
19267 this.picker().addClass('top');
19268 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19273 this.picker().addClass('bottom');
19275 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19278 parseDate : function(value)
19280 if(!value || value instanceof Date){
19283 var v = Date.parseDate(value, this.format);
19284 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19285 v = Date.parseDate(value, 'Y-m-d');
19287 if(!v && this.altFormats){
19288 if(!this.altFormatsArray){
19289 this.altFormatsArray = this.altFormats.split("|");
19291 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19292 v = Date.parseDate(value, this.altFormatsArray[i]);
19298 formatDate : function(date, fmt)
19300 return (!date || !(date instanceof Date)) ?
19301 date : date.dateFormat(fmt || this.format);
19304 onFocus : function()
19306 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19310 onBlur : function()
19312 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19314 var d = this.inputEl().getValue();
19321 showPopup : function()
19323 this.picker().show();
19327 this.fireEvent('showpopup', this, this.date);
19330 hidePopup : function()
19332 if(this.isInline) {
19335 this.picker().hide();
19336 this.viewMode = this.startViewMode;
19339 this.fireEvent('hidepopup', this, this.date);
19343 onMousedown: function(e)
19345 e.stopPropagation();
19346 e.preventDefault();
19351 Roo.bootstrap.DateField.superclass.keyup.call(this);
19355 setValue: function(v)
19357 if(this.fireEvent('beforeselect', this, v) !== false){
19358 var d = new Date(this.parseDate(v) ).clearTime();
19360 if(isNaN(d.getTime())){
19361 this.date = this.viewDate = '';
19362 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19366 v = this.formatDate(d);
19368 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19370 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19374 this.fireEvent('select', this, this.date);
19378 getValue: function()
19380 return this.formatDate(this.date);
19383 fireKey: function(e)
19385 if (!this.picker().isVisible()){
19386 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19392 var dateChanged = false,
19394 newDate, newViewDate;
19399 e.preventDefault();
19403 if (!this.keyboardNavigation) {
19406 dir = e.keyCode == 37 ? -1 : 1;
19409 newDate = this.moveYear(this.date, dir);
19410 newViewDate = this.moveYear(this.viewDate, dir);
19411 } else if (e.shiftKey){
19412 newDate = this.moveMonth(this.date, dir);
19413 newViewDate = this.moveMonth(this.viewDate, dir);
19415 newDate = new Date(this.date);
19416 newDate.setUTCDate(this.date.getUTCDate() + dir);
19417 newViewDate = new Date(this.viewDate);
19418 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19420 if (this.dateWithinRange(newDate)){
19421 this.date = newDate;
19422 this.viewDate = newViewDate;
19423 this.setValue(this.formatDate(this.date));
19425 e.preventDefault();
19426 dateChanged = true;
19431 if (!this.keyboardNavigation) {
19434 dir = e.keyCode == 38 ? -1 : 1;
19436 newDate = this.moveYear(this.date, dir);
19437 newViewDate = this.moveYear(this.viewDate, dir);
19438 } else if (e.shiftKey){
19439 newDate = this.moveMonth(this.date, dir);
19440 newViewDate = this.moveMonth(this.viewDate, dir);
19442 newDate = new Date(this.date);
19443 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19444 newViewDate = new Date(this.viewDate);
19445 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19447 if (this.dateWithinRange(newDate)){
19448 this.date = newDate;
19449 this.viewDate = newViewDate;
19450 this.setValue(this.formatDate(this.date));
19452 e.preventDefault();
19453 dateChanged = true;
19457 this.setValue(this.formatDate(this.date));
19459 e.preventDefault();
19462 this.setValue(this.formatDate(this.date));
19476 onClick: function(e)
19478 e.stopPropagation();
19479 e.preventDefault();
19481 var target = e.getTarget();
19483 if(target.nodeName.toLowerCase() === 'i'){
19484 target = Roo.get(target).dom.parentNode;
19487 var nodeName = target.nodeName;
19488 var className = target.className;
19489 var html = target.innerHTML;
19490 //Roo.log(nodeName);
19492 switch(nodeName.toLowerCase()) {
19494 switch(className) {
19500 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19501 switch(this.viewMode){
19503 this.viewDate = this.moveMonth(this.viewDate, dir);
19507 this.viewDate = this.moveYear(this.viewDate, dir);
19513 var date = new Date();
19514 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19516 this.setValue(this.formatDate(this.date));
19523 if (className.indexOf('disabled') < 0) {
19524 this.viewDate.setUTCDate(1);
19525 if (className.indexOf('month') > -1) {
19526 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19528 var year = parseInt(html, 10) || 0;
19529 this.viewDate.setUTCFullYear(year);
19533 if(this.singleMode){
19534 this.setValue(this.formatDate(this.viewDate));
19545 //Roo.log(className);
19546 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19547 var day = parseInt(html, 10) || 1;
19548 var year = this.viewDate.getUTCFullYear(),
19549 month = this.viewDate.getUTCMonth();
19551 if (className.indexOf('old') > -1) {
19558 } else if (className.indexOf('new') > -1) {
19566 //Roo.log([year,month,day]);
19567 this.date = this.UTCDate(year, month, day,0,0,0,0);
19568 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19570 //Roo.log(this.formatDate(this.date));
19571 this.setValue(this.formatDate(this.date));
19578 setStartDate: function(startDate)
19580 this.startDate = startDate || -Infinity;
19581 if (this.startDate !== -Infinity) {
19582 this.startDate = this.parseDate(this.startDate);
19585 this.updateNavArrows();
19588 setEndDate: function(endDate)
19590 this.endDate = endDate || Infinity;
19591 if (this.endDate !== Infinity) {
19592 this.endDate = this.parseDate(this.endDate);
19595 this.updateNavArrows();
19598 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19600 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19601 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19602 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19604 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19605 return parseInt(d, 10);
19608 this.updateNavArrows();
19611 updateNavArrows: function()
19613 if(this.singleMode){
19617 var d = new Date(this.viewDate),
19618 year = d.getUTCFullYear(),
19619 month = d.getUTCMonth();
19621 Roo.each(this.picker().select('.prev', true).elements, function(v){
19623 switch (this.viewMode) {
19626 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19632 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19639 Roo.each(this.picker().select('.next', true).elements, function(v){
19641 switch (this.viewMode) {
19644 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19650 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19658 moveMonth: function(date, dir)
19663 var new_date = new Date(date.valueOf()),
19664 day = new_date.getUTCDate(),
19665 month = new_date.getUTCMonth(),
19666 mag = Math.abs(dir),
19668 dir = dir > 0 ? 1 : -1;
19671 // If going back one month, make sure month is not current month
19672 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19674 return new_date.getUTCMonth() == month;
19676 // If going forward one month, make sure month is as expected
19677 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19679 return new_date.getUTCMonth() != new_month;
19681 new_month = month + dir;
19682 new_date.setUTCMonth(new_month);
19683 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19684 if (new_month < 0 || new_month > 11) {
19685 new_month = (new_month + 12) % 12;
19688 // For magnitudes >1, move one month at a time...
19689 for (var i=0; i<mag; i++) {
19690 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19691 new_date = this.moveMonth(new_date, dir);
19693 // ...then reset the day, keeping it in the new month
19694 new_month = new_date.getUTCMonth();
19695 new_date.setUTCDate(day);
19697 return new_month != new_date.getUTCMonth();
19700 // Common date-resetting loop -- if date is beyond end of month, make it
19703 new_date.setUTCDate(--day);
19704 new_date.setUTCMonth(new_month);
19709 moveYear: function(date, dir)
19711 return this.moveMonth(date, dir*12);
19714 dateWithinRange: function(date)
19716 return date >= this.startDate && date <= this.endDate;
19722 this.picker().remove();
19725 validateValue : function(value)
19727 if(this.getVisibilityEl().hasClass('hidden')){
19731 if(value.length < 1) {
19732 if(this.allowBlank){
19738 if(value.length < this.minLength){
19741 if(value.length > this.maxLength){
19745 var vt = Roo.form.VTypes;
19746 if(!vt[this.vtype](value, this)){
19750 if(typeof this.validator == "function"){
19751 var msg = this.validator(value);
19757 if(this.regex && !this.regex.test(value)){
19761 if(typeof(this.parseDate(value)) == 'undefined'){
19765 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19769 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19779 this.date = this.viewDate = '';
19781 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19786 Roo.apply(Roo.bootstrap.DateField, {
19797 html: '<i class="fa fa-arrow-left"/>'
19807 html: '<i class="fa fa-arrow-right"/>'
19849 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19850 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19851 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19852 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19853 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19866 navFnc: 'FullYear',
19871 navFnc: 'FullYear',
19876 Roo.apply(Roo.bootstrap.DateField, {
19880 cls: 'datepicker dropdown-menu roo-dynamic',
19884 cls: 'datepicker-days',
19888 cls: 'table-condensed',
19890 Roo.bootstrap.DateField.head,
19894 Roo.bootstrap.DateField.footer
19901 cls: 'datepicker-months',
19905 cls: 'table-condensed',
19907 Roo.bootstrap.DateField.head,
19908 Roo.bootstrap.DateField.content,
19909 Roo.bootstrap.DateField.footer
19916 cls: 'datepicker-years',
19920 cls: 'table-condensed',
19922 Roo.bootstrap.DateField.head,
19923 Roo.bootstrap.DateField.content,
19924 Roo.bootstrap.DateField.footer
19943 * @class Roo.bootstrap.TimeField
19944 * @extends Roo.bootstrap.Input
19945 * Bootstrap DateField class
19949 * Create a new TimeField
19950 * @param {Object} config The config object
19953 Roo.bootstrap.TimeField = function(config){
19954 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19958 * Fires when this field show.
19959 * @param {Roo.bootstrap.DateField} thisthis
19960 * @param {Mixed} date The date value
19965 * Fires when this field hide.
19966 * @param {Roo.bootstrap.DateField} this
19967 * @param {Mixed} date The date value
19972 * Fires when select a date.
19973 * @param {Roo.bootstrap.DateField} this
19974 * @param {Mixed} date The date value
19980 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19983 * @cfg {String} format
19984 * The default time format string which can be overriden for localization support. The format must be
19985 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19989 onRender: function(ct, position)
19992 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19994 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19996 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19998 this.pop = this.picker().select('>.datepicker-time',true).first();
19999 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20001 this.picker().on('mousedown', this.onMousedown, this);
20002 this.picker().on('click', this.onClick, this);
20004 this.picker().addClass('datepicker-dropdown');
20009 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20010 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20011 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20012 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20013 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20014 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20018 fireKey: function(e){
20019 if (!this.picker().isVisible()){
20020 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20026 e.preventDefault();
20034 this.onTogglePeriod();
20037 this.onIncrementMinutes();
20040 this.onDecrementMinutes();
20049 onClick: function(e) {
20050 e.stopPropagation();
20051 e.preventDefault();
20054 picker : function()
20056 return this.el.select('.datepicker', true).first();
20059 fillTime: function()
20061 var time = this.pop.select('tbody', true).first();
20063 time.dom.innerHTML = '';
20078 cls: 'hours-up glyphicon glyphicon-chevron-up'
20098 cls: 'minutes-up glyphicon glyphicon-chevron-up'
20119 cls: 'timepicker-hour',
20134 cls: 'timepicker-minute',
20149 cls: 'btn btn-primary period',
20171 cls: 'hours-down glyphicon glyphicon-chevron-down'
20191 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20209 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20216 var hours = this.time.getHours();
20217 var minutes = this.time.getMinutes();
20230 hours = hours - 12;
20234 hours = '0' + hours;
20238 minutes = '0' + minutes;
20241 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20242 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20243 this.pop.select('button', true).first().dom.innerHTML = period;
20249 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20251 var cls = ['bottom'];
20253 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20260 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20265 this.picker().addClass(cls.join('-'));
20269 Roo.each(cls, function(c){
20271 _this.picker().setTop(_this.inputEl().getHeight());
20275 _this.picker().setTop(0 - _this.picker().getHeight());
20280 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20284 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20291 onFocus : function()
20293 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20297 onBlur : function()
20299 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20305 this.picker().show();
20310 this.fireEvent('show', this, this.date);
20315 this.picker().hide();
20318 this.fireEvent('hide', this, this.date);
20321 setTime : function()
20324 this.setValue(this.time.format(this.format));
20326 this.fireEvent('select', this, this.date);
20331 onMousedown: function(e){
20332 e.stopPropagation();
20333 e.preventDefault();
20336 onIncrementHours: function()
20338 Roo.log('onIncrementHours');
20339 this.time = this.time.add(Date.HOUR, 1);
20344 onDecrementHours: function()
20346 Roo.log('onDecrementHours');
20347 this.time = this.time.add(Date.HOUR, -1);
20351 onIncrementMinutes: function()
20353 Roo.log('onIncrementMinutes');
20354 this.time = this.time.add(Date.MINUTE, 1);
20358 onDecrementMinutes: function()
20360 Roo.log('onDecrementMinutes');
20361 this.time = this.time.add(Date.MINUTE, -1);
20365 onTogglePeriod: function()
20367 Roo.log('onTogglePeriod');
20368 this.time = this.time.add(Date.HOUR, 12);
20375 Roo.apply(Roo.bootstrap.TimeField, {
20405 cls: 'btn btn-info ok',
20417 Roo.apply(Roo.bootstrap.TimeField, {
20421 cls: 'datepicker dropdown-menu',
20425 cls: 'datepicker-time',
20429 cls: 'table-condensed',
20431 Roo.bootstrap.TimeField.content,
20432 Roo.bootstrap.TimeField.footer
20451 * @class Roo.bootstrap.MonthField
20452 * @extends Roo.bootstrap.Input
20453 * Bootstrap MonthField class
20455 * @cfg {String} language default en
20458 * Create a new MonthField
20459 * @param {Object} config The config object
20462 Roo.bootstrap.MonthField = function(config){
20463 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20468 * Fires when this field show.
20469 * @param {Roo.bootstrap.MonthField} this
20470 * @param {Mixed} date The date value
20475 * Fires when this field hide.
20476 * @param {Roo.bootstrap.MonthField} this
20477 * @param {Mixed} date The date value
20482 * Fires when select a date.
20483 * @param {Roo.bootstrap.MonthField} this
20484 * @param {String} oldvalue The old value
20485 * @param {String} newvalue The new value
20491 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20493 onRender: function(ct, position)
20496 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20498 this.language = this.language || 'en';
20499 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20500 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20502 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20503 this.isInline = false;
20504 this.isInput = true;
20505 this.component = this.el.select('.add-on', true).first() || false;
20506 this.component = (this.component && this.component.length === 0) ? false : this.component;
20507 this.hasInput = this.component && this.inputEL().length;
20509 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20511 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20513 this.picker().on('mousedown', this.onMousedown, this);
20514 this.picker().on('click', this.onClick, this);
20516 this.picker().addClass('datepicker-dropdown');
20518 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20519 v.setStyle('width', '189px');
20526 if(this.isInline) {
20532 setValue: function(v, suppressEvent)
20534 var o = this.getValue();
20536 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20540 if(suppressEvent !== true){
20541 this.fireEvent('select', this, o, v);
20546 getValue: function()
20551 onClick: function(e)
20553 e.stopPropagation();
20554 e.preventDefault();
20556 var target = e.getTarget();
20558 if(target.nodeName.toLowerCase() === 'i'){
20559 target = Roo.get(target).dom.parentNode;
20562 var nodeName = target.nodeName;
20563 var className = target.className;
20564 var html = target.innerHTML;
20566 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20570 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20572 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20578 picker : function()
20580 return this.pickerEl;
20583 fillMonths: function()
20586 var months = this.picker().select('>.datepicker-months td', true).first();
20588 months.dom.innerHTML = '';
20594 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20597 months.createChild(month);
20606 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20607 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20610 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20611 e.removeClass('active');
20613 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20614 e.addClass('active');
20621 if(this.isInline) {
20625 this.picker().removeClass(['bottom', 'top']);
20627 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20629 * place to the top of element!
20633 this.picker().addClass('top');
20634 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20639 this.picker().addClass('bottom');
20641 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20644 onFocus : function()
20646 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20650 onBlur : function()
20652 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20654 var d = this.inputEl().getValue();
20663 this.picker().show();
20664 this.picker().select('>.datepicker-months', true).first().show();
20668 this.fireEvent('show', this, this.date);
20673 if(this.isInline) {
20676 this.picker().hide();
20677 this.fireEvent('hide', this, this.date);
20681 onMousedown: function(e)
20683 e.stopPropagation();
20684 e.preventDefault();
20689 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20693 fireKey: function(e)
20695 if (!this.picker().isVisible()){
20696 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20707 e.preventDefault();
20711 dir = e.keyCode == 37 ? -1 : 1;
20713 this.vIndex = this.vIndex + dir;
20715 if(this.vIndex < 0){
20719 if(this.vIndex > 11){
20723 if(isNaN(this.vIndex)){
20727 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20733 dir = e.keyCode == 38 ? -1 : 1;
20735 this.vIndex = this.vIndex + dir * 4;
20737 if(this.vIndex < 0){
20741 if(this.vIndex > 11){
20745 if(isNaN(this.vIndex)){
20749 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20754 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20755 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20759 e.preventDefault();
20762 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20763 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20779 this.picker().remove();
20784 Roo.apply(Roo.bootstrap.MonthField, {
20803 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20804 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20809 Roo.apply(Roo.bootstrap.MonthField, {
20813 cls: 'datepicker dropdown-menu roo-dynamic',
20817 cls: 'datepicker-months',
20821 cls: 'table-condensed',
20823 Roo.bootstrap.DateField.content
20843 * @class Roo.bootstrap.CheckBox
20844 * @extends Roo.bootstrap.Input
20845 * Bootstrap CheckBox class
20847 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20848 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20849 * @cfg {String} boxLabel The text that appears beside the checkbox
20850 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20851 * @cfg {Boolean} checked initnal the element
20852 * @cfg {Boolean} inline inline the element (default false)
20853 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20854 * @cfg {String} tooltip label tooltip
20857 * Create a new CheckBox
20858 * @param {Object} config The config object
20861 Roo.bootstrap.CheckBox = function(config){
20862 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20867 * Fires when the element is checked or unchecked.
20868 * @param {Roo.bootstrap.CheckBox} this This input
20869 * @param {Boolean} checked The new checked value
20874 * Fires when the element is click.
20875 * @param {Roo.bootstrap.CheckBox} this This input
20882 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20884 inputType: 'checkbox',
20893 getAutoCreate : function()
20895 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20901 cfg.cls = 'form-group ' + this.inputType; //input-group
20904 cfg.cls += ' ' + this.inputType + '-inline';
20910 type : this.inputType,
20911 value : this.inputValue,
20912 cls : 'roo-' + this.inputType, //'form-box',
20913 placeholder : this.placeholder || ''
20917 if(this.inputType != 'radio'){
20921 cls : 'roo-hidden-value',
20922 value : this.checked ? this.inputValue : this.valueOff
20927 if (this.weight) { // Validity check?
20928 cfg.cls += " " + this.inputType + "-" + this.weight;
20931 if (this.disabled) {
20932 input.disabled=true;
20936 input.checked = this.checked;
20941 input.name = this.name;
20943 if(this.inputType != 'radio'){
20944 hidden.name = this.name;
20945 input.name = '_hidden_' + this.name;
20950 input.cls += ' input-' + this.size;
20955 ['xs','sm','md','lg'].map(function(size){
20956 if (settings[size]) {
20957 cfg.cls += ' col-' + size + '-' + settings[size];
20961 var inputblock = input;
20963 if (this.before || this.after) {
20966 cls : 'input-group',
20971 inputblock.cn.push({
20973 cls : 'input-group-addon',
20978 inputblock.cn.push(input);
20980 if(this.inputType != 'radio'){
20981 inputblock.cn.push(hidden);
20985 inputblock.cn.push({
20987 cls : 'input-group-addon',
20994 if (align ==='left' && this.fieldLabel.length) {
20995 // Roo.log("left and has label");
21000 cls : 'control-label',
21001 html : this.fieldLabel
21011 if(this.labelWidth > 12){
21012 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21015 if(this.labelWidth < 13 && this.labelmd == 0){
21016 this.labelmd = this.labelWidth;
21019 if(this.labellg > 0){
21020 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21021 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21024 if(this.labelmd > 0){
21025 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21026 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21029 if(this.labelsm > 0){
21030 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21031 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21034 if(this.labelxs > 0){
21035 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21036 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21039 } else if ( this.fieldLabel.length) {
21040 // Roo.log(" label");
21044 tag: this.boxLabel ? 'span' : 'label',
21046 cls: 'control-label box-input-label',
21047 //cls : 'input-group-addon',
21048 html : this.fieldLabel
21057 // Roo.log(" no label && no align");
21058 cfg.cn = [ inputblock ] ;
21064 var boxLabelCfg = {
21066 //'for': id, // box label is handled by onclick - so no for...
21068 html: this.boxLabel
21072 boxLabelCfg.tooltip = this.tooltip;
21075 cfg.cn.push(boxLabelCfg);
21078 if(this.inputType != 'radio'){
21079 cfg.cn.push(hidden);
21087 * return the real input element.
21089 inputEl: function ()
21091 return this.el.select('input.roo-' + this.inputType,true).first();
21093 hiddenEl: function ()
21095 return this.el.select('input.roo-hidden-value',true).first();
21098 labelEl: function()
21100 return this.el.select('label.control-label',true).first();
21102 /* depricated... */
21106 return this.labelEl();
21109 boxLabelEl: function()
21111 return this.el.select('label.box-label',true).first();
21114 initEvents : function()
21116 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21118 this.inputEl().on('click', this.onClick, this);
21120 if (this.boxLabel) {
21121 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
21124 this.startValue = this.getValue();
21127 Roo.bootstrap.CheckBox.register(this);
21131 onClick : function(e)
21133 if(this.fireEvent('click', this, e) !== false){
21134 this.setChecked(!this.checked);
21139 setChecked : function(state,suppressEvent)
21141 this.startValue = this.getValue();
21143 if(this.inputType == 'radio'){
21145 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21146 e.dom.checked = false;
21149 this.inputEl().dom.checked = true;
21151 this.inputEl().dom.value = this.inputValue;
21153 if(suppressEvent !== true){
21154 this.fireEvent('check', this, true);
21162 this.checked = state;
21164 this.inputEl().dom.checked = state;
21167 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21169 if(suppressEvent !== true){
21170 this.fireEvent('check', this, state);
21176 getValue : function()
21178 if(this.inputType == 'radio'){
21179 return this.getGroupValue();
21182 return this.hiddenEl().dom.value;
21186 getGroupValue : function()
21188 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21192 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21195 setValue : function(v,suppressEvent)
21197 if(this.inputType == 'radio'){
21198 this.setGroupValue(v, suppressEvent);
21202 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21207 setGroupValue : function(v, suppressEvent)
21209 this.startValue = this.getValue();
21211 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21212 e.dom.checked = false;
21214 if(e.dom.value == v){
21215 e.dom.checked = true;
21219 if(suppressEvent !== true){
21220 this.fireEvent('check', this, true);
21228 validate : function()
21230 if(this.getVisibilityEl().hasClass('hidden')){
21236 (this.inputType == 'radio' && this.validateRadio()) ||
21237 (this.inputType == 'checkbox' && this.validateCheckbox())
21243 this.markInvalid();
21247 validateRadio : function()
21249 if(this.getVisibilityEl().hasClass('hidden')){
21253 if(this.allowBlank){
21259 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21260 if(!e.dom.checked){
21272 validateCheckbox : function()
21275 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21276 //return (this.getValue() == this.inputValue) ? true : false;
21279 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21287 for(var i in group){
21288 if(group[i].el.isVisible(true)){
21296 for(var i in group){
21301 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21308 * Mark this field as valid
21310 markValid : function()
21314 this.fireEvent('valid', this);
21316 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21319 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21326 if(this.inputType == 'radio'){
21327 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21328 var fg = e.findParent('.form-group', false, true);
21329 if (Roo.bootstrap.version == 3) {
21330 fg.removeClass([_this.invalidClass, _this.validClass]);
21331 fg.addClass(_this.validClass);
21333 fg.removeClass(['is-valid', 'is-invalid']);
21334 fg.addClass('is-valid');
21342 var fg = this.el.findParent('.form-group', false, true);
21343 if (Roo.bootstrap.version == 3) {
21344 fg.removeClass([this.invalidClass, this.validClass]);
21345 fg.addClass(this.validClass);
21347 fg.removeClass(['is-valid', 'is-invalid']);
21348 fg.addClass('is-valid');
21353 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21359 for(var i in group){
21360 var fg = group[i].el.findParent('.form-group', false, true);
21361 if (Roo.bootstrap.version == 3) {
21362 fg.removeClass([this.invalidClass, this.validClass]);
21363 fg.addClass(this.validClass);
21365 fg.removeClass(['is-valid', 'is-invalid']);
21366 fg.addClass('is-valid');
21372 * Mark this field as invalid
21373 * @param {String} msg The validation message
21375 markInvalid : function(msg)
21377 if(this.allowBlank){
21383 this.fireEvent('invalid', this, msg);
21385 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21388 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21392 label.markInvalid();
21395 if(this.inputType == 'radio'){
21397 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21398 var fg = e.findParent('.form-group', false, true);
21399 if (Roo.bootstrap.version == 3) {
21400 fg.removeClass([_this.invalidClass, _this.validClass]);
21401 fg.addClass(_this.invalidClass);
21403 fg.removeClass(['is-invalid', 'is-valid']);
21404 fg.addClass('is-invalid');
21412 var fg = this.el.findParent('.form-group', false, true);
21413 if (Roo.bootstrap.version == 3) {
21414 fg.removeClass([_this.invalidClass, _this.validClass]);
21415 fg.addClass(_this.invalidClass);
21417 fg.removeClass(['is-invalid', 'is-valid']);
21418 fg.addClass('is-invalid');
21423 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21429 for(var i in group){
21430 var fg = group[i].el.findParent('.form-group', false, true);
21431 if (Roo.bootstrap.version == 3) {
21432 fg.removeClass([_this.invalidClass, _this.validClass]);
21433 fg.addClass(_this.invalidClass);
21435 fg.removeClass(['is-invalid', 'is-valid']);
21436 fg.addClass('is-invalid');
21442 clearInvalid : function()
21444 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21446 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21448 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21450 if (label && label.iconEl) {
21451 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21452 label.iconEl.removeClass(['is-invalid', 'is-valid']);
21456 disable : function()
21458 if(this.inputType != 'radio'){
21459 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21466 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21467 _this.getActionEl().addClass(this.disabledClass);
21468 e.dom.disabled = true;
21472 this.disabled = true;
21473 this.fireEvent("disable", this);
21477 enable : function()
21479 if(this.inputType != 'radio'){
21480 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21487 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21488 _this.getActionEl().removeClass(this.disabledClass);
21489 e.dom.disabled = false;
21493 this.disabled = false;
21494 this.fireEvent("enable", this);
21498 setBoxLabel : function(v)
21503 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21509 Roo.apply(Roo.bootstrap.CheckBox, {
21514 * register a CheckBox Group
21515 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21517 register : function(checkbox)
21519 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21520 this.groups[checkbox.groupId] = {};
21523 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21527 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21531 * fetch a CheckBox Group based on the group ID
21532 * @param {string} the group ID
21533 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21535 get: function(groupId) {
21536 if (typeof(this.groups[groupId]) == 'undefined') {
21540 return this.groups[groupId] ;
21553 * @class Roo.bootstrap.Radio
21554 * @extends Roo.bootstrap.Component
21555 * Bootstrap Radio class
21556 * @cfg {String} boxLabel - the label associated
21557 * @cfg {String} value - the value of radio
21560 * Create a new Radio
21561 * @param {Object} config The config object
21563 Roo.bootstrap.Radio = function(config){
21564 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21568 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21574 getAutoCreate : function()
21578 cls : 'form-group radio',
21583 html : this.boxLabel
21591 initEvents : function()
21593 this.parent().register(this);
21595 this.el.on('click', this.onClick, this);
21599 onClick : function(e)
21601 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21602 this.setChecked(true);
21606 setChecked : function(state, suppressEvent)
21608 this.parent().setValue(this.value, suppressEvent);
21612 setBoxLabel : function(v)
21617 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21632 * @class Roo.bootstrap.SecurePass
21633 * @extends Roo.bootstrap.Input
21634 * Bootstrap SecurePass class
21638 * Create a new SecurePass
21639 * @param {Object} config The config object
21642 Roo.bootstrap.SecurePass = function (config) {
21643 // these go here, so the translation tool can replace them..
21645 PwdEmpty: "Please type a password, and then retype it to confirm.",
21646 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21647 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21648 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21649 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21650 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21651 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21652 TooWeak: "Your password is Too Weak."
21654 this.meterLabel = "Password strength:";
21655 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21656 this.meterClass = [
21657 "roo-password-meter-tooweak",
21658 "roo-password-meter-weak",
21659 "roo-password-meter-medium",
21660 "roo-password-meter-strong",
21661 "roo-password-meter-grey"
21666 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21669 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21671 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21673 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21674 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21675 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21676 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21677 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21678 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21679 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21689 * @cfg {String/Object} Label for the strength meter (defaults to
21690 * 'Password strength:')
21695 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21696 * ['Weak', 'Medium', 'Strong'])
21699 pwdStrengths: false,
21712 initEvents: function ()
21714 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21716 if (this.el.is('input[type=password]') && Roo.isSafari) {
21717 this.el.on('keydown', this.SafariOnKeyDown, this);
21720 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21723 onRender: function (ct, position)
21725 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21726 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21727 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21729 this.trigger.createChild({
21734 cls: 'roo-password-meter-grey col-xs-12',
21737 //width: this.meterWidth + 'px'
21741 cls: 'roo-password-meter-text'
21747 if (this.hideTrigger) {
21748 this.trigger.setDisplayed(false);
21750 this.setSize(this.width || '', this.height || '');
21753 onDestroy: function ()
21755 if (this.trigger) {
21756 this.trigger.removeAllListeners();
21757 this.trigger.remove();
21760 this.wrap.remove();
21762 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21765 checkStrength: function ()
21767 var pwd = this.inputEl().getValue();
21768 if (pwd == this._lastPwd) {
21773 if (this.ClientSideStrongPassword(pwd)) {
21775 } else if (this.ClientSideMediumPassword(pwd)) {
21777 } else if (this.ClientSideWeakPassword(pwd)) {
21783 Roo.log('strength1: ' + strength);
21785 //var pm = this.trigger.child('div/div/div').dom;
21786 var pm = this.trigger.child('div/div');
21787 pm.removeClass(this.meterClass);
21788 pm.addClass(this.meterClass[strength]);
21791 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21793 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21795 this._lastPwd = pwd;
21799 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21801 this._lastPwd = '';
21803 var pm = this.trigger.child('div/div');
21804 pm.removeClass(this.meterClass);
21805 pm.addClass('roo-password-meter-grey');
21808 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21811 this.inputEl().dom.type='password';
21814 validateValue: function (value)
21817 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21820 if (value.length == 0) {
21821 if (this.allowBlank) {
21822 this.clearInvalid();
21826 this.markInvalid(this.errors.PwdEmpty);
21827 this.errorMsg = this.errors.PwdEmpty;
21835 if ('[\x21-\x7e]*'.match(value)) {
21836 this.markInvalid(this.errors.PwdBadChar);
21837 this.errorMsg = this.errors.PwdBadChar;
21840 if (value.length < 6) {
21841 this.markInvalid(this.errors.PwdShort);
21842 this.errorMsg = this.errors.PwdShort;
21845 if (value.length > 16) {
21846 this.markInvalid(this.errors.PwdLong);
21847 this.errorMsg = this.errors.PwdLong;
21851 if (this.ClientSideStrongPassword(value)) {
21853 } else if (this.ClientSideMediumPassword(value)) {
21855 } else if (this.ClientSideWeakPassword(value)) {
21862 if (strength < 2) {
21863 //this.markInvalid(this.errors.TooWeak);
21864 this.errorMsg = this.errors.TooWeak;
21869 console.log('strength2: ' + strength);
21871 //var pm = this.trigger.child('div/div/div').dom;
21873 var pm = this.trigger.child('div/div');
21874 pm.removeClass(this.meterClass);
21875 pm.addClass(this.meterClass[strength]);
21877 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21879 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21881 this.errorMsg = '';
21885 CharacterSetChecks: function (type)
21888 this.fResult = false;
21891 isctype: function (character, type)
21894 case this.kCapitalLetter:
21895 if (character >= 'A' && character <= 'Z') {
21900 case this.kSmallLetter:
21901 if (character >= 'a' && character <= 'z') {
21907 if (character >= '0' && character <= '9') {
21912 case this.kPunctuation:
21913 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21924 IsLongEnough: function (pwd, size)
21926 return !(pwd == null || isNaN(size) || pwd.length < size);
21929 SpansEnoughCharacterSets: function (word, nb)
21931 if (!this.IsLongEnough(word, nb))
21936 var characterSetChecks = new Array(
21937 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21938 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21941 for (var index = 0; index < word.length; ++index) {
21942 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21943 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21944 characterSetChecks[nCharSet].fResult = true;
21951 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21952 if (characterSetChecks[nCharSet].fResult) {
21957 if (nCharSets < nb) {
21963 ClientSideStrongPassword: function (pwd)
21965 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21968 ClientSideMediumPassword: function (pwd)
21970 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21973 ClientSideWeakPassword: function (pwd)
21975 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21978 })//<script type="text/javascript">
21981 * Based Ext JS Library 1.1.1
21982 * Copyright(c) 2006-2007, Ext JS, LLC.
21988 * @class Roo.HtmlEditorCore
21989 * @extends Roo.Component
21990 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21992 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21995 Roo.HtmlEditorCore = function(config){
21998 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22003 * @event initialize
22004 * Fires when the editor is fully initialized (including the iframe)
22005 * @param {Roo.HtmlEditorCore} this
22010 * Fires when the editor is first receives the focus. Any insertion must wait
22011 * until after this event.
22012 * @param {Roo.HtmlEditorCore} this
22016 * @event beforesync
22017 * Fires before the textarea is updated with content from the editor iframe. Return false
22018 * to cancel the sync.
22019 * @param {Roo.HtmlEditorCore} this
22020 * @param {String} html
22024 * @event beforepush
22025 * Fires before the iframe editor is updated with content from the textarea. Return false
22026 * to cancel the push.
22027 * @param {Roo.HtmlEditorCore} this
22028 * @param {String} html
22033 * Fires when the textarea is updated with content from the editor iframe.
22034 * @param {Roo.HtmlEditorCore} this
22035 * @param {String} html
22040 * Fires when the iframe editor is updated with content from the textarea.
22041 * @param {Roo.HtmlEditorCore} this
22042 * @param {String} html
22047 * @event editorevent
22048 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22049 * @param {Roo.HtmlEditorCore} this
22055 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22057 // defaults : white / black...
22058 this.applyBlacklists();
22065 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
22069 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
22075 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22080 * @cfg {Number} height (in pixels)
22084 * @cfg {Number} width (in pixels)
22089 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22092 stylesheets: false,
22097 // private properties
22098 validationEvent : false,
22100 initialized : false,
22102 sourceEditMode : false,
22103 onFocus : Roo.emptyFn,
22105 hideMode:'offsets',
22109 // blacklist + whitelisted elements..
22116 * Protected method that will not generally be called directly. It
22117 * is called when the editor initializes the iframe with HTML contents. Override this method if you
22118 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22120 getDocMarkup : function(){
22124 // inherit styels from page...??
22125 if (this.stylesheets === false) {
22127 Roo.get(document.head).select('style').each(function(node) {
22128 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22131 Roo.get(document.head).select('link').each(function(node) {
22132 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22135 } else if (!this.stylesheets.length) {
22137 st = '<style type="text/css">' +
22138 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22141 st = '<style type="text/css">' +
22146 st += '<style type="text/css">' +
22147 'IMG { cursor: pointer } ' +
22150 var cls = 'roo-htmleditor-body';
22152 if(this.bodyCls.length){
22153 cls += ' ' + this.bodyCls;
22156 return '<html><head>' + st +
22157 //<style type="text/css">' +
22158 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22160 ' </head><body class="' + cls + '"></body></html>';
22164 onRender : function(ct, position)
22167 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22168 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22171 this.el.dom.style.border = '0 none';
22172 this.el.dom.setAttribute('tabIndex', -1);
22173 this.el.addClass('x-hidden hide');
22177 if(Roo.isIE){ // fix IE 1px bogus margin
22178 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22182 this.frameId = Roo.id();
22186 var iframe = this.owner.wrap.createChild({
22188 cls: 'form-control', // bootstrap..
22190 name: this.frameId,
22191 frameBorder : 'no',
22192 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
22197 this.iframe = iframe.dom;
22199 this.assignDocWin();
22201 this.doc.designMode = 'on';
22204 this.doc.write(this.getDocMarkup());
22208 var task = { // must defer to wait for browser to be ready
22210 //console.log("run task?" + this.doc.readyState);
22211 this.assignDocWin();
22212 if(this.doc.body || this.doc.readyState == 'complete'){
22214 this.doc.designMode="on";
22218 Roo.TaskMgr.stop(task);
22219 this.initEditor.defer(10, this);
22226 Roo.TaskMgr.start(task);
22231 onResize : function(w, h)
22233 Roo.log('resize: ' +w + ',' + h );
22234 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22238 if(typeof w == 'number'){
22240 this.iframe.style.width = w + 'px';
22242 if(typeof h == 'number'){
22244 this.iframe.style.height = h + 'px';
22246 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22253 * Toggles the editor between standard and source edit mode.
22254 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22256 toggleSourceEdit : function(sourceEditMode){
22258 this.sourceEditMode = sourceEditMode === true;
22260 if(this.sourceEditMode){
22262 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22265 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22266 //this.iframe.className = '';
22269 //this.setSize(this.owner.wrap.getSize());
22270 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22277 * Protected method that will not generally be called directly. If you need/want
22278 * custom HTML cleanup, this is the method you should override.
22279 * @param {String} html The HTML to be cleaned
22280 * return {String} The cleaned HTML
22282 cleanHtml : function(html){
22283 html = String(html);
22284 if(html.length > 5){
22285 if(Roo.isSafari){ // strip safari nonsense
22286 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22289 if(html == ' '){
22296 * HTML Editor -> Textarea
22297 * Protected method that will not generally be called directly. Syncs the contents
22298 * of the editor iframe with the textarea.
22300 syncValue : function(){
22301 if(this.initialized){
22302 var bd = (this.doc.body || this.doc.documentElement);
22303 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22304 var html = bd.innerHTML;
22306 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22307 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22309 html = '<div style="'+m[0]+'">' + html + '</div>';
22312 html = this.cleanHtml(html);
22313 // fix up the special chars.. normaly like back quotes in word...
22314 // however we do not want to do this with chinese..
22315 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22316 var cc = b.charCodeAt();
22318 (cc >= 0x4E00 && cc < 0xA000 ) ||
22319 (cc >= 0x3400 && cc < 0x4E00 ) ||
22320 (cc >= 0xf900 && cc < 0xfb00 )
22326 if(this.owner.fireEvent('beforesync', this, html) !== false){
22327 this.el.dom.value = html;
22328 this.owner.fireEvent('sync', this, html);
22334 * Protected method that will not generally be called directly. Pushes the value of the textarea
22335 * into the iframe editor.
22337 pushValue : function(){
22338 if(this.initialized){
22339 var v = this.el.dom.value.trim();
22341 // if(v.length < 1){
22345 if(this.owner.fireEvent('beforepush', this, v) !== false){
22346 var d = (this.doc.body || this.doc.documentElement);
22348 this.cleanUpPaste();
22349 this.el.dom.value = d.innerHTML;
22350 this.owner.fireEvent('push', this, v);
22356 deferFocus : function(){
22357 this.focus.defer(10, this);
22361 focus : function(){
22362 if(this.win && !this.sourceEditMode){
22369 assignDocWin: function()
22371 var iframe = this.iframe;
22374 this.doc = iframe.contentWindow.document;
22375 this.win = iframe.contentWindow;
22377 // if (!Roo.get(this.frameId)) {
22380 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22381 // this.win = Roo.get(this.frameId).dom.contentWindow;
22383 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22387 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22388 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22393 initEditor : function(){
22394 //console.log("INIT EDITOR");
22395 this.assignDocWin();
22399 this.doc.designMode="on";
22401 this.doc.write(this.getDocMarkup());
22404 var dbody = (this.doc.body || this.doc.documentElement);
22405 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22406 // this copies styles from the containing element into thsi one..
22407 // not sure why we need all of this..
22408 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22410 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22411 //ss['background-attachment'] = 'fixed'; // w3c
22412 dbody.bgProperties = 'fixed'; // ie
22413 //Roo.DomHelper.applyStyles(dbody, ss);
22414 Roo.EventManager.on(this.doc, {
22415 //'mousedown': this.onEditorEvent,
22416 'mouseup': this.onEditorEvent,
22417 'dblclick': this.onEditorEvent,
22418 'click': this.onEditorEvent,
22419 'keyup': this.onEditorEvent,
22424 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22426 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22427 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22429 this.initialized = true;
22431 this.owner.fireEvent('initialize', this);
22436 onDestroy : function(){
22442 //for (var i =0; i < this.toolbars.length;i++) {
22443 // // fixme - ask toolbars for heights?
22444 // this.toolbars[i].onDestroy();
22447 //this.wrap.dom.innerHTML = '';
22448 //this.wrap.remove();
22453 onFirstFocus : function(){
22455 this.assignDocWin();
22458 this.activated = true;
22461 if(Roo.isGecko){ // prevent silly gecko errors
22463 var s = this.win.getSelection();
22464 if(!s.focusNode || s.focusNode.nodeType != 3){
22465 var r = s.getRangeAt(0);
22466 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22471 this.execCmd('useCSS', true);
22472 this.execCmd('styleWithCSS', false);
22475 this.owner.fireEvent('activate', this);
22479 adjustFont: function(btn){
22480 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22481 //if(Roo.isSafari){ // safari
22484 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22485 if(Roo.isSafari){ // safari
22486 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22487 v = (v < 10) ? 10 : v;
22488 v = (v > 48) ? 48 : v;
22489 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22494 v = Math.max(1, v+adjust);
22496 this.execCmd('FontSize', v );
22499 onEditorEvent : function(e)
22501 this.owner.fireEvent('editorevent', this, e);
22502 // this.updateToolbar();
22503 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22506 insertTag : function(tg)
22508 // could be a bit smarter... -> wrap the current selected tRoo..
22509 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22511 range = this.createRange(this.getSelection());
22512 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22513 wrappingNode.appendChild(range.extractContents());
22514 range.insertNode(wrappingNode);
22521 this.execCmd("formatblock", tg);
22525 insertText : function(txt)
22529 var range = this.createRange();
22530 range.deleteContents();
22531 //alert(Sender.getAttribute('label'));
22533 range.insertNode(this.doc.createTextNode(txt));
22539 * Executes a Midas editor command on the editor document and performs necessary focus and
22540 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22541 * @param {String} cmd The Midas command
22542 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22544 relayCmd : function(cmd, value){
22546 this.execCmd(cmd, value);
22547 this.owner.fireEvent('editorevent', this);
22548 //this.updateToolbar();
22549 this.owner.deferFocus();
22553 * Executes a Midas editor command directly on the editor document.
22554 * For visual commands, you should use {@link #relayCmd} instead.
22555 * <b>This should only be called after the editor is initialized.</b>
22556 * @param {String} cmd The Midas command
22557 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22559 execCmd : function(cmd, value){
22560 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22567 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22569 * @param {String} text | dom node..
22571 insertAtCursor : function(text)
22574 if(!this.activated){
22580 var r = this.doc.selection.createRange();
22591 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22595 // from jquery ui (MIT licenced)
22597 var win = this.win;
22599 if (win.getSelection && win.getSelection().getRangeAt) {
22600 range = win.getSelection().getRangeAt(0);
22601 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22602 range.insertNode(node);
22603 } else if (win.document.selection && win.document.selection.createRange) {
22604 // no firefox support
22605 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22606 win.document.selection.createRange().pasteHTML(txt);
22608 // no firefox support
22609 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22610 this.execCmd('InsertHTML', txt);
22619 mozKeyPress : function(e){
22621 var c = e.getCharCode(), cmd;
22624 c = String.fromCharCode(c).toLowerCase();
22638 this.cleanUpPaste.defer(100, this);
22646 e.preventDefault();
22654 fixKeys : function(){ // load time branching for fastest keydown performance
22656 return function(e){
22657 var k = e.getKey(), r;
22660 r = this.doc.selection.createRange();
22663 r.pasteHTML('    ');
22670 r = this.doc.selection.createRange();
22672 var target = r.parentElement();
22673 if(!target || target.tagName.toLowerCase() != 'li'){
22675 r.pasteHTML('<br />');
22681 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22682 this.cleanUpPaste.defer(100, this);
22688 }else if(Roo.isOpera){
22689 return function(e){
22690 var k = e.getKey();
22694 this.execCmd('InsertHTML','    ');
22697 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22698 this.cleanUpPaste.defer(100, this);
22703 }else if(Roo.isSafari){
22704 return function(e){
22705 var k = e.getKey();
22709 this.execCmd('InsertText','\t');
22713 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22714 this.cleanUpPaste.defer(100, this);
22722 getAllAncestors: function()
22724 var p = this.getSelectedNode();
22727 a.push(p); // push blank onto stack..
22728 p = this.getParentElement();
22732 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22736 a.push(this.doc.body);
22740 lastSelNode : false,
22743 getSelection : function()
22745 this.assignDocWin();
22746 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22749 getSelectedNode: function()
22751 // this may only work on Gecko!!!
22753 // should we cache this!!!!
22758 var range = this.createRange(this.getSelection()).cloneRange();
22761 var parent = range.parentElement();
22763 var testRange = range.duplicate();
22764 testRange.moveToElementText(parent);
22765 if (testRange.inRange(range)) {
22768 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22771 parent = parent.parentElement;
22776 // is ancestor a text element.
22777 var ac = range.commonAncestorContainer;
22778 if (ac.nodeType == 3) {
22779 ac = ac.parentNode;
22782 var ar = ac.childNodes;
22785 var other_nodes = [];
22786 var has_other_nodes = false;
22787 for (var i=0;i<ar.length;i++) {
22788 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22791 // fullly contained node.
22793 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22798 // probably selected..
22799 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22800 other_nodes.push(ar[i]);
22804 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22809 has_other_nodes = true;
22811 if (!nodes.length && other_nodes.length) {
22812 nodes= other_nodes;
22814 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22820 createRange: function(sel)
22822 // this has strange effects when using with
22823 // top toolbar - not sure if it's a great idea.
22824 //this.editor.contentWindow.focus();
22825 if (typeof sel != "undefined") {
22827 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22829 return this.doc.createRange();
22832 return this.doc.createRange();
22835 getParentElement: function()
22838 this.assignDocWin();
22839 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22841 var range = this.createRange(sel);
22844 var p = range.commonAncestorContainer;
22845 while (p.nodeType == 3) { // text node
22856 * Range intersection.. the hard stuff...
22860 * [ -- selected range --- ]
22864 * if end is before start or hits it. fail.
22865 * if start is after end or hits it fail.
22867 * if either hits (but other is outside. - then it's not
22873 // @see http://www.thismuchiknow.co.uk/?p=64.
22874 rangeIntersectsNode : function(range, node)
22876 var nodeRange = node.ownerDocument.createRange();
22878 nodeRange.selectNode(node);
22880 nodeRange.selectNodeContents(node);
22883 var rangeStartRange = range.cloneRange();
22884 rangeStartRange.collapse(true);
22886 var rangeEndRange = range.cloneRange();
22887 rangeEndRange.collapse(false);
22889 var nodeStartRange = nodeRange.cloneRange();
22890 nodeStartRange.collapse(true);
22892 var nodeEndRange = nodeRange.cloneRange();
22893 nodeEndRange.collapse(false);
22895 return rangeStartRange.compareBoundaryPoints(
22896 Range.START_TO_START, nodeEndRange) == -1 &&
22897 rangeEndRange.compareBoundaryPoints(
22898 Range.START_TO_START, nodeStartRange) == 1;
22902 rangeCompareNode : function(range, node)
22904 var nodeRange = node.ownerDocument.createRange();
22906 nodeRange.selectNode(node);
22908 nodeRange.selectNodeContents(node);
22912 range.collapse(true);
22914 nodeRange.collapse(true);
22916 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22917 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22919 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22921 var nodeIsBefore = ss == 1;
22922 var nodeIsAfter = ee == -1;
22924 if (nodeIsBefore && nodeIsAfter) {
22927 if (!nodeIsBefore && nodeIsAfter) {
22928 return 1; //right trailed.
22931 if (nodeIsBefore && !nodeIsAfter) {
22932 return 2; // left trailed.
22938 // private? - in a new class?
22939 cleanUpPaste : function()
22941 // cleans up the whole document..
22942 Roo.log('cleanuppaste');
22944 this.cleanUpChildren(this.doc.body);
22945 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22946 if (clean != this.doc.body.innerHTML) {
22947 this.doc.body.innerHTML = clean;
22952 cleanWordChars : function(input) {// change the chars to hex code
22953 var he = Roo.HtmlEditorCore;
22955 var output = input;
22956 Roo.each(he.swapCodes, function(sw) {
22957 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22959 output = output.replace(swapper, sw[1]);
22966 cleanUpChildren : function (n)
22968 if (!n.childNodes.length) {
22971 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22972 this.cleanUpChild(n.childNodes[i]);
22979 cleanUpChild : function (node)
22982 //console.log(node);
22983 if (node.nodeName == "#text") {
22984 // clean up silly Windows -- stuff?
22987 if (node.nodeName == "#comment") {
22988 node.parentNode.removeChild(node);
22989 // clean up silly Windows -- stuff?
22992 var lcname = node.tagName.toLowerCase();
22993 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22994 // whitelist of tags..
22996 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22998 node.parentNode.removeChild(node);
23003 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23005 // remove <a name=....> as rendering on yahoo mailer is borked with this.
23006 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23008 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23009 // remove_keep_children = true;
23012 if (remove_keep_children) {
23013 this.cleanUpChildren(node);
23014 // inserts everything just before this node...
23015 while (node.childNodes.length) {
23016 var cn = node.childNodes[0];
23017 node.removeChild(cn);
23018 node.parentNode.insertBefore(cn, node);
23020 node.parentNode.removeChild(node);
23024 if (!node.attributes || !node.attributes.length) {
23025 this.cleanUpChildren(node);
23029 function cleanAttr(n,v)
23032 if (v.match(/^\./) || v.match(/^\//)) {
23035 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23038 if (v.match(/^#/)) {
23041 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23042 node.removeAttribute(n);
23046 var cwhite = this.cwhite;
23047 var cblack = this.cblack;
23049 function cleanStyle(n,v)
23051 if (v.match(/expression/)) { //XSS?? should we even bother..
23052 node.removeAttribute(n);
23056 var parts = v.split(/;/);
23059 Roo.each(parts, function(p) {
23060 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23064 var l = p.split(':').shift().replace(/\s+/g,'');
23065 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23067 if ( cwhite.length && cblack.indexOf(l) > -1) {
23068 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23069 //node.removeAttribute(n);
23073 // only allow 'c whitelisted system attributes'
23074 if ( cwhite.length && cwhite.indexOf(l) < 0) {
23075 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23076 //node.removeAttribute(n);
23086 if (clean.length) {
23087 node.setAttribute(n, clean.join(';'));
23089 node.removeAttribute(n);
23095 for (var i = node.attributes.length-1; i > -1 ; i--) {
23096 var a = node.attributes[i];
23099 if (a.name.toLowerCase().substr(0,2)=='on') {
23100 node.removeAttribute(a.name);
23103 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23104 node.removeAttribute(a.name);
23107 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23108 cleanAttr(a.name,a.value); // fixme..
23111 if (a.name == 'style') {
23112 cleanStyle(a.name,a.value);
23115 /// clean up MS crap..
23116 // tecnically this should be a list of valid class'es..
23119 if (a.name == 'class') {
23120 if (a.value.match(/^Mso/)) {
23121 node.className = '';
23124 if (a.value.match(/^body$/)) {
23125 node.className = '';
23136 this.cleanUpChildren(node);
23142 * Clean up MS wordisms...
23144 cleanWord : function(node)
23149 this.cleanWord(this.doc.body);
23152 if (node.nodeName == "#text") {
23153 // clean up silly Windows -- stuff?
23156 if (node.nodeName == "#comment") {
23157 node.parentNode.removeChild(node);
23158 // clean up silly Windows -- stuff?
23162 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23163 node.parentNode.removeChild(node);
23167 // remove - but keep children..
23168 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23169 while (node.childNodes.length) {
23170 var cn = node.childNodes[0];
23171 node.removeChild(cn);
23172 node.parentNode.insertBefore(cn, node);
23174 node.parentNode.removeChild(node);
23175 this.iterateChildren(node, this.cleanWord);
23179 if (node.className.length) {
23181 var cn = node.className.split(/\W+/);
23183 Roo.each(cn, function(cls) {
23184 if (cls.match(/Mso[a-zA-Z]+/)) {
23189 node.className = cna.length ? cna.join(' ') : '';
23191 node.removeAttribute("class");
23195 if (node.hasAttribute("lang")) {
23196 node.removeAttribute("lang");
23199 if (node.hasAttribute("style")) {
23201 var styles = node.getAttribute("style").split(";");
23203 Roo.each(styles, function(s) {
23204 if (!s.match(/:/)) {
23207 var kv = s.split(":");
23208 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23211 // what ever is left... we allow.
23214 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23215 if (!nstyle.length) {
23216 node.removeAttribute('style');
23219 this.iterateChildren(node, this.cleanWord);
23225 * iterateChildren of a Node, calling fn each time, using this as the scole..
23226 * @param {DomNode} node node to iterate children of.
23227 * @param {Function} fn method of this class to call on each item.
23229 iterateChildren : function(node, fn)
23231 if (!node.childNodes.length) {
23234 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23235 fn.call(this, node.childNodes[i])
23241 * cleanTableWidths.
23243 * Quite often pasting from word etc.. results in tables with column and widths.
23244 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23247 cleanTableWidths : function(node)
23252 this.cleanTableWidths(this.doc.body);
23257 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23260 Roo.log(node.tagName);
23261 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23262 this.iterateChildren(node, this.cleanTableWidths);
23265 if (node.hasAttribute('width')) {
23266 node.removeAttribute('width');
23270 if (node.hasAttribute("style")) {
23273 var styles = node.getAttribute("style").split(";");
23275 Roo.each(styles, function(s) {
23276 if (!s.match(/:/)) {
23279 var kv = s.split(":");
23280 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23283 // what ever is left... we allow.
23286 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23287 if (!nstyle.length) {
23288 node.removeAttribute('style');
23292 this.iterateChildren(node, this.cleanTableWidths);
23300 domToHTML : function(currentElement, depth, nopadtext) {
23302 depth = depth || 0;
23303 nopadtext = nopadtext || false;
23305 if (!currentElement) {
23306 return this.domToHTML(this.doc.body);
23309 //Roo.log(currentElement);
23311 var allText = false;
23312 var nodeName = currentElement.nodeName;
23313 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23315 if (nodeName == '#text') {
23317 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23322 if (nodeName != 'BODY') {
23325 // Prints the node tagName, such as <A>, <IMG>, etc
23328 for(i = 0; i < currentElement.attributes.length;i++) {
23330 var aname = currentElement.attributes.item(i).name;
23331 if (!currentElement.attributes.item(i).value.length) {
23334 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23337 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23346 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23349 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23354 // Traverse the tree
23356 var currentElementChild = currentElement.childNodes.item(i);
23357 var allText = true;
23358 var innerHTML = '';
23360 while (currentElementChild) {
23361 // Formatting code (indent the tree so it looks nice on the screen)
23362 var nopad = nopadtext;
23363 if (lastnode == 'SPAN') {
23367 if (currentElementChild.nodeName == '#text') {
23368 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23369 toadd = nopadtext ? toadd : toadd.trim();
23370 if (!nopad && toadd.length > 80) {
23371 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23373 innerHTML += toadd;
23376 currentElementChild = currentElement.childNodes.item(i);
23382 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23384 // Recursively traverse the tree structure of the child node
23385 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23386 lastnode = currentElementChild.nodeName;
23388 currentElementChild=currentElement.childNodes.item(i);
23394 // The remaining code is mostly for formatting the tree
23395 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23400 ret+= "</"+tagName+">";
23406 applyBlacklists : function()
23408 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23409 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23413 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23414 if (b.indexOf(tag) > -1) {
23417 this.white.push(tag);
23421 Roo.each(w, function(tag) {
23422 if (b.indexOf(tag) > -1) {
23425 if (this.white.indexOf(tag) > -1) {
23428 this.white.push(tag);
23433 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23434 if (w.indexOf(tag) > -1) {
23437 this.black.push(tag);
23441 Roo.each(b, function(tag) {
23442 if (w.indexOf(tag) > -1) {
23445 if (this.black.indexOf(tag) > -1) {
23448 this.black.push(tag);
23453 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23454 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23458 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23459 if (b.indexOf(tag) > -1) {
23462 this.cwhite.push(tag);
23466 Roo.each(w, function(tag) {
23467 if (b.indexOf(tag) > -1) {
23470 if (this.cwhite.indexOf(tag) > -1) {
23473 this.cwhite.push(tag);
23478 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23479 if (w.indexOf(tag) > -1) {
23482 this.cblack.push(tag);
23486 Roo.each(b, function(tag) {
23487 if (w.indexOf(tag) > -1) {
23490 if (this.cblack.indexOf(tag) > -1) {
23493 this.cblack.push(tag);
23498 setStylesheets : function(stylesheets)
23500 if(typeof(stylesheets) == 'string'){
23501 Roo.get(this.iframe.contentDocument.head).createChild({
23503 rel : 'stylesheet',
23512 Roo.each(stylesheets, function(s) {
23517 Roo.get(_this.iframe.contentDocument.head).createChild({
23519 rel : 'stylesheet',
23528 removeStylesheets : function()
23532 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23537 setStyle : function(style)
23539 Roo.get(this.iframe.contentDocument.head).createChild({
23548 // hide stuff that is not compatible
23562 * @event specialkey
23566 * @cfg {String} fieldClass @hide
23569 * @cfg {String} focusClass @hide
23572 * @cfg {String} autoCreate @hide
23575 * @cfg {String} inputType @hide
23578 * @cfg {String} invalidClass @hide
23581 * @cfg {String} invalidText @hide
23584 * @cfg {String} msgFx @hide
23587 * @cfg {String} validateOnBlur @hide
23591 Roo.HtmlEditorCore.white = [
23592 'area', 'br', 'img', 'input', 'hr', 'wbr',
23594 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23595 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23596 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23597 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23598 'table', 'ul', 'xmp',
23600 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23603 'dir', 'menu', 'ol', 'ul', 'dl',
23609 Roo.HtmlEditorCore.black = [
23610 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23612 'base', 'basefont', 'bgsound', 'blink', 'body',
23613 'frame', 'frameset', 'head', 'html', 'ilayer',
23614 'iframe', 'layer', 'link', 'meta', 'object',
23615 'script', 'style' ,'title', 'xml' // clean later..
23617 Roo.HtmlEditorCore.clean = [
23618 'script', 'style', 'title', 'xml'
23620 Roo.HtmlEditorCore.remove = [
23625 Roo.HtmlEditorCore.ablack = [
23629 Roo.HtmlEditorCore.aclean = [
23630 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23634 Roo.HtmlEditorCore.pwhite= [
23635 'http', 'https', 'mailto'
23638 // white listed style attributes.
23639 Roo.HtmlEditorCore.cwhite= [
23640 // 'text-align', /// default is to allow most things..
23646 // black listed style attributes.
23647 Roo.HtmlEditorCore.cblack= [
23648 // 'font-size' -- this can be set by the project
23652 Roo.HtmlEditorCore.swapCodes =[
23671 * @class Roo.bootstrap.HtmlEditor
23672 * @extends Roo.bootstrap.TextArea
23673 * Bootstrap HtmlEditor class
23676 * Create a new HtmlEditor
23677 * @param {Object} config The config object
23680 Roo.bootstrap.HtmlEditor = function(config){
23681 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23682 if (!this.toolbars) {
23683 this.toolbars = [];
23686 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23689 * @event initialize
23690 * Fires when the editor is fully initialized (including the iframe)
23691 * @param {HtmlEditor} this
23696 * Fires when the editor is first receives the focus. Any insertion must wait
23697 * until after this event.
23698 * @param {HtmlEditor} this
23702 * @event beforesync
23703 * Fires before the textarea is updated with content from the editor iframe. Return false
23704 * to cancel the sync.
23705 * @param {HtmlEditor} this
23706 * @param {String} html
23710 * @event beforepush
23711 * Fires before the iframe editor is updated with content from the textarea. Return false
23712 * to cancel the push.
23713 * @param {HtmlEditor} this
23714 * @param {String} html
23719 * Fires when the textarea is updated with content from the editor iframe.
23720 * @param {HtmlEditor} this
23721 * @param {String} html
23726 * Fires when the iframe editor is updated with content from the textarea.
23727 * @param {HtmlEditor} this
23728 * @param {String} html
23732 * @event editmodechange
23733 * Fires when the editor switches edit modes
23734 * @param {HtmlEditor} this
23735 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23737 editmodechange: true,
23739 * @event editorevent
23740 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23741 * @param {HtmlEditor} this
23745 * @event firstfocus
23746 * Fires when on first focus - needed by toolbars..
23747 * @param {HtmlEditor} this
23752 * Auto save the htmlEditor value as a file into Events
23753 * @param {HtmlEditor} this
23757 * @event savedpreview
23758 * preview the saved version of htmlEditor
23759 * @param {HtmlEditor} this
23766 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23770 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23775 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23780 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23785 * @cfg {Number} height (in pixels)
23789 * @cfg {Number} width (in pixels)
23794 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23797 stylesheets: false,
23802 // private properties
23803 validationEvent : false,
23805 initialized : false,
23808 onFocus : Roo.emptyFn,
23810 hideMode:'offsets',
23812 tbContainer : false,
23816 toolbarContainer :function() {
23817 return this.wrap.select('.x-html-editor-tb',true).first();
23821 * Protected method that will not generally be called directly. It
23822 * is called when the editor creates its toolbar. Override this method if you need to
23823 * add custom toolbar buttons.
23824 * @param {HtmlEditor} editor
23826 createToolbar : function(){
23827 Roo.log('renewing');
23828 Roo.log("create toolbars");
23830 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23831 this.toolbars[0].render(this.toolbarContainer());
23835 // if (!editor.toolbars || !editor.toolbars.length) {
23836 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23839 // for (var i =0 ; i < editor.toolbars.length;i++) {
23840 // editor.toolbars[i] = Roo.factory(
23841 // typeof(editor.toolbars[i]) == 'string' ?
23842 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23843 // Roo.bootstrap.HtmlEditor);
23844 // editor.toolbars[i].init(editor);
23850 onRender : function(ct, position)
23852 // Roo.log("Call onRender: " + this.xtype);
23854 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23856 this.wrap = this.inputEl().wrap({
23857 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23860 this.editorcore.onRender(ct, position);
23862 if (this.resizable) {
23863 this.resizeEl = new Roo.Resizable(this.wrap, {
23867 minHeight : this.height,
23868 height: this.height,
23869 handles : this.resizable,
23872 resize : function(r, w, h) {
23873 _t.onResize(w,h); // -something
23879 this.createToolbar(this);
23882 if(!this.width && this.resizable){
23883 this.setSize(this.wrap.getSize());
23885 if (this.resizeEl) {
23886 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23887 // should trigger onReize..
23893 onResize : function(w, h)
23895 Roo.log('resize: ' +w + ',' + h );
23896 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23900 if(this.inputEl() ){
23901 if(typeof w == 'number'){
23902 var aw = w - this.wrap.getFrameWidth('lr');
23903 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23906 if(typeof h == 'number'){
23907 var tbh = -11; // fixme it needs to tool bar size!
23908 for (var i =0; i < this.toolbars.length;i++) {
23909 // fixme - ask toolbars for heights?
23910 tbh += this.toolbars[i].el.getHeight();
23911 //if (this.toolbars[i].footer) {
23912 // tbh += this.toolbars[i].footer.el.getHeight();
23920 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23921 ah -= 5; // knock a few pixes off for look..
23922 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23926 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23927 this.editorcore.onResize(ew,eh);
23932 * Toggles the editor between standard and source edit mode.
23933 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23935 toggleSourceEdit : function(sourceEditMode)
23937 this.editorcore.toggleSourceEdit(sourceEditMode);
23939 if(this.editorcore.sourceEditMode){
23940 Roo.log('editor - showing textarea');
23943 // Roo.log(this.syncValue());
23945 this.inputEl().removeClass(['hide', 'x-hidden']);
23946 this.inputEl().dom.removeAttribute('tabIndex');
23947 this.inputEl().focus();
23949 Roo.log('editor - hiding textarea');
23951 // Roo.log(this.pushValue());
23954 this.inputEl().addClass(['hide', 'x-hidden']);
23955 this.inputEl().dom.setAttribute('tabIndex', -1);
23956 //this.deferFocus();
23959 if(this.resizable){
23960 this.setSize(this.wrap.getSize());
23963 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23966 // private (for BoxComponent)
23967 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23969 // private (for BoxComponent)
23970 getResizeEl : function(){
23974 // private (for BoxComponent)
23975 getPositionEl : function(){
23980 initEvents : function(){
23981 this.originalValue = this.getValue();
23985 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23988 // markInvalid : Roo.emptyFn,
23990 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23993 // clearInvalid : Roo.emptyFn,
23995 setValue : function(v){
23996 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23997 this.editorcore.pushValue();
24002 deferFocus : function(){
24003 this.focus.defer(10, this);
24007 focus : function(){
24008 this.editorcore.focus();
24014 onDestroy : function(){
24020 for (var i =0; i < this.toolbars.length;i++) {
24021 // fixme - ask toolbars for heights?
24022 this.toolbars[i].onDestroy();
24025 this.wrap.dom.innerHTML = '';
24026 this.wrap.remove();
24031 onFirstFocus : function(){
24032 //Roo.log("onFirstFocus");
24033 this.editorcore.onFirstFocus();
24034 for (var i =0; i < this.toolbars.length;i++) {
24035 this.toolbars[i].onFirstFocus();
24041 syncValue : function()
24043 this.editorcore.syncValue();
24046 pushValue : function()
24048 this.editorcore.pushValue();
24052 // hide stuff that is not compatible
24066 * @event specialkey
24070 * @cfg {String} fieldClass @hide
24073 * @cfg {String} focusClass @hide
24076 * @cfg {String} autoCreate @hide
24079 * @cfg {String} inputType @hide
24083 * @cfg {String} invalidText @hide
24086 * @cfg {String} msgFx @hide
24089 * @cfg {String} validateOnBlur @hide
24098 Roo.namespace('Roo.bootstrap.htmleditor');
24100 * @class Roo.bootstrap.HtmlEditorToolbar1
24106 new Roo.bootstrap.HtmlEditor({
24109 new Roo.bootstrap.HtmlEditorToolbar1({
24110 disable : { fonts: 1 , format: 1, ..., ... , ...],
24116 * @cfg {Object} disable List of elements to disable..
24117 * @cfg {Array} btns List of additional buttons.
24121 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24124 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24127 Roo.apply(this, config);
24129 // default disabled, based on 'good practice'..
24130 this.disable = this.disable || {};
24131 Roo.applyIf(this.disable, {
24134 specialElements : true
24136 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24138 this.editor = config.editor;
24139 this.editorcore = config.editor.editorcore;
24141 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24143 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24144 // dont call parent... till later.
24146 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
24151 editorcore : false,
24156 "h1","h2","h3","h4","h5","h6",
24158 "abbr", "acronym", "address", "cite", "samp", "var",
24162 onRender : function(ct, position)
24164 // Roo.log("Call onRender: " + this.xtype);
24166 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24168 this.el.dom.style.marginBottom = '0';
24170 var editorcore = this.editorcore;
24171 var editor= this.editor;
24174 var btn = function(id,cmd , toggle, handler, html){
24176 var event = toggle ? 'toggle' : 'click';
24181 xns: Roo.bootstrap,
24185 enableToggle:toggle !== false,
24187 pressed : toggle ? false : null,
24190 a.listeners[toggle ? 'toggle' : 'click'] = function() {
24191 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
24197 // var cb_box = function...
24202 xns: Roo.bootstrap,
24207 xns: Roo.bootstrap,
24211 Roo.each(this.formats, function(f) {
24212 style.menu.items.push({
24214 xns: Roo.bootstrap,
24215 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24220 editorcore.insertTag(this.tagname);
24227 children.push(style);
24229 btn('bold',false,true);
24230 btn('italic',false,true);
24231 btn('align-left', 'justifyleft',true);
24232 btn('align-center', 'justifycenter',true);
24233 btn('align-right' , 'justifyright',true);
24234 btn('link', false, false, function(btn) {
24235 //Roo.log("create link?");
24236 var url = prompt(this.createLinkText, this.defaultLinkValue);
24237 if(url && url != 'http:/'+'/'){
24238 this.editorcore.relayCmd('createlink', url);
24241 btn('list','insertunorderedlist',true);
24242 btn('pencil', false,true, function(btn){
24244 this.toggleSourceEdit(btn.pressed);
24247 if (this.editor.btns.length > 0) {
24248 for (var i = 0; i<this.editor.btns.length; i++) {
24249 children.push(this.editor.btns[i]);
24257 xns: Roo.bootstrap,
24262 xns: Roo.bootstrap,
24267 cog.menu.items.push({
24269 xns: Roo.bootstrap,
24270 html : Clean styles,
24275 editorcore.insertTag(this.tagname);
24284 this.xtype = 'NavSimplebar';
24286 for(var i=0;i< children.length;i++) {
24288 this.buttons.add(this.addxtypeChild(children[i]));
24292 editor.on('editorevent', this.updateToolbar, this);
24294 onBtnClick : function(id)
24296 this.editorcore.relayCmd(id);
24297 this.editorcore.focus();
24301 * Protected method that will not generally be called directly. It triggers
24302 * a toolbar update by reading the markup state of the current selection in the editor.
24304 updateToolbar: function(){
24306 if(!this.editorcore.activated){
24307 this.editor.onFirstFocus(); // is this neeed?
24311 var btns = this.buttons;
24312 var doc = this.editorcore.doc;
24313 btns.get('bold').setActive(doc.queryCommandState('bold'));
24314 btns.get('italic').setActive(doc.queryCommandState('italic'));
24315 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24317 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24318 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24319 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24321 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24322 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24325 var ans = this.editorcore.getAllAncestors();
24326 if (this.formatCombo) {
24329 var store = this.formatCombo.store;
24330 this.formatCombo.setValue("");
24331 for (var i =0; i < ans.length;i++) {
24332 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24334 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24342 // hides menus... - so this cant be on a menu...
24343 Roo.bootstrap.MenuMgr.hideAll();
24345 Roo.bootstrap.MenuMgr.hideAll();
24346 //this.editorsyncValue();
24348 onFirstFocus: function() {
24349 this.buttons.each(function(item){
24353 toggleSourceEdit : function(sourceEditMode){
24356 if(sourceEditMode){
24357 Roo.log("disabling buttons");
24358 this.buttons.each( function(item){
24359 if(item.cmd != 'pencil'){
24365 Roo.log("enabling buttons");
24366 if(this.editorcore.initialized){
24367 this.buttons.each( function(item){
24373 Roo.log("calling toggole on editor");
24374 // tell the editor that it's been pressed..
24375 this.editor.toggleSourceEdit(sourceEditMode);
24385 * @class Roo.bootstrap.Table.AbstractSelectionModel
24386 * @extends Roo.util.Observable
24387 * Abstract base class for grid SelectionModels. It provides the interface that should be
24388 * implemented by descendant classes. This class should not be directly instantiated.
24391 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24392 this.locked = false;
24393 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24397 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24398 /** @ignore Called by the grid automatically. Do not call directly. */
24399 init : function(grid){
24405 * Locks the selections.
24408 this.locked = true;
24412 * Unlocks the selections.
24414 unlock : function(){
24415 this.locked = false;
24419 * Returns true if the selections are locked.
24420 * @return {Boolean}
24422 isLocked : function(){
24423 return this.locked;
24427 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24428 * @class Roo.bootstrap.Table.RowSelectionModel
24429 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24430 * It supports multiple selections and keyboard selection/navigation.
24432 * @param {Object} config
24435 Roo.bootstrap.Table.RowSelectionModel = function(config){
24436 Roo.apply(this, config);
24437 this.selections = new Roo.util.MixedCollection(false, function(o){
24442 this.lastActive = false;
24446 * @event selectionchange
24447 * Fires when the selection changes
24448 * @param {SelectionModel} this
24450 "selectionchange" : true,
24452 * @event afterselectionchange
24453 * Fires after the selection changes (eg. by key press or clicking)
24454 * @param {SelectionModel} this
24456 "afterselectionchange" : true,
24458 * @event beforerowselect
24459 * Fires when a row is selected being selected, return false to cancel.
24460 * @param {SelectionModel} this
24461 * @param {Number} rowIndex The selected index
24462 * @param {Boolean} keepExisting False if other selections will be cleared
24464 "beforerowselect" : true,
24467 * Fires when a row is selected.
24468 * @param {SelectionModel} this
24469 * @param {Number} rowIndex The selected index
24470 * @param {Roo.data.Record} r The record
24472 "rowselect" : true,
24474 * @event rowdeselect
24475 * Fires when a row is deselected.
24476 * @param {SelectionModel} this
24477 * @param {Number} rowIndex The selected index
24479 "rowdeselect" : true
24481 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24482 this.locked = false;
24485 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24487 * @cfg {Boolean} singleSelect
24488 * True to allow selection of only one row at a time (defaults to false)
24490 singleSelect : false,
24493 initEvents : function()
24496 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24497 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24498 //}else{ // allow click to work like normal
24499 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24501 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24502 this.grid.on("rowclick", this.handleMouseDown, this);
24504 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24505 "up" : function(e){
24507 this.selectPrevious(e.shiftKey);
24508 }else if(this.last !== false && this.lastActive !== false){
24509 var last = this.last;
24510 this.selectRange(this.last, this.lastActive-1);
24511 this.grid.getView().focusRow(this.lastActive);
24512 if(last !== false){
24516 this.selectFirstRow();
24518 this.fireEvent("afterselectionchange", this);
24520 "down" : function(e){
24522 this.selectNext(e.shiftKey);
24523 }else if(this.last !== false && this.lastActive !== false){
24524 var last = this.last;
24525 this.selectRange(this.last, this.lastActive+1);
24526 this.grid.getView().focusRow(this.lastActive);
24527 if(last !== false){
24531 this.selectFirstRow();
24533 this.fireEvent("afterselectionchange", this);
24537 this.grid.store.on('load', function(){
24538 this.selections.clear();
24541 var view = this.grid.view;
24542 view.on("refresh", this.onRefresh, this);
24543 view.on("rowupdated", this.onRowUpdated, this);
24544 view.on("rowremoved", this.onRemove, this);
24549 onRefresh : function()
24551 var ds = this.grid.store, i, v = this.grid.view;
24552 var s = this.selections;
24553 s.each(function(r){
24554 if((i = ds.indexOfId(r.id)) != -1){
24563 onRemove : function(v, index, r){
24564 this.selections.remove(r);
24568 onRowUpdated : function(v, index, r){
24569 if(this.isSelected(r)){
24570 v.onRowSelect(index);
24576 * @param {Array} records The records to select
24577 * @param {Boolean} keepExisting (optional) True to keep existing selections
24579 selectRecords : function(records, keepExisting)
24582 this.clearSelections();
24584 var ds = this.grid.store;
24585 for(var i = 0, len = records.length; i < len; i++){
24586 this.selectRow(ds.indexOf(records[i]), true);
24591 * Gets the number of selected rows.
24594 getCount : function(){
24595 return this.selections.length;
24599 * Selects the first row in the grid.
24601 selectFirstRow : function(){
24606 * Select the last row.
24607 * @param {Boolean} keepExisting (optional) True to keep existing selections
24609 selectLastRow : function(keepExisting){
24610 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24611 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24615 * Selects the row immediately following the last selected row.
24616 * @param {Boolean} keepExisting (optional) True to keep existing selections
24618 selectNext : function(keepExisting)
24620 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24621 this.selectRow(this.last+1, keepExisting);
24622 this.grid.getView().focusRow(this.last);
24627 * Selects the row that precedes the last selected row.
24628 * @param {Boolean} keepExisting (optional) True to keep existing selections
24630 selectPrevious : function(keepExisting){
24632 this.selectRow(this.last-1, keepExisting);
24633 this.grid.getView().focusRow(this.last);
24638 * Returns the selected records
24639 * @return {Array} Array of selected records
24641 getSelections : function(){
24642 return [].concat(this.selections.items);
24646 * Returns the first selected record.
24649 getSelected : function(){
24650 return this.selections.itemAt(0);
24655 * Clears all selections.
24657 clearSelections : function(fast)
24663 var ds = this.grid.store;
24664 var s = this.selections;
24665 s.each(function(r){
24666 this.deselectRow(ds.indexOfId(r.id));
24670 this.selections.clear();
24677 * Selects all rows.
24679 selectAll : function(){
24683 this.selections.clear();
24684 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24685 this.selectRow(i, true);
24690 * Returns True if there is a selection.
24691 * @return {Boolean}
24693 hasSelection : function(){
24694 return this.selections.length > 0;
24698 * Returns True if the specified row is selected.
24699 * @param {Number/Record} record The record or index of the record to check
24700 * @return {Boolean}
24702 isSelected : function(index){
24703 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24704 return (r && this.selections.key(r.id) ? true : false);
24708 * Returns True if the specified record id is selected.
24709 * @param {String} id The id of record to check
24710 * @return {Boolean}
24712 isIdSelected : function(id){
24713 return (this.selections.key(id) ? true : false);
24718 handleMouseDBClick : function(e, t){
24722 handleMouseDown : function(e, t)
24724 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24725 if(this.isLocked() || rowIndex < 0 ){
24728 if(e.shiftKey && this.last !== false){
24729 var last = this.last;
24730 this.selectRange(last, rowIndex, e.ctrlKey);
24731 this.last = last; // reset the last
24735 var isSelected = this.isSelected(rowIndex);
24736 //Roo.log("select row:" + rowIndex);
24738 this.deselectRow(rowIndex);
24740 this.selectRow(rowIndex, true);
24744 if(e.button !== 0 && isSelected){
24745 alert('rowIndex 2: ' + rowIndex);
24746 view.focusRow(rowIndex);
24747 }else if(e.ctrlKey && isSelected){
24748 this.deselectRow(rowIndex);
24749 }else if(!isSelected){
24750 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24751 view.focusRow(rowIndex);
24755 this.fireEvent("afterselectionchange", this);
24758 handleDragableRowClick : function(grid, rowIndex, e)
24760 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24761 this.selectRow(rowIndex, false);
24762 grid.view.focusRow(rowIndex);
24763 this.fireEvent("afterselectionchange", this);
24768 * Selects multiple rows.
24769 * @param {Array} rows Array of the indexes of the row to select
24770 * @param {Boolean} keepExisting (optional) True to keep existing selections
24772 selectRows : function(rows, keepExisting){
24774 this.clearSelections();
24776 for(var i = 0, len = rows.length; i < len; i++){
24777 this.selectRow(rows[i], true);
24782 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24783 * @param {Number} startRow The index of the first row in the range
24784 * @param {Number} endRow The index of the last row in the range
24785 * @param {Boolean} keepExisting (optional) True to retain existing selections
24787 selectRange : function(startRow, endRow, keepExisting){
24792 this.clearSelections();
24794 if(startRow <= endRow){
24795 for(var i = startRow; i <= endRow; i++){
24796 this.selectRow(i, true);
24799 for(var i = startRow; i >= endRow; i--){
24800 this.selectRow(i, true);
24806 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24807 * @param {Number} startRow The index of the first row in the range
24808 * @param {Number} endRow The index of the last row in the range
24810 deselectRange : function(startRow, endRow, preventViewNotify){
24814 for(var i = startRow; i <= endRow; i++){
24815 this.deselectRow(i, preventViewNotify);
24821 * @param {Number} row The index of the row to select
24822 * @param {Boolean} keepExisting (optional) True to keep existing selections
24824 selectRow : function(index, keepExisting, preventViewNotify)
24826 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24829 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24830 if(!keepExisting || this.singleSelect){
24831 this.clearSelections();
24834 var r = this.grid.store.getAt(index);
24835 //console.log('selectRow - record id :' + r.id);
24837 this.selections.add(r);
24838 this.last = this.lastActive = index;
24839 if(!preventViewNotify){
24840 var proxy = new Roo.Element(
24841 this.grid.getRowDom(index)
24843 proxy.addClass('bg-info info');
24845 this.fireEvent("rowselect", this, index, r);
24846 this.fireEvent("selectionchange", this);
24852 * @param {Number} row The index of the row to deselect
24854 deselectRow : function(index, preventViewNotify)
24859 if(this.last == index){
24862 if(this.lastActive == index){
24863 this.lastActive = false;
24866 var r = this.grid.store.getAt(index);
24871 this.selections.remove(r);
24872 //.console.log('deselectRow - record id :' + r.id);
24873 if(!preventViewNotify){
24875 var proxy = new Roo.Element(
24876 this.grid.getRowDom(index)
24878 proxy.removeClass('bg-info info');
24880 this.fireEvent("rowdeselect", this, index);
24881 this.fireEvent("selectionchange", this);
24885 restoreLast : function(){
24887 this.last = this._last;
24892 acceptsNav : function(row, col, cm){
24893 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24897 onEditorKey : function(field, e){
24898 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24903 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24905 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24907 }else if(k == e.ENTER && !e.ctrlKey){
24911 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24913 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24915 }else if(k == e.ESC){
24919 g.startEditing(newCell[0], newCell[1]);
24925 * Ext JS Library 1.1.1
24926 * Copyright(c) 2006-2007, Ext JS, LLC.
24928 * Originally Released Under LGPL - original licence link has changed is not relivant.
24931 * <script type="text/javascript">
24935 * @class Roo.bootstrap.PagingToolbar
24936 * @extends Roo.bootstrap.NavSimplebar
24937 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24939 * Create a new PagingToolbar
24940 * @param {Object} config The config object
24941 * @param {Roo.data.Store} store
24943 Roo.bootstrap.PagingToolbar = function(config)
24945 // old args format still supported... - xtype is prefered..
24946 // created from xtype...
24948 this.ds = config.dataSource;
24950 if (config.store && !this.ds) {
24951 this.store= Roo.factory(config.store, Roo.data);
24952 this.ds = this.store;
24953 this.ds.xmodule = this.xmodule || false;
24956 this.toolbarItems = [];
24957 if (config.items) {
24958 this.toolbarItems = config.items;
24961 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24966 this.bind(this.ds);
24969 if (Roo.bootstrap.version == 4) {
24970 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24972 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24977 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24979 * @cfg {Roo.data.Store} dataSource
24980 * The underlying data store providing the paged data
24983 * @cfg {String/HTMLElement/Element} container
24984 * container The id or element that will contain the toolbar
24987 * @cfg {Boolean} displayInfo
24988 * True to display the displayMsg (defaults to false)
24991 * @cfg {Number} pageSize
24992 * The number of records to display per page (defaults to 20)
24996 * @cfg {String} displayMsg
24997 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24999 displayMsg : 'Displaying {0} - {1} of {2}',
25001 * @cfg {String} emptyMsg
25002 * The message to display when no records are found (defaults to "No data to display")
25004 emptyMsg : 'No data to display',
25006 * Customizable piece of the default paging text (defaults to "Page")
25009 beforePageText : "Page",
25011 * Customizable piece of the default paging text (defaults to "of %0")
25014 afterPageText : "of {0}",
25016 * Customizable piece of the default paging text (defaults to "First Page")
25019 firstText : "First Page",
25021 * Customizable piece of the default paging text (defaults to "Previous Page")
25024 prevText : "Previous Page",
25026 * Customizable piece of the default paging text (defaults to "Next Page")
25029 nextText : "Next Page",
25031 * Customizable piece of the default paging text (defaults to "Last Page")
25034 lastText : "Last Page",
25036 * Customizable piece of the default paging text (defaults to "Refresh")
25039 refreshText : "Refresh",
25043 onRender : function(ct, position)
25045 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25046 this.navgroup.parentId = this.id;
25047 this.navgroup.onRender(this.el, null);
25048 // add the buttons to the navgroup
25050 if(this.displayInfo){
25051 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25052 this.displayEl = this.el.select('.x-paging-info', true).first();
25053 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25054 // this.displayEl = navel.el.select('span',true).first();
25060 Roo.each(_this.buttons, function(e){ // this might need to use render????
25061 Roo.factory(e).render(_this.el);
25065 Roo.each(_this.toolbarItems, function(e) {
25066 _this.navgroup.addItem(e);
25070 this.first = this.navgroup.addItem({
25071 tooltip: this.firstText,
25072 cls: "prev btn-outline-secondary",
25073 html : ' <i class="fa fa-step-backward"></i>',
25075 preventDefault: true,
25076 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25079 this.prev = this.navgroup.addItem({
25080 tooltip: this.prevText,
25081 cls: "prev btn-outline-secondary",
25082 html : ' <i class="fa fa-backward"></i>',
25084 preventDefault: true,
25085 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
25087 //this.addSeparator();
25090 var field = this.navgroup.addItem( {
25092 cls : 'x-paging-position btn-outline-secondary',
25094 html : this.beforePageText +
25095 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25096 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
25099 this.field = field.el.select('input', true).first();
25100 this.field.on("keydown", this.onPagingKeydown, this);
25101 this.field.on("focus", function(){this.dom.select();});
25104 this.afterTextEl = field.el.select('.x-paging-after',true).first();
25105 //this.field.setHeight(18);
25106 //this.addSeparator();
25107 this.next = this.navgroup.addItem({
25108 tooltip: this.nextText,
25109 cls: "next btn-outline-secondary",
25110 html : ' <i class="fa fa-forward"></i>',
25112 preventDefault: true,
25113 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
25115 this.last = this.navgroup.addItem({
25116 tooltip: this.lastText,
25117 html : ' <i class="fa fa-step-forward"></i>',
25118 cls: "next btn-outline-secondary",
25120 preventDefault: true,
25121 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
25123 //this.addSeparator();
25124 this.loading = this.navgroup.addItem({
25125 tooltip: this.refreshText,
25126 cls: "btn-outline-secondary",
25127 html : ' <i class="fa fa-refresh"></i>',
25128 preventDefault: true,
25129 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25135 updateInfo : function(){
25136 if(this.displayEl){
25137 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25138 var msg = count == 0 ?
25142 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
25144 this.displayEl.update(msg);
25149 onLoad : function(ds, r, o)
25151 this.cursor = o.params.start ? o.params.start : 0;
25153 var d = this.getPageData(),
25158 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25159 this.field.dom.value = ap;
25160 this.first.setDisabled(ap == 1);
25161 this.prev.setDisabled(ap == 1);
25162 this.next.setDisabled(ap == ps);
25163 this.last.setDisabled(ap == ps);
25164 this.loading.enable();
25169 getPageData : function(){
25170 var total = this.ds.getTotalCount();
25173 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25174 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25179 onLoadError : function(){
25180 this.loading.enable();
25184 onPagingKeydown : function(e){
25185 var k = e.getKey();
25186 var d = this.getPageData();
25188 var v = this.field.dom.value, pageNum;
25189 if(!v || isNaN(pageNum = parseInt(v, 10))){
25190 this.field.dom.value = d.activePage;
25193 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25194 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25197 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))
25199 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25200 this.field.dom.value = pageNum;
25201 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25204 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25206 var v = this.field.dom.value, pageNum;
25207 var increment = (e.shiftKey) ? 10 : 1;
25208 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25211 if(!v || isNaN(pageNum = parseInt(v, 10))) {
25212 this.field.dom.value = d.activePage;
25215 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25217 this.field.dom.value = parseInt(v, 10) + increment;
25218 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25219 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25226 beforeLoad : function(){
25228 this.loading.disable();
25233 onClick : function(which){
25242 ds.load({params:{start: 0, limit: this.pageSize}});
25245 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25248 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25251 var total = ds.getTotalCount();
25252 var extra = total % this.pageSize;
25253 var lastStart = extra ? (total - extra) : total-this.pageSize;
25254 ds.load({params:{start: lastStart, limit: this.pageSize}});
25257 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25263 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25264 * @param {Roo.data.Store} store The data store to unbind
25266 unbind : function(ds){
25267 ds.un("beforeload", this.beforeLoad, this);
25268 ds.un("load", this.onLoad, this);
25269 ds.un("loadexception", this.onLoadError, this);
25270 ds.un("remove", this.updateInfo, this);
25271 ds.un("add", this.updateInfo, this);
25272 this.ds = undefined;
25276 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25277 * @param {Roo.data.Store} store The data store to bind
25279 bind : function(ds){
25280 ds.on("beforeload", this.beforeLoad, this);
25281 ds.on("load", this.onLoad, this);
25282 ds.on("loadexception", this.onLoadError, this);
25283 ds.on("remove", this.updateInfo, this);
25284 ds.on("add", this.updateInfo, this);
25295 * @class Roo.bootstrap.MessageBar
25296 * @extends Roo.bootstrap.Component
25297 * Bootstrap MessageBar class
25298 * @cfg {String} html contents of the MessageBar
25299 * @cfg {String} weight (info | success | warning | danger) default info
25300 * @cfg {String} beforeClass insert the bar before the given class
25301 * @cfg {Boolean} closable (true | false) default false
25302 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25305 * Create a new Element
25306 * @param {Object} config The config object
25309 Roo.bootstrap.MessageBar = function(config){
25310 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25313 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25319 beforeClass: 'bootstrap-sticky-wrap',
25321 getAutoCreate : function(){
25325 cls: 'alert alert-dismissable alert-' + this.weight,
25330 html: this.html || ''
25336 cfg.cls += ' alert-messages-fixed';
25350 onRender : function(ct, position)
25352 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25355 var cfg = Roo.apply({}, this.getAutoCreate());
25359 cfg.cls += ' ' + this.cls;
25362 cfg.style = this.style;
25364 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25366 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25369 this.el.select('>button.close').on('click', this.hide, this);
25375 if (!this.rendered) {
25381 this.fireEvent('show', this);
25387 if (!this.rendered) {
25393 this.fireEvent('hide', this);
25396 update : function()
25398 // var e = this.el.dom.firstChild;
25400 // if(this.closable){
25401 // e = e.nextSibling;
25404 // e.data = this.html || '';
25406 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25422 * @class Roo.bootstrap.Graph
25423 * @extends Roo.bootstrap.Component
25424 * Bootstrap Graph class
25428 @cfg {String} graphtype bar | vbar | pie
25429 @cfg {number} g_x coodinator | centre x (pie)
25430 @cfg {number} g_y coodinator | centre y (pie)
25431 @cfg {number} g_r radius (pie)
25432 @cfg {number} g_height height of the chart (respected by all elements in the set)
25433 @cfg {number} g_width width of the chart (respected by all elements in the set)
25434 @cfg {Object} title The title of the chart
25437 -opts (object) options for the chart
25439 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25440 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25442 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.
25443 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25445 o stretch (boolean)
25447 -opts (object) options for the pie
25450 o startAngle (number)
25451 o endAngle (number)
25455 * Create a new Input
25456 * @param {Object} config The config object
25459 Roo.bootstrap.Graph = function(config){
25460 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25466 * The img click event for the img.
25467 * @param {Roo.EventObject} e
25473 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25484 //g_colors: this.colors,
25491 getAutoCreate : function(){
25502 onRender : function(ct,position){
25505 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25507 if (typeof(Raphael) == 'undefined') {
25508 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25512 this.raphael = Raphael(this.el.dom);
25514 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25515 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25516 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25517 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25519 r.text(160, 10, "Single Series Chart").attr(txtattr);
25520 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25521 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25522 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25524 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25525 r.barchart(330, 10, 300, 220, data1);
25526 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25527 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25530 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25531 // r.barchart(30, 30, 560, 250, xdata, {
25532 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25533 // axis : "0 0 1 1",
25534 // axisxlabels : xdata
25535 // //yvalues : cols,
25538 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25540 // this.load(null,xdata,{
25541 // axis : "0 0 1 1",
25542 // axisxlabels : xdata
25547 load : function(graphtype,xdata,opts)
25549 this.raphael.clear();
25551 graphtype = this.graphtype;
25556 var r = this.raphael,
25557 fin = function () {
25558 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25560 fout = function () {
25561 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25563 pfin = function() {
25564 this.sector.stop();
25565 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25568 this.label[0].stop();
25569 this.label[0].attr({ r: 7.5 });
25570 this.label[1].attr({ "font-weight": 800 });
25573 pfout = function() {
25574 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25577 this.label[0].animate({ r: 5 }, 500, "bounce");
25578 this.label[1].attr({ "font-weight": 400 });
25584 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25587 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25590 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25591 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25593 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25600 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25605 setTitle: function(o)
25610 initEvents: function() {
25613 this.el.on('click', this.onClick, this);
25617 onClick : function(e)
25619 Roo.log('img onclick');
25620 this.fireEvent('click', this, e);
25632 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25635 * @class Roo.bootstrap.dash.NumberBox
25636 * @extends Roo.bootstrap.Component
25637 * Bootstrap NumberBox class
25638 * @cfg {String} headline Box headline
25639 * @cfg {String} content Box content
25640 * @cfg {String} icon Box icon
25641 * @cfg {String} footer Footer text
25642 * @cfg {String} fhref Footer href
25645 * Create a new NumberBox
25646 * @param {Object} config The config object
25650 Roo.bootstrap.dash.NumberBox = function(config){
25651 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25655 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25664 getAutoCreate : function(){
25668 cls : 'small-box ',
25676 cls : 'roo-headline',
25677 html : this.headline
25681 cls : 'roo-content',
25682 html : this.content
25696 cls : 'ion ' + this.icon
25705 cls : 'small-box-footer',
25706 href : this.fhref || '#',
25710 cfg.cn.push(footer);
25717 onRender : function(ct,position){
25718 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25725 setHeadline: function (value)
25727 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25730 setFooter: function (value, href)
25732 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25735 this.el.select('a.small-box-footer',true).first().attr('href', href);
25740 setContent: function (value)
25742 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25745 initEvents: function()
25759 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25762 * @class Roo.bootstrap.dash.TabBox
25763 * @extends Roo.bootstrap.Component
25764 * Bootstrap TabBox class
25765 * @cfg {String} title Title of the TabBox
25766 * @cfg {String} icon Icon of the TabBox
25767 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25768 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25771 * Create a new TabBox
25772 * @param {Object} config The config object
25776 Roo.bootstrap.dash.TabBox = function(config){
25777 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25782 * When a pane is added
25783 * @param {Roo.bootstrap.dash.TabPane} pane
25787 * @event activatepane
25788 * When a pane is activated
25789 * @param {Roo.bootstrap.dash.TabPane} pane
25791 "activatepane" : true
25799 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25804 tabScrollable : false,
25806 getChildContainer : function()
25808 return this.el.select('.tab-content', true).first();
25811 getAutoCreate : function(){
25815 cls: 'pull-left header',
25823 cls: 'fa ' + this.icon
25829 cls: 'nav nav-tabs pull-right',
25835 if(this.tabScrollable){
25842 cls: 'nav nav-tabs pull-right',
25853 cls: 'nav-tabs-custom',
25858 cls: 'tab-content no-padding',
25866 initEvents : function()
25868 //Roo.log('add add pane handler');
25869 this.on('addpane', this.onAddPane, this);
25872 * Updates the box title
25873 * @param {String} html to set the title to.
25875 setTitle : function(value)
25877 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25879 onAddPane : function(pane)
25881 this.panes.push(pane);
25882 //Roo.log('addpane');
25884 // tabs are rendere left to right..
25885 if(!this.showtabs){
25889 var ctr = this.el.select('.nav-tabs', true).first();
25892 var existing = ctr.select('.nav-tab',true);
25893 var qty = existing.getCount();;
25896 var tab = ctr.createChild({
25898 cls : 'nav-tab' + (qty ? '' : ' active'),
25906 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25909 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25911 pane.el.addClass('active');
25916 onTabClick : function(ev,un,ob,pane)
25918 //Roo.log('tab - prev default');
25919 ev.preventDefault();
25922 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25923 pane.tab.addClass('active');
25924 //Roo.log(pane.title);
25925 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25926 // technically we should have a deactivate event.. but maybe add later.
25927 // and it should not de-activate the selected tab...
25928 this.fireEvent('activatepane', pane);
25929 pane.el.addClass('active');
25930 pane.fireEvent('activate');
25935 getActivePane : function()
25938 Roo.each(this.panes, function(p) {
25939 if(p.el.hasClass('active')){
25960 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25962 * @class Roo.bootstrap.TabPane
25963 * @extends Roo.bootstrap.Component
25964 * Bootstrap TabPane class
25965 * @cfg {Boolean} active (false | true) Default false
25966 * @cfg {String} title title of panel
25970 * Create a new TabPane
25971 * @param {Object} config The config object
25974 Roo.bootstrap.dash.TabPane = function(config){
25975 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25981 * When a pane is activated
25982 * @param {Roo.bootstrap.dash.TabPane} pane
25989 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25994 // the tabBox that this is attached to.
25997 getAutoCreate : function()
26005 cfg.cls += ' active';
26010 initEvents : function()
26012 //Roo.log('trigger add pane handler');
26013 this.parent().fireEvent('addpane', this)
26017 * Updates the tab title
26018 * @param {String} html to set the title to.
26020 setTitle: function(str)
26026 this.tab.select('a', true).first().dom.innerHTML = str;
26043 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26046 * @class Roo.bootstrap.menu.Menu
26047 * @extends Roo.bootstrap.Component
26048 * Bootstrap Menu class - container for Menu
26049 * @cfg {String} html Text of the menu
26050 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26051 * @cfg {String} icon Font awesome icon
26052 * @cfg {String} pos Menu align to (top | bottom) default bottom
26056 * Create a new Menu
26057 * @param {Object} config The config object
26061 Roo.bootstrap.menu.Menu = function(config){
26062 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26066 * @event beforeshow
26067 * Fires before this menu is displayed
26068 * @param {Roo.bootstrap.menu.Menu} this
26072 * @event beforehide
26073 * Fires before this menu is hidden
26074 * @param {Roo.bootstrap.menu.Menu} this
26079 * Fires after this menu is displayed
26080 * @param {Roo.bootstrap.menu.Menu} this
26085 * Fires after this menu is hidden
26086 * @param {Roo.bootstrap.menu.Menu} this
26091 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26092 * @param {Roo.bootstrap.menu.Menu} this
26093 * @param {Roo.EventObject} e
26100 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
26104 weight : 'default',
26109 getChildContainer : function() {
26110 if(this.isSubMenu){
26114 return this.el.select('ul.dropdown-menu', true).first();
26117 getAutoCreate : function()
26122 cls : 'roo-menu-text',
26130 cls : 'fa ' + this.icon
26141 cls : 'dropdown-button btn btn-' + this.weight,
26146 cls : 'dropdown-toggle btn btn-' + this.weight,
26156 cls : 'dropdown-menu'
26162 if(this.pos == 'top'){
26163 cfg.cls += ' dropup';
26166 if(this.isSubMenu){
26169 cls : 'dropdown-menu'
26176 onRender : function(ct, position)
26178 this.isSubMenu = ct.hasClass('dropdown-submenu');
26180 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26183 initEvents : function()
26185 if(this.isSubMenu){
26189 this.hidden = true;
26191 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26192 this.triggerEl.on('click', this.onTriggerPress, this);
26194 this.buttonEl = this.el.select('button.dropdown-button', true).first();
26195 this.buttonEl.on('click', this.onClick, this);
26201 if(this.isSubMenu){
26205 return this.el.select('ul.dropdown-menu', true).first();
26208 onClick : function(e)
26210 this.fireEvent("click", this, e);
26213 onTriggerPress : function(e)
26215 if (this.isVisible()) {
26222 isVisible : function(){
26223 return !this.hidden;
26228 this.fireEvent("beforeshow", this);
26230 this.hidden = false;
26231 this.el.addClass('open');
26233 Roo.get(document).on("mouseup", this.onMouseUp, this);
26235 this.fireEvent("show", this);
26242 this.fireEvent("beforehide", this);
26244 this.hidden = true;
26245 this.el.removeClass('open');
26247 Roo.get(document).un("mouseup", this.onMouseUp);
26249 this.fireEvent("hide", this);
26252 onMouseUp : function()
26266 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26269 * @class Roo.bootstrap.menu.Item
26270 * @extends Roo.bootstrap.Component
26271 * Bootstrap MenuItem class
26272 * @cfg {Boolean} submenu (true | false) default false
26273 * @cfg {String} html text of the item
26274 * @cfg {String} href the link
26275 * @cfg {Boolean} disable (true | false) default false
26276 * @cfg {Boolean} preventDefault (true | false) default true
26277 * @cfg {String} icon Font awesome icon
26278 * @cfg {String} pos Submenu align to (left | right) default right
26282 * Create a new Item
26283 * @param {Object} config The config object
26287 Roo.bootstrap.menu.Item = function(config){
26288 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26292 * Fires when the mouse is hovering over this menu
26293 * @param {Roo.bootstrap.menu.Item} this
26294 * @param {Roo.EventObject} e
26299 * Fires when the mouse exits this menu
26300 * @param {Roo.bootstrap.menu.Item} this
26301 * @param {Roo.EventObject} e
26307 * The raw click event for the entire grid.
26308 * @param {Roo.EventObject} e
26314 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26319 preventDefault: true,
26324 getAutoCreate : function()
26329 cls : 'roo-menu-item-text',
26337 cls : 'fa ' + this.icon
26346 href : this.href || '#',
26353 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26357 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26359 if(this.pos == 'left'){
26360 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26367 initEvents : function()
26369 this.el.on('mouseover', this.onMouseOver, this);
26370 this.el.on('mouseout', this.onMouseOut, this);
26372 this.el.select('a', true).first().on('click', this.onClick, this);
26376 onClick : function(e)
26378 if(this.preventDefault){
26379 e.preventDefault();
26382 this.fireEvent("click", this, e);
26385 onMouseOver : function(e)
26387 if(this.submenu && this.pos == 'left'){
26388 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26391 this.fireEvent("mouseover", this, e);
26394 onMouseOut : function(e)
26396 this.fireEvent("mouseout", this, e);
26408 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26411 * @class Roo.bootstrap.menu.Separator
26412 * @extends Roo.bootstrap.Component
26413 * Bootstrap Separator class
26416 * Create a new Separator
26417 * @param {Object} config The config object
26421 Roo.bootstrap.menu.Separator = function(config){
26422 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26425 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26427 getAutoCreate : function(){
26448 * @class Roo.bootstrap.Tooltip
26449 * Bootstrap Tooltip class
26450 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26451 * to determine which dom element triggers the tooltip.
26453 * It needs to add support for additional attributes like tooltip-position
26456 * Create a new Toolti
26457 * @param {Object} config The config object
26460 Roo.bootstrap.Tooltip = function(config){
26461 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26463 this.alignment = Roo.bootstrap.Tooltip.alignment;
26465 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26466 this.alignment = config.alignment;
26471 Roo.apply(Roo.bootstrap.Tooltip, {
26473 * @function init initialize tooltip monitoring.
26477 currentTip : false,
26478 currentRegion : false,
26484 Roo.get(document).on('mouseover', this.enter ,this);
26485 Roo.get(document).on('mouseout', this.leave, this);
26488 this.currentTip = new Roo.bootstrap.Tooltip();
26491 enter : function(ev)
26493 var dom = ev.getTarget();
26495 //Roo.log(['enter',dom]);
26496 var el = Roo.fly(dom);
26497 if (this.currentEl) {
26499 //Roo.log(this.currentEl);
26500 //Roo.log(this.currentEl.contains(dom));
26501 if (this.currentEl == el) {
26504 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26510 if (this.currentTip.el) {
26511 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26515 if(!el || el.dom == document){
26521 // you can not look for children, as if el is the body.. then everythign is the child..
26522 if (!el.attr('tooltip')) { //
26523 if (!el.select("[tooltip]").elements.length) {
26526 // is the mouse over this child...?
26527 bindEl = el.select("[tooltip]").first();
26528 var xy = ev.getXY();
26529 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26530 //Roo.log("not in region.");
26533 //Roo.log("child element over..");
26536 this.currentEl = bindEl;
26537 this.currentTip.bind(bindEl);
26538 this.currentRegion = Roo.lib.Region.getRegion(dom);
26539 this.currentTip.enter();
26542 leave : function(ev)
26544 var dom = ev.getTarget();
26545 //Roo.log(['leave',dom]);
26546 if (!this.currentEl) {
26551 if (dom != this.currentEl.dom) {
26554 var xy = ev.getXY();
26555 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26558 // only activate leave if mouse cursor is outside... bounding box..
26563 if (this.currentTip) {
26564 this.currentTip.leave();
26566 //Roo.log('clear currentEl');
26567 this.currentEl = false;
26572 'left' : ['r-l', [-2,0], 'right'],
26573 'right' : ['l-r', [2,0], 'left'],
26574 'bottom' : ['t-b', [0,2], 'top'],
26575 'top' : [ 'b-t', [0,-2], 'bottom']
26581 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26586 delay : null, // can be { show : 300 , hide: 500}
26590 hoverState : null, //???
26592 placement : 'bottom',
26596 getAutoCreate : function(){
26603 cls : 'tooltip-arrow'
26606 cls : 'tooltip-inner'
26613 bind : function(el)
26619 enter : function () {
26621 if (this.timeout != null) {
26622 clearTimeout(this.timeout);
26625 this.hoverState = 'in';
26626 //Roo.log("enter - show");
26627 if (!this.delay || !this.delay.show) {
26632 this.timeout = setTimeout(function () {
26633 if (_t.hoverState == 'in') {
26636 }, this.delay.show);
26640 clearTimeout(this.timeout);
26642 this.hoverState = 'out';
26643 if (!this.delay || !this.delay.hide) {
26649 this.timeout = setTimeout(function () {
26650 //Roo.log("leave - timeout");
26652 if (_t.hoverState == 'out') {
26654 Roo.bootstrap.Tooltip.currentEl = false;
26659 show : function (msg)
26662 this.render(document.body);
26665 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26667 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26669 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26671 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26673 var placement = typeof this.placement == 'function' ?
26674 this.placement.call(this, this.el, on_el) :
26677 var autoToken = /\s?auto?\s?/i;
26678 var autoPlace = autoToken.test(placement);
26680 placement = placement.replace(autoToken, '') || 'top';
26684 //this.el.setXY([0,0]);
26686 //this.el.dom.style.display='block';
26688 //this.el.appendTo(on_el);
26690 var p = this.getPosition();
26691 var box = this.el.getBox();
26697 var align = this.alignment[placement];
26699 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26701 if(placement == 'top' || placement == 'bottom'){
26703 placement = 'right';
26706 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26707 placement = 'left';
26710 var scroll = Roo.select('body', true).first().getScroll();
26712 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26716 align = this.alignment[placement];
26719 this.el.alignTo(this.bindEl, align[0],align[1]);
26720 //var arrow = this.el.select('.arrow',true).first();
26721 //arrow.set(align[2],
26723 this.el.addClass(placement);
26725 this.el.addClass('in fade');
26727 this.hoverState = null;
26729 if (this.el.hasClass('fade')) {
26740 //this.el.setXY([0,0]);
26741 this.el.removeClass('in');
26757 * @class Roo.bootstrap.LocationPicker
26758 * @extends Roo.bootstrap.Component
26759 * Bootstrap LocationPicker class
26760 * @cfg {Number} latitude Position when init default 0
26761 * @cfg {Number} longitude Position when init default 0
26762 * @cfg {Number} zoom default 15
26763 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26764 * @cfg {Boolean} mapTypeControl default false
26765 * @cfg {Boolean} disableDoubleClickZoom default false
26766 * @cfg {Boolean} scrollwheel default true
26767 * @cfg {Boolean} streetViewControl default false
26768 * @cfg {Number} radius default 0
26769 * @cfg {String} locationName
26770 * @cfg {Boolean} draggable default true
26771 * @cfg {Boolean} enableAutocomplete default false
26772 * @cfg {Boolean} enableReverseGeocode default true
26773 * @cfg {String} markerTitle
26776 * Create a new LocationPicker
26777 * @param {Object} config The config object
26781 Roo.bootstrap.LocationPicker = function(config){
26783 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26788 * Fires when the picker initialized.
26789 * @param {Roo.bootstrap.LocationPicker} this
26790 * @param {Google Location} location
26794 * @event positionchanged
26795 * Fires when the picker position changed.
26796 * @param {Roo.bootstrap.LocationPicker} this
26797 * @param {Google Location} location
26799 positionchanged : true,
26802 * Fires when the map resize.
26803 * @param {Roo.bootstrap.LocationPicker} this
26808 * Fires when the map show.
26809 * @param {Roo.bootstrap.LocationPicker} this
26814 * Fires when the map hide.
26815 * @param {Roo.bootstrap.LocationPicker} this
26820 * Fires when click the map.
26821 * @param {Roo.bootstrap.LocationPicker} this
26822 * @param {Map event} e
26826 * @event mapRightClick
26827 * Fires when right click the map.
26828 * @param {Roo.bootstrap.LocationPicker} this
26829 * @param {Map event} e
26831 mapRightClick : true,
26833 * @event markerClick
26834 * Fires when click the marker.
26835 * @param {Roo.bootstrap.LocationPicker} this
26836 * @param {Map event} e
26838 markerClick : true,
26840 * @event markerRightClick
26841 * Fires when right click the marker.
26842 * @param {Roo.bootstrap.LocationPicker} this
26843 * @param {Map event} e
26845 markerRightClick : true,
26847 * @event OverlayViewDraw
26848 * Fires when OverlayView Draw
26849 * @param {Roo.bootstrap.LocationPicker} this
26851 OverlayViewDraw : true,
26853 * @event OverlayViewOnAdd
26854 * Fires when OverlayView Draw
26855 * @param {Roo.bootstrap.LocationPicker} this
26857 OverlayViewOnAdd : true,
26859 * @event OverlayViewOnRemove
26860 * Fires when OverlayView Draw
26861 * @param {Roo.bootstrap.LocationPicker} this
26863 OverlayViewOnRemove : true,
26865 * @event OverlayViewShow
26866 * Fires when OverlayView Draw
26867 * @param {Roo.bootstrap.LocationPicker} this
26868 * @param {Pixel} cpx
26870 OverlayViewShow : true,
26872 * @event OverlayViewHide
26873 * Fires when OverlayView Draw
26874 * @param {Roo.bootstrap.LocationPicker} this
26876 OverlayViewHide : true,
26878 * @event loadexception
26879 * Fires when load google lib failed.
26880 * @param {Roo.bootstrap.LocationPicker} this
26882 loadexception : true
26887 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26889 gMapContext: false,
26895 mapTypeControl: false,
26896 disableDoubleClickZoom: false,
26898 streetViewControl: false,
26902 enableAutocomplete: false,
26903 enableReverseGeocode: true,
26906 getAutoCreate: function()
26911 cls: 'roo-location-picker'
26917 initEvents: function(ct, position)
26919 if(!this.el.getWidth() || this.isApplied()){
26923 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26928 initial: function()
26930 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26931 this.fireEvent('loadexception', this);
26935 if(!this.mapTypeId){
26936 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26939 this.gMapContext = this.GMapContext();
26941 this.initOverlayView();
26943 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26947 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26948 _this.setPosition(_this.gMapContext.marker.position);
26951 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26952 _this.fireEvent('mapClick', this, event);
26956 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26957 _this.fireEvent('mapRightClick', this, event);
26961 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26962 _this.fireEvent('markerClick', this, event);
26966 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26967 _this.fireEvent('markerRightClick', this, event);
26971 this.setPosition(this.gMapContext.location);
26973 this.fireEvent('initial', this, this.gMapContext.location);
26976 initOverlayView: function()
26980 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26984 _this.fireEvent('OverlayViewDraw', _this);
26989 _this.fireEvent('OverlayViewOnAdd', _this);
26992 onRemove: function()
26994 _this.fireEvent('OverlayViewOnRemove', _this);
26997 show: function(cpx)
26999 _this.fireEvent('OverlayViewShow', _this, cpx);
27004 _this.fireEvent('OverlayViewHide', _this);
27010 fromLatLngToContainerPixel: function(event)
27012 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27015 isApplied: function()
27017 return this.getGmapContext() == false ? false : true;
27020 getGmapContext: function()
27022 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27025 GMapContext: function()
27027 var position = new google.maps.LatLng(this.latitude, this.longitude);
27029 var _map = new google.maps.Map(this.el.dom, {
27032 mapTypeId: this.mapTypeId,
27033 mapTypeControl: this.mapTypeControl,
27034 disableDoubleClickZoom: this.disableDoubleClickZoom,
27035 scrollwheel: this.scrollwheel,
27036 streetViewControl: this.streetViewControl,
27037 locationName: this.locationName,
27038 draggable: this.draggable,
27039 enableAutocomplete: this.enableAutocomplete,
27040 enableReverseGeocode: this.enableReverseGeocode
27043 var _marker = new google.maps.Marker({
27044 position: position,
27046 title: this.markerTitle,
27047 draggable: this.draggable
27054 location: position,
27055 radius: this.radius,
27056 locationName: this.locationName,
27057 addressComponents: {
27058 formatted_address: null,
27059 addressLine1: null,
27060 addressLine2: null,
27062 streetNumber: null,
27066 stateOrProvince: null
27069 domContainer: this.el.dom,
27070 geodecoder: new google.maps.Geocoder()
27074 drawCircle: function(center, radius, options)
27076 if (this.gMapContext.circle != null) {
27077 this.gMapContext.circle.setMap(null);
27081 options = Roo.apply({}, options, {
27082 strokeColor: "#0000FF",
27083 strokeOpacity: .35,
27085 fillColor: "#0000FF",
27089 options.map = this.gMapContext.map;
27090 options.radius = radius;
27091 options.center = center;
27092 this.gMapContext.circle = new google.maps.Circle(options);
27093 return this.gMapContext.circle;
27099 setPosition: function(location)
27101 this.gMapContext.location = location;
27102 this.gMapContext.marker.setPosition(location);
27103 this.gMapContext.map.panTo(location);
27104 this.drawCircle(location, this.gMapContext.radius, {});
27108 if (this.gMapContext.settings.enableReverseGeocode) {
27109 this.gMapContext.geodecoder.geocode({
27110 latLng: this.gMapContext.location
27111 }, function(results, status) {
27113 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27114 _this.gMapContext.locationName = results[0].formatted_address;
27115 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27117 _this.fireEvent('positionchanged', this, location);
27124 this.fireEvent('positionchanged', this, location);
27129 google.maps.event.trigger(this.gMapContext.map, "resize");
27131 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27133 this.fireEvent('resize', this);
27136 setPositionByLatLng: function(latitude, longitude)
27138 this.setPosition(new google.maps.LatLng(latitude, longitude));
27141 getCurrentPosition: function()
27144 latitude: this.gMapContext.location.lat(),
27145 longitude: this.gMapContext.location.lng()
27149 getAddressName: function()
27151 return this.gMapContext.locationName;
27154 getAddressComponents: function()
27156 return this.gMapContext.addressComponents;
27159 address_component_from_google_geocode: function(address_components)
27163 for (var i = 0; i < address_components.length; i++) {
27164 var component = address_components[i];
27165 if (component.types.indexOf("postal_code") >= 0) {
27166 result.postalCode = component.short_name;
27167 } else if (component.types.indexOf("street_number") >= 0) {
27168 result.streetNumber = component.short_name;
27169 } else if (component.types.indexOf("route") >= 0) {
27170 result.streetName = component.short_name;
27171 } else if (component.types.indexOf("neighborhood") >= 0) {
27172 result.city = component.short_name;
27173 } else if (component.types.indexOf("locality") >= 0) {
27174 result.city = component.short_name;
27175 } else if (component.types.indexOf("sublocality") >= 0) {
27176 result.district = component.short_name;
27177 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27178 result.stateOrProvince = component.short_name;
27179 } else if (component.types.indexOf("country") >= 0) {
27180 result.country = component.short_name;
27184 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27185 result.addressLine2 = "";
27189 setZoomLevel: function(zoom)
27191 this.gMapContext.map.setZoom(zoom);
27204 this.fireEvent('show', this);
27215 this.fireEvent('hide', this);
27220 Roo.apply(Roo.bootstrap.LocationPicker, {
27222 OverlayView : function(map, options)
27224 options = options || {};
27231 * @class Roo.bootstrap.Alert
27232 * @extends Roo.bootstrap.Component
27233 * Bootstrap Alert class - shows an alert area box
27235 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27236 Enter a valid email address
27239 * @cfg {String} title The title of alert
27240 * @cfg {String} html The content of alert
27241 * @cfg {String} weight ( success | info | warning | danger )
27242 * @cfg {String} faicon font-awesomeicon
27245 * Create a new alert
27246 * @param {Object} config The config object
27250 Roo.bootstrap.Alert = function(config){
27251 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27255 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27262 getAutoCreate : function()
27271 cls : 'roo-alert-icon'
27276 cls : 'roo-alert-title',
27281 cls : 'roo-alert-text',
27288 cfg.cn[0].cls += ' fa ' + this.faicon;
27292 cfg.cls += ' alert-' + this.weight;
27298 initEvents: function()
27300 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27303 setTitle : function(str)
27305 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27308 setText : function(str)
27310 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27313 setWeight : function(weight)
27316 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27319 this.weight = weight;
27321 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27324 setIcon : function(icon)
27327 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27330 this.faicon = icon;
27332 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27353 * @class Roo.bootstrap.UploadCropbox
27354 * @extends Roo.bootstrap.Component
27355 * Bootstrap UploadCropbox class
27356 * @cfg {String} emptyText show when image has been loaded
27357 * @cfg {String} rotateNotify show when image too small to rotate
27358 * @cfg {Number} errorTimeout default 3000
27359 * @cfg {Number} minWidth default 300
27360 * @cfg {Number} minHeight default 300
27361 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27362 * @cfg {Boolean} isDocument (true|false) default false
27363 * @cfg {String} url action url
27364 * @cfg {String} paramName default 'imageUpload'
27365 * @cfg {String} method default POST
27366 * @cfg {Boolean} loadMask (true|false) default true
27367 * @cfg {Boolean} loadingText default 'Loading...'
27370 * Create a new UploadCropbox
27371 * @param {Object} config The config object
27374 Roo.bootstrap.UploadCropbox = function(config){
27375 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27379 * @event beforeselectfile
27380 * Fire before select file
27381 * @param {Roo.bootstrap.UploadCropbox} this
27383 "beforeselectfile" : true,
27386 * Fire after initEvent
27387 * @param {Roo.bootstrap.UploadCropbox} this
27392 * Fire after initEvent
27393 * @param {Roo.bootstrap.UploadCropbox} this
27394 * @param {String} data
27399 * Fire when preparing the file data
27400 * @param {Roo.bootstrap.UploadCropbox} this
27401 * @param {Object} file
27406 * Fire when get exception
27407 * @param {Roo.bootstrap.UploadCropbox} this
27408 * @param {XMLHttpRequest} xhr
27410 "exception" : true,
27412 * @event beforeloadcanvas
27413 * Fire before load the canvas
27414 * @param {Roo.bootstrap.UploadCropbox} this
27415 * @param {String} src
27417 "beforeloadcanvas" : true,
27420 * Fire when trash image
27421 * @param {Roo.bootstrap.UploadCropbox} this
27426 * Fire when download the image
27427 * @param {Roo.bootstrap.UploadCropbox} this
27431 * @event footerbuttonclick
27432 * Fire when footerbuttonclick
27433 * @param {Roo.bootstrap.UploadCropbox} this
27434 * @param {String} type
27436 "footerbuttonclick" : true,
27440 * @param {Roo.bootstrap.UploadCropbox} this
27445 * Fire when rotate the image
27446 * @param {Roo.bootstrap.UploadCropbox} this
27447 * @param {String} pos
27452 * Fire when inspect the file
27453 * @param {Roo.bootstrap.UploadCropbox} this
27454 * @param {Object} file
27459 * Fire when xhr upload the file
27460 * @param {Roo.bootstrap.UploadCropbox} this
27461 * @param {Object} data
27466 * Fire when arrange the file data
27467 * @param {Roo.bootstrap.UploadCropbox} this
27468 * @param {Object} formData
27473 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27476 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27478 emptyText : 'Click to upload image',
27479 rotateNotify : 'Image is too small to rotate',
27480 errorTimeout : 3000,
27494 cropType : 'image/jpeg',
27496 canvasLoaded : false,
27497 isDocument : false,
27499 paramName : 'imageUpload',
27501 loadingText : 'Loading...',
27504 getAutoCreate : function()
27508 cls : 'roo-upload-cropbox',
27512 cls : 'roo-upload-cropbox-selector',
27517 cls : 'roo-upload-cropbox-body',
27518 style : 'cursor:pointer',
27522 cls : 'roo-upload-cropbox-preview'
27526 cls : 'roo-upload-cropbox-thumb'
27530 cls : 'roo-upload-cropbox-empty-notify',
27531 html : this.emptyText
27535 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27536 html : this.rotateNotify
27542 cls : 'roo-upload-cropbox-footer',
27545 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27555 onRender : function(ct, position)
27557 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27559 if (this.buttons.length) {
27561 Roo.each(this.buttons, function(bb) {
27563 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27565 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27571 this.maskEl = this.el;
27575 initEvents : function()
27577 this.urlAPI = (window.createObjectURL && window) ||
27578 (window.URL && URL.revokeObjectURL && URL) ||
27579 (window.webkitURL && webkitURL);
27581 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27582 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27584 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27585 this.selectorEl.hide();
27587 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27588 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27590 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27591 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27592 this.thumbEl.hide();
27594 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27595 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27597 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27598 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27599 this.errorEl.hide();
27601 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27602 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27603 this.footerEl.hide();
27605 this.setThumbBoxSize();
27611 this.fireEvent('initial', this);
27618 window.addEventListener("resize", function() { _this.resize(); } );
27620 this.bodyEl.on('click', this.beforeSelectFile, this);
27623 this.bodyEl.on('touchstart', this.onTouchStart, this);
27624 this.bodyEl.on('touchmove', this.onTouchMove, this);
27625 this.bodyEl.on('touchend', this.onTouchEnd, this);
27629 this.bodyEl.on('mousedown', this.onMouseDown, this);
27630 this.bodyEl.on('mousemove', this.onMouseMove, this);
27631 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27632 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27633 Roo.get(document).on('mouseup', this.onMouseUp, this);
27636 this.selectorEl.on('change', this.onFileSelected, this);
27642 this.baseScale = 1;
27644 this.baseRotate = 1;
27645 this.dragable = false;
27646 this.pinching = false;
27649 this.cropData = false;
27650 this.notifyEl.dom.innerHTML = this.emptyText;
27652 this.selectorEl.dom.value = '';
27656 resize : function()
27658 if(this.fireEvent('resize', this) != false){
27659 this.setThumbBoxPosition();
27660 this.setCanvasPosition();
27664 onFooterButtonClick : function(e, el, o, type)
27667 case 'rotate-left' :
27668 this.onRotateLeft(e);
27670 case 'rotate-right' :
27671 this.onRotateRight(e);
27674 this.beforeSelectFile(e);
27689 this.fireEvent('footerbuttonclick', this, type);
27692 beforeSelectFile : function(e)
27694 e.preventDefault();
27696 if(this.fireEvent('beforeselectfile', this) != false){
27697 this.selectorEl.dom.click();
27701 onFileSelected : function(e)
27703 e.preventDefault();
27705 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27709 var file = this.selectorEl.dom.files[0];
27711 if(this.fireEvent('inspect', this, file) != false){
27712 this.prepare(file);
27717 trash : function(e)
27719 this.fireEvent('trash', this);
27722 download : function(e)
27724 this.fireEvent('download', this);
27727 loadCanvas : function(src)
27729 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27733 this.imageEl = document.createElement('img');
27737 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27739 this.imageEl.src = src;
27743 onLoadCanvas : function()
27745 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27746 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27748 this.bodyEl.un('click', this.beforeSelectFile, this);
27750 this.notifyEl.hide();
27751 this.thumbEl.show();
27752 this.footerEl.show();
27754 this.baseRotateLevel();
27756 if(this.isDocument){
27757 this.setThumbBoxSize();
27760 this.setThumbBoxPosition();
27762 this.baseScaleLevel();
27768 this.canvasLoaded = true;
27771 this.maskEl.unmask();
27776 setCanvasPosition : function()
27778 if(!this.canvasEl){
27782 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27783 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27785 this.previewEl.setLeft(pw);
27786 this.previewEl.setTop(ph);
27790 onMouseDown : function(e)
27794 this.dragable = true;
27795 this.pinching = false;
27797 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27798 this.dragable = false;
27802 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27803 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27807 onMouseMove : function(e)
27811 if(!this.canvasLoaded){
27815 if (!this.dragable){
27819 var minX = Math.ceil(this.thumbEl.getLeft(true));
27820 var minY = Math.ceil(this.thumbEl.getTop(true));
27822 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27823 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27825 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27826 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27828 x = x - this.mouseX;
27829 y = y - this.mouseY;
27831 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27832 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27834 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27835 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27837 this.previewEl.setLeft(bgX);
27838 this.previewEl.setTop(bgY);
27840 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27841 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27844 onMouseUp : function(e)
27848 this.dragable = false;
27851 onMouseWheel : function(e)
27855 this.startScale = this.scale;
27857 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27859 if(!this.zoomable()){
27860 this.scale = this.startScale;
27869 zoomable : function()
27871 var minScale = this.thumbEl.getWidth() / this.minWidth;
27873 if(this.minWidth < this.minHeight){
27874 minScale = this.thumbEl.getHeight() / this.minHeight;
27877 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27878 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27882 (this.rotate == 0 || this.rotate == 180) &&
27884 width > this.imageEl.OriginWidth ||
27885 height > this.imageEl.OriginHeight ||
27886 (width < this.minWidth && height < this.minHeight)
27894 (this.rotate == 90 || this.rotate == 270) &&
27896 width > this.imageEl.OriginWidth ||
27897 height > this.imageEl.OriginHeight ||
27898 (width < this.minHeight && height < this.minWidth)
27905 !this.isDocument &&
27906 (this.rotate == 0 || this.rotate == 180) &&
27908 width < this.minWidth ||
27909 width > this.imageEl.OriginWidth ||
27910 height < this.minHeight ||
27911 height > this.imageEl.OriginHeight
27918 !this.isDocument &&
27919 (this.rotate == 90 || this.rotate == 270) &&
27921 width < this.minHeight ||
27922 width > this.imageEl.OriginWidth ||
27923 height < this.minWidth ||
27924 height > this.imageEl.OriginHeight
27934 onRotateLeft : function(e)
27936 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27938 var minScale = this.thumbEl.getWidth() / this.minWidth;
27940 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27941 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27943 this.startScale = this.scale;
27945 while (this.getScaleLevel() < minScale){
27947 this.scale = this.scale + 1;
27949 if(!this.zoomable()){
27954 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27955 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27960 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27967 this.scale = this.startScale;
27969 this.onRotateFail();
27974 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27976 if(this.isDocument){
27977 this.setThumbBoxSize();
27978 this.setThumbBoxPosition();
27979 this.setCanvasPosition();
27984 this.fireEvent('rotate', this, 'left');
27988 onRotateRight : function(e)
27990 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27992 var minScale = this.thumbEl.getWidth() / this.minWidth;
27994 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27995 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27997 this.startScale = this.scale;
27999 while (this.getScaleLevel() < minScale){
28001 this.scale = this.scale + 1;
28003 if(!this.zoomable()){
28008 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28009 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28014 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28021 this.scale = this.startScale;
28023 this.onRotateFail();
28028 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28030 if(this.isDocument){
28031 this.setThumbBoxSize();
28032 this.setThumbBoxPosition();
28033 this.setCanvasPosition();
28038 this.fireEvent('rotate', this, 'right');
28041 onRotateFail : function()
28043 this.errorEl.show(true);
28047 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28052 this.previewEl.dom.innerHTML = '';
28054 var canvasEl = document.createElement("canvas");
28056 var contextEl = canvasEl.getContext("2d");
28058 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28059 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28060 var center = this.imageEl.OriginWidth / 2;
28062 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28063 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28064 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28065 center = this.imageEl.OriginHeight / 2;
28068 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28070 contextEl.translate(center, center);
28071 contextEl.rotate(this.rotate * Math.PI / 180);
28073 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28075 this.canvasEl = document.createElement("canvas");
28077 this.contextEl = this.canvasEl.getContext("2d");
28079 switch (this.rotate) {
28082 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28083 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28085 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28090 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28091 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28093 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28094 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);
28098 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28103 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28104 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28106 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28107 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);
28111 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);
28116 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28117 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28119 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28120 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28124 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);
28131 this.previewEl.appendChild(this.canvasEl);
28133 this.setCanvasPosition();
28138 if(!this.canvasLoaded){
28142 var imageCanvas = document.createElement("canvas");
28144 var imageContext = imageCanvas.getContext("2d");
28146 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28147 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28149 var center = imageCanvas.width / 2;
28151 imageContext.translate(center, center);
28153 imageContext.rotate(this.rotate * Math.PI / 180);
28155 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28157 var canvas = document.createElement("canvas");
28159 var context = canvas.getContext("2d");
28161 canvas.width = this.minWidth;
28162 canvas.height = this.minHeight;
28164 switch (this.rotate) {
28167 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28168 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28170 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28171 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28173 var targetWidth = this.minWidth - 2 * x;
28174 var targetHeight = this.minHeight - 2 * y;
28178 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28179 scale = targetWidth / width;
28182 if(x > 0 && y == 0){
28183 scale = targetHeight / height;
28186 if(x > 0 && y > 0){
28187 scale = targetWidth / width;
28189 if(width < height){
28190 scale = targetHeight / height;
28194 context.scale(scale, scale);
28196 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28197 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28199 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28200 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28202 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28207 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28208 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28210 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28211 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28213 var targetWidth = this.minWidth - 2 * x;
28214 var targetHeight = this.minHeight - 2 * y;
28218 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28219 scale = targetWidth / width;
28222 if(x > 0 && y == 0){
28223 scale = targetHeight / height;
28226 if(x > 0 && y > 0){
28227 scale = targetWidth / width;
28229 if(width < height){
28230 scale = targetHeight / height;
28234 context.scale(scale, scale);
28236 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28237 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28239 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28240 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28242 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28244 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28249 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28250 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28252 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28253 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28255 var targetWidth = this.minWidth - 2 * x;
28256 var targetHeight = this.minHeight - 2 * y;
28260 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28261 scale = targetWidth / width;
28264 if(x > 0 && y == 0){
28265 scale = targetHeight / height;
28268 if(x > 0 && y > 0){
28269 scale = targetWidth / width;
28271 if(width < height){
28272 scale = targetHeight / height;
28276 context.scale(scale, scale);
28278 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28279 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28281 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28282 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28284 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28285 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28287 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28292 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28293 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28295 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28296 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28298 var targetWidth = this.minWidth - 2 * x;
28299 var targetHeight = this.minHeight - 2 * y;
28303 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28304 scale = targetWidth / width;
28307 if(x > 0 && y == 0){
28308 scale = targetHeight / height;
28311 if(x > 0 && y > 0){
28312 scale = targetWidth / width;
28314 if(width < height){
28315 scale = targetHeight / height;
28319 context.scale(scale, scale);
28321 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28322 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28324 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28325 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28327 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28329 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28336 this.cropData = canvas.toDataURL(this.cropType);
28338 if(this.fireEvent('crop', this, this.cropData) !== false){
28339 this.process(this.file, this.cropData);
28346 setThumbBoxSize : function()
28350 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28351 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28352 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28354 this.minWidth = width;
28355 this.minHeight = height;
28357 if(this.rotate == 90 || this.rotate == 270){
28358 this.minWidth = height;
28359 this.minHeight = width;
28364 width = Math.ceil(this.minWidth * height / this.minHeight);
28366 if(this.minWidth > this.minHeight){
28368 height = Math.ceil(this.minHeight * width / this.minWidth);
28371 this.thumbEl.setStyle({
28372 width : width + 'px',
28373 height : height + 'px'
28380 setThumbBoxPosition : function()
28382 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28383 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28385 this.thumbEl.setLeft(x);
28386 this.thumbEl.setTop(y);
28390 baseRotateLevel : function()
28392 this.baseRotate = 1;
28395 typeof(this.exif) != 'undefined' &&
28396 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28397 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28399 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28402 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28406 baseScaleLevel : function()
28410 if(this.isDocument){
28412 if(this.baseRotate == 6 || this.baseRotate == 8){
28414 height = this.thumbEl.getHeight();
28415 this.baseScale = height / this.imageEl.OriginWidth;
28417 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28418 width = this.thumbEl.getWidth();
28419 this.baseScale = width / this.imageEl.OriginHeight;
28425 height = this.thumbEl.getHeight();
28426 this.baseScale = height / this.imageEl.OriginHeight;
28428 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28429 width = this.thumbEl.getWidth();
28430 this.baseScale = width / this.imageEl.OriginWidth;
28436 if(this.baseRotate == 6 || this.baseRotate == 8){
28438 width = this.thumbEl.getHeight();
28439 this.baseScale = width / this.imageEl.OriginHeight;
28441 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28442 height = this.thumbEl.getWidth();
28443 this.baseScale = height / this.imageEl.OriginHeight;
28446 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28447 height = this.thumbEl.getWidth();
28448 this.baseScale = height / this.imageEl.OriginHeight;
28450 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28451 width = this.thumbEl.getHeight();
28452 this.baseScale = width / this.imageEl.OriginWidth;
28459 width = this.thumbEl.getWidth();
28460 this.baseScale = width / this.imageEl.OriginWidth;
28462 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28463 height = this.thumbEl.getHeight();
28464 this.baseScale = height / this.imageEl.OriginHeight;
28467 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28469 height = this.thumbEl.getHeight();
28470 this.baseScale = height / this.imageEl.OriginHeight;
28472 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28473 width = this.thumbEl.getWidth();
28474 this.baseScale = width / this.imageEl.OriginWidth;
28482 getScaleLevel : function()
28484 return this.baseScale * Math.pow(1.1, this.scale);
28487 onTouchStart : function(e)
28489 if(!this.canvasLoaded){
28490 this.beforeSelectFile(e);
28494 var touches = e.browserEvent.touches;
28500 if(touches.length == 1){
28501 this.onMouseDown(e);
28505 if(touches.length != 2){
28511 for(var i = 0, finger; finger = touches[i]; i++){
28512 coords.push(finger.pageX, finger.pageY);
28515 var x = Math.pow(coords[0] - coords[2], 2);
28516 var y = Math.pow(coords[1] - coords[3], 2);
28518 this.startDistance = Math.sqrt(x + y);
28520 this.startScale = this.scale;
28522 this.pinching = true;
28523 this.dragable = false;
28527 onTouchMove : function(e)
28529 if(!this.pinching && !this.dragable){
28533 var touches = e.browserEvent.touches;
28540 this.onMouseMove(e);
28546 for(var i = 0, finger; finger = touches[i]; i++){
28547 coords.push(finger.pageX, finger.pageY);
28550 var x = Math.pow(coords[0] - coords[2], 2);
28551 var y = Math.pow(coords[1] - coords[3], 2);
28553 this.endDistance = Math.sqrt(x + y);
28555 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28557 if(!this.zoomable()){
28558 this.scale = this.startScale;
28566 onTouchEnd : function(e)
28568 this.pinching = false;
28569 this.dragable = false;
28573 process : function(file, crop)
28576 this.maskEl.mask(this.loadingText);
28579 this.xhr = new XMLHttpRequest();
28581 file.xhr = this.xhr;
28583 this.xhr.open(this.method, this.url, true);
28586 "Accept": "application/json",
28587 "Cache-Control": "no-cache",
28588 "X-Requested-With": "XMLHttpRequest"
28591 for (var headerName in headers) {
28592 var headerValue = headers[headerName];
28594 this.xhr.setRequestHeader(headerName, headerValue);
28600 this.xhr.onload = function()
28602 _this.xhrOnLoad(_this.xhr);
28605 this.xhr.onerror = function()
28607 _this.xhrOnError(_this.xhr);
28610 var formData = new FormData();
28612 formData.append('returnHTML', 'NO');
28615 formData.append('crop', crop);
28618 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28619 formData.append(this.paramName, file, file.name);
28622 if(typeof(file.filename) != 'undefined'){
28623 formData.append('filename', file.filename);
28626 if(typeof(file.mimetype) != 'undefined'){
28627 formData.append('mimetype', file.mimetype);
28630 if(this.fireEvent('arrange', this, formData) != false){
28631 this.xhr.send(formData);
28635 xhrOnLoad : function(xhr)
28638 this.maskEl.unmask();
28641 if (xhr.readyState !== 4) {
28642 this.fireEvent('exception', this, xhr);
28646 var response = Roo.decode(xhr.responseText);
28648 if(!response.success){
28649 this.fireEvent('exception', this, xhr);
28653 var response = Roo.decode(xhr.responseText);
28655 this.fireEvent('upload', this, response);
28659 xhrOnError : function()
28662 this.maskEl.unmask();
28665 Roo.log('xhr on error');
28667 var response = Roo.decode(xhr.responseText);
28673 prepare : function(file)
28676 this.maskEl.mask(this.loadingText);
28682 if(typeof(file) === 'string'){
28683 this.loadCanvas(file);
28687 if(!file || !this.urlAPI){
28692 this.cropType = file.type;
28696 if(this.fireEvent('prepare', this, this.file) != false){
28698 var reader = new FileReader();
28700 reader.onload = function (e) {
28701 if (e.target.error) {
28702 Roo.log(e.target.error);
28706 var buffer = e.target.result,
28707 dataView = new DataView(buffer),
28709 maxOffset = dataView.byteLength - 4,
28713 if (dataView.getUint16(0) === 0xffd8) {
28714 while (offset < maxOffset) {
28715 markerBytes = dataView.getUint16(offset);
28717 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28718 markerLength = dataView.getUint16(offset + 2) + 2;
28719 if (offset + markerLength > dataView.byteLength) {
28720 Roo.log('Invalid meta data: Invalid segment size.');
28724 if(markerBytes == 0xffe1){
28725 _this.parseExifData(
28732 offset += markerLength;
28742 var url = _this.urlAPI.createObjectURL(_this.file);
28744 _this.loadCanvas(url);
28749 reader.readAsArrayBuffer(this.file);
28755 parseExifData : function(dataView, offset, length)
28757 var tiffOffset = offset + 10,
28761 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28762 // No Exif data, might be XMP data instead
28766 // Check for the ASCII code for "Exif" (0x45786966):
28767 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28768 // No Exif data, might be XMP data instead
28771 if (tiffOffset + 8 > dataView.byteLength) {
28772 Roo.log('Invalid Exif data: Invalid segment size.');
28775 // Check for the two null bytes:
28776 if (dataView.getUint16(offset + 8) !== 0x0000) {
28777 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28780 // Check the byte alignment:
28781 switch (dataView.getUint16(tiffOffset)) {
28783 littleEndian = true;
28786 littleEndian = false;
28789 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28792 // Check for the TIFF tag marker (0x002A):
28793 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28794 Roo.log('Invalid Exif data: Missing TIFF marker.');
28797 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28798 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28800 this.parseExifTags(
28803 tiffOffset + dirOffset,
28808 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28813 if (dirOffset + 6 > dataView.byteLength) {
28814 Roo.log('Invalid Exif data: Invalid directory offset.');
28817 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28818 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28819 if (dirEndOffset + 4 > dataView.byteLength) {
28820 Roo.log('Invalid Exif data: Invalid directory size.');
28823 for (i = 0; i < tagsNumber; i += 1) {
28827 dirOffset + 2 + 12 * i, // tag offset
28831 // Return the offset to the next directory:
28832 return dataView.getUint32(dirEndOffset, littleEndian);
28835 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28837 var tag = dataView.getUint16(offset, littleEndian);
28839 this.exif[tag] = this.getExifValue(
28843 dataView.getUint16(offset + 2, littleEndian), // tag type
28844 dataView.getUint32(offset + 4, littleEndian), // tag length
28849 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28851 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28860 Roo.log('Invalid Exif data: Invalid tag type.');
28864 tagSize = tagType.size * length;
28865 // Determine if the value is contained in the dataOffset bytes,
28866 // or if the value at the dataOffset is a pointer to the actual data:
28867 dataOffset = tagSize > 4 ?
28868 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28869 if (dataOffset + tagSize > dataView.byteLength) {
28870 Roo.log('Invalid Exif data: Invalid data offset.');
28873 if (length === 1) {
28874 return tagType.getValue(dataView, dataOffset, littleEndian);
28877 for (i = 0; i < length; i += 1) {
28878 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28881 if (tagType.ascii) {
28883 // Concatenate the chars:
28884 for (i = 0; i < values.length; i += 1) {
28886 // Ignore the terminating NULL byte(s):
28887 if (c === '\u0000') {
28899 Roo.apply(Roo.bootstrap.UploadCropbox, {
28901 'Orientation': 0x0112
28905 1: 0, //'top-left',
28907 3: 180, //'bottom-right',
28908 // 4: 'bottom-left',
28910 6: 90, //'right-top',
28911 // 7: 'right-bottom',
28912 8: 270 //'left-bottom'
28916 // byte, 8-bit unsigned int:
28918 getValue: function (dataView, dataOffset) {
28919 return dataView.getUint8(dataOffset);
28923 // ascii, 8-bit byte:
28925 getValue: function (dataView, dataOffset) {
28926 return String.fromCharCode(dataView.getUint8(dataOffset));
28931 // short, 16 bit int:
28933 getValue: function (dataView, dataOffset, littleEndian) {
28934 return dataView.getUint16(dataOffset, littleEndian);
28938 // long, 32 bit int:
28940 getValue: function (dataView, dataOffset, littleEndian) {
28941 return dataView.getUint32(dataOffset, littleEndian);
28945 // rational = two long values, first is numerator, second is denominator:
28947 getValue: function (dataView, dataOffset, littleEndian) {
28948 return dataView.getUint32(dataOffset, littleEndian) /
28949 dataView.getUint32(dataOffset + 4, littleEndian);
28953 // slong, 32 bit signed int:
28955 getValue: function (dataView, dataOffset, littleEndian) {
28956 return dataView.getInt32(dataOffset, littleEndian);
28960 // srational, two slongs, first is numerator, second is denominator:
28962 getValue: function (dataView, dataOffset, littleEndian) {
28963 return dataView.getInt32(dataOffset, littleEndian) /
28964 dataView.getInt32(dataOffset + 4, littleEndian);
28974 cls : 'btn-group roo-upload-cropbox-rotate-left',
28975 action : 'rotate-left',
28979 cls : 'btn btn-default',
28980 html : '<i class="fa fa-undo"></i>'
28986 cls : 'btn-group roo-upload-cropbox-picture',
28987 action : 'picture',
28991 cls : 'btn btn-default',
28992 html : '<i class="fa fa-picture-o"></i>'
28998 cls : 'btn-group roo-upload-cropbox-rotate-right',
28999 action : 'rotate-right',
29003 cls : 'btn btn-default',
29004 html : '<i class="fa fa-repeat"></i>'
29012 cls : 'btn-group roo-upload-cropbox-rotate-left',
29013 action : 'rotate-left',
29017 cls : 'btn btn-default',
29018 html : '<i class="fa fa-undo"></i>'
29024 cls : 'btn-group roo-upload-cropbox-download',
29025 action : 'download',
29029 cls : 'btn btn-default',
29030 html : '<i class="fa fa-download"></i>'
29036 cls : 'btn-group roo-upload-cropbox-crop',
29041 cls : 'btn btn-default',
29042 html : '<i class="fa fa-crop"></i>'
29048 cls : 'btn-group roo-upload-cropbox-trash',
29053 cls : 'btn btn-default',
29054 html : '<i class="fa fa-trash"></i>'
29060 cls : 'btn-group roo-upload-cropbox-rotate-right',
29061 action : 'rotate-right',
29065 cls : 'btn btn-default',
29066 html : '<i class="fa fa-repeat"></i>'
29074 cls : 'btn-group roo-upload-cropbox-rotate-left',
29075 action : 'rotate-left',
29079 cls : 'btn btn-default',
29080 html : '<i class="fa fa-undo"></i>'
29086 cls : 'btn-group roo-upload-cropbox-rotate-right',
29087 action : 'rotate-right',
29091 cls : 'btn btn-default',
29092 html : '<i class="fa fa-repeat"></i>'
29105 * @class Roo.bootstrap.DocumentManager
29106 * @extends Roo.bootstrap.Component
29107 * Bootstrap DocumentManager class
29108 * @cfg {String} paramName default 'imageUpload'
29109 * @cfg {String} toolTipName default 'filename'
29110 * @cfg {String} method default POST
29111 * @cfg {String} url action url
29112 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29113 * @cfg {Boolean} multiple multiple upload default true
29114 * @cfg {Number} thumbSize default 300
29115 * @cfg {String} fieldLabel
29116 * @cfg {Number} labelWidth default 4
29117 * @cfg {String} labelAlign (left|top) default left
29118 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29119 * @cfg {Number} labellg set the width of label (1-12)
29120 * @cfg {Number} labelmd set the width of label (1-12)
29121 * @cfg {Number} labelsm set the width of label (1-12)
29122 * @cfg {Number} labelxs set the width of label (1-12)
29125 * Create a new DocumentManager
29126 * @param {Object} config The config object
29129 Roo.bootstrap.DocumentManager = function(config){
29130 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29133 this.delegates = [];
29138 * Fire when initial the DocumentManager
29139 * @param {Roo.bootstrap.DocumentManager} this
29144 * inspect selected file
29145 * @param {Roo.bootstrap.DocumentManager} this
29146 * @param {File} file
29151 * Fire when xhr load exception
29152 * @param {Roo.bootstrap.DocumentManager} this
29153 * @param {XMLHttpRequest} xhr
29155 "exception" : true,
29157 * @event afterupload
29158 * Fire when xhr load exception
29159 * @param {Roo.bootstrap.DocumentManager} this
29160 * @param {XMLHttpRequest} xhr
29162 "afterupload" : true,
29165 * prepare the form data
29166 * @param {Roo.bootstrap.DocumentManager} this
29167 * @param {Object} formData
29172 * Fire when remove the file
29173 * @param {Roo.bootstrap.DocumentManager} this
29174 * @param {Object} file
29179 * Fire after refresh the file
29180 * @param {Roo.bootstrap.DocumentManager} this
29185 * Fire after click the image
29186 * @param {Roo.bootstrap.DocumentManager} this
29187 * @param {Object} file
29192 * Fire when upload a image and editable set to true
29193 * @param {Roo.bootstrap.DocumentManager} this
29194 * @param {Object} file
29198 * @event beforeselectfile
29199 * Fire before select file
29200 * @param {Roo.bootstrap.DocumentManager} this
29202 "beforeselectfile" : true,
29205 * Fire before process file
29206 * @param {Roo.bootstrap.DocumentManager} this
29207 * @param {Object} file
29211 * @event previewrendered
29212 * Fire when preview rendered
29213 * @param {Roo.bootstrap.DocumentManager} this
29214 * @param {Object} file
29216 "previewrendered" : true,
29219 "previewResize" : true
29224 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29233 paramName : 'imageUpload',
29234 toolTipName : 'filename',
29237 labelAlign : 'left',
29247 getAutoCreate : function()
29249 var managerWidget = {
29251 cls : 'roo-document-manager',
29255 cls : 'roo-document-manager-selector',
29260 cls : 'roo-document-manager-uploader',
29264 cls : 'roo-document-manager-upload-btn',
29265 html : '<i class="fa fa-plus"></i>'
29276 cls : 'column col-md-12',
29281 if(this.fieldLabel.length){
29286 cls : 'column col-md-12',
29287 html : this.fieldLabel
29291 cls : 'column col-md-12',
29296 if(this.labelAlign == 'left'){
29301 html : this.fieldLabel
29310 if(this.labelWidth > 12){
29311 content[0].style = "width: " + this.labelWidth + 'px';
29314 if(this.labelWidth < 13 && this.labelmd == 0){
29315 this.labelmd = this.labelWidth;
29318 if(this.labellg > 0){
29319 content[0].cls += ' col-lg-' + this.labellg;
29320 content[1].cls += ' col-lg-' + (12 - this.labellg);
29323 if(this.labelmd > 0){
29324 content[0].cls += ' col-md-' + this.labelmd;
29325 content[1].cls += ' col-md-' + (12 - this.labelmd);
29328 if(this.labelsm > 0){
29329 content[0].cls += ' col-sm-' + this.labelsm;
29330 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29333 if(this.labelxs > 0){
29334 content[0].cls += ' col-xs-' + this.labelxs;
29335 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29343 cls : 'row clearfix',
29351 initEvents : function()
29353 this.managerEl = this.el.select('.roo-document-manager', true).first();
29354 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29356 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29357 this.selectorEl.hide();
29360 this.selectorEl.attr('multiple', 'multiple');
29363 this.selectorEl.on('change', this.onFileSelected, this);
29365 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29366 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29368 this.uploader.on('click', this.onUploaderClick, this);
29370 this.renderProgressDialog();
29374 window.addEventListener("resize", function() { _this.refresh(); } );
29376 this.fireEvent('initial', this);
29379 renderProgressDialog : function()
29383 this.progressDialog = new Roo.bootstrap.Modal({
29384 cls : 'roo-document-manager-progress-dialog',
29385 allow_close : false,
29396 btnclick : function() {
29397 _this.uploadCancel();
29403 this.progressDialog.render(Roo.get(document.body));
29405 this.progress = new Roo.bootstrap.Progress({
29406 cls : 'roo-document-manager-progress',
29411 this.progress.render(this.progressDialog.getChildContainer());
29413 this.progressBar = new Roo.bootstrap.ProgressBar({
29414 cls : 'roo-document-manager-progress-bar',
29417 aria_valuemax : 12,
29421 this.progressBar.render(this.progress.getChildContainer());
29424 onUploaderClick : function(e)
29426 e.preventDefault();
29428 if(this.fireEvent('beforeselectfile', this) != false){
29429 this.selectorEl.dom.click();
29434 onFileSelected : function(e)
29436 e.preventDefault();
29438 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29442 Roo.each(this.selectorEl.dom.files, function(file){
29443 if(this.fireEvent('inspect', this, file) != false){
29444 this.files.push(file);
29454 this.selectorEl.dom.value = '';
29456 if(!this.files || !this.files.length){
29460 if(this.boxes > 0 && this.files.length > this.boxes){
29461 this.files = this.files.slice(0, this.boxes);
29464 this.uploader.show();
29466 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29467 this.uploader.hide();
29476 Roo.each(this.files, function(file){
29478 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29479 var f = this.renderPreview(file);
29484 if(file.type.indexOf('image') != -1){
29485 this.delegates.push(
29487 _this.process(file);
29488 }).createDelegate(this)
29496 _this.process(file);
29497 }).createDelegate(this)
29502 this.files = files;
29504 this.delegates = this.delegates.concat(docs);
29506 if(!this.delegates.length){
29511 this.progressBar.aria_valuemax = this.delegates.length;
29518 arrange : function()
29520 if(!this.delegates.length){
29521 this.progressDialog.hide();
29526 var delegate = this.delegates.shift();
29528 this.progressDialog.show();
29530 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29532 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29537 refresh : function()
29539 this.uploader.show();
29541 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29542 this.uploader.hide();
29545 Roo.isTouch ? this.closable(false) : this.closable(true);
29547 this.fireEvent('refresh', this);
29550 onRemove : function(e, el, o)
29552 e.preventDefault();
29554 this.fireEvent('remove', this, o);
29558 remove : function(o)
29562 Roo.each(this.files, function(file){
29563 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29572 this.files = files;
29579 Roo.each(this.files, function(file){
29584 file.target.remove();
29593 onClick : function(e, el, o)
29595 e.preventDefault();
29597 this.fireEvent('click', this, o);
29601 closable : function(closable)
29603 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29605 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29617 xhrOnLoad : function(xhr)
29619 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29623 if (xhr.readyState !== 4) {
29625 this.fireEvent('exception', this, xhr);
29629 var response = Roo.decode(xhr.responseText);
29631 if(!response.success){
29633 this.fireEvent('exception', this, xhr);
29637 var file = this.renderPreview(response.data);
29639 this.files.push(file);
29643 this.fireEvent('afterupload', this, xhr);
29647 xhrOnError : function(xhr)
29649 Roo.log('xhr on error');
29651 var response = Roo.decode(xhr.responseText);
29658 process : function(file)
29660 if(this.fireEvent('process', this, file) !== false){
29661 if(this.editable && file.type.indexOf('image') != -1){
29662 this.fireEvent('edit', this, file);
29666 this.uploadStart(file, false);
29673 uploadStart : function(file, crop)
29675 this.xhr = new XMLHttpRequest();
29677 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29682 file.xhr = this.xhr;
29684 this.managerEl.createChild({
29686 cls : 'roo-document-manager-loading',
29690 tooltip : file.name,
29691 cls : 'roo-document-manager-thumb',
29692 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29698 this.xhr.open(this.method, this.url, true);
29701 "Accept": "application/json",
29702 "Cache-Control": "no-cache",
29703 "X-Requested-With": "XMLHttpRequest"
29706 for (var headerName in headers) {
29707 var headerValue = headers[headerName];
29709 this.xhr.setRequestHeader(headerName, headerValue);
29715 this.xhr.onload = function()
29717 _this.xhrOnLoad(_this.xhr);
29720 this.xhr.onerror = function()
29722 _this.xhrOnError(_this.xhr);
29725 var formData = new FormData();
29727 formData.append('returnHTML', 'NO');
29730 formData.append('crop', crop);
29733 formData.append(this.paramName, file, file.name);
29740 if(this.fireEvent('prepare', this, formData, options) != false){
29742 if(options.manually){
29746 this.xhr.send(formData);
29750 this.uploadCancel();
29753 uploadCancel : function()
29759 this.delegates = [];
29761 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29768 renderPreview : function(file)
29770 if(typeof(file.target) != 'undefined' && file.target){
29774 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29776 var previewEl = this.managerEl.createChild({
29778 cls : 'roo-document-manager-preview',
29782 tooltip : file[this.toolTipName],
29783 cls : 'roo-document-manager-thumb',
29784 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29789 html : '<i class="fa fa-times-circle"></i>'
29794 var close = previewEl.select('button.close', true).first();
29796 close.on('click', this.onRemove, this, file);
29798 file.target = previewEl;
29800 var image = previewEl.select('img', true).first();
29804 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29806 image.on('click', this.onClick, this, file);
29808 this.fireEvent('previewrendered', this, file);
29814 onPreviewLoad : function(file, image)
29816 if(typeof(file.target) == 'undefined' || !file.target){
29820 var width = image.dom.naturalWidth || image.dom.width;
29821 var height = image.dom.naturalHeight || image.dom.height;
29823 if(!this.previewResize) {
29827 if(width > height){
29828 file.target.addClass('wide');
29832 file.target.addClass('tall');
29837 uploadFromSource : function(file, crop)
29839 this.xhr = new XMLHttpRequest();
29841 this.managerEl.createChild({
29843 cls : 'roo-document-manager-loading',
29847 tooltip : file.name,
29848 cls : 'roo-document-manager-thumb',
29849 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29855 this.xhr.open(this.method, this.url, true);
29858 "Accept": "application/json",
29859 "Cache-Control": "no-cache",
29860 "X-Requested-With": "XMLHttpRequest"
29863 for (var headerName in headers) {
29864 var headerValue = headers[headerName];
29866 this.xhr.setRequestHeader(headerName, headerValue);
29872 this.xhr.onload = function()
29874 _this.xhrOnLoad(_this.xhr);
29877 this.xhr.onerror = function()
29879 _this.xhrOnError(_this.xhr);
29882 var formData = new FormData();
29884 formData.append('returnHTML', 'NO');
29886 formData.append('crop', crop);
29888 if(typeof(file.filename) != 'undefined'){
29889 formData.append('filename', file.filename);
29892 if(typeof(file.mimetype) != 'undefined'){
29893 formData.append('mimetype', file.mimetype);
29898 if(this.fireEvent('prepare', this, formData) != false){
29899 this.xhr.send(formData);
29909 * @class Roo.bootstrap.DocumentViewer
29910 * @extends Roo.bootstrap.Component
29911 * Bootstrap DocumentViewer class
29912 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29913 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29916 * Create a new DocumentViewer
29917 * @param {Object} config The config object
29920 Roo.bootstrap.DocumentViewer = function(config){
29921 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29926 * Fire after initEvent
29927 * @param {Roo.bootstrap.DocumentViewer} this
29933 * @param {Roo.bootstrap.DocumentViewer} this
29938 * Fire after download button
29939 * @param {Roo.bootstrap.DocumentViewer} this
29944 * Fire after trash button
29945 * @param {Roo.bootstrap.DocumentViewer} this
29952 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29954 showDownload : true,
29958 getAutoCreate : function()
29962 cls : 'roo-document-viewer',
29966 cls : 'roo-document-viewer-body',
29970 cls : 'roo-document-viewer-thumb',
29974 cls : 'roo-document-viewer-image'
29982 cls : 'roo-document-viewer-footer',
29985 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29989 cls : 'btn-group roo-document-viewer-download',
29993 cls : 'btn btn-default',
29994 html : '<i class="fa fa-download"></i>'
30000 cls : 'btn-group roo-document-viewer-trash',
30004 cls : 'btn btn-default',
30005 html : '<i class="fa fa-trash"></i>'
30018 initEvents : function()
30020 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30021 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30023 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30024 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30026 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30027 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30029 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30030 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30032 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30033 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30035 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30036 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30038 this.bodyEl.on('click', this.onClick, this);
30039 this.downloadBtn.on('click', this.onDownload, this);
30040 this.trashBtn.on('click', this.onTrash, this);
30042 this.downloadBtn.hide();
30043 this.trashBtn.hide();
30045 if(this.showDownload){
30046 this.downloadBtn.show();
30049 if(this.showTrash){
30050 this.trashBtn.show();
30053 if(!this.showDownload && !this.showTrash) {
30054 this.footerEl.hide();
30059 initial : function()
30061 this.fireEvent('initial', this);
30065 onClick : function(e)
30067 e.preventDefault();
30069 this.fireEvent('click', this);
30072 onDownload : function(e)
30074 e.preventDefault();
30076 this.fireEvent('download', this);
30079 onTrash : function(e)
30081 e.preventDefault();
30083 this.fireEvent('trash', this);
30095 * @class Roo.bootstrap.NavProgressBar
30096 * @extends Roo.bootstrap.Component
30097 * Bootstrap NavProgressBar class
30100 * Create a new nav progress bar
30101 * @param {Object} config The config object
30104 Roo.bootstrap.NavProgressBar = function(config){
30105 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30107 this.bullets = this.bullets || [];
30109 // Roo.bootstrap.NavProgressBar.register(this);
30113 * Fires when the active item changes
30114 * @param {Roo.bootstrap.NavProgressBar} this
30115 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30116 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
30123 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
30128 getAutoCreate : function()
30130 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30134 cls : 'roo-navigation-bar-group',
30138 cls : 'roo-navigation-top-bar'
30142 cls : 'roo-navigation-bullets-bar',
30146 cls : 'roo-navigation-bar'
30153 cls : 'roo-navigation-bottom-bar'
30163 initEvents: function()
30168 onRender : function(ct, position)
30170 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30172 if(this.bullets.length){
30173 Roo.each(this.bullets, function(b){
30182 addItem : function(cfg)
30184 var item = new Roo.bootstrap.NavProgressItem(cfg);
30186 item.parentId = this.id;
30187 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30190 var top = new Roo.bootstrap.Element({
30192 cls : 'roo-navigation-bar-text'
30195 var bottom = new Roo.bootstrap.Element({
30197 cls : 'roo-navigation-bar-text'
30200 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30201 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30203 var topText = new Roo.bootstrap.Element({
30205 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30208 var bottomText = new Roo.bootstrap.Element({
30210 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30213 topText.onRender(top.el, null);
30214 bottomText.onRender(bottom.el, null);
30217 item.bottomEl = bottom;
30220 this.barItems.push(item);
30225 getActive : function()
30227 var active = false;
30229 Roo.each(this.barItems, function(v){
30231 if (!v.isActive()) {
30243 setActiveItem : function(item)
30247 Roo.each(this.barItems, function(v){
30248 if (v.rid == item.rid) {
30252 if (v.isActive()) {
30253 v.setActive(false);
30258 item.setActive(true);
30260 this.fireEvent('changed', this, item, prev);
30263 getBarItem: function(rid)
30267 Roo.each(this.barItems, function(e) {
30268 if (e.rid != rid) {
30279 indexOfItem : function(item)
30283 Roo.each(this.barItems, function(v, i){
30285 if (v.rid != item.rid) {
30296 setActiveNext : function()
30298 var i = this.indexOfItem(this.getActive());
30300 if (i > this.barItems.length) {
30304 this.setActiveItem(this.barItems[i+1]);
30307 setActivePrev : function()
30309 var i = this.indexOfItem(this.getActive());
30315 this.setActiveItem(this.barItems[i-1]);
30318 format : function()
30320 if(!this.barItems.length){
30324 var width = 100 / this.barItems.length;
30326 Roo.each(this.barItems, function(i){
30327 i.el.setStyle('width', width + '%');
30328 i.topEl.el.setStyle('width', width + '%');
30329 i.bottomEl.el.setStyle('width', width + '%');
30338 * Nav Progress Item
30343 * @class Roo.bootstrap.NavProgressItem
30344 * @extends Roo.bootstrap.Component
30345 * Bootstrap NavProgressItem class
30346 * @cfg {String} rid the reference id
30347 * @cfg {Boolean} active (true|false) Is item active default false
30348 * @cfg {Boolean} disabled (true|false) Is item active default false
30349 * @cfg {String} html
30350 * @cfg {String} position (top|bottom) text position default bottom
30351 * @cfg {String} icon show icon instead of number
30354 * Create a new NavProgressItem
30355 * @param {Object} config The config object
30357 Roo.bootstrap.NavProgressItem = function(config){
30358 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30363 * The raw click event for the entire grid.
30364 * @param {Roo.bootstrap.NavProgressItem} this
30365 * @param {Roo.EventObject} e
30372 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30378 position : 'bottom',
30381 getAutoCreate : function()
30383 var iconCls = 'roo-navigation-bar-item-icon';
30385 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30389 cls: 'roo-navigation-bar-item',
30399 cfg.cls += ' active';
30402 cfg.cls += ' disabled';
30408 disable : function()
30410 this.setDisabled(true);
30413 enable : function()
30415 this.setDisabled(false);
30418 initEvents: function()
30420 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30422 this.iconEl.on('click', this.onClick, this);
30425 onClick : function(e)
30427 e.preventDefault();
30433 if(this.fireEvent('click', this, e) === false){
30437 this.parent().setActiveItem(this);
30440 isActive: function ()
30442 return this.active;
30445 setActive : function(state)
30447 if(this.active == state){
30451 this.active = state;
30454 this.el.addClass('active');
30458 this.el.removeClass('active');
30463 setDisabled : function(state)
30465 if(this.disabled == state){
30469 this.disabled = state;
30472 this.el.addClass('disabled');
30476 this.el.removeClass('disabled');
30479 tooltipEl : function()
30481 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30494 * @class Roo.bootstrap.FieldLabel
30495 * @extends Roo.bootstrap.Component
30496 * Bootstrap FieldLabel class
30497 * @cfg {String} html contents of the element
30498 * @cfg {String} tag tag of the element default label
30499 * @cfg {String} cls class of the element
30500 * @cfg {String} target label target
30501 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30502 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30503 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30504 * @cfg {String} iconTooltip default "This field is required"
30505 * @cfg {String} indicatorpos (left|right) default left
30508 * Create a new FieldLabel
30509 * @param {Object} config The config object
30512 Roo.bootstrap.FieldLabel = function(config){
30513 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30518 * Fires after the field has been marked as invalid.
30519 * @param {Roo.form.FieldLabel} this
30520 * @param {String} msg The validation message
30525 * Fires after the field has been validated with no errors.
30526 * @param {Roo.form.FieldLabel} this
30532 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30539 invalidClass : 'has-warning',
30540 validClass : 'has-success',
30541 iconTooltip : 'This field is required',
30542 indicatorpos : 'left',
30544 getAutoCreate : function(){
30547 if (!this.allowBlank) {
30553 cls : 'roo-bootstrap-field-label ' + this.cls,
30558 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30559 tooltip : this.iconTooltip
30568 if(this.indicatorpos == 'right'){
30571 cls : 'roo-bootstrap-field-label ' + this.cls,
30580 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30581 tooltip : this.iconTooltip
30590 initEvents: function()
30592 Roo.bootstrap.Element.superclass.initEvents.call(this);
30594 this.indicator = this.indicatorEl();
30596 if(this.indicator){
30597 this.indicator.removeClass('visible');
30598 this.indicator.addClass('invisible');
30601 Roo.bootstrap.FieldLabel.register(this);
30604 indicatorEl : function()
30606 var indicator = this.el.select('i.roo-required-indicator',true).first();
30617 * Mark this field as valid
30619 markValid : function()
30621 if(this.indicator){
30622 this.indicator.removeClass('visible');
30623 this.indicator.addClass('invisible');
30625 if (Roo.bootstrap.version == 3) {
30626 this.el.removeClass(this.invalidClass);
30627 this.el.addClass(this.validClass);
30629 this.el.removeClass('is-invalid');
30630 this.el.addClass('is-valid');
30634 this.fireEvent('valid', this);
30638 * Mark this field as invalid
30639 * @param {String} msg The validation message
30641 markInvalid : function(msg)
30643 if(this.indicator){
30644 this.indicator.removeClass('invisible');
30645 this.indicator.addClass('visible');
30647 if (Roo.bootstrap.version == 3) {
30648 this.el.removeClass(this.validClass);
30649 this.el.addClass(this.invalidClass);
30651 this.el.removeClass('is-valid');
30652 this.el.addClass('is-invalid');
30656 this.fireEvent('invalid', this, msg);
30662 Roo.apply(Roo.bootstrap.FieldLabel, {
30667 * register a FieldLabel Group
30668 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30670 register : function(label)
30672 if(this.groups.hasOwnProperty(label.target)){
30676 this.groups[label.target] = label;
30680 * fetch a FieldLabel Group based on the target
30681 * @param {string} target
30682 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30684 get: function(target) {
30685 if (typeof(this.groups[target]) == 'undefined') {
30689 return this.groups[target] ;
30698 * page DateSplitField.
30704 * @class Roo.bootstrap.DateSplitField
30705 * @extends Roo.bootstrap.Component
30706 * Bootstrap DateSplitField class
30707 * @cfg {string} fieldLabel - the label associated
30708 * @cfg {Number} labelWidth set the width of label (0-12)
30709 * @cfg {String} labelAlign (top|left)
30710 * @cfg {Boolean} dayAllowBlank (true|false) default false
30711 * @cfg {Boolean} monthAllowBlank (true|false) default false
30712 * @cfg {Boolean} yearAllowBlank (true|false) default false
30713 * @cfg {string} dayPlaceholder
30714 * @cfg {string} monthPlaceholder
30715 * @cfg {string} yearPlaceholder
30716 * @cfg {string} dayFormat default 'd'
30717 * @cfg {string} monthFormat default 'm'
30718 * @cfg {string} yearFormat default 'Y'
30719 * @cfg {Number} labellg set the width of label (1-12)
30720 * @cfg {Number} labelmd set the width of label (1-12)
30721 * @cfg {Number} labelsm set the width of label (1-12)
30722 * @cfg {Number} labelxs set the width of label (1-12)
30726 * Create a new DateSplitField
30727 * @param {Object} config The config object
30730 Roo.bootstrap.DateSplitField = function(config){
30731 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30737 * getting the data of years
30738 * @param {Roo.bootstrap.DateSplitField} this
30739 * @param {Object} years
30744 * getting the data of days
30745 * @param {Roo.bootstrap.DateSplitField} this
30746 * @param {Object} days
30751 * Fires after the field has been marked as invalid.
30752 * @param {Roo.form.Field} this
30753 * @param {String} msg The validation message
30758 * Fires after the field has been validated with no errors.
30759 * @param {Roo.form.Field} this
30765 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30768 labelAlign : 'top',
30770 dayAllowBlank : false,
30771 monthAllowBlank : false,
30772 yearAllowBlank : false,
30773 dayPlaceholder : '',
30774 monthPlaceholder : '',
30775 yearPlaceholder : '',
30779 isFormField : true,
30785 getAutoCreate : function()
30789 cls : 'row roo-date-split-field-group',
30794 cls : 'form-hidden-field roo-date-split-field-group-value',
30800 var labelCls = 'col-md-12';
30801 var contentCls = 'col-md-4';
30803 if(this.fieldLabel){
30807 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30811 html : this.fieldLabel
30816 if(this.labelAlign == 'left'){
30818 if(this.labelWidth > 12){
30819 label.style = "width: " + this.labelWidth + 'px';
30822 if(this.labelWidth < 13 && this.labelmd == 0){
30823 this.labelmd = this.labelWidth;
30826 if(this.labellg > 0){
30827 labelCls = ' col-lg-' + this.labellg;
30828 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30831 if(this.labelmd > 0){
30832 labelCls = ' col-md-' + this.labelmd;
30833 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30836 if(this.labelsm > 0){
30837 labelCls = ' col-sm-' + this.labelsm;
30838 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30841 if(this.labelxs > 0){
30842 labelCls = ' col-xs-' + this.labelxs;
30843 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30847 label.cls += ' ' + labelCls;
30849 cfg.cn.push(label);
30852 Roo.each(['day', 'month', 'year'], function(t){
30855 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30862 inputEl: function ()
30864 return this.el.select('.roo-date-split-field-group-value', true).first();
30867 onRender : function(ct, position)
30871 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30873 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30875 this.dayField = new Roo.bootstrap.ComboBox({
30876 allowBlank : this.dayAllowBlank,
30877 alwaysQuery : true,
30878 displayField : 'value',
30881 forceSelection : true,
30883 placeholder : this.dayPlaceholder,
30884 selectOnFocus : true,
30885 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30886 triggerAction : 'all',
30888 valueField : 'value',
30889 store : new Roo.data.SimpleStore({
30890 data : (function() {
30892 _this.fireEvent('days', _this, days);
30895 fields : [ 'value' ]
30898 select : function (_self, record, index)
30900 _this.setValue(_this.getValue());
30905 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30907 this.monthField = new Roo.bootstrap.MonthField({
30908 after : '<i class=\"fa fa-calendar\"></i>',
30909 allowBlank : this.monthAllowBlank,
30910 placeholder : this.monthPlaceholder,
30913 render : function (_self)
30915 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30916 e.preventDefault();
30920 select : function (_self, oldvalue, newvalue)
30922 _this.setValue(_this.getValue());
30927 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30929 this.yearField = new Roo.bootstrap.ComboBox({
30930 allowBlank : this.yearAllowBlank,
30931 alwaysQuery : true,
30932 displayField : 'value',
30935 forceSelection : true,
30937 placeholder : this.yearPlaceholder,
30938 selectOnFocus : true,
30939 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30940 triggerAction : 'all',
30942 valueField : 'value',
30943 store : new Roo.data.SimpleStore({
30944 data : (function() {
30946 _this.fireEvent('years', _this, years);
30949 fields : [ 'value' ]
30952 select : function (_self, record, index)
30954 _this.setValue(_this.getValue());
30959 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30962 setValue : function(v, format)
30964 this.inputEl.dom.value = v;
30966 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30968 var d = Date.parseDate(v, f);
30975 this.setDay(d.format(this.dayFormat));
30976 this.setMonth(d.format(this.monthFormat));
30977 this.setYear(d.format(this.yearFormat));
30984 setDay : function(v)
30986 this.dayField.setValue(v);
30987 this.inputEl.dom.value = this.getValue();
30992 setMonth : function(v)
30994 this.monthField.setValue(v, true);
30995 this.inputEl.dom.value = this.getValue();
31000 setYear : function(v)
31002 this.yearField.setValue(v);
31003 this.inputEl.dom.value = this.getValue();
31008 getDay : function()
31010 return this.dayField.getValue();
31013 getMonth : function()
31015 return this.monthField.getValue();
31018 getYear : function()
31020 return this.yearField.getValue();
31023 getValue : function()
31025 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31027 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31037 this.inputEl.dom.value = '';
31042 validate : function()
31044 var d = this.dayField.validate();
31045 var m = this.monthField.validate();
31046 var y = this.yearField.validate();
31051 (!this.dayAllowBlank && !d) ||
31052 (!this.monthAllowBlank && !m) ||
31053 (!this.yearAllowBlank && !y)
31058 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31067 this.markInvalid();
31072 markValid : function()
31075 var label = this.el.select('label', true).first();
31076 var icon = this.el.select('i.fa-star', true).first();
31082 this.fireEvent('valid', this);
31086 * Mark this field as invalid
31087 * @param {String} msg The validation message
31089 markInvalid : function(msg)
31092 var label = this.el.select('label', true).first();
31093 var icon = this.el.select('i.fa-star', true).first();
31095 if(label && !icon){
31096 this.el.select('.roo-date-split-field-label', true).createChild({
31098 cls : 'text-danger fa fa-lg fa-star',
31099 tooltip : 'This field is required',
31100 style : 'margin-right:5px;'
31104 this.fireEvent('invalid', this, msg);
31107 clearInvalid : function()
31109 var label = this.el.select('label', true).first();
31110 var icon = this.el.select('i.fa-star', true).first();
31116 this.fireEvent('valid', this);
31119 getName: function()
31129 * http://masonry.desandro.com
31131 * The idea is to render all the bricks based on vertical width...
31133 * The original code extends 'outlayer' - we might need to use that....
31139 * @class Roo.bootstrap.LayoutMasonry
31140 * @extends Roo.bootstrap.Component
31141 * Bootstrap Layout Masonry class
31144 * Create a new Element
31145 * @param {Object} config The config object
31148 Roo.bootstrap.LayoutMasonry = function(config){
31150 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31154 Roo.bootstrap.LayoutMasonry.register(this);
31160 * Fire after layout the items
31161 * @param {Roo.bootstrap.LayoutMasonry} this
31162 * @param {Roo.EventObject} e
31169 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
31172 * @cfg {Boolean} isLayoutInstant = no animation?
31174 isLayoutInstant : false, // needed?
31177 * @cfg {Number} boxWidth width of the columns
31182 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
31187 * @cfg {Number} padWidth padding below box..
31192 * @cfg {Number} gutter gutter width..
31197 * @cfg {Number} maxCols maximum number of columns
31203 * @cfg {Boolean} isAutoInitial defalut true
31205 isAutoInitial : true,
31210 * @cfg {Boolean} isHorizontal defalut false
31212 isHorizontal : false,
31214 currentSize : null,
31220 bricks: null, //CompositeElement
31224 _isLayoutInited : false,
31226 // isAlternative : false, // only use for vertical layout...
31229 * @cfg {Number} alternativePadWidth padding below box..
31231 alternativePadWidth : 50,
31233 selectedBrick : [],
31235 getAutoCreate : function(){
31237 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31241 cls: 'blog-masonary-wrapper ' + this.cls,
31243 cls : 'mas-boxes masonary'
31250 getChildContainer: function( )
31252 if (this.boxesEl) {
31253 return this.boxesEl;
31256 this.boxesEl = this.el.select('.mas-boxes').first();
31258 return this.boxesEl;
31262 initEvents : function()
31266 if(this.isAutoInitial){
31267 Roo.log('hook children rendered');
31268 this.on('childrenrendered', function() {
31269 Roo.log('children rendered');
31275 initial : function()
31277 this.selectedBrick = [];
31279 this.currentSize = this.el.getBox(true);
31281 Roo.EventManager.onWindowResize(this.resize, this);
31283 if(!this.isAutoInitial){
31291 //this.layout.defer(500,this);
31295 resize : function()
31297 var cs = this.el.getBox(true);
31300 this.currentSize.width == cs.width &&
31301 this.currentSize.x == cs.x &&
31302 this.currentSize.height == cs.height &&
31303 this.currentSize.y == cs.y
31305 Roo.log("no change in with or X or Y");
31309 this.currentSize = cs;
31315 layout : function()
31317 this._resetLayout();
31319 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31321 this.layoutItems( isInstant );
31323 this._isLayoutInited = true;
31325 this.fireEvent('layout', this);
31329 _resetLayout : function()
31331 if(this.isHorizontal){
31332 this.horizontalMeasureColumns();
31336 this.verticalMeasureColumns();
31340 verticalMeasureColumns : function()
31342 this.getContainerWidth();
31344 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31345 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31349 var boxWidth = this.boxWidth + this.padWidth;
31351 if(this.containerWidth < this.boxWidth){
31352 boxWidth = this.containerWidth
31355 var containerWidth = this.containerWidth;
31357 var cols = Math.floor(containerWidth / boxWidth);
31359 this.cols = Math.max( cols, 1 );
31361 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31363 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31365 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31367 this.colWidth = boxWidth + avail - this.padWidth;
31369 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31370 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31373 horizontalMeasureColumns : function()
31375 this.getContainerWidth();
31377 var boxWidth = this.boxWidth;
31379 if(this.containerWidth < boxWidth){
31380 boxWidth = this.containerWidth;
31383 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31385 this.el.setHeight(boxWidth);
31389 getContainerWidth : function()
31391 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31394 layoutItems : function( isInstant )
31396 Roo.log(this.bricks);
31398 var items = Roo.apply([], this.bricks);
31400 if(this.isHorizontal){
31401 this._horizontalLayoutItems( items , isInstant );
31405 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31406 // this._verticalAlternativeLayoutItems( items , isInstant );
31410 this._verticalLayoutItems( items , isInstant );
31414 _verticalLayoutItems : function ( items , isInstant)
31416 if ( !items || !items.length ) {
31421 ['xs', 'xs', 'xs', 'tall'],
31422 ['xs', 'xs', 'tall'],
31423 ['xs', 'xs', 'sm'],
31424 ['xs', 'xs', 'xs'],
31430 ['sm', 'xs', 'xs'],
31434 ['tall', 'xs', 'xs', 'xs'],
31435 ['tall', 'xs', 'xs'],
31447 Roo.each(items, function(item, k){
31449 switch (item.size) {
31450 // these layouts take up a full box,
31461 boxes.push([item]);
31484 var filterPattern = function(box, length)
31492 var pattern = box.slice(0, length);
31496 Roo.each(pattern, function(i){
31497 format.push(i.size);
31500 Roo.each(standard, function(s){
31502 if(String(s) != String(format)){
31511 if(!match && length == 1){
31516 filterPattern(box, length - 1);
31520 queue.push(pattern);
31522 box = box.slice(length, box.length);
31524 filterPattern(box, 4);
31530 Roo.each(boxes, function(box, k){
31536 if(box.length == 1){
31541 filterPattern(box, 4);
31545 this._processVerticalLayoutQueue( queue, isInstant );
31549 // _verticalAlternativeLayoutItems : function( items , isInstant )
31551 // if ( !items || !items.length ) {
31555 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31559 _horizontalLayoutItems : function ( items , isInstant)
31561 if ( !items || !items.length || items.length < 3) {
31567 var eItems = items.slice(0, 3);
31569 items = items.slice(3, items.length);
31572 ['xs', 'xs', 'xs', 'wide'],
31573 ['xs', 'xs', 'wide'],
31574 ['xs', 'xs', 'sm'],
31575 ['xs', 'xs', 'xs'],
31581 ['sm', 'xs', 'xs'],
31585 ['wide', 'xs', 'xs', 'xs'],
31586 ['wide', 'xs', 'xs'],
31599 Roo.each(items, function(item, k){
31601 switch (item.size) {
31612 boxes.push([item]);
31636 var filterPattern = function(box, length)
31644 var pattern = box.slice(0, length);
31648 Roo.each(pattern, function(i){
31649 format.push(i.size);
31652 Roo.each(standard, function(s){
31654 if(String(s) != String(format)){
31663 if(!match && length == 1){
31668 filterPattern(box, length - 1);
31672 queue.push(pattern);
31674 box = box.slice(length, box.length);
31676 filterPattern(box, 4);
31682 Roo.each(boxes, function(box, k){
31688 if(box.length == 1){
31693 filterPattern(box, 4);
31700 var pos = this.el.getBox(true);
31704 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31706 var hit_end = false;
31708 Roo.each(queue, function(box){
31712 Roo.each(box, function(b){
31714 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31724 Roo.each(box, function(b){
31726 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31729 mx = Math.max(mx, b.x);
31733 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31737 Roo.each(box, function(b){
31739 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31753 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31756 /** Sets position of item in DOM
31757 * @param {Element} item
31758 * @param {Number} x - horizontal position
31759 * @param {Number} y - vertical position
31760 * @param {Boolean} isInstant - disables transitions
31762 _processVerticalLayoutQueue : function( queue, isInstant )
31764 var pos = this.el.getBox(true);
31769 for (var i = 0; i < this.cols; i++){
31773 Roo.each(queue, function(box, k){
31775 var col = k % this.cols;
31777 Roo.each(box, function(b,kk){
31779 b.el.position('absolute');
31781 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31782 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31784 if(b.size == 'md-left' || b.size == 'md-right'){
31785 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31786 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31789 b.el.setWidth(width);
31790 b.el.setHeight(height);
31792 b.el.select('iframe',true).setSize(width,height);
31796 for (var i = 0; i < this.cols; i++){
31798 if(maxY[i] < maxY[col]){
31803 col = Math.min(col, i);
31807 x = pos.x + col * (this.colWidth + this.padWidth);
31811 var positions = [];
31813 switch (box.length){
31815 positions = this.getVerticalOneBoxColPositions(x, y, box);
31818 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31821 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31824 positions = this.getVerticalFourBoxColPositions(x, y, box);
31830 Roo.each(box, function(b,kk){
31832 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31834 var sz = b.el.getSize();
31836 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31844 for (var i = 0; i < this.cols; i++){
31845 mY = Math.max(mY, maxY[i]);
31848 this.el.setHeight(mY - pos.y);
31852 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31854 // var pos = this.el.getBox(true);
31857 // var maxX = pos.right;
31859 // var maxHeight = 0;
31861 // Roo.each(items, function(item, k){
31865 // item.el.position('absolute');
31867 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31869 // item.el.setWidth(width);
31871 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31873 // item.el.setHeight(height);
31876 // item.el.setXY([x, y], isInstant ? false : true);
31878 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31881 // y = y + height + this.alternativePadWidth;
31883 // maxHeight = maxHeight + height + this.alternativePadWidth;
31887 // this.el.setHeight(maxHeight);
31891 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31893 var pos = this.el.getBox(true);
31898 var maxX = pos.right;
31900 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31902 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31904 Roo.each(queue, function(box, k){
31906 Roo.each(box, function(b, kk){
31908 b.el.position('absolute');
31910 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31911 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31913 if(b.size == 'md-left' || b.size == 'md-right'){
31914 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31915 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31918 b.el.setWidth(width);
31919 b.el.setHeight(height);
31927 var positions = [];
31929 switch (box.length){
31931 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31934 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31937 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31940 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31946 Roo.each(box, function(b,kk){
31948 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31950 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31958 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31960 Roo.each(eItems, function(b,k){
31962 b.size = (k == 0) ? 'sm' : 'xs';
31963 b.x = (k == 0) ? 2 : 1;
31964 b.y = (k == 0) ? 2 : 1;
31966 b.el.position('absolute');
31968 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31970 b.el.setWidth(width);
31972 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31974 b.el.setHeight(height);
31978 var positions = [];
31981 x : maxX - this.unitWidth * 2 - this.gutter,
31986 x : maxX - this.unitWidth,
31987 y : minY + (this.unitWidth + this.gutter) * 2
31991 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31995 Roo.each(eItems, function(b,k){
31997 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32003 getVerticalOneBoxColPositions : function(x, y, box)
32007 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32009 if(box[0].size == 'md-left'){
32013 if(box[0].size == 'md-right'){
32018 x : x + (this.unitWidth + this.gutter) * rand,
32025 getVerticalTwoBoxColPositions : function(x, y, box)
32029 if(box[0].size == 'xs'){
32033 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32037 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32051 x : x + (this.unitWidth + this.gutter) * 2,
32052 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32059 getVerticalThreeBoxColPositions : function(x, y, box)
32063 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32071 x : x + (this.unitWidth + this.gutter) * 1,
32076 x : x + (this.unitWidth + this.gutter) * 2,
32084 if(box[0].size == 'xs' && box[1].size == 'xs'){
32093 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32097 x : x + (this.unitWidth + this.gutter) * 1,
32111 x : x + (this.unitWidth + this.gutter) * 2,
32116 x : x + (this.unitWidth + this.gutter) * 2,
32117 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32124 getVerticalFourBoxColPositions : function(x, y, box)
32128 if(box[0].size == 'xs'){
32137 y : y + (this.unitHeight + this.gutter) * 1
32142 y : y + (this.unitHeight + this.gutter) * 2
32146 x : x + (this.unitWidth + this.gutter) * 1,
32160 x : x + (this.unitWidth + this.gutter) * 2,
32165 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32166 y : y + (this.unitHeight + this.gutter) * 1
32170 x : x + (this.unitWidth + this.gutter) * 2,
32171 y : y + (this.unitWidth + this.gutter) * 2
32178 getHorizontalOneBoxColPositions : function(maxX, minY, box)
32182 if(box[0].size == 'md-left'){
32184 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32191 if(box[0].size == 'md-right'){
32193 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32194 y : minY + (this.unitWidth + this.gutter) * 1
32200 var rand = Math.floor(Math.random() * (4 - box[0].y));
32203 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32204 y : minY + (this.unitWidth + this.gutter) * rand
32211 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32215 if(box[0].size == 'xs'){
32218 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32223 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32224 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32232 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32237 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32238 y : minY + (this.unitWidth + this.gutter) * 2
32245 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32249 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32252 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32257 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32258 y : minY + (this.unitWidth + this.gutter) * 1
32262 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32263 y : minY + (this.unitWidth + this.gutter) * 2
32270 if(box[0].size == 'xs' && box[1].size == 'xs'){
32273 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32278 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32283 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32284 y : minY + (this.unitWidth + this.gutter) * 1
32292 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32297 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32298 y : minY + (this.unitWidth + this.gutter) * 2
32302 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32303 y : minY + (this.unitWidth + this.gutter) * 2
32310 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32314 if(box[0].size == 'xs'){
32317 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32322 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32327 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),
32332 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32333 y : minY + (this.unitWidth + this.gutter) * 1
32341 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32346 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32347 y : minY + (this.unitWidth + this.gutter) * 2
32351 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32352 y : minY + (this.unitWidth + this.gutter) * 2
32356 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),
32357 y : minY + (this.unitWidth + this.gutter) * 2
32365 * remove a Masonry Brick
32366 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32368 removeBrick : function(brick_id)
32374 for (var i = 0; i<this.bricks.length; i++) {
32375 if (this.bricks[i].id == brick_id) {
32376 this.bricks.splice(i,1);
32377 this.el.dom.removeChild(Roo.get(brick_id).dom);
32384 * adds a Masonry Brick
32385 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32387 addBrick : function(cfg)
32389 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32390 //this.register(cn);
32391 cn.parentId = this.id;
32392 cn.render(this.el);
32397 * register a Masonry Brick
32398 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32401 register : function(brick)
32403 this.bricks.push(brick);
32404 brick.masonryId = this.id;
32408 * clear all the Masonry Brick
32410 clearAll : function()
32413 //this.getChildContainer().dom.innerHTML = "";
32414 this.el.dom.innerHTML = '';
32417 getSelected : function()
32419 if (!this.selectedBrick) {
32423 return this.selectedBrick;
32427 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32431 * register a Masonry Layout
32432 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32435 register : function(layout)
32437 this.groups[layout.id] = layout;
32440 * fetch a Masonry Layout based on the masonry layout ID
32441 * @param {string} the masonry layout to add
32442 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32445 get: function(layout_id) {
32446 if (typeof(this.groups[layout_id]) == 'undefined') {
32449 return this.groups[layout_id] ;
32461 * http://masonry.desandro.com
32463 * The idea is to render all the bricks based on vertical width...
32465 * The original code extends 'outlayer' - we might need to use that....
32471 * @class Roo.bootstrap.LayoutMasonryAuto
32472 * @extends Roo.bootstrap.Component
32473 * Bootstrap Layout Masonry class
32476 * Create a new Element
32477 * @param {Object} config The config object
32480 Roo.bootstrap.LayoutMasonryAuto = function(config){
32481 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32484 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32487 * @cfg {Boolean} isFitWidth - resize the width..
32489 isFitWidth : false, // options..
32491 * @cfg {Boolean} isOriginLeft = left align?
32493 isOriginLeft : true,
32495 * @cfg {Boolean} isOriginTop = top align?
32497 isOriginTop : false,
32499 * @cfg {Boolean} isLayoutInstant = no animation?
32501 isLayoutInstant : false, // needed?
32503 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32505 isResizingContainer : true,
32507 * @cfg {Number} columnWidth width of the columns
32513 * @cfg {Number} maxCols maximum number of columns
32518 * @cfg {Number} padHeight padding below box..
32524 * @cfg {Boolean} isAutoInitial defalut true
32527 isAutoInitial : true,
32533 initialColumnWidth : 0,
32534 currentSize : null,
32536 colYs : null, // array.
32543 bricks: null, //CompositeElement
32544 cols : 0, // array?
32545 // element : null, // wrapped now this.el
32546 _isLayoutInited : null,
32549 getAutoCreate : function(){
32553 cls: 'blog-masonary-wrapper ' + this.cls,
32555 cls : 'mas-boxes masonary'
32562 getChildContainer: function( )
32564 if (this.boxesEl) {
32565 return this.boxesEl;
32568 this.boxesEl = this.el.select('.mas-boxes').first();
32570 return this.boxesEl;
32574 initEvents : function()
32578 if(this.isAutoInitial){
32579 Roo.log('hook children rendered');
32580 this.on('childrenrendered', function() {
32581 Roo.log('children rendered');
32588 initial : function()
32590 this.reloadItems();
32592 this.currentSize = this.el.getBox(true);
32594 /// was window resize... - let's see if this works..
32595 Roo.EventManager.onWindowResize(this.resize, this);
32597 if(!this.isAutoInitial){
32602 this.layout.defer(500,this);
32605 reloadItems: function()
32607 this.bricks = this.el.select('.masonry-brick', true);
32609 this.bricks.each(function(b) {
32610 //Roo.log(b.getSize());
32611 if (!b.attr('originalwidth')) {
32612 b.attr('originalwidth', b.getSize().width);
32617 Roo.log(this.bricks.elements.length);
32620 resize : function()
32623 var cs = this.el.getBox(true);
32625 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32626 Roo.log("no change in with or X");
32629 this.currentSize = cs;
32633 layout : function()
32636 this._resetLayout();
32637 //this._manageStamps();
32639 // don't animate first layout
32640 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32641 this.layoutItems( isInstant );
32643 // flag for initalized
32644 this._isLayoutInited = true;
32647 layoutItems : function( isInstant )
32649 //var items = this._getItemsForLayout( this.items );
32650 // original code supports filtering layout items.. we just ignore it..
32652 this._layoutItems( this.bricks , isInstant );
32654 this._postLayout();
32656 _layoutItems : function ( items , isInstant)
32658 //this.fireEvent( 'layout', this, items );
32661 if ( !items || !items.elements.length ) {
32662 // no items, emit event with empty array
32667 items.each(function(item) {
32668 Roo.log("layout item");
32670 // get x/y object from method
32671 var position = this._getItemLayoutPosition( item );
32673 position.item = item;
32674 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32675 queue.push( position );
32678 this._processLayoutQueue( queue );
32680 /** Sets position of item in DOM
32681 * @param {Element} item
32682 * @param {Number} x - horizontal position
32683 * @param {Number} y - vertical position
32684 * @param {Boolean} isInstant - disables transitions
32686 _processLayoutQueue : function( queue )
32688 for ( var i=0, len = queue.length; i < len; i++ ) {
32689 var obj = queue[i];
32690 obj.item.position('absolute');
32691 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32697 * Any logic you want to do after each layout,
32698 * i.e. size the container
32700 _postLayout : function()
32702 this.resizeContainer();
32705 resizeContainer : function()
32707 if ( !this.isResizingContainer ) {
32710 var size = this._getContainerSize();
32712 this.el.setSize(size.width,size.height);
32713 this.boxesEl.setSize(size.width,size.height);
32719 _resetLayout : function()
32721 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32722 this.colWidth = this.el.getWidth();
32723 //this.gutter = this.el.getWidth();
32725 this.measureColumns();
32731 this.colYs.push( 0 );
32737 measureColumns : function()
32739 this.getContainerWidth();
32740 // if columnWidth is 0, default to outerWidth of first item
32741 if ( !this.columnWidth ) {
32742 var firstItem = this.bricks.first();
32743 Roo.log(firstItem);
32744 this.columnWidth = this.containerWidth;
32745 if (firstItem && firstItem.attr('originalwidth') ) {
32746 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32748 // columnWidth fall back to item of first element
32749 Roo.log("set column width?");
32750 this.initialColumnWidth = this.columnWidth ;
32752 // if first elem has no width, default to size of container
32757 if (this.initialColumnWidth) {
32758 this.columnWidth = this.initialColumnWidth;
32763 // column width is fixed at the top - however if container width get's smaller we should
32766 // this bit calcs how man columns..
32768 var columnWidth = this.columnWidth += this.gutter;
32770 // calculate columns
32771 var containerWidth = this.containerWidth + this.gutter;
32773 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32774 // fix rounding errors, typically with gutters
32775 var excess = columnWidth - containerWidth % columnWidth;
32778 // if overshoot is less than a pixel, round up, otherwise floor it
32779 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32780 cols = Math[ mathMethod ]( cols );
32781 this.cols = Math.max( cols, 1 );
32782 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32784 // padding positioning..
32785 var totalColWidth = this.cols * this.columnWidth;
32786 var padavail = this.containerWidth - totalColWidth;
32787 // so for 2 columns - we need 3 'pads'
32789 var padNeeded = (1+this.cols) * this.padWidth;
32791 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32793 this.columnWidth += padExtra
32794 //this.padWidth = Math.floor(padavail / ( this.cols));
32796 // adjust colum width so that padding is fixed??
32798 // we have 3 columns ... total = width * 3
32799 // we have X left over... that should be used by
32801 //if (this.expandC) {
32809 getContainerWidth : function()
32811 /* // container is parent if fit width
32812 var container = this.isFitWidth ? this.element.parentNode : this.element;
32813 // check that this.size and size are there
32814 // IE8 triggers resize on body size change, so they might not be
32816 var size = getSize( container ); //FIXME
32817 this.containerWidth = size && size.innerWidth; //FIXME
32820 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32824 _getItemLayoutPosition : function( item ) // what is item?
32826 // we resize the item to our columnWidth..
32828 item.setWidth(this.columnWidth);
32829 item.autoBoxAdjust = false;
32831 var sz = item.getSize();
32833 // how many columns does this brick span
32834 var remainder = this.containerWidth % this.columnWidth;
32836 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32837 // round if off by 1 pixel, otherwise use ceil
32838 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32839 colSpan = Math.min( colSpan, this.cols );
32841 // normally this should be '1' as we dont' currently allow multi width columns..
32843 var colGroup = this._getColGroup( colSpan );
32844 // get the minimum Y value from the columns
32845 var minimumY = Math.min.apply( Math, colGroup );
32846 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32848 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32850 // position the brick
32852 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32853 y: this.currentSize.y + minimumY + this.padHeight
32857 // apply setHeight to necessary columns
32858 var setHeight = minimumY + sz.height + this.padHeight;
32859 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32861 var setSpan = this.cols + 1 - colGroup.length;
32862 for ( var i = 0; i < setSpan; i++ ) {
32863 this.colYs[ shortColIndex + i ] = setHeight ;
32870 * @param {Number} colSpan - number of columns the element spans
32871 * @returns {Array} colGroup
32873 _getColGroup : function( colSpan )
32875 if ( colSpan < 2 ) {
32876 // if brick spans only one column, use all the column Ys
32881 // how many different places could this brick fit horizontally
32882 var groupCount = this.cols + 1 - colSpan;
32883 // for each group potential horizontal position
32884 for ( var i = 0; i < groupCount; i++ ) {
32885 // make an array of colY values for that one group
32886 var groupColYs = this.colYs.slice( i, i + colSpan );
32887 // and get the max value of the array
32888 colGroup[i] = Math.max.apply( Math, groupColYs );
32893 _manageStamp : function( stamp )
32895 var stampSize = stamp.getSize();
32896 var offset = stamp.getBox();
32897 // get the columns that this stamp affects
32898 var firstX = this.isOriginLeft ? offset.x : offset.right;
32899 var lastX = firstX + stampSize.width;
32900 var firstCol = Math.floor( firstX / this.columnWidth );
32901 firstCol = Math.max( 0, firstCol );
32903 var lastCol = Math.floor( lastX / this.columnWidth );
32904 // lastCol should not go over if multiple of columnWidth #425
32905 lastCol -= lastX % this.columnWidth ? 0 : 1;
32906 lastCol = Math.min( this.cols - 1, lastCol );
32908 // set colYs to bottom of the stamp
32909 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32912 for ( var i = firstCol; i <= lastCol; i++ ) {
32913 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32918 _getContainerSize : function()
32920 this.maxY = Math.max.apply( Math, this.colYs );
32925 if ( this.isFitWidth ) {
32926 size.width = this._getContainerFitWidth();
32932 _getContainerFitWidth : function()
32934 var unusedCols = 0;
32935 // count unused columns
32938 if ( this.colYs[i] !== 0 ) {
32943 // fit container to columns that have been used
32944 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32947 needsResizeLayout : function()
32949 var previousWidth = this.containerWidth;
32950 this.getContainerWidth();
32951 return previousWidth !== this.containerWidth;
32966 * @class Roo.bootstrap.MasonryBrick
32967 * @extends Roo.bootstrap.Component
32968 * Bootstrap MasonryBrick class
32971 * Create a new MasonryBrick
32972 * @param {Object} config The config object
32975 Roo.bootstrap.MasonryBrick = function(config){
32977 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32979 Roo.bootstrap.MasonryBrick.register(this);
32985 * When a MasonryBrick is clcik
32986 * @param {Roo.bootstrap.MasonryBrick} this
32987 * @param {Roo.EventObject} e
32993 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32996 * @cfg {String} title
33000 * @cfg {String} html
33004 * @cfg {String} bgimage
33008 * @cfg {String} videourl
33012 * @cfg {String} cls
33016 * @cfg {String} href
33020 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33025 * @cfg {String} placetitle (center|bottom)
33030 * @cfg {Boolean} isFitContainer defalut true
33032 isFitContainer : true,
33035 * @cfg {Boolean} preventDefault defalut false
33037 preventDefault : false,
33040 * @cfg {Boolean} inverse defalut false
33042 maskInverse : false,
33044 getAutoCreate : function()
33046 if(!this.isFitContainer){
33047 return this.getSplitAutoCreate();
33050 var cls = 'masonry-brick masonry-brick-full';
33052 if(this.href.length){
33053 cls += ' masonry-brick-link';
33056 if(this.bgimage.length){
33057 cls += ' masonry-brick-image';
33060 if(this.maskInverse){
33061 cls += ' mask-inverse';
33064 if(!this.html.length && !this.maskInverse && !this.videourl.length){
33065 cls += ' enable-mask';
33069 cls += ' masonry-' + this.size + '-brick';
33072 if(this.placetitle.length){
33074 switch (this.placetitle) {
33076 cls += ' masonry-center-title';
33079 cls += ' masonry-bottom-title';
33086 if(!this.html.length && !this.bgimage.length){
33087 cls += ' masonry-center-title';
33090 if(!this.html.length && this.bgimage.length){
33091 cls += ' masonry-bottom-title';
33096 cls += ' ' + this.cls;
33100 tag: (this.href.length) ? 'a' : 'div',
33105 cls: 'masonry-brick-mask'
33109 cls: 'masonry-brick-paragraph',
33115 if(this.href.length){
33116 cfg.href = this.href;
33119 var cn = cfg.cn[1].cn;
33121 if(this.title.length){
33124 cls: 'masonry-brick-title',
33129 if(this.html.length){
33132 cls: 'masonry-brick-text',
33137 if (!this.title.length && !this.html.length) {
33138 cfg.cn[1].cls += ' hide';
33141 if(this.bgimage.length){
33144 cls: 'masonry-brick-image-view',
33149 if(this.videourl.length){
33150 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33151 // youtube support only?
33154 cls: 'masonry-brick-image-view',
33157 allowfullscreen : true
33165 getSplitAutoCreate : function()
33167 var cls = 'masonry-brick masonry-brick-split';
33169 if(this.href.length){
33170 cls += ' masonry-brick-link';
33173 if(this.bgimage.length){
33174 cls += ' masonry-brick-image';
33178 cls += ' masonry-' + this.size + '-brick';
33181 switch (this.placetitle) {
33183 cls += ' masonry-center-title';
33186 cls += ' masonry-bottom-title';
33189 if(!this.bgimage.length){
33190 cls += ' masonry-center-title';
33193 if(this.bgimage.length){
33194 cls += ' masonry-bottom-title';
33200 cls += ' ' + this.cls;
33204 tag: (this.href.length) ? 'a' : 'div',
33209 cls: 'masonry-brick-split-head',
33213 cls: 'masonry-brick-paragraph',
33220 cls: 'masonry-brick-split-body',
33226 if(this.href.length){
33227 cfg.href = this.href;
33230 if(this.title.length){
33231 cfg.cn[0].cn[0].cn.push({
33233 cls: 'masonry-brick-title',
33238 if(this.html.length){
33239 cfg.cn[1].cn.push({
33241 cls: 'masonry-brick-text',
33246 if(this.bgimage.length){
33247 cfg.cn[0].cn.push({
33249 cls: 'masonry-brick-image-view',
33254 if(this.videourl.length){
33255 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33256 // youtube support only?
33257 cfg.cn[0].cn.cn.push({
33259 cls: 'masonry-brick-image-view',
33262 allowfullscreen : true
33269 initEvents: function()
33271 switch (this.size) {
33304 this.el.on('touchstart', this.onTouchStart, this);
33305 this.el.on('touchmove', this.onTouchMove, this);
33306 this.el.on('touchend', this.onTouchEnd, this);
33307 this.el.on('contextmenu', this.onContextMenu, this);
33309 this.el.on('mouseenter' ,this.enter, this);
33310 this.el.on('mouseleave', this.leave, this);
33311 this.el.on('click', this.onClick, this);
33314 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33315 this.parent().bricks.push(this);
33320 onClick: function(e, el)
33322 var time = this.endTimer - this.startTimer;
33323 // Roo.log(e.preventDefault());
33326 e.preventDefault();
33331 if(!this.preventDefault){
33335 e.preventDefault();
33337 if (this.activeClass != '') {
33338 this.selectBrick();
33341 this.fireEvent('click', this, e);
33344 enter: function(e, el)
33346 e.preventDefault();
33348 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33352 if(this.bgimage.length && this.html.length){
33353 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33357 leave: function(e, el)
33359 e.preventDefault();
33361 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33365 if(this.bgimage.length && this.html.length){
33366 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33370 onTouchStart: function(e, el)
33372 // e.preventDefault();
33374 this.touchmoved = false;
33376 if(!this.isFitContainer){
33380 if(!this.bgimage.length || !this.html.length){
33384 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33386 this.timer = new Date().getTime();
33390 onTouchMove: function(e, el)
33392 this.touchmoved = true;
33395 onContextMenu : function(e,el)
33397 e.preventDefault();
33398 e.stopPropagation();
33402 onTouchEnd: function(e, el)
33404 // e.preventDefault();
33406 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33413 if(!this.bgimage.length || !this.html.length){
33415 if(this.href.length){
33416 window.location.href = this.href;
33422 if(!this.isFitContainer){
33426 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33428 window.location.href = this.href;
33431 //selection on single brick only
33432 selectBrick : function() {
33434 if (!this.parentId) {
33438 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33439 var index = m.selectedBrick.indexOf(this.id);
33442 m.selectedBrick.splice(index,1);
33443 this.el.removeClass(this.activeClass);
33447 for(var i = 0; i < m.selectedBrick.length; i++) {
33448 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33449 b.el.removeClass(b.activeClass);
33452 m.selectedBrick = [];
33454 m.selectedBrick.push(this.id);
33455 this.el.addClass(this.activeClass);
33459 isSelected : function(){
33460 return this.el.hasClass(this.activeClass);
33465 Roo.apply(Roo.bootstrap.MasonryBrick, {
33468 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33470 * register a Masonry Brick
33471 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33474 register : function(brick)
33476 //this.groups[brick.id] = brick;
33477 this.groups.add(brick.id, brick);
33480 * fetch a masonry brick based on the masonry brick ID
33481 * @param {string} the masonry brick to add
33482 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33485 get: function(brick_id)
33487 // if (typeof(this.groups[brick_id]) == 'undefined') {
33490 // return this.groups[brick_id] ;
33492 if(this.groups.key(brick_id)) {
33493 return this.groups.key(brick_id);
33511 * @class Roo.bootstrap.Brick
33512 * @extends Roo.bootstrap.Component
33513 * Bootstrap Brick class
33516 * Create a new Brick
33517 * @param {Object} config The config object
33520 Roo.bootstrap.Brick = function(config){
33521 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33527 * When a Brick is click
33528 * @param {Roo.bootstrap.Brick} this
33529 * @param {Roo.EventObject} e
33535 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33538 * @cfg {String} title
33542 * @cfg {String} html
33546 * @cfg {String} bgimage
33550 * @cfg {String} cls
33554 * @cfg {String} href
33558 * @cfg {String} video
33562 * @cfg {Boolean} square
33566 getAutoCreate : function()
33568 var cls = 'roo-brick';
33570 if(this.href.length){
33571 cls += ' roo-brick-link';
33574 if(this.bgimage.length){
33575 cls += ' roo-brick-image';
33578 if(!this.html.length && !this.bgimage.length){
33579 cls += ' roo-brick-center-title';
33582 if(!this.html.length && this.bgimage.length){
33583 cls += ' roo-brick-bottom-title';
33587 cls += ' ' + this.cls;
33591 tag: (this.href.length) ? 'a' : 'div',
33596 cls: 'roo-brick-paragraph',
33602 if(this.href.length){
33603 cfg.href = this.href;
33606 var cn = cfg.cn[0].cn;
33608 if(this.title.length){
33611 cls: 'roo-brick-title',
33616 if(this.html.length){
33619 cls: 'roo-brick-text',
33626 if(this.bgimage.length){
33629 cls: 'roo-brick-image-view',
33637 initEvents: function()
33639 if(this.title.length || this.html.length){
33640 this.el.on('mouseenter' ,this.enter, this);
33641 this.el.on('mouseleave', this.leave, this);
33644 Roo.EventManager.onWindowResize(this.resize, this);
33646 if(this.bgimage.length){
33647 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33648 this.imageEl.on('load', this.onImageLoad, this);
33655 onImageLoad : function()
33660 resize : function()
33662 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33664 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33666 if(this.bgimage.length){
33667 var image = this.el.select('.roo-brick-image-view', true).first();
33669 image.setWidth(paragraph.getWidth());
33672 image.setHeight(paragraph.getWidth());
33675 this.el.setHeight(image.getHeight());
33676 paragraph.setHeight(image.getHeight());
33682 enter: function(e, el)
33684 e.preventDefault();
33686 if(this.bgimage.length){
33687 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33688 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33692 leave: function(e, el)
33694 e.preventDefault();
33696 if(this.bgimage.length){
33697 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33698 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33713 * @class Roo.bootstrap.NumberField
33714 * @extends Roo.bootstrap.Input
33715 * Bootstrap NumberField class
33721 * Create a new NumberField
33722 * @param {Object} config The config object
33725 Roo.bootstrap.NumberField = function(config){
33726 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33729 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33732 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33734 allowDecimals : true,
33736 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33738 decimalSeparator : ".",
33740 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33742 decimalPrecision : 2,
33744 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33746 allowNegative : true,
33749 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33753 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33755 minValue : Number.NEGATIVE_INFINITY,
33757 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33759 maxValue : Number.MAX_VALUE,
33761 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33763 minText : "The minimum value for this field is {0}",
33765 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33767 maxText : "The maximum value for this field is {0}",
33769 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33770 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33772 nanText : "{0} is not a valid number",
33774 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33776 thousandsDelimiter : false,
33778 * @cfg {String} valueAlign alignment of value
33780 valueAlign : "left",
33782 getAutoCreate : function()
33784 var hiddenInput = {
33788 cls: 'hidden-number-input'
33792 hiddenInput.name = this.name;
33797 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33799 this.name = hiddenInput.name;
33801 if(cfg.cn.length > 0) {
33802 cfg.cn.push(hiddenInput);
33809 initEvents : function()
33811 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33813 var allowed = "0123456789";
33815 if(this.allowDecimals){
33816 allowed += this.decimalSeparator;
33819 if(this.allowNegative){
33823 if(this.thousandsDelimiter) {
33827 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33829 var keyPress = function(e){
33831 var k = e.getKey();
33833 var c = e.getCharCode();
33836 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33837 allowed.indexOf(String.fromCharCode(c)) === -1
33843 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33847 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33852 this.el.on("keypress", keyPress, this);
33855 validateValue : function(value)
33858 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33862 var num = this.parseValue(value);
33865 this.markInvalid(String.format(this.nanText, value));
33869 if(num < this.minValue){
33870 this.markInvalid(String.format(this.minText, this.minValue));
33874 if(num > this.maxValue){
33875 this.markInvalid(String.format(this.maxText, this.maxValue));
33882 getValue : function()
33884 var v = this.hiddenEl().getValue();
33886 return this.fixPrecision(this.parseValue(v));
33889 parseValue : function(value)
33891 if(this.thousandsDelimiter) {
33893 r = new RegExp(",", "g");
33894 value = value.replace(r, "");
33897 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33898 return isNaN(value) ? '' : value;
33901 fixPrecision : function(value)
33903 if(this.thousandsDelimiter) {
33905 r = new RegExp(",", "g");
33906 value = value.replace(r, "");
33909 var nan = isNaN(value);
33911 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33912 return nan ? '' : value;
33914 return parseFloat(value).toFixed(this.decimalPrecision);
33917 setValue : function(v)
33919 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33925 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33927 this.inputEl().dom.value = (v == '') ? '' :
33928 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33930 if(!this.allowZero && v === '0') {
33931 this.hiddenEl().dom.value = '';
33932 this.inputEl().dom.value = '';
33939 decimalPrecisionFcn : function(v)
33941 return Math.floor(v);
33944 beforeBlur : function()
33946 var v = this.parseValue(this.getRawValue());
33948 if(v || v === 0 || v === ''){
33953 hiddenEl : function()
33955 return this.el.select('input.hidden-number-input',true).first();
33967 * @class Roo.bootstrap.DocumentSlider
33968 * @extends Roo.bootstrap.Component
33969 * Bootstrap DocumentSlider class
33972 * Create a new DocumentViewer
33973 * @param {Object} config The config object
33976 Roo.bootstrap.DocumentSlider = function(config){
33977 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33984 * Fire after initEvent
33985 * @param {Roo.bootstrap.DocumentSlider} this
33990 * Fire after update
33991 * @param {Roo.bootstrap.DocumentSlider} this
33997 * @param {Roo.bootstrap.DocumentSlider} this
34003 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
34009 getAutoCreate : function()
34013 cls : 'roo-document-slider',
34017 cls : 'roo-document-slider-header',
34021 cls : 'roo-document-slider-header-title'
34027 cls : 'roo-document-slider-body',
34031 cls : 'roo-document-slider-prev',
34035 cls : 'fa fa-chevron-left'
34041 cls : 'roo-document-slider-thumb',
34045 cls : 'roo-document-slider-image'
34051 cls : 'roo-document-slider-next',
34055 cls : 'fa fa-chevron-right'
34067 initEvents : function()
34069 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34070 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34072 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34073 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34075 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34076 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34078 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34079 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34081 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34082 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34084 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34085 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34087 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34088 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34090 this.thumbEl.on('click', this.onClick, this);
34092 this.prevIndicator.on('click', this.prev, this);
34094 this.nextIndicator.on('click', this.next, this);
34098 initial : function()
34100 if(this.files.length){
34101 this.indicator = 1;
34105 this.fireEvent('initial', this);
34108 update : function()
34110 this.imageEl.attr('src', this.files[this.indicator - 1]);
34112 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34114 this.prevIndicator.show();
34116 if(this.indicator == 1){
34117 this.prevIndicator.hide();
34120 this.nextIndicator.show();
34122 if(this.indicator == this.files.length){
34123 this.nextIndicator.hide();
34126 this.thumbEl.scrollTo('top');
34128 this.fireEvent('update', this);
34131 onClick : function(e)
34133 e.preventDefault();
34135 this.fireEvent('click', this);
34140 e.preventDefault();
34142 this.indicator = Math.max(1, this.indicator - 1);
34149 e.preventDefault();
34151 this.indicator = Math.min(this.files.length, this.indicator + 1);
34165 * @class Roo.bootstrap.RadioSet
34166 * @extends Roo.bootstrap.Input
34167 * Bootstrap RadioSet class
34168 * @cfg {String} indicatorpos (left|right) default left
34169 * @cfg {Boolean} inline (true|false) inline the element (default true)
34170 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34172 * Create a new RadioSet
34173 * @param {Object} config The config object
34176 Roo.bootstrap.RadioSet = function(config){
34178 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34182 Roo.bootstrap.RadioSet.register(this);
34187 * Fires when the element is checked or unchecked.
34188 * @param {Roo.bootstrap.RadioSet} this This radio
34189 * @param {Roo.bootstrap.Radio} item The checked item
34194 * Fires when the element is click.
34195 * @param {Roo.bootstrap.RadioSet} this This radio set
34196 * @param {Roo.bootstrap.Radio} item The checked item
34197 * @param {Roo.EventObject} e The event object
34204 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
34212 indicatorpos : 'left',
34214 getAutoCreate : function()
34218 cls : 'roo-radio-set-label',
34222 html : this.fieldLabel
34226 if (Roo.bootstrap.version == 3) {
34229 if(this.indicatorpos == 'left'){
34232 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34233 tooltip : 'This field is required'
34238 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34239 tooltip : 'This field is required'
34245 cls : 'roo-radio-set-items'
34248 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34250 if (align === 'left' && this.fieldLabel.length) {
34253 cls : "roo-radio-set-right",
34259 if(this.labelWidth > 12){
34260 label.style = "width: " + this.labelWidth + 'px';
34263 if(this.labelWidth < 13 && this.labelmd == 0){
34264 this.labelmd = this.labelWidth;
34267 if(this.labellg > 0){
34268 label.cls += ' col-lg-' + this.labellg;
34269 items.cls += ' col-lg-' + (12 - this.labellg);
34272 if(this.labelmd > 0){
34273 label.cls += ' col-md-' + this.labelmd;
34274 items.cls += ' col-md-' + (12 - this.labelmd);
34277 if(this.labelsm > 0){
34278 label.cls += ' col-sm-' + this.labelsm;
34279 items.cls += ' col-sm-' + (12 - this.labelsm);
34282 if(this.labelxs > 0){
34283 label.cls += ' col-xs-' + this.labelxs;
34284 items.cls += ' col-xs-' + (12 - this.labelxs);
34290 cls : 'roo-radio-set',
34294 cls : 'roo-radio-set-input',
34297 value : this.value ? this.value : ''
34304 if(this.weight.length){
34305 cfg.cls += ' roo-radio-' + this.weight;
34309 cfg.cls += ' roo-radio-set-inline';
34313 ['xs','sm','md','lg'].map(function(size){
34314 if (settings[size]) {
34315 cfg.cls += ' col-' + size + '-' + settings[size];
34323 initEvents : function()
34325 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34326 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34328 if(!this.fieldLabel.length){
34329 this.labelEl.hide();
34332 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34333 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34335 this.indicator = this.indicatorEl();
34337 if(this.indicator){
34338 this.indicator.addClass('invisible');
34341 this.originalValue = this.getValue();
34345 inputEl: function ()
34347 return this.el.select('.roo-radio-set-input', true).first();
34350 getChildContainer : function()
34352 return this.itemsEl;
34355 register : function(item)
34357 this.radioes.push(item);
34361 validate : function()
34363 if(this.getVisibilityEl().hasClass('hidden')){
34369 Roo.each(this.radioes, function(i){
34378 if(this.allowBlank) {
34382 if(this.disabled || valid){
34387 this.markInvalid();
34392 markValid : function()
34394 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34395 this.indicatorEl().removeClass('visible');
34396 this.indicatorEl().addClass('invisible');
34400 if (Roo.bootstrap.version == 3) {
34401 this.el.removeClass([this.invalidClass, this.validClass]);
34402 this.el.addClass(this.validClass);
34404 this.el.removeClass(['is-invalid','is-valid']);
34405 this.el.addClass(['is-valid']);
34407 this.fireEvent('valid', this);
34410 markInvalid : function(msg)
34412 if(this.allowBlank || this.disabled){
34416 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34417 this.indicatorEl().removeClass('invisible');
34418 this.indicatorEl().addClass('visible');
34420 if (Roo.bootstrap.version == 3) {
34421 this.el.removeClass([this.invalidClass, this.validClass]);
34422 this.el.addClass(this.invalidClass);
34424 this.el.removeClass(['is-invalid','is-valid']);
34425 this.el.addClass(['is-invalid']);
34428 this.fireEvent('invalid', this, msg);
34432 setValue : function(v, suppressEvent)
34434 if(this.value === v){
34441 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34444 Roo.each(this.radioes, function(i){
34446 i.el.removeClass('checked');
34449 Roo.each(this.radioes, function(i){
34451 if(i.value === v || i.value.toString() === v.toString()){
34453 i.el.addClass('checked');
34455 if(suppressEvent !== true){
34456 this.fireEvent('check', this, i);
34467 clearInvalid : function(){
34469 if(!this.el || this.preventMark){
34473 this.el.removeClass([this.invalidClass]);
34475 this.fireEvent('valid', this);
34480 Roo.apply(Roo.bootstrap.RadioSet, {
34484 register : function(set)
34486 this.groups[set.name] = set;
34489 get: function(name)
34491 if (typeof(this.groups[name]) == 'undefined') {
34495 return this.groups[name] ;
34501 * Ext JS Library 1.1.1
34502 * Copyright(c) 2006-2007, Ext JS, LLC.
34504 * Originally Released Under LGPL - original licence link has changed is not relivant.
34507 * <script type="text/javascript">
34512 * @class Roo.bootstrap.SplitBar
34513 * @extends Roo.util.Observable
34514 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34518 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34519 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34520 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34521 split.minSize = 100;
34522 split.maxSize = 600;
34523 split.animate = true;
34524 split.on('moved', splitterMoved);
34527 * Create a new SplitBar
34528 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34529 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34530 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34531 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34532 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34533 position of the SplitBar).
34535 Roo.bootstrap.SplitBar = function(cfg){
34540 // dragElement : elm
34541 // resizingElement: el,
34543 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34544 // placement : Roo.bootstrap.SplitBar.LEFT ,
34545 // existingProxy ???
34548 this.el = Roo.get(cfg.dragElement, true);
34549 this.el.dom.unselectable = "on";
34551 this.resizingEl = Roo.get(cfg.resizingElement, true);
34555 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34556 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34559 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34562 * The minimum size of the resizing element. (Defaults to 0)
34568 * The maximum size of the resizing element. (Defaults to 2000)
34571 this.maxSize = 2000;
34574 * Whether to animate the transition to the new size
34577 this.animate = false;
34580 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34583 this.useShim = false;
34588 if(!cfg.existingProxy){
34590 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34592 this.proxy = Roo.get(cfg.existingProxy).dom;
34595 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34598 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34601 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34604 this.dragSpecs = {};
34607 * @private The adapter to use to positon and resize elements
34609 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34610 this.adapter.init(this);
34612 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34614 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34615 this.el.addClass("roo-splitbar-h");
34618 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34619 this.el.addClass("roo-splitbar-v");
34625 * Fires when the splitter is moved (alias for {@link #event-moved})
34626 * @param {Roo.bootstrap.SplitBar} this
34627 * @param {Number} newSize the new width or height
34632 * Fires when the splitter is moved
34633 * @param {Roo.bootstrap.SplitBar} this
34634 * @param {Number} newSize the new width or height
34638 * @event beforeresize
34639 * Fires before the splitter is dragged
34640 * @param {Roo.bootstrap.SplitBar} this
34642 "beforeresize" : true,
34644 "beforeapply" : true
34647 Roo.util.Observable.call(this);
34650 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34651 onStartProxyDrag : function(x, y){
34652 this.fireEvent("beforeresize", this);
34654 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34656 o.enableDisplayMode("block");
34657 // all splitbars share the same overlay
34658 Roo.bootstrap.SplitBar.prototype.overlay = o;
34660 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34661 this.overlay.show();
34662 Roo.get(this.proxy).setDisplayed("block");
34663 var size = this.adapter.getElementSize(this);
34664 this.activeMinSize = this.getMinimumSize();;
34665 this.activeMaxSize = this.getMaximumSize();;
34666 var c1 = size - this.activeMinSize;
34667 var c2 = Math.max(this.activeMaxSize - size, 0);
34668 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34669 this.dd.resetConstraints();
34670 this.dd.setXConstraint(
34671 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34672 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34674 this.dd.setYConstraint(0, 0);
34676 this.dd.resetConstraints();
34677 this.dd.setXConstraint(0, 0);
34678 this.dd.setYConstraint(
34679 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34680 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34683 this.dragSpecs.startSize = size;
34684 this.dragSpecs.startPoint = [x, y];
34685 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34689 * @private Called after the drag operation by the DDProxy
34691 onEndProxyDrag : function(e){
34692 Roo.get(this.proxy).setDisplayed(false);
34693 var endPoint = Roo.lib.Event.getXY(e);
34695 this.overlay.hide();
34698 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34699 newSize = this.dragSpecs.startSize +
34700 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34701 endPoint[0] - this.dragSpecs.startPoint[0] :
34702 this.dragSpecs.startPoint[0] - endPoint[0]
34705 newSize = this.dragSpecs.startSize +
34706 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34707 endPoint[1] - this.dragSpecs.startPoint[1] :
34708 this.dragSpecs.startPoint[1] - endPoint[1]
34711 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34712 if(newSize != this.dragSpecs.startSize){
34713 if(this.fireEvent('beforeapply', this, newSize) !== false){
34714 this.adapter.setElementSize(this, newSize);
34715 this.fireEvent("moved", this, newSize);
34716 this.fireEvent("resize", this, newSize);
34722 * Get the adapter this SplitBar uses
34723 * @return The adapter object
34725 getAdapter : function(){
34726 return this.adapter;
34730 * Set the adapter this SplitBar uses
34731 * @param {Object} adapter A SplitBar adapter object
34733 setAdapter : function(adapter){
34734 this.adapter = adapter;
34735 this.adapter.init(this);
34739 * Gets the minimum size for the resizing element
34740 * @return {Number} The minimum size
34742 getMinimumSize : function(){
34743 return this.minSize;
34747 * Sets the minimum size for the resizing element
34748 * @param {Number} minSize The minimum size
34750 setMinimumSize : function(minSize){
34751 this.minSize = minSize;
34755 * Gets the maximum size for the resizing element
34756 * @return {Number} The maximum size
34758 getMaximumSize : function(){
34759 return this.maxSize;
34763 * Sets the maximum size for the resizing element
34764 * @param {Number} maxSize The maximum size
34766 setMaximumSize : function(maxSize){
34767 this.maxSize = maxSize;
34771 * Sets the initialize size for the resizing element
34772 * @param {Number} size The initial size
34774 setCurrentSize : function(size){
34775 var oldAnimate = this.animate;
34776 this.animate = false;
34777 this.adapter.setElementSize(this, size);
34778 this.animate = oldAnimate;
34782 * Destroy this splitbar.
34783 * @param {Boolean} removeEl True to remove the element
34785 destroy : function(removeEl){
34787 this.shim.remove();
34790 this.proxy.parentNode.removeChild(this.proxy);
34798 * @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.
34800 Roo.bootstrap.SplitBar.createProxy = function(dir){
34801 var proxy = new Roo.Element(document.createElement("div"));
34802 proxy.unselectable();
34803 var cls = 'roo-splitbar-proxy';
34804 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34805 document.body.appendChild(proxy.dom);
34810 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34811 * Default Adapter. It assumes the splitter and resizing element are not positioned
34812 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34814 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34817 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34818 // do nothing for now
34819 init : function(s){
34823 * Called before drag operations to get the current size of the resizing element.
34824 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34826 getElementSize : function(s){
34827 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34828 return s.resizingEl.getWidth();
34830 return s.resizingEl.getHeight();
34835 * Called after drag operations to set the size of the resizing element.
34836 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34837 * @param {Number} newSize The new size to set
34838 * @param {Function} onComplete A function to be invoked when resizing is complete
34840 setElementSize : function(s, newSize, onComplete){
34841 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34843 s.resizingEl.setWidth(newSize);
34845 onComplete(s, newSize);
34848 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34853 s.resizingEl.setHeight(newSize);
34855 onComplete(s, newSize);
34858 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34865 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34866 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34867 * Adapter that moves the splitter element to align with the resized sizing element.
34868 * Used with an absolute positioned SplitBar.
34869 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34870 * document.body, make sure you assign an id to the body element.
34872 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34873 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34874 this.container = Roo.get(container);
34877 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34878 init : function(s){
34879 this.basic.init(s);
34882 getElementSize : function(s){
34883 return this.basic.getElementSize(s);
34886 setElementSize : function(s, newSize, onComplete){
34887 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34890 moveSplitter : function(s){
34891 var yes = Roo.bootstrap.SplitBar;
34892 switch(s.placement){
34894 s.el.setX(s.resizingEl.getRight());
34897 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34900 s.el.setY(s.resizingEl.getBottom());
34903 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34910 * Orientation constant - Create a vertical SplitBar
34914 Roo.bootstrap.SplitBar.VERTICAL = 1;
34917 * Orientation constant - Create a horizontal SplitBar
34921 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34924 * Placement constant - The resizing element is to the left of the splitter element
34928 Roo.bootstrap.SplitBar.LEFT = 1;
34931 * Placement constant - The resizing element is to the right of the splitter element
34935 Roo.bootstrap.SplitBar.RIGHT = 2;
34938 * Placement constant - The resizing element is positioned above the splitter element
34942 Roo.bootstrap.SplitBar.TOP = 3;
34945 * Placement constant - The resizing element is positioned under splitter element
34949 Roo.bootstrap.SplitBar.BOTTOM = 4;
34950 Roo.namespace("Roo.bootstrap.layout");/*
34952 * Ext JS Library 1.1.1
34953 * Copyright(c) 2006-2007, Ext JS, LLC.
34955 * Originally Released Under LGPL - original licence link has changed is not relivant.
34958 * <script type="text/javascript">
34962 * @class Roo.bootstrap.layout.Manager
34963 * @extends Roo.bootstrap.Component
34964 * Base class for layout managers.
34966 Roo.bootstrap.layout.Manager = function(config)
34968 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34974 /** false to disable window resize monitoring @type Boolean */
34975 this.monitorWindowResize = true;
34980 * Fires when a layout is performed.
34981 * @param {Roo.LayoutManager} this
34985 * @event regionresized
34986 * Fires when the user resizes a region.
34987 * @param {Roo.LayoutRegion} region The resized region
34988 * @param {Number} newSize The new size (width for east/west, height for north/south)
34990 "regionresized" : true,
34992 * @event regioncollapsed
34993 * Fires when a region is collapsed.
34994 * @param {Roo.LayoutRegion} region The collapsed region
34996 "regioncollapsed" : true,
34998 * @event regionexpanded
34999 * Fires when a region is expanded.
35000 * @param {Roo.LayoutRegion} region The expanded region
35002 "regionexpanded" : true
35004 this.updating = false;
35007 this.el = Roo.get(config.el);
35013 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35018 monitorWindowResize : true,
35024 onRender : function(ct, position)
35027 this.el = Roo.get(ct);
35030 //this.fireEvent('render',this);
35034 initEvents: function()
35038 // ie scrollbar fix
35039 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35040 document.body.scroll = "no";
35041 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35042 this.el.position('relative');
35044 this.id = this.el.id;
35045 this.el.addClass("roo-layout-container");
35046 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35047 if(this.el.dom != document.body ) {
35048 this.el.on('resize', this.layout,this);
35049 this.el.on('show', this.layout,this);
35055 * Returns true if this layout is currently being updated
35056 * @return {Boolean}
35058 isUpdating : function(){
35059 return this.updating;
35063 * Suspend the LayoutManager from doing auto-layouts while
35064 * making multiple add or remove calls
35066 beginUpdate : function(){
35067 this.updating = true;
35071 * Restore auto-layouts and optionally disable the manager from performing a layout
35072 * @param {Boolean} noLayout true to disable a layout update
35074 endUpdate : function(noLayout){
35075 this.updating = false;
35081 layout: function(){
35085 onRegionResized : function(region, newSize){
35086 this.fireEvent("regionresized", region, newSize);
35090 onRegionCollapsed : function(region){
35091 this.fireEvent("regioncollapsed", region);
35094 onRegionExpanded : function(region){
35095 this.fireEvent("regionexpanded", region);
35099 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35100 * performs box-model adjustments.
35101 * @return {Object} The size as an object {width: (the width), height: (the height)}
35103 getViewSize : function()
35106 if(this.el.dom != document.body){
35107 size = this.el.getSize();
35109 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35111 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35112 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35117 * Returns the Element this layout is bound to.
35118 * @return {Roo.Element}
35120 getEl : function(){
35125 * Returns the specified region.
35126 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35127 * @return {Roo.LayoutRegion}
35129 getRegion : function(target){
35130 return this.regions[target.toLowerCase()];
35133 onWindowResize : function(){
35134 if(this.monitorWindowResize){
35141 * Ext JS Library 1.1.1
35142 * Copyright(c) 2006-2007, Ext JS, LLC.
35144 * Originally Released Under LGPL - original licence link has changed is not relivant.
35147 * <script type="text/javascript">
35150 * @class Roo.bootstrap.layout.Border
35151 * @extends Roo.bootstrap.layout.Manager
35152 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35153 * please see: examples/bootstrap/nested.html<br><br>
35155 <b>The container the layout is rendered into can be either the body element or any other element.
35156 If it is not the body element, the container needs to either be an absolute positioned element,
35157 or you will need to add "position:relative" to the css of the container. You will also need to specify
35158 the container size if it is not the body element.</b>
35161 * Create a new Border
35162 * @param {Object} config Configuration options
35164 Roo.bootstrap.layout.Border = function(config){
35165 config = config || {};
35166 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35170 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35171 if(config[region]){
35172 config[region].region = region;
35173 this.addRegion(config[region]);
35179 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
35181 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35183 parent : false, // this might point to a 'nest' or a ???
35186 * Creates and adds a new region if it doesn't already exist.
35187 * @param {String} target The target region key (north, south, east, west or center).
35188 * @param {Object} config The regions config object
35189 * @return {BorderLayoutRegion} The new region
35191 addRegion : function(config)
35193 if(!this.regions[config.region]){
35194 var r = this.factory(config);
35195 this.bindRegion(r);
35197 return this.regions[config.region];
35201 bindRegion : function(r){
35202 this.regions[r.config.region] = r;
35204 r.on("visibilitychange", this.layout, this);
35205 r.on("paneladded", this.layout, this);
35206 r.on("panelremoved", this.layout, this);
35207 r.on("invalidated", this.layout, this);
35208 r.on("resized", this.onRegionResized, this);
35209 r.on("collapsed", this.onRegionCollapsed, this);
35210 r.on("expanded", this.onRegionExpanded, this);
35214 * Performs a layout update.
35216 layout : function()
35218 if(this.updating) {
35222 // render all the rebions if they have not been done alreayd?
35223 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35224 if(this.regions[region] && !this.regions[region].bodyEl){
35225 this.regions[region].onRender(this.el)
35229 var size = this.getViewSize();
35230 var w = size.width;
35231 var h = size.height;
35236 //var x = 0, y = 0;
35238 var rs = this.regions;
35239 var north = rs["north"];
35240 var south = rs["south"];
35241 var west = rs["west"];
35242 var east = rs["east"];
35243 var center = rs["center"];
35244 //if(this.hideOnLayout){ // not supported anymore
35245 //c.el.setStyle("display", "none");
35247 if(north && north.isVisible()){
35248 var b = north.getBox();
35249 var m = north.getMargins();
35250 b.width = w - (m.left+m.right);
35253 centerY = b.height + b.y + m.bottom;
35254 centerH -= centerY;
35255 north.updateBox(this.safeBox(b));
35257 if(south && south.isVisible()){
35258 var b = south.getBox();
35259 var m = south.getMargins();
35260 b.width = w - (m.left+m.right);
35262 var totalHeight = (b.height + m.top + m.bottom);
35263 b.y = h - totalHeight + m.top;
35264 centerH -= totalHeight;
35265 south.updateBox(this.safeBox(b));
35267 if(west && west.isVisible()){
35268 var b = west.getBox();
35269 var m = west.getMargins();
35270 b.height = centerH - (m.top+m.bottom);
35272 b.y = centerY + m.top;
35273 var totalWidth = (b.width + m.left + m.right);
35274 centerX += totalWidth;
35275 centerW -= totalWidth;
35276 west.updateBox(this.safeBox(b));
35278 if(east && east.isVisible()){
35279 var b = east.getBox();
35280 var m = east.getMargins();
35281 b.height = centerH - (m.top+m.bottom);
35282 var totalWidth = (b.width + m.left + m.right);
35283 b.x = w - totalWidth + m.left;
35284 b.y = centerY + m.top;
35285 centerW -= totalWidth;
35286 east.updateBox(this.safeBox(b));
35289 var m = center.getMargins();
35291 x: centerX + m.left,
35292 y: centerY + m.top,
35293 width: centerW - (m.left+m.right),
35294 height: centerH - (m.top+m.bottom)
35296 //if(this.hideOnLayout){
35297 //center.el.setStyle("display", "block");
35299 center.updateBox(this.safeBox(centerBox));
35302 this.fireEvent("layout", this);
35306 safeBox : function(box){
35307 box.width = Math.max(0, box.width);
35308 box.height = Math.max(0, box.height);
35313 * Adds a ContentPanel (or subclass) to this layout.
35314 * @param {String} target The target region key (north, south, east, west or center).
35315 * @param {Roo.ContentPanel} panel The panel to add
35316 * @return {Roo.ContentPanel} The added panel
35318 add : function(target, panel){
35320 target = target.toLowerCase();
35321 return this.regions[target].add(panel);
35325 * Remove a ContentPanel (or subclass) to this layout.
35326 * @param {String} target The target region key (north, south, east, west or center).
35327 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35328 * @return {Roo.ContentPanel} The removed panel
35330 remove : function(target, panel){
35331 target = target.toLowerCase();
35332 return this.regions[target].remove(panel);
35336 * Searches all regions for a panel with the specified id
35337 * @param {String} panelId
35338 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35340 findPanel : function(panelId){
35341 var rs = this.regions;
35342 for(var target in rs){
35343 if(typeof rs[target] != "function"){
35344 var p = rs[target].getPanel(panelId);
35354 * Searches all regions for a panel with the specified id and activates (shows) it.
35355 * @param {String/ContentPanel} panelId The panels id or the panel itself
35356 * @return {Roo.ContentPanel} The shown panel or null
35358 showPanel : function(panelId) {
35359 var rs = this.regions;
35360 for(var target in rs){
35361 var r = rs[target];
35362 if(typeof r != "function"){
35363 if(r.hasPanel(panelId)){
35364 return r.showPanel(panelId);
35372 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35373 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35376 restoreState : function(provider){
35378 provider = Roo.state.Manager;
35380 var sm = new Roo.LayoutStateManager();
35381 sm.init(this, provider);
35387 * Adds a xtype elements to the layout.
35391 xtype : 'ContentPanel',
35398 xtype : 'NestedLayoutPanel',
35404 items : [ ... list of content panels or nested layout panels.. ]
35408 * @param {Object} cfg Xtype definition of item to add.
35410 addxtype : function(cfg)
35412 // basically accepts a pannel...
35413 // can accept a layout region..!?!?
35414 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35417 // theory? children can only be panels??
35419 //if (!cfg.xtype.match(/Panel$/)) {
35424 if (typeof(cfg.region) == 'undefined') {
35425 Roo.log("Failed to add Panel, region was not set");
35429 var region = cfg.region;
35435 xitems = cfg.items;
35440 if ( region == 'center') {
35441 Roo.log("Center: " + cfg.title);
35447 case 'Content': // ContentPanel (el, cfg)
35448 case 'Scroll': // ContentPanel (el, cfg)
35450 cfg.autoCreate = cfg.autoCreate || true;
35451 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35453 // var el = this.el.createChild();
35454 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35457 this.add(region, ret);
35461 case 'TreePanel': // our new panel!
35462 cfg.el = this.el.createChild();
35463 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35464 this.add(region, ret);
35469 // create a new Layout (which is a Border Layout...
35471 var clayout = cfg.layout;
35472 clayout.el = this.el.createChild();
35473 clayout.items = clayout.items || [];
35477 // replace this exitems with the clayout ones..
35478 xitems = clayout.items;
35480 // force background off if it's in center...
35481 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35482 cfg.background = false;
35484 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35487 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35488 //console.log('adding nested layout panel ' + cfg.toSource());
35489 this.add(region, ret);
35490 nb = {}; /// find first...
35495 // needs grid and region
35497 //var el = this.getRegion(region).el.createChild();
35499 *var el = this.el.createChild();
35500 // create the grid first...
35501 cfg.grid.container = el;
35502 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35505 if (region == 'center' && this.active ) {
35506 cfg.background = false;
35509 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35511 this.add(region, ret);
35513 if (cfg.background) {
35514 // render grid on panel activation (if panel background)
35515 ret.on('activate', function(gp) {
35516 if (!gp.grid.rendered) {
35517 // gp.grid.render(el);
35521 // cfg.grid.render(el);
35527 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35528 // it was the old xcomponent building that caused this before.
35529 // espeically if border is the top element in the tree.
35539 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35541 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35542 this.add(region, ret);
35546 throw "Can not add '" + cfg.xtype + "' to Border";
35552 this.beginUpdate();
35556 Roo.each(xitems, function(i) {
35557 region = nb && i.region ? i.region : false;
35559 var add = ret.addxtype(i);
35562 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35563 if (!i.background) {
35564 abn[region] = nb[region] ;
35571 // make the last non-background panel active..
35572 //if (nb) { Roo.log(abn); }
35575 for(var r in abn) {
35576 region = this.getRegion(r);
35578 // tried using nb[r], but it does not work..
35580 region.showPanel(abn[r]);
35591 factory : function(cfg)
35594 var validRegions = Roo.bootstrap.layout.Border.regions;
35596 var target = cfg.region;
35599 var r = Roo.bootstrap.layout;
35603 return new r.North(cfg);
35605 return new r.South(cfg);
35607 return new r.East(cfg);
35609 return new r.West(cfg);
35611 return new r.Center(cfg);
35613 throw 'Layout region "'+target+'" not supported.';
35620 * Ext JS Library 1.1.1
35621 * Copyright(c) 2006-2007, Ext JS, LLC.
35623 * Originally Released Under LGPL - original licence link has changed is not relivant.
35626 * <script type="text/javascript">
35630 * @class Roo.bootstrap.layout.Basic
35631 * @extends Roo.util.Observable
35632 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35633 * and does not have a titlebar, tabs or any other features. All it does is size and position
35634 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35635 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35636 * @cfg {string} region the region that it inhabits..
35637 * @cfg {bool} skipConfig skip config?
35641 Roo.bootstrap.layout.Basic = function(config){
35643 this.mgr = config.mgr;
35645 this.position = config.region;
35647 var skipConfig = config.skipConfig;
35651 * @scope Roo.BasicLayoutRegion
35655 * @event beforeremove
35656 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35657 * @param {Roo.LayoutRegion} this
35658 * @param {Roo.ContentPanel} panel The panel
35659 * @param {Object} e The cancel event object
35661 "beforeremove" : true,
35663 * @event invalidated
35664 * Fires when the layout for this region is changed.
35665 * @param {Roo.LayoutRegion} this
35667 "invalidated" : true,
35669 * @event visibilitychange
35670 * Fires when this region is shown or hidden
35671 * @param {Roo.LayoutRegion} this
35672 * @param {Boolean} visibility true or false
35674 "visibilitychange" : true,
35676 * @event paneladded
35677 * Fires when a panel is added.
35678 * @param {Roo.LayoutRegion} this
35679 * @param {Roo.ContentPanel} panel The panel
35681 "paneladded" : true,
35683 * @event panelremoved
35684 * Fires when a panel is removed.
35685 * @param {Roo.LayoutRegion} this
35686 * @param {Roo.ContentPanel} panel The panel
35688 "panelremoved" : true,
35690 * @event beforecollapse
35691 * Fires when this region before collapse.
35692 * @param {Roo.LayoutRegion} this
35694 "beforecollapse" : true,
35697 * Fires when this region is collapsed.
35698 * @param {Roo.LayoutRegion} this
35700 "collapsed" : true,
35703 * Fires when this region is expanded.
35704 * @param {Roo.LayoutRegion} this
35709 * Fires when this region is slid into view.
35710 * @param {Roo.LayoutRegion} this
35712 "slideshow" : true,
35715 * Fires when this region slides out of view.
35716 * @param {Roo.LayoutRegion} this
35718 "slidehide" : true,
35720 * @event panelactivated
35721 * Fires when a panel is activated.
35722 * @param {Roo.LayoutRegion} this
35723 * @param {Roo.ContentPanel} panel The activated panel
35725 "panelactivated" : true,
35728 * Fires when the user resizes this region.
35729 * @param {Roo.LayoutRegion} this
35730 * @param {Number} newSize The new size (width for east/west, height for north/south)
35734 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35735 this.panels = new Roo.util.MixedCollection();
35736 this.panels.getKey = this.getPanelId.createDelegate(this);
35738 this.activePanel = null;
35739 // ensure listeners are added...
35741 if (config.listeners || config.events) {
35742 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35743 listeners : config.listeners || {},
35744 events : config.events || {}
35748 if(skipConfig !== true){
35749 this.applyConfig(config);
35753 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35755 getPanelId : function(p){
35759 applyConfig : function(config){
35760 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35761 this.config = config;
35766 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35767 * the width, for horizontal (north, south) the height.
35768 * @param {Number} newSize The new width or height
35770 resizeTo : function(newSize){
35771 var el = this.el ? this.el :
35772 (this.activePanel ? this.activePanel.getEl() : null);
35774 switch(this.position){
35777 el.setWidth(newSize);
35778 this.fireEvent("resized", this, newSize);
35782 el.setHeight(newSize);
35783 this.fireEvent("resized", this, newSize);
35789 getBox : function(){
35790 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35793 getMargins : function(){
35794 return this.margins;
35797 updateBox : function(box){
35799 var el = this.activePanel.getEl();
35800 el.dom.style.left = box.x + "px";
35801 el.dom.style.top = box.y + "px";
35802 this.activePanel.setSize(box.width, box.height);
35806 * Returns the container element for this region.
35807 * @return {Roo.Element}
35809 getEl : function(){
35810 return this.activePanel;
35814 * Returns true if this region is currently visible.
35815 * @return {Boolean}
35817 isVisible : function(){
35818 return this.activePanel ? true : false;
35821 setActivePanel : function(panel){
35822 panel = this.getPanel(panel);
35823 if(this.activePanel && this.activePanel != panel){
35824 this.activePanel.setActiveState(false);
35825 this.activePanel.getEl().setLeftTop(-10000,-10000);
35827 this.activePanel = panel;
35828 panel.setActiveState(true);
35830 panel.setSize(this.box.width, this.box.height);
35832 this.fireEvent("panelactivated", this, panel);
35833 this.fireEvent("invalidated");
35837 * Show the specified panel.
35838 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35839 * @return {Roo.ContentPanel} The shown panel or null
35841 showPanel : function(panel){
35842 panel = this.getPanel(panel);
35844 this.setActivePanel(panel);
35850 * Get the active panel for this region.
35851 * @return {Roo.ContentPanel} The active panel or null
35853 getActivePanel : function(){
35854 return this.activePanel;
35858 * Add the passed ContentPanel(s)
35859 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35860 * @return {Roo.ContentPanel} The panel added (if only one was added)
35862 add : function(panel){
35863 if(arguments.length > 1){
35864 for(var i = 0, len = arguments.length; i < len; i++) {
35865 this.add(arguments[i]);
35869 if(this.hasPanel(panel)){
35870 this.showPanel(panel);
35873 var el = panel.getEl();
35874 if(el.dom.parentNode != this.mgr.el.dom){
35875 this.mgr.el.dom.appendChild(el.dom);
35877 if(panel.setRegion){
35878 panel.setRegion(this);
35880 this.panels.add(panel);
35881 el.setStyle("position", "absolute");
35882 if(!panel.background){
35883 this.setActivePanel(panel);
35884 if(this.config.initialSize && this.panels.getCount()==1){
35885 this.resizeTo(this.config.initialSize);
35888 this.fireEvent("paneladded", this, panel);
35893 * Returns true if the panel is in this region.
35894 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35895 * @return {Boolean}
35897 hasPanel : function(panel){
35898 if(typeof panel == "object"){ // must be panel obj
35899 panel = panel.getId();
35901 return this.getPanel(panel) ? true : false;
35905 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35906 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35907 * @param {Boolean} preservePanel Overrides the config preservePanel option
35908 * @return {Roo.ContentPanel} The panel that was removed
35910 remove : function(panel, preservePanel){
35911 panel = this.getPanel(panel);
35916 this.fireEvent("beforeremove", this, panel, e);
35917 if(e.cancel === true){
35920 var panelId = panel.getId();
35921 this.panels.removeKey(panelId);
35926 * Returns the panel specified or null if it's not in this region.
35927 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35928 * @return {Roo.ContentPanel}
35930 getPanel : function(id){
35931 if(typeof id == "object"){ // must be panel obj
35934 return this.panels.get(id);
35938 * Returns this regions position (north/south/east/west/center).
35941 getPosition: function(){
35942 return this.position;
35946 * Ext JS Library 1.1.1
35947 * Copyright(c) 2006-2007, Ext JS, LLC.
35949 * Originally Released Under LGPL - original licence link has changed is not relivant.
35952 * <script type="text/javascript">
35956 * @class Roo.bootstrap.layout.Region
35957 * @extends Roo.bootstrap.layout.Basic
35958 * This class represents a region in a layout manager.
35960 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35961 * @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})
35962 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35963 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35964 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35965 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35966 * @cfg {String} title The title for the region (overrides panel titles)
35967 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35968 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35969 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35970 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35971 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35972 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35973 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35974 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35975 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35976 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35978 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35979 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35980 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35981 * @cfg {Number} width For East/West panels
35982 * @cfg {Number} height For North/South panels
35983 * @cfg {Boolean} split To show the splitter
35984 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35986 * @cfg {string} cls Extra CSS classes to add to region
35988 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35989 * @cfg {string} region the region that it inhabits..
35992 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35993 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35995 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35996 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35997 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35999 Roo.bootstrap.layout.Region = function(config)
36001 this.applyConfig(config);
36003 var mgr = config.mgr;
36004 var pos = config.region;
36005 config.skipConfig = true;
36006 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36009 this.onRender(mgr.el);
36012 this.visible = true;
36013 this.collapsed = false;
36014 this.unrendered_panels = [];
36017 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36019 position: '', // set by wrapper (eg. north/south etc..)
36020 unrendered_panels : null, // unrendered panels.
36022 tabPosition : false,
36024 mgr: false, // points to 'Border'
36027 createBody : function(){
36028 /** This region's body element
36029 * @type Roo.Element */
36030 this.bodyEl = this.el.createChild({
36032 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36036 onRender: function(ctr, pos)
36038 var dh = Roo.DomHelper;
36039 /** This region's container element
36040 * @type Roo.Element */
36041 this.el = dh.append(ctr.dom, {
36043 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36045 /** This region's title element
36046 * @type Roo.Element */
36048 this.titleEl = dh.append(this.el.dom, {
36050 unselectable: "on",
36051 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36053 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
36054 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36058 this.titleEl.enableDisplayMode();
36059 /** This region's title text element
36060 * @type HTMLElement */
36061 this.titleTextEl = this.titleEl.dom.firstChild;
36062 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36064 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36065 this.closeBtn.enableDisplayMode();
36066 this.closeBtn.on("click", this.closeClicked, this);
36067 this.closeBtn.hide();
36069 this.createBody(this.config);
36070 if(this.config.hideWhenEmpty){
36072 this.on("paneladded", this.validateVisibility, this);
36073 this.on("panelremoved", this.validateVisibility, this);
36075 if(this.autoScroll){
36076 this.bodyEl.setStyle("overflow", "auto");
36078 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36080 //if(c.titlebar !== false){
36081 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36082 this.titleEl.hide();
36084 this.titleEl.show();
36085 if(this.config.title){
36086 this.titleTextEl.innerHTML = this.config.title;
36090 if(this.config.collapsed){
36091 this.collapse(true);
36093 if(this.config.hidden){
36097 if (this.unrendered_panels && this.unrendered_panels.length) {
36098 for (var i =0;i< this.unrendered_panels.length; i++) {
36099 this.add(this.unrendered_panels[i]);
36101 this.unrendered_panels = null;
36107 applyConfig : function(c)
36110 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36111 var dh = Roo.DomHelper;
36112 if(c.titlebar !== false){
36113 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36114 this.collapseBtn.on("click", this.collapse, this);
36115 this.collapseBtn.enableDisplayMode();
36117 if(c.showPin === true || this.showPin){
36118 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36119 this.stickBtn.enableDisplayMode();
36120 this.stickBtn.on("click", this.expand, this);
36121 this.stickBtn.hide();
36126 /** This region's collapsed element
36127 * @type Roo.Element */
36130 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36131 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36134 if(c.floatable !== false){
36135 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36136 this.collapsedEl.on("click", this.collapseClick, this);
36139 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36140 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36141 id: "message", unselectable: "on", style:{"float":"left"}});
36142 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36144 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36145 this.expandBtn.on("click", this.expand, this);
36149 if(this.collapseBtn){
36150 this.collapseBtn.setVisible(c.collapsible == true);
36153 this.cmargins = c.cmargins || this.cmargins ||
36154 (this.position == "west" || this.position == "east" ?
36155 {top: 0, left: 2, right:2, bottom: 0} :
36156 {top: 2, left: 0, right:0, bottom: 2});
36158 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36161 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36163 this.autoScroll = c.autoScroll || false;
36168 this.duration = c.duration || .30;
36169 this.slideDuration = c.slideDuration || .45;
36174 * Returns true if this region is currently visible.
36175 * @return {Boolean}
36177 isVisible : function(){
36178 return this.visible;
36182 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36183 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
36185 //setCollapsedTitle : function(title){
36186 // title = title || " ";
36187 // if(this.collapsedTitleTextEl){
36188 // this.collapsedTitleTextEl.innerHTML = title;
36192 getBox : function(){
36194 // if(!this.collapsed){
36195 b = this.el.getBox(false, true);
36197 // b = this.collapsedEl.getBox(false, true);
36202 getMargins : function(){
36203 return this.margins;
36204 //return this.collapsed ? this.cmargins : this.margins;
36207 highlight : function(){
36208 this.el.addClass("x-layout-panel-dragover");
36211 unhighlight : function(){
36212 this.el.removeClass("x-layout-panel-dragover");
36215 updateBox : function(box)
36217 if (!this.bodyEl) {
36218 return; // not rendered yet..
36222 if(!this.collapsed){
36223 this.el.dom.style.left = box.x + "px";
36224 this.el.dom.style.top = box.y + "px";
36225 this.updateBody(box.width, box.height);
36227 this.collapsedEl.dom.style.left = box.x + "px";
36228 this.collapsedEl.dom.style.top = box.y + "px";
36229 this.collapsedEl.setSize(box.width, box.height);
36232 this.tabs.autoSizeTabs();
36236 updateBody : function(w, h)
36239 this.el.setWidth(w);
36240 w -= this.el.getBorderWidth("rl");
36241 if(this.config.adjustments){
36242 w += this.config.adjustments[0];
36245 if(h !== null && h > 0){
36246 this.el.setHeight(h);
36247 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36248 h -= this.el.getBorderWidth("tb");
36249 if(this.config.adjustments){
36250 h += this.config.adjustments[1];
36252 this.bodyEl.setHeight(h);
36254 h = this.tabs.syncHeight(h);
36257 if(this.panelSize){
36258 w = w !== null ? w : this.panelSize.width;
36259 h = h !== null ? h : this.panelSize.height;
36261 if(this.activePanel){
36262 var el = this.activePanel.getEl();
36263 w = w !== null ? w : el.getWidth();
36264 h = h !== null ? h : el.getHeight();
36265 this.panelSize = {width: w, height: h};
36266 this.activePanel.setSize(w, h);
36268 if(Roo.isIE && this.tabs){
36269 this.tabs.el.repaint();
36274 * Returns the container element for this region.
36275 * @return {Roo.Element}
36277 getEl : function(){
36282 * Hides this region.
36285 //if(!this.collapsed){
36286 this.el.dom.style.left = "-2000px";
36289 // this.collapsedEl.dom.style.left = "-2000px";
36290 // this.collapsedEl.hide();
36292 this.visible = false;
36293 this.fireEvent("visibilitychange", this, false);
36297 * Shows this region if it was previously hidden.
36300 //if(!this.collapsed){
36303 // this.collapsedEl.show();
36305 this.visible = true;
36306 this.fireEvent("visibilitychange", this, true);
36309 closeClicked : function(){
36310 if(this.activePanel){
36311 this.remove(this.activePanel);
36315 collapseClick : function(e){
36317 e.stopPropagation();
36320 e.stopPropagation();
36326 * Collapses this region.
36327 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36330 collapse : function(skipAnim, skipCheck = false){
36331 if(this.collapsed) {
36335 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36337 this.collapsed = true;
36339 this.split.el.hide();
36341 if(this.config.animate && skipAnim !== true){
36342 this.fireEvent("invalidated", this);
36343 this.animateCollapse();
36345 this.el.setLocation(-20000,-20000);
36347 this.collapsedEl.show();
36348 this.fireEvent("collapsed", this);
36349 this.fireEvent("invalidated", this);
36355 animateCollapse : function(){
36360 * Expands this region if it was previously collapsed.
36361 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36362 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36365 expand : function(e, skipAnim){
36367 e.stopPropagation();
36369 if(!this.collapsed || this.el.hasActiveFx()) {
36373 this.afterSlideIn();
36376 this.collapsed = false;
36377 if(this.config.animate && skipAnim !== true){
36378 this.animateExpand();
36382 this.split.el.show();
36384 this.collapsedEl.setLocation(-2000,-2000);
36385 this.collapsedEl.hide();
36386 this.fireEvent("invalidated", this);
36387 this.fireEvent("expanded", this);
36391 animateExpand : function(){
36395 initTabs : function()
36397 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36399 var ts = new Roo.bootstrap.panel.Tabs({
36400 el: this.bodyEl.dom,
36402 tabPosition: this.tabPosition ? this.tabPosition : 'top',
36403 disableTooltips: this.config.disableTabTips,
36404 toolbar : this.config.toolbar
36407 if(this.config.hideTabs){
36408 ts.stripWrap.setDisplayed(false);
36411 ts.resizeTabs = this.config.resizeTabs === true;
36412 ts.minTabWidth = this.config.minTabWidth || 40;
36413 ts.maxTabWidth = this.config.maxTabWidth || 250;
36414 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36415 ts.monitorResize = false;
36416 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36417 ts.bodyEl.addClass('roo-layout-tabs-body');
36418 this.panels.each(this.initPanelAsTab, this);
36421 initPanelAsTab : function(panel){
36422 var ti = this.tabs.addTab(
36426 this.config.closeOnTab && panel.isClosable(),
36429 if(panel.tabTip !== undefined){
36430 ti.setTooltip(panel.tabTip);
36432 ti.on("activate", function(){
36433 this.setActivePanel(panel);
36436 if(this.config.closeOnTab){
36437 ti.on("beforeclose", function(t, e){
36439 this.remove(panel);
36443 panel.tabItem = ti;
36448 updatePanelTitle : function(panel, title)
36450 if(this.activePanel == panel){
36451 this.updateTitle(title);
36454 var ti = this.tabs.getTab(panel.getEl().id);
36456 if(panel.tabTip !== undefined){
36457 ti.setTooltip(panel.tabTip);
36462 updateTitle : function(title){
36463 if(this.titleTextEl && !this.config.title){
36464 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36468 setActivePanel : function(panel)
36470 panel = this.getPanel(panel);
36471 if(this.activePanel && this.activePanel != panel){
36472 if(this.activePanel.setActiveState(false) === false){
36476 this.activePanel = panel;
36477 panel.setActiveState(true);
36478 if(this.panelSize){
36479 panel.setSize(this.panelSize.width, this.panelSize.height);
36482 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36484 this.updateTitle(panel.getTitle());
36486 this.fireEvent("invalidated", this);
36488 this.fireEvent("panelactivated", this, panel);
36492 * Shows the specified panel.
36493 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36494 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36496 showPanel : function(panel)
36498 panel = this.getPanel(panel);
36501 var tab = this.tabs.getTab(panel.getEl().id);
36502 if(tab.isHidden()){
36503 this.tabs.unhideTab(tab.id);
36507 this.setActivePanel(panel);
36514 * Get the active panel for this region.
36515 * @return {Roo.ContentPanel} The active panel or null
36517 getActivePanel : function(){
36518 return this.activePanel;
36521 validateVisibility : function(){
36522 if(this.panels.getCount() < 1){
36523 this.updateTitle(" ");
36524 this.closeBtn.hide();
36527 if(!this.isVisible()){
36534 * Adds the passed ContentPanel(s) to this region.
36535 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36536 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36538 add : function(panel)
36540 if(arguments.length > 1){
36541 for(var i = 0, len = arguments.length; i < len; i++) {
36542 this.add(arguments[i]);
36547 // if we have not been rendered yet, then we can not really do much of this..
36548 if (!this.bodyEl) {
36549 this.unrendered_panels.push(panel);
36556 if(this.hasPanel(panel)){
36557 this.showPanel(panel);
36560 panel.setRegion(this);
36561 this.panels.add(panel);
36562 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36563 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36564 // and hide them... ???
36565 this.bodyEl.dom.appendChild(panel.getEl().dom);
36566 if(panel.background !== true){
36567 this.setActivePanel(panel);
36569 this.fireEvent("paneladded", this, panel);
36576 this.initPanelAsTab(panel);
36580 if(panel.background !== true){
36581 this.tabs.activate(panel.getEl().id);
36583 this.fireEvent("paneladded", this, panel);
36588 * Hides the tab for the specified panel.
36589 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36591 hidePanel : function(panel){
36592 if(this.tabs && (panel = this.getPanel(panel))){
36593 this.tabs.hideTab(panel.getEl().id);
36598 * Unhides the tab for a previously hidden panel.
36599 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36601 unhidePanel : function(panel){
36602 if(this.tabs && (panel = this.getPanel(panel))){
36603 this.tabs.unhideTab(panel.getEl().id);
36607 clearPanels : function(){
36608 while(this.panels.getCount() > 0){
36609 this.remove(this.panels.first());
36614 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36615 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36616 * @param {Boolean} preservePanel Overrides the config preservePanel option
36617 * @return {Roo.ContentPanel} The panel that was removed
36619 remove : function(panel, preservePanel)
36621 panel = this.getPanel(panel);
36626 this.fireEvent("beforeremove", this, panel, e);
36627 if(e.cancel === true){
36630 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36631 var panelId = panel.getId();
36632 this.panels.removeKey(panelId);
36634 document.body.appendChild(panel.getEl().dom);
36637 this.tabs.removeTab(panel.getEl().id);
36638 }else if (!preservePanel){
36639 this.bodyEl.dom.removeChild(panel.getEl().dom);
36641 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36642 var p = this.panels.first();
36643 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36644 tempEl.appendChild(p.getEl().dom);
36645 this.bodyEl.update("");
36646 this.bodyEl.dom.appendChild(p.getEl().dom);
36648 this.updateTitle(p.getTitle());
36650 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36651 this.setActivePanel(p);
36653 panel.setRegion(null);
36654 if(this.activePanel == panel){
36655 this.activePanel = null;
36657 if(this.config.autoDestroy !== false && preservePanel !== true){
36658 try{panel.destroy();}catch(e){}
36660 this.fireEvent("panelremoved", this, panel);
36665 * Returns the TabPanel component used by this region
36666 * @return {Roo.TabPanel}
36668 getTabs : function(){
36672 createTool : function(parentEl, className){
36673 var btn = Roo.DomHelper.append(parentEl, {
36675 cls: "x-layout-tools-button",
36678 cls: "roo-layout-tools-button-inner " + className,
36682 btn.addClassOnOver("roo-layout-tools-button-over");
36687 * Ext JS Library 1.1.1
36688 * Copyright(c) 2006-2007, Ext JS, LLC.
36690 * Originally Released Under LGPL - original licence link has changed is not relivant.
36693 * <script type="text/javascript">
36699 * @class Roo.SplitLayoutRegion
36700 * @extends Roo.LayoutRegion
36701 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36703 Roo.bootstrap.layout.Split = function(config){
36704 this.cursor = config.cursor;
36705 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36708 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36710 splitTip : "Drag to resize.",
36711 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36712 useSplitTips : false,
36714 applyConfig : function(config){
36715 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36718 onRender : function(ctr,pos) {
36720 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36721 if(!this.config.split){
36726 var splitEl = Roo.DomHelper.append(ctr.dom, {
36728 id: this.el.id + "-split",
36729 cls: "roo-layout-split roo-layout-split-"+this.position,
36732 /** The SplitBar for this region
36733 * @type Roo.SplitBar */
36734 // does not exist yet...
36735 Roo.log([this.position, this.orientation]);
36737 this.split = new Roo.bootstrap.SplitBar({
36738 dragElement : splitEl,
36739 resizingElement: this.el,
36740 orientation : this.orientation
36743 this.split.on("moved", this.onSplitMove, this);
36744 this.split.useShim = this.config.useShim === true;
36745 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36746 if(this.useSplitTips){
36747 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36749 //if(config.collapsible){
36750 // this.split.el.on("dblclick", this.collapse, this);
36753 if(typeof this.config.minSize != "undefined"){
36754 this.split.minSize = this.config.minSize;
36756 if(typeof this.config.maxSize != "undefined"){
36757 this.split.maxSize = this.config.maxSize;
36759 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36760 this.hideSplitter();
36765 getHMaxSize : function(){
36766 var cmax = this.config.maxSize || 10000;
36767 var center = this.mgr.getRegion("center");
36768 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36771 getVMaxSize : function(){
36772 var cmax = this.config.maxSize || 10000;
36773 var center = this.mgr.getRegion("center");
36774 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36777 onSplitMove : function(split, newSize){
36778 this.fireEvent("resized", this, newSize);
36782 * Returns the {@link Roo.SplitBar} for this region.
36783 * @return {Roo.SplitBar}
36785 getSplitBar : function(){
36790 this.hideSplitter();
36791 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36794 hideSplitter : function(){
36796 this.split.el.setLocation(-2000,-2000);
36797 this.split.el.hide();
36803 this.split.el.show();
36805 Roo.bootstrap.layout.Split.superclass.show.call(this);
36808 beforeSlide: function(){
36809 if(Roo.isGecko){// firefox overflow auto bug workaround
36810 this.bodyEl.clip();
36812 this.tabs.bodyEl.clip();
36814 if(this.activePanel){
36815 this.activePanel.getEl().clip();
36817 if(this.activePanel.beforeSlide){
36818 this.activePanel.beforeSlide();
36824 afterSlide : function(){
36825 if(Roo.isGecko){// firefox overflow auto bug workaround
36826 this.bodyEl.unclip();
36828 this.tabs.bodyEl.unclip();
36830 if(this.activePanel){
36831 this.activePanel.getEl().unclip();
36832 if(this.activePanel.afterSlide){
36833 this.activePanel.afterSlide();
36839 initAutoHide : function(){
36840 if(this.autoHide !== false){
36841 if(!this.autoHideHd){
36842 var st = new Roo.util.DelayedTask(this.slideIn, this);
36843 this.autoHideHd = {
36844 "mouseout": function(e){
36845 if(!e.within(this.el, true)){
36849 "mouseover" : function(e){
36855 this.el.on(this.autoHideHd);
36859 clearAutoHide : function(){
36860 if(this.autoHide !== false){
36861 this.el.un("mouseout", this.autoHideHd.mouseout);
36862 this.el.un("mouseover", this.autoHideHd.mouseover);
36866 clearMonitor : function(){
36867 Roo.get(document).un("click", this.slideInIf, this);
36870 // these names are backwards but not changed for compat
36871 slideOut : function(){
36872 if(this.isSlid || this.el.hasActiveFx()){
36875 this.isSlid = true;
36876 if(this.collapseBtn){
36877 this.collapseBtn.hide();
36879 this.closeBtnState = this.closeBtn.getStyle('display');
36880 this.closeBtn.hide();
36882 this.stickBtn.show();
36885 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36886 this.beforeSlide();
36887 this.el.setStyle("z-index", 10001);
36888 this.el.slideIn(this.getSlideAnchor(), {
36889 callback: function(){
36891 this.initAutoHide();
36892 Roo.get(document).on("click", this.slideInIf, this);
36893 this.fireEvent("slideshow", this);
36900 afterSlideIn : function(){
36901 this.clearAutoHide();
36902 this.isSlid = false;
36903 this.clearMonitor();
36904 this.el.setStyle("z-index", "");
36905 if(this.collapseBtn){
36906 this.collapseBtn.show();
36908 this.closeBtn.setStyle('display', this.closeBtnState);
36910 this.stickBtn.hide();
36912 this.fireEvent("slidehide", this);
36915 slideIn : function(cb){
36916 if(!this.isSlid || this.el.hasActiveFx()){
36920 this.isSlid = false;
36921 this.beforeSlide();
36922 this.el.slideOut(this.getSlideAnchor(), {
36923 callback: function(){
36924 this.el.setLeftTop(-10000, -10000);
36926 this.afterSlideIn();
36934 slideInIf : function(e){
36935 if(!e.within(this.el)){
36940 animateCollapse : function(){
36941 this.beforeSlide();
36942 this.el.setStyle("z-index", 20000);
36943 var anchor = this.getSlideAnchor();
36944 this.el.slideOut(anchor, {
36945 callback : function(){
36946 this.el.setStyle("z-index", "");
36947 this.collapsedEl.slideIn(anchor, {duration:.3});
36949 this.el.setLocation(-10000,-10000);
36951 this.fireEvent("collapsed", this);
36958 animateExpand : function(){
36959 this.beforeSlide();
36960 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36961 this.el.setStyle("z-index", 20000);
36962 this.collapsedEl.hide({
36965 this.el.slideIn(this.getSlideAnchor(), {
36966 callback : function(){
36967 this.el.setStyle("z-index", "");
36970 this.split.el.show();
36972 this.fireEvent("invalidated", this);
36973 this.fireEvent("expanded", this);
37001 getAnchor : function(){
37002 return this.anchors[this.position];
37005 getCollapseAnchor : function(){
37006 return this.canchors[this.position];
37009 getSlideAnchor : function(){
37010 return this.sanchors[this.position];
37013 getAlignAdj : function(){
37014 var cm = this.cmargins;
37015 switch(this.position){
37031 getExpandAdj : function(){
37032 var c = this.collapsedEl, cm = this.cmargins;
37033 switch(this.position){
37035 return [-(cm.right+c.getWidth()+cm.left), 0];
37038 return [cm.right+c.getWidth()+cm.left, 0];
37041 return [0, -(cm.top+cm.bottom+c.getHeight())];
37044 return [0, cm.top+cm.bottom+c.getHeight()];
37050 * Ext JS Library 1.1.1
37051 * Copyright(c) 2006-2007, Ext JS, LLC.
37053 * Originally Released Under LGPL - original licence link has changed is not relivant.
37056 * <script type="text/javascript">
37059 * These classes are private internal classes
37061 Roo.bootstrap.layout.Center = function(config){
37062 config.region = "center";
37063 Roo.bootstrap.layout.Region.call(this, config);
37064 this.visible = true;
37065 this.minWidth = config.minWidth || 20;
37066 this.minHeight = config.minHeight || 20;
37069 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37071 // center panel can't be hidden
37075 // center panel can't be hidden
37078 getMinWidth: function(){
37079 return this.minWidth;
37082 getMinHeight: function(){
37083 return this.minHeight;
37097 Roo.bootstrap.layout.North = function(config)
37099 config.region = 'north';
37100 config.cursor = 'n-resize';
37102 Roo.bootstrap.layout.Split.call(this, config);
37106 this.split.placement = Roo.bootstrap.SplitBar.TOP;
37107 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37108 this.split.el.addClass("roo-layout-split-v");
37110 var size = config.initialSize || config.height;
37111 if(typeof size != "undefined"){
37112 this.el.setHeight(size);
37115 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37117 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37121 getBox : function(){
37122 if(this.collapsed){
37123 return this.collapsedEl.getBox();
37125 var box = this.el.getBox();
37127 box.height += this.split.el.getHeight();
37132 updateBox : function(box){
37133 if(this.split && !this.collapsed){
37134 box.height -= this.split.el.getHeight();
37135 this.split.el.setLeft(box.x);
37136 this.split.el.setTop(box.y+box.height);
37137 this.split.el.setWidth(box.width);
37139 if(this.collapsed){
37140 this.updateBody(box.width, null);
37142 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37150 Roo.bootstrap.layout.South = function(config){
37151 config.region = 'south';
37152 config.cursor = 's-resize';
37153 Roo.bootstrap.layout.Split.call(this, config);
37155 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37156 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37157 this.split.el.addClass("roo-layout-split-v");
37159 var size = config.initialSize || config.height;
37160 if(typeof size != "undefined"){
37161 this.el.setHeight(size);
37165 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37166 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37167 getBox : function(){
37168 if(this.collapsed){
37169 return this.collapsedEl.getBox();
37171 var box = this.el.getBox();
37173 var sh = this.split.el.getHeight();
37180 updateBox : function(box){
37181 if(this.split && !this.collapsed){
37182 var sh = this.split.el.getHeight();
37185 this.split.el.setLeft(box.x);
37186 this.split.el.setTop(box.y-sh);
37187 this.split.el.setWidth(box.width);
37189 if(this.collapsed){
37190 this.updateBody(box.width, null);
37192 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37196 Roo.bootstrap.layout.East = function(config){
37197 config.region = "east";
37198 config.cursor = "e-resize";
37199 Roo.bootstrap.layout.Split.call(this, config);
37201 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37202 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37203 this.split.el.addClass("roo-layout-split-h");
37205 var size = config.initialSize || config.width;
37206 if(typeof size != "undefined"){
37207 this.el.setWidth(size);
37210 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37211 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37212 getBox : function(){
37213 if(this.collapsed){
37214 return this.collapsedEl.getBox();
37216 var box = this.el.getBox();
37218 var sw = this.split.el.getWidth();
37225 updateBox : function(box){
37226 if(this.split && !this.collapsed){
37227 var sw = this.split.el.getWidth();
37229 this.split.el.setLeft(box.x);
37230 this.split.el.setTop(box.y);
37231 this.split.el.setHeight(box.height);
37234 if(this.collapsed){
37235 this.updateBody(null, box.height);
37237 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37241 Roo.bootstrap.layout.West = function(config){
37242 config.region = "west";
37243 config.cursor = "w-resize";
37245 Roo.bootstrap.layout.Split.call(this, config);
37247 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37248 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37249 this.split.el.addClass("roo-layout-split-h");
37253 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37254 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37256 onRender: function(ctr, pos)
37258 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37259 var size = this.config.initialSize || this.config.width;
37260 if(typeof size != "undefined"){
37261 this.el.setWidth(size);
37265 getBox : function(){
37266 if(this.collapsed){
37267 return this.collapsedEl.getBox();
37269 var box = this.el.getBox();
37271 box.width += this.split.el.getWidth();
37276 updateBox : function(box){
37277 if(this.split && !this.collapsed){
37278 var sw = this.split.el.getWidth();
37280 this.split.el.setLeft(box.x+box.width);
37281 this.split.el.setTop(box.y);
37282 this.split.el.setHeight(box.height);
37284 if(this.collapsed){
37285 this.updateBody(null, box.height);
37287 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37289 });Roo.namespace("Roo.bootstrap.panel");/*
37291 * Ext JS Library 1.1.1
37292 * Copyright(c) 2006-2007, Ext JS, LLC.
37294 * Originally Released Under LGPL - original licence link has changed is not relivant.
37297 * <script type="text/javascript">
37300 * @class Roo.ContentPanel
37301 * @extends Roo.util.Observable
37302 * A basic ContentPanel element.
37303 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37304 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37305 * @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
37306 * @cfg {Boolean} closable True if the panel can be closed/removed
37307 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37308 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37309 * @cfg {Toolbar} toolbar A toolbar for this panel
37310 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37311 * @cfg {String} title The title for this panel
37312 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37313 * @cfg {String} url Calls {@link #setUrl} with this value
37314 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37315 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37316 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37317 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37318 * @cfg {Boolean} badges render the badges
37321 * Create a new ContentPanel.
37322 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37323 * @param {String/Object} config A string to set only the title or a config object
37324 * @param {String} content (optional) Set the HTML content for this panel
37325 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37327 Roo.bootstrap.panel.Content = function( config){
37329 this.tpl = config.tpl || false;
37331 var el = config.el;
37332 var content = config.content;
37334 if(config.autoCreate){ // xtype is available if this is called from factory
37337 this.el = Roo.get(el);
37338 if(!this.el && config && config.autoCreate){
37339 if(typeof config.autoCreate == "object"){
37340 if(!config.autoCreate.id){
37341 config.autoCreate.id = config.id||el;
37343 this.el = Roo.DomHelper.append(document.body,
37344 config.autoCreate, true);
37346 var elcfg = { tag: "div",
37347 cls: "roo-layout-inactive-content",
37351 elcfg.html = config.html;
37355 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37358 this.closable = false;
37359 this.loaded = false;
37360 this.active = false;
37363 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37365 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37367 this.wrapEl = this.el; //this.el.wrap();
37369 if (config.toolbar.items) {
37370 ti = config.toolbar.items ;
37371 delete config.toolbar.items ;
37375 this.toolbar.render(this.wrapEl, 'before');
37376 for(var i =0;i < ti.length;i++) {
37377 // Roo.log(['add child', items[i]]);
37378 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37380 this.toolbar.items = nitems;
37381 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37382 delete config.toolbar;
37386 // xtype created footer. - not sure if will work as we normally have to render first..
37387 if (this.footer && !this.footer.el && this.footer.xtype) {
37388 if (!this.wrapEl) {
37389 this.wrapEl = this.el.wrap();
37392 this.footer.container = this.wrapEl.createChild();
37394 this.footer = Roo.factory(this.footer, Roo);
37399 if(typeof config == "string"){
37400 this.title = config;
37402 Roo.apply(this, config);
37406 this.resizeEl = Roo.get(this.resizeEl, true);
37408 this.resizeEl = this.el;
37410 // handle view.xtype
37418 * Fires when this panel is activated.
37419 * @param {Roo.ContentPanel} this
37423 * @event deactivate
37424 * Fires when this panel is activated.
37425 * @param {Roo.ContentPanel} this
37427 "deactivate" : true,
37431 * Fires when this panel is resized if fitToFrame is true.
37432 * @param {Roo.ContentPanel} this
37433 * @param {Number} width The width after any component adjustments
37434 * @param {Number} height The height after any component adjustments
37440 * Fires when this tab is created
37441 * @param {Roo.ContentPanel} this
37452 if(this.autoScroll){
37453 this.resizeEl.setStyle("overflow", "auto");
37455 // fix randome scrolling
37456 //this.el.on('scroll', function() {
37457 // Roo.log('fix random scolling');
37458 // this.scrollTo('top',0);
37461 content = content || this.content;
37463 this.setContent(content);
37465 if(config && config.url){
37466 this.setUrl(this.url, this.params, this.loadOnce);
37471 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37473 if (this.view && typeof(this.view.xtype) != 'undefined') {
37474 this.view.el = this.el.appendChild(document.createElement("div"));
37475 this.view = Roo.factory(this.view);
37476 this.view.render && this.view.render(false, '');
37480 this.fireEvent('render', this);
37483 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37487 setRegion : function(region){
37488 this.region = region;
37489 this.setActiveClass(region && !this.background);
37493 setActiveClass: function(state)
37496 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37497 this.el.setStyle('position','relative');
37499 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37500 this.el.setStyle('position', 'absolute');
37505 * Returns the toolbar for this Panel if one was configured.
37506 * @return {Roo.Toolbar}
37508 getToolbar : function(){
37509 return this.toolbar;
37512 setActiveState : function(active)
37514 this.active = active;
37515 this.setActiveClass(active);
37517 if(this.fireEvent("deactivate", this) === false){
37522 this.fireEvent("activate", this);
37526 * Updates this panel's element
37527 * @param {String} content The new content
37528 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37530 setContent : function(content, loadScripts){
37531 this.el.update(content, loadScripts);
37534 ignoreResize : function(w, h){
37535 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37538 this.lastSize = {width: w, height: h};
37543 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37544 * @return {Roo.UpdateManager} The UpdateManager
37546 getUpdateManager : function(){
37547 return this.el.getUpdateManager();
37550 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37551 * @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:
37554 url: "your-url.php",
37555 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37556 callback: yourFunction,
37557 scope: yourObject, //(optional scope)
37560 text: "Loading...",
37565 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37566 * 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.
37567 * @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}
37568 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37569 * @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.
37570 * @return {Roo.ContentPanel} this
37573 var um = this.el.getUpdateManager();
37574 um.update.apply(um, arguments);
37580 * 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.
37581 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37582 * @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)
37583 * @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)
37584 * @return {Roo.UpdateManager} The UpdateManager
37586 setUrl : function(url, params, loadOnce){
37587 if(this.refreshDelegate){
37588 this.removeListener("activate", this.refreshDelegate);
37590 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37591 this.on("activate", this.refreshDelegate);
37592 return this.el.getUpdateManager();
37595 _handleRefresh : function(url, params, loadOnce){
37596 if(!loadOnce || !this.loaded){
37597 var updater = this.el.getUpdateManager();
37598 updater.update(url, params, this._setLoaded.createDelegate(this));
37602 _setLoaded : function(){
37603 this.loaded = true;
37607 * Returns this panel's id
37610 getId : function(){
37615 * Returns this panel's element - used by regiosn to add.
37616 * @return {Roo.Element}
37618 getEl : function(){
37619 return this.wrapEl || this.el;
37624 adjustForComponents : function(width, height)
37626 //Roo.log('adjustForComponents ');
37627 if(this.resizeEl != this.el){
37628 width -= this.el.getFrameWidth('lr');
37629 height -= this.el.getFrameWidth('tb');
37632 var te = this.toolbar.getEl();
37633 te.setWidth(width);
37634 height -= te.getHeight();
37637 var te = this.footer.getEl();
37638 te.setWidth(width);
37639 height -= te.getHeight();
37643 if(this.adjustments){
37644 width += this.adjustments[0];
37645 height += this.adjustments[1];
37647 return {"width": width, "height": height};
37650 setSize : function(width, height){
37651 if(this.fitToFrame && !this.ignoreResize(width, height)){
37652 if(this.fitContainer && this.resizeEl != this.el){
37653 this.el.setSize(width, height);
37655 var size = this.adjustForComponents(width, height);
37656 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37657 this.fireEvent('resize', this, size.width, size.height);
37662 * Returns this panel's title
37665 getTitle : function(){
37667 if (typeof(this.title) != 'object') {
37672 for (var k in this.title) {
37673 if (!this.title.hasOwnProperty(k)) {
37677 if (k.indexOf('-') >= 0) {
37678 var s = k.split('-');
37679 for (var i = 0; i<s.length; i++) {
37680 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37683 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37690 * Set this panel's title
37691 * @param {String} title
37693 setTitle : function(title){
37694 this.title = title;
37696 this.region.updatePanelTitle(this, title);
37701 * Returns true is this panel was configured to be closable
37702 * @return {Boolean}
37704 isClosable : function(){
37705 return this.closable;
37708 beforeSlide : function(){
37710 this.resizeEl.clip();
37713 afterSlide : function(){
37715 this.resizeEl.unclip();
37719 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37720 * Will fail silently if the {@link #setUrl} method has not been called.
37721 * This does not activate the panel, just updates its content.
37723 refresh : function(){
37724 if(this.refreshDelegate){
37725 this.loaded = false;
37726 this.refreshDelegate();
37731 * Destroys this panel
37733 destroy : function(){
37734 this.el.removeAllListeners();
37735 var tempEl = document.createElement("span");
37736 tempEl.appendChild(this.el.dom);
37737 tempEl.innerHTML = "";
37743 * form - if the content panel contains a form - this is a reference to it.
37744 * @type {Roo.form.Form}
37748 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37749 * This contains a reference to it.
37755 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37765 * @param {Object} cfg Xtype definition of item to add.
37769 getChildContainer: function () {
37770 return this.getEl();
37775 var ret = new Roo.factory(cfg);
37780 if (cfg.xtype.match(/^Form$/)) {
37783 //if (this.footer) {
37784 // el = this.footer.container.insertSibling(false, 'before');
37786 el = this.el.createChild();
37789 this.form = new Roo.form.Form(cfg);
37792 if ( this.form.allItems.length) {
37793 this.form.render(el.dom);
37797 // should only have one of theses..
37798 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37799 // views.. should not be just added - used named prop 'view''
37801 cfg.el = this.el.appendChild(document.createElement("div"));
37804 var ret = new Roo.factory(cfg);
37806 ret.render && ret.render(false, ''); // render blank..
37816 * @class Roo.bootstrap.panel.Grid
37817 * @extends Roo.bootstrap.panel.Content
37819 * Create a new GridPanel.
37820 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37821 * @param {Object} config A the config object
37827 Roo.bootstrap.panel.Grid = function(config)
37831 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37832 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37834 config.el = this.wrapper;
37835 //this.el = this.wrapper;
37837 if (config.container) {
37838 // ctor'ed from a Border/panel.grid
37841 this.wrapper.setStyle("overflow", "hidden");
37842 this.wrapper.addClass('roo-grid-container');
37847 if(config.toolbar){
37848 var tool_el = this.wrapper.createChild();
37849 this.toolbar = Roo.factory(config.toolbar);
37851 if (config.toolbar.items) {
37852 ti = config.toolbar.items ;
37853 delete config.toolbar.items ;
37857 this.toolbar.render(tool_el);
37858 for(var i =0;i < ti.length;i++) {
37859 // Roo.log(['add child', items[i]]);
37860 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37862 this.toolbar.items = nitems;
37864 delete config.toolbar;
37867 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37868 config.grid.scrollBody = true;;
37869 config.grid.monitorWindowResize = false; // turn off autosizing
37870 config.grid.autoHeight = false;
37871 config.grid.autoWidth = false;
37873 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37875 if (config.background) {
37876 // render grid on panel activation (if panel background)
37877 this.on('activate', function(gp) {
37878 if (!gp.grid.rendered) {
37879 gp.grid.render(this.wrapper);
37880 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37885 this.grid.render(this.wrapper);
37886 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37889 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37890 // ??? needed ??? config.el = this.wrapper;
37895 // xtype created footer. - not sure if will work as we normally have to render first..
37896 if (this.footer && !this.footer.el && this.footer.xtype) {
37898 var ctr = this.grid.getView().getFooterPanel(true);
37899 this.footer.dataSource = this.grid.dataSource;
37900 this.footer = Roo.factory(this.footer, Roo);
37901 this.footer.render(ctr);
37911 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37912 getId : function(){
37913 return this.grid.id;
37917 * Returns the grid for this panel
37918 * @return {Roo.bootstrap.Table}
37920 getGrid : function(){
37924 setSize : function(width, height){
37925 if(!this.ignoreResize(width, height)){
37926 var grid = this.grid;
37927 var size = this.adjustForComponents(width, height);
37928 var gridel = grid.getGridEl();
37929 gridel.setSize(size.width, size.height);
37931 var thd = grid.getGridEl().select('thead',true).first();
37932 var tbd = grid.getGridEl().select('tbody', true).first();
37934 tbd.setSize(width, height - thd.getHeight());
37943 beforeSlide : function(){
37944 this.grid.getView().scroller.clip();
37947 afterSlide : function(){
37948 this.grid.getView().scroller.unclip();
37951 destroy : function(){
37952 this.grid.destroy();
37954 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37959 * @class Roo.bootstrap.panel.Nest
37960 * @extends Roo.bootstrap.panel.Content
37962 * Create a new Panel, that can contain a layout.Border.
37965 * @param {Roo.BorderLayout} layout The layout for this panel
37966 * @param {String/Object} config A string to set only the title or a config object
37968 Roo.bootstrap.panel.Nest = function(config)
37970 // construct with only one argument..
37971 /* FIXME - implement nicer consturctors
37972 if (layout.layout) {
37974 layout = config.layout;
37975 delete config.layout;
37977 if (layout.xtype && !layout.getEl) {
37978 // then layout needs constructing..
37979 layout = Roo.factory(layout, Roo);
37983 config.el = config.layout.getEl();
37985 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37987 config.layout.monitorWindowResize = false; // turn off autosizing
37988 this.layout = config.layout;
37989 this.layout.getEl().addClass("roo-layout-nested-layout");
37990 this.layout.parent = this;
37997 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37999 setSize : function(width, height){
38000 if(!this.ignoreResize(width, height)){
38001 var size = this.adjustForComponents(width, height);
38002 var el = this.layout.getEl();
38003 if (size.height < 1) {
38004 el.setWidth(size.width);
38006 el.setSize(size.width, size.height);
38008 var touch = el.dom.offsetWidth;
38009 this.layout.layout();
38010 // ie requires a double layout on the first pass
38011 if(Roo.isIE && !this.initialized){
38012 this.initialized = true;
38013 this.layout.layout();
38018 // activate all subpanels if not currently active..
38020 setActiveState : function(active){
38021 this.active = active;
38022 this.setActiveClass(active);
38025 this.fireEvent("deactivate", this);
38029 this.fireEvent("activate", this);
38030 // not sure if this should happen before or after..
38031 if (!this.layout) {
38032 return; // should not happen..
38035 for (var r in this.layout.regions) {
38036 reg = this.layout.getRegion(r);
38037 if (reg.getActivePanel()) {
38038 //reg.showPanel(reg.getActivePanel()); // force it to activate..
38039 reg.setActivePanel(reg.getActivePanel());
38042 if (!reg.panels.length) {
38045 reg.showPanel(reg.getPanel(0));
38054 * Returns the nested BorderLayout for this panel
38055 * @return {Roo.BorderLayout}
38057 getLayout : function(){
38058 return this.layout;
38062 * Adds a xtype elements to the layout of the nested panel
38066 xtype : 'ContentPanel',
38073 xtype : 'NestedLayoutPanel',
38079 items : [ ... list of content panels or nested layout panels.. ]
38083 * @param {Object} cfg Xtype definition of item to add.
38085 addxtype : function(cfg) {
38086 return this.layout.addxtype(cfg);
38091 * Ext JS Library 1.1.1
38092 * Copyright(c) 2006-2007, Ext JS, LLC.
38094 * Originally Released Under LGPL - original licence link has changed is not relivant.
38097 * <script type="text/javascript">
38100 * @class Roo.TabPanel
38101 * @extends Roo.util.Observable
38102 * A lightweight tab container.
38106 // basic tabs 1, built from existing content
38107 var tabs = new Roo.TabPanel("tabs1");
38108 tabs.addTab("script", "View Script");
38109 tabs.addTab("markup", "View Markup");
38110 tabs.activate("script");
38112 // more advanced tabs, built from javascript
38113 var jtabs = new Roo.TabPanel("jtabs");
38114 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38116 // set up the UpdateManager
38117 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38118 var updater = tab2.getUpdateManager();
38119 updater.setDefaultUrl("ajax1.htm");
38120 tab2.on('activate', updater.refresh, updater, true);
38122 // Use setUrl for Ajax loading
38123 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38124 tab3.setUrl("ajax2.htm", null, true);
38127 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38130 jtabs.activate("jtabs-1");
38133 * Create a new TabPanel.
38134 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38135 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38137 Roo.bootstrap.panel.Tabs = function(config){
38139 * The container element for this TabPanel.
38140 * @type Roo.Element
38142 this.el = Roo.get(config.el);
38145 if(typeof config == "boolean"){
38146 this.tabPosition = config ? "bottom" : "top";
38148 Roo.apply(this, config);
38152 if(this.tabPosition == "bottom"){
38153 // if tabs are at the bottom = create the body first.
38154 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38155 this.el.addClass("roo-tabs-bottom");
38157 // next create the tabs holders
38159 if (this.tabPosition == "west"){
38161 var reg = this.region; // fake it..
38163 if (!reg.mgr.parent) {
38166 reg = reg.mgr.parent.region;
38168 Roo.log("got nest?");
38170 if (reg.mgr.getRegion('west')) {
38171 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38172 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38173 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38174 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38175 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38183 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38184 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38185 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38186 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38191 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38194 // finally - if tabs are at the top, then create the body last..
38195 if(this.tabPosition != "bottom"){
38196 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38197 * @type Roo.Element
38199 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38200 this.el.addClass("roo-tabs-top");
38204 this.bodyEl.setStyle("position", "relative");
38206 this.active = null;
38207 this.activateDelegate = this.activate.createDelegate(this);
38212 * Fires when the active tab changes
38213 * @param {Roo.TabPanel} this
38214 * @param {Roo.TabPanelItem} activePanel The new active tab
38218 * @event beforetabchange
38219 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38220 * @param {Roo.TabPanel} this
38221 * @param {Object} e Set cancel to true on this object to cancel the tab change
38222 * @param {Roo.TabPanelItem} tab The tab being changed to
38224 "beforetabchange" : true
38227 Roo.EventManager.onWindowResize(this.onResize, this);
38228 this.cpad = this.el.getPadding("lr");
38229 this.hiddenCount = 0;
38232 // toolbar on the tabbar support...
38233 if (this.toolbar) {
38234 alert("no toolbar support yet");
38235 this.toolbar = false;
38237 var tcfg = this.toolbar;
38238 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
38239 this.toolbar = new Roo.Toolbar(tcfg);
38240 if (Roo.isSafari) {
38241 var tbl = tcfg.container.child('table', true);
38242 tbl.setAttribute('width', '100%');
38250 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38253 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38255 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38257 tabPosition : "top",
38259 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38261 currentTabWidth : 0,
38263 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38267 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38271 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38273 preferredTabWidth : 175,
38275 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38277 resizeTabs : false,
38279 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38281 monitorResize : true,
38283 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
38285 toolbar : false, // set by caller..
38287 region : false, /// set by caller
38289 disableTooltips : true, // not used yet...
38292 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38293 * @param {String} id The id of the div to use <b>or create</b>
38294 * @param {String} text The text for the tab
38295 * @param {String} content (optional) Content to put in the TabPanelItem body
38296 * @param {Boolean} closable (optional) True to create a close icon on the tab
38297 * @return {Roo.TabPanelItem} The created TabPanelItem
38299 addTab : function(id, text, content, closable, tpl)
38301 var item = new Roo.bootstrap.panel.TabItem({
38305 closable : closable,
38308 this.addTabItem(item);
38310 item.setContent(content);
38316 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38317 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38318 * @return {Roo.TabPanelItem}
38320 getTab : function(id){
38321 return this.items[id];
38325 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38326 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38328 hideTab : function(id){
38329 var t = this.items[id];
38332 this.hiddenCount++;
38333 this.autoSizeTabs();
38338 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38339 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38341 unhideTab : function(id){
38342 var t = this.items[id];
38344 t.setHidden(false);
38345 this.hiddenCount--;
38346 this.autoSizeTabs();
38351 * Adds an existing {@link Roo.TabPanelItem}.
38352 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38354 addTabItem : function(item)
38356 this.items[item.id] = item;
38357 this.items.push(item);
38358 this.autoSizeTabs();
38359 // if(this.resizeTabs){
38360 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38361 // this.autoSizeTabs();
38363 // item.autoSize();
38368 * Removes a {@link Roo.TabPanelItem}.
38369 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38371 removeTab : function(id){
38372 var items = this.items;
38373 var tab = items[id];
38374 if(!tab) { return; }
38375 var index = items.indexOf(tab);
38376 if(this.active == tab && items.length > 1){
38377 var newTab = this.getNextAvailable(index);
38382 this.stripEl.dom.removeChild(tab.pnode.dom);
38383 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38384 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38386 items.splice(index, 1);
38387 delete this.items[tab.id];
38388 tab.fireEvent("close", tab);
38389 tab.purgeListeners();
38390 this.autoSizeTabs();
38393 getNextAvailable : function(start){
38394 var items = this.items;
38396 // look for a next tab that will slide over to
38397 // replace the one being removed
38398 while(index < items.length){
38399 var item = items[++index];
38400 if(item && !item.isHidden()){
38404 // if one isn't found select the previous tab (on the left)
38407 var item = items[--index];
38408 if(item && !item.isHidden()){
38416 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38417 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38419 disableTab : function(id){
38420 var tab = this.items[id];
38421 if(tab && this.active != tab){
38427 * Enables a {@link Roo.TabPanelItem} that is disabled.
38428 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38430 enableTab : function(id){
38431 var tab = this.items[id];
38436 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38437 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38438 * @return {Roo.TabPanelItem} The TabPanelItem.
38440 activate : function(id)
38442 //Roo.log('activite:' + id);
38444 var tab = this.items[id];
38448 if(tab == this.active || tab.disabled){
38452 this.fireEvent("beforetabchange", this, e, tab);
38453 if(e.cancel !== true && !tab.disabled){
38455 this.active.hide();
38457 this.active = this.items[id];
38458 this.active.show();
38459 this.fireEvent("tabchange", this, this.active);
38465 * Gets the active {@link Roo.TabPanelItem}.
38466 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38468 getActiveTab : function(){
38469 return this.active;
38473 * Updates the tab body element to fit the height of the container element
38474 * for overflow scrolling
38475 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38477 syncHeight : function(targetHeight){
38478 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38479 var bm = this.bodyEl.getMargins();
38480 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38481 this.bodyEl.setHeight(newHeight);
38485 onResize : function(){
38486 if(this.monitorResize){
38487 this.autoSizeTabs();
38492 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38494 beginUpdate : function(){
38495 this.updating = true;
38499 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38501 endUpdate : function(){
38502 this.updating = false;
38503 this.autoSizeTabs();
38507 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38509 autoSizeTabs : function()
38511 var count = this.items.length;
38512 var vcount = count - this.hiddenCount;
38515 this.stripEl.hide();
38517 this.stripEl.show();
38520 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38525 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38526 var availWidth = Math.floor(w / vcount);
38527 var b = this.stripBody;
38528 if(b.getWidth() > w){
38529 var tabs = this.items;
38530 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38531 if(availWidth < this.minTabWidth){
38532 /*if(!this.sleft){ // incomplete scrolling code
38533 this.createScrollButtons();
38536 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38539 if(this.currentTabWidth < this.preferredTabWidth){
38540 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38546 * Returns the number of tabs in this TabPanel.
38549 getCount : function(){
38550 return this.items.length;
38554 * Resizes all the tabs to the passed width
38555 * @param {Number} The new width
38557 setTabWidth : function(width){
38558 this.currentTabWidth = width;
38559 for(var i = 0, len = this.items.length; i < len; i++) {
38560 if(!this.items[i].isHidden()) {
38561 this.items[i].setWidth(width);
38567 * Destroys this TabPanel
38568 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38570 destroy : function(removeEl){
38571 Roo.EventManager.removeResizeListener(this.onResize, this);
38572 for(var i = 0, len = this.items.length; i < len; i++){
38573 this.items[i].purgeListeners();
38575 if(removeEl === true){
38576 this.el.update("");
38581 createStrip : function(container)
38583 var strip = document.createElement("nav");
38584 strip.className = Roo.bootstrap.version == 4 ?
38585 "navbar-light bg-light" :
38586 "navbar navbar-default"; //"x-tabs-wrap";
38587 container.appendChild(strip);
38591 createStripList : function(strip)
38593 // div wrapper for retard IE
38594 // returns the "tr" element.
38595 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38596 //'<div class="x-tabs-strip-wrap">'+
38597 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38598 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38599 return strip.firstChild; //.firstChild.firstChild.firstChild;
38601 createBody : function(container)
38603 var body = document.createElement("div");
38604 Roo.id(body, "tab-body");
38605 //Roo.fly(body).addClass("x-tabs-body");
38606 Roo.fly(body).addClass("tab-content");
38607 container.appendChild(body);
38610 createItemBody :function(bodyEl, id){
38611 var body = Roo.getDom(id);
38613 body = document.createElement("div");
38616 //Roo.fly(body).addClass("x-tabs-item-body");
38617 Roo.fly(body).addClass("tab-pane");
38618 bodyEl.insertBefore(body, bodyEl.firstChild);
38622 createStripElements : function(stripEl, text, closable, tpl)
38624 var td = document.createElement("li"); // was td..
38625 td.className = 'nav-item';
38627 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38630 stripEl.appendChild(td);
38632 td.className = "x-tabs-closable";
38633 if(!this.closeTpl){
38634 this.closeTpl = new Roo.Template(
38635 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38636 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38637 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38640 var el = this.closeTpl.overwrite(td, {"text": text});
38641 var close = el.getElementsByTagName("div")[0];
38642 var inner = el.getElementsByTagName("em")[0];
38643 return {"el": el, "close": close, "inner": inner};
38646 // not sure what this is..
38647 // if(!this.tabTpl){
38648 //this.tabTpl = new Roo.Template(
38649 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38650 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38652 // this.tabTpl = new Roo.Template(
38653 // '<a href="#">' +
38654 // '<span unselectable="on"' +
38655 // (this.disableTooltips ? '' : ' title="{text}"') +
38656 // ' >{text}</span></a>'
38662 var template = tpl || this.tabTpl || false;
38665 template = new Roo.Template(
38666 Roo.bootstrap.version == 4 ?
38668 '<a class="nav-link" href="#" unselectable="on"' +
38669 (this.disableTooltips ? '' : ' title="{text}"') +
38672 '<a class="nav-link" href="#">' +
38673 '<span unselectable="on"' +
38674 (this.disableTooltips ? '' : ' title="{text}"') +
38675 ' >{text}</span></a>'
38680 switch (typeof(template)) {
38684 template = new Roo.Template(template);
38690 var el = template.overwrite(td, {"text": text});
38692 var inner = el.getElementsByTagName("span")[0];
38694 return {"el": el, "inner": inner};
38702 * @class Roo.TabPanelItem
38703 * @extends Roo.util.Observable
38704 * Represents an individual item (tab plus body) in a TabPanel.
38705 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38706 * @param {String} id The id of this TabPanelItem
38707 * @param {String} text The text for the tab of this TabPanelItem
38708 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38710 Roo.bootstrap.panel.TabItem = function(config){
38712 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38713 * @type Roo.TabPanel
38715 this.tabPanel = config.panel;
38717 * The id for this TabPanelItem
38720 this.id = config.id;
38722 this.disabled = false;
38724 this.text = config.text;
38726 this.loaded = false;
38727 this.closable = config.closable;
38730 * The body element for this TabPanelItem.
38731 * @type Roo.Element
38733 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38734 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38735 this.bodyEl.setStyle("display", "block");
38736 this.bodyEl.setStyle("zoom", "1");
38737 //this.hideAction();
38739 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38741 this.el = Roo.get(els.el);
38742 this.inner = Roo.get(els.inner, true);
38743 this.textEl = Roo.bootstrap.version == 4 ?
38744 this.el : Roo.get(this.el.dom.firstChild, true);
38746 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38747 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38750 // this.el.on("mousedown", this.onTabMouseDown, this);
38751 this.el.on("click", this.onTabClick, this);
38753 if(config.closable){
38754 var c = Roo.get(els.close, true);
38755 c.dom.title = this.closeText;
38756 c.addClassOnOver("close-over");
38757 c.on("click", this.closeClick, this);
38763 * Fires when this tab becomes the active tab.
38764 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38765 * @param {Roo.TabPanelItem} this
38769 * @event beforeclose
38770 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38771 * @param {Roo.TabPanelItem} this
38772 * @param {Object} e Set cancel to true on this object to cancel the close.
38774 "beforeclose": true,
38777 * Fires when this tab is closed.
38778 * @param {Roo.TabPanelItem} this
38782 * @event deactivate
38783 * Fires when this tab is no longer the active tab.
38784 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38785 * @param {Roo.TabPanelItem} this
38787 "deactivate" : true
38789 this.hidden = false;
38791 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38794 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38796 purgeListeners : function(){
38797 Roo.util.Observable.prototype.purgeListeners.call(this);
38798 this.el.removeAllListeners();
38801 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38804 this.status_node.addClass("active");
38807 this.tabPanel.stripWrap.repaint();
38809 this.fireEvent("activate", this.tabPanel, this);
38813 * Returns true if this tab is the active tab.
38814 * @return {Boolean}
38816 isActive : function(){
38817 return this.tabPanel.getActiveTab() == this;
38821 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38824 this.status_node.removeClass("active");
38826 this.fireEvent("deactivate", this.tabPanel, this);
38829 hideAction : function(){
38830 this.bodyEl.hide();
38831 this.bodyEl.setStyle("position", "absolute");
38832 this.bodyEl.setLeft("-20000px");
38833 this.bodyEl.setTop("-20000px");
38836 showAction : function(){
38837 this.bodyEl.setStyle("position", "relative");
38838 this.bodyEl.setTop("");
38839 this.bodyEl.setLeft("");
38840 this.bodyEl.show();
38844 * Set the tooltip for the tab.
38845 * @param {String} tooltip The tab's tooltip
38847 setTooltip : function(text){
38848 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38849 this.textEl.dom.qtip = text;
38850 this.textEl.dom.removeAttribute('title');
38852 this.textEl.dom.title = text;
38856 onTabClick : function(e){
38857 e.preventDefault();
38858 this.tabPanel.activate(this.id);
38861 onTabMouseDown : function(e){
38862 e.preventDefault();
38863 this.tabPanel.activate(this.id);
38866 getWidth : function(){
38867 return this.inner.getWidth();
38870 setWidth : function(width){
38871 var iwidth = width - this.linode.getPadding("lr");
38872 this.inner.setWidth(iwidth);
38873 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38874 this.linode.setWidth(width);
38878 * Show or hide the tab
38879 * @param {Boolean} hidden True to hide or false to show.
38881 setHidden : function(hidden){
38882 this.hidden = hidden;
38883 this.linode.setStyle("display", hidden ? "none" : "");
38887 * Returns true if this tab is "hidden"
38888 * @return {Boolean}
38890 isHidden : function(){
38891 return this.hidden;
38895 * Returns the text for this tab
38898 getText : function(){
38902 autoSize : function(){
38903 //this.el.beginMeasure();
38904 this.textEl.setWidth(1);
38906 * #2804 [new] Tabs in Roojs
38907 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38909 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38910 //this.el.endMeasure();
38914 * Sets the text for the tab (Note: this also sets the tooltip text)
38915 * @param {String} text The tab's text and tooltip
38917 setText : function(text){
38919 this.textEl.update(text);
38920 this.setTooltip(text);
38921 //if(!this.tabPanel.resizeTabs){
38922 // this.autoSize();
38926 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38928 activate : function(){
38929 this.tabPanel.activate(this.id);
38933 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38935 disable : function(){
38936 if(this.tabPanel.active != this){
38937 this.disabled = true;
38938 this.status_node.addClass("disabled");
38943 * Enables this TabPanelItem if it was previously disabled.
38945 enable : function(){
38946 this.disabled = false;
38947 this.status_node.removeClass("disabled");
38951 * Sets the content for this TabPanelItem.
38952 * @param {String} content The content
38953 * @param {Boolean} loadScripts true to look for and load scripts
38955 setContent : function(content, loadScripts){
38956 this.bodyEl.update(content, loadScripts);
38960 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38961 * @return {Roo.UpdateManager} The UpdateManager
38963 getUpdateManager : function(){
38964 return this.bodyEl.getUpdateManager();
38968 * Set a URL to be used to load the content for this TabPanelItem.
38969 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38970 * @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)
38971 * @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)
38972 * @return {Roo.UpdateManager} The UpdateManager
38974 setUrl : function(url, params, loadOnce){
38975 if(this.refreshDelegate){
38976 this.un('activate', this.refreshDelegate);
38978 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38979 this.on("activate", this.refreshDelegate);
38980 return this.bodyEl.getUpdateManager();
38984 _handleRefresh : function(url, params, loadOnce){
38985 if(!loadOnce || !this.loaded){
38986 var updater = this.bodyEl.getUpdateManager();
38987 updater.update(url, params, this._setLoaded.createDelegate(this));
38992 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38993 * Will fail silently if the setUrl method has not been called.
38994 * This does not activate the panel, just updates its content.
38996 refresh : function(){
38997 if(this.refreshDelegate){
38998 this.loaded = false;
38999 this.refreshDelegate();
39004 _setLoaded : function(){
39005 this.loaded = true;
39009 closeClick : function(e){
39012 this.fireEvent("beforeclose", this, o);
39013 if(o.cancel !== true){
39014 this.tabPanel.removeTab(this.id);
39018 * The text displayed in the tooltip for the close icon.
39021 closeText : "Close this tab"
39024 * This script refer to:
39025 * Title: International Telephone Input
39026 * Author: Jack O'Connor
39027 * Code version: v12.1.12
39028 * Availability: https://github.com/jackocnr/intl-tel-input.git
39031 Roo.bootstrap.PhoneInputData = function() {
39034 "Afghanistan (افغانستان)",
39039 "Albania (Shqipëri)",
39044 "Algeria (الجزائر)",
39069 "Antigua and Barbuda",
39079 "Armenia (Հայաստան)",
39095 "Austria (Österreich)",
39100 "Azerbaijan (Azərbaycan)",
39110 "Bahrain (البحرين)",
39115 "Bangladesh (বাংলাদেশ)",
39125 "Belarus (Беларусь)",
39130 "Belgium (België)",
39160 "Bosnia and Herzegovina (Босна и Херцеговина)",
39175 "British Indian Ocean Territory",
39180 "British Virgin Islands",
39190 "Bulgaria (България)",
39200 "Burundi (Uburundi)",
39205 "Cambodia (កម្ពុជា)",
39210 "Cameroon (Cameroun)",
39219 ["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"]
39222 "Cape Verde (Kabu Verdi)",
39227 "Caribbean Netherlands",
39238 "Central African Republic (République centrafricaine)",
39258 "Christmas Island",
39264 "Cocos (Keeling) Islands",
39275 "Comoros (جزر القمر)",
39280 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39285 "Congo (Republic) (Congo-Brazzaville)",
39305 "Croatia (Hrvatska)",
39326 "Czech Republic (Česká republika)",
39331 "Denmark (Danmark)",
39346 "Dominican Republic (República Dominicana)",
39350 ["809", "829", "849"]
39368 "Equatorial Guinea (Guinea Ecuatorial)",
39388 "Falkland Islands (Islas Malvinas)",
39393 "Faroe Islands (Føroyar)",
39414 "French Guiana (Guyane française)",
39419 "French Polynesia (Polynésie française)",
39434 "Georgia (საქართველო)",
39439 "Germany (Deutschland)",
39459 "Greenland (Kalaallit Nunaat)",
39496 "Guinea-Bissau (Guiné Bissau)",
39521 "Hungary (Magyarország)",
39526 "Iceland (Ísland)",
39546 "Iraq (العراق)",
39562 "Israel (ישראל)",
39589 "Jordan (الأردن)",
39594 "Kazakhstan (Казахстан)",
39615 "Kuwait (الكويت)",
39620 "Kyrgyzstan (Кыргызстан)",
39630 "Latvia (Latvija)",
39635 "Lebanon (لبنان)",
39650 "Libya (ليبيا)",
39660 "Lithuania (Lietuva)",
39675 "Macedonia (FYROM) (Македонија)",
39680 "Madagascar (Madagasikara)",
39710 "Marshall Islands",
39720 "Mauritania (موريتانيا)",
39725 "Mauritius (Moris)",
39746 "Moldova (Republica Moldova)",
39756 "Mongolia (Монгол)",
39761 "Montenegro (Crna Gora)",
39771 "Morocco (المغرب)",
39777 "Mozambique (Moçambique)",
39782 "Myanmar (Burma) (မြန်မာ)",
39787 "Namibia (Namibië)",
39802 "Netherlands (Nederland)",
39807 "New Caledonia (Nouvelle-Calédonie)",
39842 "North Korea (조선 민주주의 인민 공화국)",
39847 "Northern Mariana Islands",
39863 "Pakistan (پاکستان)",
39873 "Palestine (فلسطين)",
39883 "Papua New Guinea",
39925 "Réunion (La Réunion)",
39931 "Romania (România)",
39947 "Saint Barthélemy",
39958 "Saint Kitts and Nevis",
39968 "Saint Martin (Saint-Martin (partie française))",
39974 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39979 "Saint Vincent and the Grenadines",
39994 "São Tomé and Príncipe (São Tomé e Príncipe)",
39999 "Saudi Arabia (المملكة العربية السعودية)",
40004 "Senegal (Sénégal)",
40034 "Slovakia (Slovensko)",
40039 "Slovenia (Slovenija)",
40049 "Somalia (Soomaaliya)",
40059 "South Korea (대한민국)",
40064 "South Sudan (جنوب السودان)",
40074 "Sri Lanka (ශ්රී ලංකාව)",
40079 "Sudan (السودان)",
40089 "Svalbard and Jan Mayen",
40100 "Sweden (Sverige)",
40105 "Switzerland (Schweiz)",
40110 "Syria (سوريا)",
40155 "Trinidad and Tobago",
40160 "Tunisia (تونس)",
40165 "Turkey (Türkiye)",
40175 "Turks and Caicos Islands",
40185 "U.S. Virgin Islands",
40195 "Ukraine (Україна)",
40200 "United Arab Emirates (الإمارات العربية المتحدة)",
40222 "Uzbekistan (Oʻzbekiston)",
40232 "Vatican City (Città del Vaticano)",
40243 "Vietnam (Việt Nam)",
40248 "Wallis and Futuna (Wallis-et-Futuna)",
40253 "Western Sahara (الصحراء الغربية)",
40259 "Yemen (اليمن)",
40283 * This script refer to:
40284 * Title: International Telephone Input
40285 * Author: Jack O'Connor
40286 * Code version: v12.1.12
40287 * Availability: https://github.com/jackocnr/intl-tel-input.git
40291 * @class Roo.bootstrap.PhoneInput
40292 * @extends Roo.bootstrap.TriggerField
40293 * An input with International dial-code selection
40295 * @cfg {String} defaultDialCode default '+852'
40296 * @cfg {Array} preferedCountries default []
40299 * Create a new PhoneInput.
40300 * @param {Object} config Configuration options
40303 Roo.bootstrap.PhoneInput = function(config) {
40304 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40307 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40309 listWidth: undefined,
40311 selectedClass: 'active',
40313 invalidClass : "has-warning",
40315 validClass: 'has-success',
40317 allowed: '0123456789',
40322 * @cfg {String} defaultDialCode The default dial code when initializing the input
40324 defaultDialCode: '+852',
40327 * @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
40329 preferedCountries: false,
40331 getAutoCreate : function()
40333 var data = Roo.bootstrap.PhoneInputData();
40334 var align = this.labelAlign || this.parentLabelAlign();
40337 this.allCountries = [];
40338 this.dialCodeMapping = [];
40340 for (var i = 0; i < data.length; i++) {
40342 this.allCountries[i] = {
40346 priority: c[3] || 0,
40347 areaCodes: c[4] || null
40349 this.dialCodeMapping[c[2]] = {
40352 priority: c[3] || 0,
40353 areaCodes: c[4] || null
40365 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40366 maxlength: this.max_length,
40367 cls : 'form-control tel-input',
40368 autocomplete: 'new-password'
40371 var hiddenInput = {
40374 cls: 'hidden-tel-input'
40378 hiddenInput.name = this.name;
40381 if (this.disabled) {
40382 input.disabled = true;
40385 var flag_container = {
40402 cls: this.hasFeedback ? 'has-feedback' : '',
40408 cls: 'dial-code-holder',
40415 cls: 'roo-select2-container input-group',
40422 if (this.fieldLabel.length) {
40425 tooltip: 'This field is required'
40431 cls: 'control-label',
40437 html: this.fieldLabel
40440 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40446 if(this.indicatorpos == 'right') {
40447 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40454 if(align == 'left') {
40462 if(this.labelWidth > 12){
40463 label.style = "width: " + this.labelWidth + 'px';
40465 if(this.labelWidth < 13 && this.labelmd == 0){
40466 this.labelmd = this.labelWidth;
40468 if(this.labellg > 0){
40469 label.cls += ' col-lg-' + this.labellg;
40470 input.cls += ' col-lg-' + (12 - this.labellg);
40472 if(this.labelmd > 0){
40473 label.cls += ' col-md-' + this.labelmd;
40474 container.cls += ' col-md-' + (12 - this.labelmd);
40476 if(this.labelsm > 0){
40477 label.cls += ' col-sm-' + this.labelsm;
40478 container.cls += ' col-sm-' + (12 - this.labelsm);
40480 if(this.labelxs > 0){
40481 label.cls += ' col-xs-' + this.labelxs;
40482 container.cls += ' col-xs-' + (12 - this.labelxs);
40492 var settings = this;
40494 ['xs','sm','md','lg'].map(function(size){
40495 if (settings[size]) {
40496 cfg.cls += ' col-' + size + '-' + settings[size];
40500 this.store = new Roo.data.Store({
40501 proxy : new Roo.data.MemoryProxy({}),
40502 reader : new Roo.data.JsonReader({
40513 'name' : 'dialCode',
40517 'name' : 'priority',
40521 'name' : 'areaCodes',
40528 if(!this.preferedCountries) {
40529 this.preferedCountries = [
40536 var p = this.preferedCountries.reverse();
40539 for (var i = 0; i < p.length; i++) {
40540 for (var j = 0; j < this.allCountries.length; j++) {
40541 if(this.allCountries[j].iso2 == p[i]) {
40542 var t = this.allCountries[j];
40543 this.allCountries.splice(j,1);
40544 this.allCountries.unshift(t);
40550 this.store.proxy.data = {
40552 data: this.allCountries
40558 initEvents : function()
40561 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40563 this.indicator = this.indicatorEl();
40564 this.flag = this.flagEl();
40565 this.dialCodeHolder = this.dialCodeHolderEl();
40567 this.trigger = this.el.select('div.flag-box',true).first();
40568 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40573 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40574 _this.list.setWidth(lw);
40577 this.list.on('mouseover', this.onViewOver, this);
40578 this.list.on('mousemove', this.onViewMove, this);
40579 this.inputEl().on("keyup", this.onKeyUp, this);
40580 this.inputEl().on("keypress", this.onKeyPress, this);
40582 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40584 this.view = new Roo.View(this.list, this.tpl, {
40585 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40588 this.view.on('click', this.onViewClick, this);
40589 this.setValue(this.defaultDialCode);
40592 onTriggerClick : function(e)
40594 Roo.log('trigger click');
40599 if(this.isExpanded()){
40601 this.hasFocus = false;
40603 this.store.load({});
40604 this.hasFocus = true;
40609 isExpanded : function()
40611 return this.list.isVisible();
40614 collapse : function()
40616 if(!this.isExpanded()){
40620 Roo.get(document).un('mousedown', this.collapseIf, this);
40621 Roo.get(document).un('mousewheel', this.collapseIf, this);
40622 this.fireEvent('collapse', this);
40626 expand : function()
40630 if(this.isExpanded() || !this.hasFocus){
40634 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40635 this.list.setWidth(lw);
40638 this.restrictHeight();
40640 Roo.get(document).on('mousedown', this.collapseIf, this);
40641 Roo.get(document).on('mousewheel', this.collapseIf, this);
40643 this.fireEvent('expand', this);
40646 restrictHeight : function()
40648 this.list.alignTo(this.inputEl(), this.listAlign);
40649 this.list.alignTo(this.inputEl(), this.listAlign);
40652 onViewOver : function(e, t)
40654 if(this.inKeyMode){
40657 var item = this.view.findItemFromChild(t);
40660 var index = this.view.indexOf(item);
40661 this.select(index, false);
40666 onViewClick : function(view, doFocus, el, e)
40668 var index = this.view.getSelectedIndexes()[0];
40670 var r = this.store.getAt(index);
40673 this.onSelect(r, index);
40675 if(doFocus !== false && !this.blockFocus){
40676 this.inputEl().focus();
40680 onViewMove : function(e, t)
40682 this.inKeyMode = false;
40685 select : function(index, scrollIntoView)
40687 this.selectedIndex = index;
40688 this.view.select(index);
40689 if(scrollIntoView !== false){
40690 var el = this.view.getNode(index);
40692 this.list.scrollChildIntoView(el, false);
40697 createList : function()
40699 this.list = Roo.get(document.body).createChild({
40701 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40702 style: 'display:none'
40705 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40708 collapseIf : function(e)
40710 var in_combo = e.within(this.el);
40711 var in_list = e.within(this.list);
40712 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40714 if (in_combo || in_list || is_list) {
40720 onSelect : function(record, index)
40722 if(this.fireEvent('beforeselect', this, record, index) !== false){
40724 this.setFlagClass(record.data.iso2);
40725 this.setDialCode(record.data.dialCode);
40726 this.hasFocus = false;
40728 this.fireEvent('select', this, record, index);
40732 flagEl : function()
40734 var flag = this.el.select('div.flag',true).first();
40741 dialCodeHolderEl : function()
40743 var d = this.el.select('input.dial-code-holder',true).first();
40750 setDialCode : function(v)
40752 this.dialCodeHolder.dom.value = '+'+v;
40755 setFlagClass : function(n)
40757 this.flag.dom.className = 'flag '+n;
40760 getValue : function()
40762 var v = this.inputEl().getValue();
40763 if(this.dialCodeHolder) {
40764 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40769 setValue : function(v)
40771 var d = this.getDialCode(v);
40773 //invalid dial code
40774 if(v.length == 0 || !d || d.length == 0) {
40776 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40777 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40783 this.setFlagClass(this.dialCodeMapping[d].iso2);
40784 this.setDialCode(d);
40785 this.inputEl().dom.value = v.replace('+'+d,'');
40786 this.hiddenEl().dom.value = this.getValue();
40791 getDialCode : function(v)
40795 if (v.length == 0) {
40796 return this.dialCodeHolder.dom.value;
40800 if (v.charAt(0) != "+") {
40803 var numericChars = "";
40804 for (var i = 1; i < v.length; i++) {
40805 var c = v.charAt(i);
40808 if (this.dialCodeMapping[numericChars]) {
40809 dialCode = v.substr(1, i);
40811 if (numericChars.length == 4) {
40821 this.setValue(this.defaultDialCode);
40825 hiddenEl : function()
40827 return this.el.select('input.hidden-tel-input',true).first();
40830 // after setting val
40831 onKeyUp : function(e){
40832 this.setValue(this.getValue());
40835 onKeyPress : function(e){
40836 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40843 * @class Roo.bootstrap.MoneyField
40844 * @extends Roo.bootstrap.ComboBox
40845 * Bootstrap MoneyField class
40848 * Create a new MoneyField.
40849 * @param {Object} config Configuration options
40852 Roo.bootstrap.MoneyField = function(config) {
40854 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40858 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40861 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40863 allowDecimals : true,
40865 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40867 decimalSeparator : ".",
40869 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40871 decimalPrecision : 0,
40873 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40875 allowNegative : true,
40877 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40881 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40883 minValue : Number.NEGATIVE_INFINITY,
40885 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40887 maxValue : Number.MAX_VALUE,
40889 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40891 minText : "The minimum value for this field is {0}",
40893 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40895 maxText : "The maximum value for this field is {0}",
40897 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40898 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40900 nanText : "{0} is not a valid number",
40902 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40906 * @cfg {String} defaults currency of the MoneyField
40907 * value should be in lkey
40909 defaultCurrency : false,
40911 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40913 thousandsDelimiter : false,
40915 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40926 getAutoCreate : function()
40928 var align = this.labelAlign || this.parentLabelAlign();
40940 cls : 'form-control roo-money-amount-input',
40941 autocomplete: 'new-password'
40944 var hiddenInput = {
40948 cls: 'hidden-number-input'
40951 if(this.max_length) {
40952 input.maxlength = this.max_length;
40956 hiddenInput.name = this.name;
40959 if (this.disabled) {
40960 input.disabled = true;
40963 var clg = 12 - this.inputlg;
40964 var cmd = 12 - this.inputmd;
40965 var csm = 12 - this.inputsm;
40966 var cxs = 12 - this.inputxs;
40970 cls : 'row roo-money-field',
40974 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40978 cls: 'roo-select2-container input-group',
40982 cls : 'form-control roo-money-currency-input',
40983 autocomplete: 'new-password',
40985 name : this.currencyName
40989 cls : 'input-group-addon',
41003 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41007 cls: this.hasFeedback ? 'has-feedback' : '',
41018 if (this.fieldLabel.length) {
41021 tooltip: 'This field is required'
41027 cls: 'control-label',
41033 html: this.fieldLabel
41036 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41042 if(this.indicatorpos == 'right') {
41043 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41050 if(align == 'left') {
41058 if(this.labelWidth > 12){
41059 label.style = "width: " + this.labelWidth + 'px';
41061 if(this.labelWidth < 13 && this.labelmd == 0){
41062 this.labelmd = this.labelWidth;
41064 if(this.labellg > 0){
41065 label.cls += ' col-lg-' + this.labellg;
41066 input.cls += ' col-lg-' + (12 - this.labellg);
41068 if(this.labelmd > 0){
41069 label.cls += ' col-md-' + this.labelmd;
41070 container.cls += ' col-md-' + (12 - this.labelmd);
41072 if(this.labelsm > 0){
41073 label.cls += ' col-sm-' + this.labelsm;
41074 container.cls += ' col-sm-' + (12 - this.labelsm);
41076 if(this.labelxs > 0){
41077 label.cls += ' col-xs-' + this.labelxs;
41078 container.cls += ' col-xs-' + (12 - this.labelxs);
41089 var settings = this;
41091 ['xs','sm','md','lg'].map(function(size){
41092 if (settings[size]) {
41093 cfg.cls += ' col-' + size + '-' + settings[size];
41100 initEvents : function()
41102 this.indicator = this.indicatorEl();
41104 this.initCurrencyEvent();
41106 this.initNumberEvent();
41109 initCurrencyEvent : function()
41112 throw "can not find store for combo";
41115 this.store = Roo.factory(this.store, Roo.data);
41116 this.store.parent = this;
41120 this.triggerEl = this.el.select('.input-group-addon', true).first();
41122 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41127 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41128 _this.list.setWidth(lw);
41131 this.list.on('mouseover', this.onViewOver, this);
41132 this.list.on('mousemove', this.onViewMove, this);
41133 this.list.on('scroll', this.onViewScroll, this);
41136 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41139 this.view = new Roo.View(this.list, this.tpl, {
41140 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41143 this.view.on('click', this.onViewClick, this);
41145 this.store.on('beforeload', this.onBeforeLoad, this);
41146 this.store.on('load', this.onLoad, this);
41147 this.store.on('loadexception', this.onLoadException, this);
41149 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41150 "up" : function(e){
41151 this.inKeyMode = true;
41155 "down" : function(e){
41156 if(!this.isExpanded()){
41157 this.onTriggerClick();
41159 this.inKeyMode = true;
41164 "enter" : function(e){
41167 if(this.fireEvent("specialkey", this, e)){
41168 this.onViewClick(false);
41174 "esc" : function(e){
41178 "tab" : function(e){
41181 if(this.fireEvent("specialkey", this, e)){
41182 this.onViewClick(false);
41190 doRelay : function(foo, bar, hname){
41191 if(hname == 'down' || this.scope.isExpanded()){
41192 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41200 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41204 initNumberEvent : function(e)
41206 this.inputEl().on("keydown" , this.fireKey, this);
41207 this.inputEl().on("focus", this.onFocus, this);
41208 this.inputEl().on("blur", this.onBlur, this);
41210 this.inputEl().relayEvent('keyup', this);
41212 if(this.indicator){
41213 this.indicator.addClass('invisible');
41216 this.originalValue = this.getValue();
41218 if(this.validationEvent == 'keyup'){
41219 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41220 this.inputEl().on('keyup', this.filterValidation, this);
41222 else if(this.validationEvent !== false){
41223 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41226 if(this.selectOnFocus){
41227 this.on("focus", this.preFocus, this);
41230 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41231 this.inputEl().on("keypress", this.filterKeys, this);
41233 this.inputEl().relayEvent('keypress', this);
41236 var allowed = "0123456789";
41238 if(this.allowDecimals){
41239 allowed += this.decimalSeparator;
41242 if(this.allowNegative){
41246 if(this.thousandsDelimiter) {
41250 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41252 var keyPress = function(e){
41254 var k = e.getKey();
41256 var c = e.getCharCode();
41259 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41260 allowed.indexOf(String.fromCharCode(c)) === -1
41266 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41270 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41275 this.inputEl().on("keypress", keyPress, this);
41279 onTriggerClick : function(e)
41286 this.loadNext = false;
41288 if(this.isExpanded()){
41293 this.hasFocus = true;
41295 if(this.triggerAction == 'all') {
41296 this.doQuery(this.allQuery, true);
41300 this.doQuery(this.getRawValue());
41303 getCurrency : function()
41305 var v = this.currencyEl().getValue();
41310 restrictHeight : function()
41312 this.list.alignTo(this.currencyEl(), this.listAlign);
41313 this.list.alignTo(this.currencyEl(), this.listAlign);
41316 onViewClick : function(view, doFocus, el, e)
41318 var index = this.view.getSelectedIndexes()[0];
41320 var r = this.store.getAt(index);
41323 this.onSelect(r, index);
41327 onSelect : function(record, index){
41329 if(this.fireEvent('beforeselect', this, record, index) !== false){
41331 this.setFromCurrencyData(index > -1 ? record.data : false);
41335 this.fireEvent('select', this, record, index);
41339 setFromCurrencyData : function(o)
41343 this.lastCurrency = o;
41345 if (this.currencyField) {
41346 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41348 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41351 this.lastSelectionText = currency;
41353 //setting default currency
41354 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41355 this.setCurrency(this.defaultCurrency);
41359 this.setCurrency(currency);
41362 setFromData : function(o)
41366 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41368 this.setFromCurrencyData(c);
41373 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41375 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41378 this.setValue(value);
41382 setCurrency : function(v)
41384 this.currencyValue = v;
41387 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41392 setValue : function(v)
41394 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41400 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41402 this.inputEl().dom.value = (v == '') ? '' :
41403 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41405 if(!this.allowZero && v === '0') {
41406 this.hiddenEl().dom.value = '';
41407 this.inputEl().dom.value = '';
41414 getRawValue : function()
41416 var v = this.inputEl().getValue();
41421 getValue : function()
41423 return this.fixPrecision(this.parseValue(this.getRawValue()));
41426 parseValue : function(value)
41428 if(this.thousandsDelimiter) {
41430 r = new RegExp(",", "g");
41431 value = value.replace(r, "");
41434 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41435 return isNaN(value) ? '' : value;
41439 fixPrecision : function(value)
41441 if(this.thousandsDelimiter) {
41443 r = new RegExp(",", "g");
41444 value = value.replace(r, "");
41447 var nan = isNaN(value);
41449 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41450 return nan ? '' : value;
41452 return parseFloat(value).toFixed(this.decimalPrecision);
41455 decimalPrecisionFcn : function(v)
41457 return Math.floor(v);
41460 validateValue : function(value)
41462 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41466 var num = this.parseValue(value);
41469 this.markInvalid(String.format(this.nanText, value));
41473 if(num < this.minValue){
41474 this.markInvalid(String.format(this.minText, this.minValue));
41478 if(num > this.maxValue){
41479 this.markInvalid(String.format(this.maxText, this.maxValue));
41486 validate : function()
41488 if(this.disabled || this.allowBlank){
41493 var currency = this.getCurrency();
41495 if(this.validateValue(this.getRawValue()) && currency.length){
41500 this.markInvalid();
41504 getName: function()
41509 beforeBlur : function()
41515 var v = this.parseValue(this.getRawValue());
41522 onBlur : function()
41526 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41527 //this.el.removeClass(this.focusClass);
41530 this.hasFocus = false;
41532 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41536 var v = this.getValue();
41538 if(String(v) !== String(this.startValue)){
41539 this.fireEvent('change', this, v, this.startValue);
41542 this.fireEvent("blur", this);
41545 inputEl : function()
41547 return this.el.select('.roo-money-amount-input', true).first();
41550 currencyEl : function()
41552 return this.el.select('.roo-money-currency-input', true).first();
41555 hiddenEl : function()
41557 return this.el.select('input.hidden-number-input',true).first();
41561 * @class Roo.bootstrap.BezierSignature
41562 * @extends Roo.bootstrap.Component
41563 * Bootstrap BezierSignature class
41564 * This script refer to:
41565 * Title: Signature Pad
41567 * Availability: https://github.com/szimek/signature_pad
41570 * Create a new BezierSignature
41571 * @param {Object} config The config object
41574 Roo.bootstrap.BezierSignature = function(config){
41575 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41581 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41588 mouse_btn_down: true,
41591 * @cfg {int} canvas height
41593 canvas_height: '200px',
41596 * @cfg {float|function} Radius of a single dot.
41601 * @cfg {float} Minimum width of a line. Defaults to 0.5.
41606 * @cfg {float} Maximum width of a line. Defaults to 2.5.
41611 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41616 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41621 * @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.
41623 bg_color: 'rgba(0, 0, 0, 0)',
41626 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41628 dot_color: 'black',
41631 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41633 velocity_filter_weight: 0.7,
41636 * @cfg {function} Callback when stroke begin.
41641 * @cfg {function} Callback when stroke end.
41645 getAutoCreate : function()
41647 var cls = 'roo-signature column';
41650 cls += ' ' + this.cls;
41660 for(var i = 0; i < col_sizes.length; i++) {
41661 if(this[col_sizes[i]]) {
41662 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41672 cls: 'roo-signature-body',
41676 cls: 'roo-signature-body-canvas',
41677 height: this.canvas_height,
41678 width: this.canvas_width
41685 style: 'display: none'
41693 initEvents: function()
41695 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41697 var canvas = this.canvasEl();
41699 // mouse && touch event swapping...
41700 canvas.dom.style.touchAction = 'none';
41701 canvas.dom.style.msTouchAction = 'none';
41703 this.mouse_btn_down = false;
41704 canvas.on('mousedown', this._handleMouseDown, this);
41705 canvas.on('mousemove', this._handleMouseMove, this);
41706 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41708 if (window.PointerEvent) {
41709 canvas.on('pointerdown', this._handleMouseDown, this);
41710 canvas.on('pointermove', this._handleMouseMove, this);
41711 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41714 if ('ontouchstart' in window) {
41715 canvas.on('touchstart', this._handleTouchStart, this);
41716 canvas.on('touchmove', this._handleTouchMove, this);
41717 canvas.on('touchend', this._handleTouchEnd, this);
41720 Roo.EventManager.onWindowResize(this.resize, this, true);
41722 // file input event
41723 this.fileEl().on('change', this.uploadImage, this);
41730 resize: function(){
41732 var canvas = this.canvasEl().dom;
41733 var ctx = this.canvasElCtx();
41734 var img_data = false;
41736 if(canvas.width > 0) {
41737 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41739 // setting canvas width will clean img data
41742 var style = window.getComputedStyle ?
41743 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41745 var padding_left = parseInt(style.paddingLeft) || 0;
41746 var padding_right = parseInt(style.paddingRight) || 0;
41748 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41751 ctx.putImageData(img_data, 0, 0);
41755 _handleMouseDown: function(e)
41757 if (e.browserEvent.which === 1) {
41758 this.mouse_btn_down = true;
41759 this.strokeBegin(e);
41763 _handleMouseMove: function (e)
41765 if (this.mouse_btn_down) {
41766 this.strokeMoveUpdate(e);
41770 _handleMouseUp: function (e)
41772 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41773 this.mouse_btn_down = false;
41778 _handleTouchStart: function (e) {
41780 e.preventDefault();
41781 if (e.browserEvent.targetTouches.length === 1) {
41782 // var touch = e.browserEvent.changedTouches[0];
41783 // this.strokeBegin(touch);
41785 this.strokeBegin(e); // assume e catching the correct xy...
41789 _handleTouchMove: function (e) {
41790 e.preventDefault();
41791 // var touch = event.targetTouches[0];
41792 // _this._strokeMoveUpdate(touch);
41793 this.strokeMoveUpdate(e);
41796 _handleTouchEnd: function (e) {
41797 var wasCanvasTouched = e.target === this.canvasEl().dom;
41798 if (wasCanvasTouched) {
41799 e.preventDefault();
41800 // var touch = event.changedTouches[0];
41801 // _this._strokeEnd(touch);
41806 reset: function () {
41807 this._lastPoints = [];
41808 this._lastVelocity = 0;
41809 this._lastWidth = (this.min_width + this.max_width) / 2;
41810 this.canvasElCtx().fillStyle = this.dot_color;
41813 strokeMoveUpdate: function(e)
41815 this.strokeUpdate(e);
41817 if (this.throttle) {
41818 this.throttleStroke(this.strokeUpdate, this.throttle);
41821 this.strokeUpdate(e);
41825 strokeBegin: function(e)
41827 var newPointGroup = {
41828 color: this.dot_color,
41832 if (typeof this.onBegin === 'function') {
41836 this.curve_data.push(newPointGroup);
41838 this.strokeUpdate(e);
41841 strokeUpdate: function(e)
41843 var rect = this.canvasEl().dom.getBoundingClientRect();
41844 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41845 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41846 var lastPoints = lastPointGroup.points;
41847 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41848 var isLastPointTooClose = lastPoint
41849 ? point.distanceTo(lastPoint) <= this.min_distance
41851 var color = lastPointGroup.color;
41852 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41853 var curve = this.addPoint(point);
41855 this.drawDot({color: color, point: point});
41858 this.drawCurve({color: color, curve: curve});
41868 strokeEnd: function(e)
41870 this.strokeUpdate(e);
41871 if (typeof this.onEnd === 'function') {
41876 addPoint: function (point) {
41877 var _lastPoints = this._lastPoints;
41878 _lastPoints.push(point);
41879 if (_lastPoints.length > 2) {
41880 if (_lastPoints.length === 3) {
41881 _lastPoints.unshift(_lastPoints[0]);
41883 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41884 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41885 _lastPoints.shift();
41891 calculateCurveWidths: function (startPoint, endPoint) {
41892 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41893 (1 - this.velocity_filter_weight) * this._lastVelocity;
41895 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41898 start: this._lastWidth
41901 this._lastVelocity = velocity;
41902 this._lastWidth = newWidth;
41906 drawDot: function (_a) {
41907 var color = _a.color, point = _a.point;
41908 var ctx = this.canvasElCtx();
41909 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
41911 this.drawCurveSegment(point.x, point.y, width);
41913 ctx.fillStyle = color;
41917 drawCurve: function (_a) {
41918 var color = _a.color, curve = _a.curve;
41919 var ctx = this.canvasElCtx();
41920 var widthDelta = curve.endWidth - curve.startWidth;
41921 var drawSteps = Math.floor(curve.length()) * 2;
41923 ctx.fillStyle = color;
41924 for (var i = 0; i < drawSteps; i += 1) {
41925 var t = i / drawSteps;
41931 var x = uuu * curve.startPoint.x;
41932 x += 3 * uu * t * curve.control1.x;
41933 x += 3 * u * tt * curve.control2.x;
41934 x += ttt * curve.endPoint.x;
41935 var y = uuu * curve.startPoint.y;
41936 y += 3 * uu * t * curve.control1.y;
41937 y += 3 * u * tt * curve.control2.y;
41938 y += ttt * curve.endPoint.y;
41939 var width = curve.startWidth + ttt * widthDelta;
41940 this.drawCurveSegment(x, y, width);
41946 drawCurveSegment: function (x, y, width) {
41947 var ctx = this.canvasElCtx();
41949 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
41950 this.is_empty = false;
41955 var ctx = this.canvasElCtx();
41956 var canvas = this.canvasEl().dom;
41957 ctx.fillStyle = this.bg_color;
41958 ctx.clearRect(0, 0, canvas.width, canvas.height);
41959 ctx.fillRect(0, 0, canvas.width, canvas.height);
41960 this.curve_data = [];
41962 this.is_empty = true;
41967 return this.el.select('input',true).first();
41970 canvasEl: function()
41972 return this.el.select('canvas',true).first();
41975 canvasElCtx: function()
41977 return this.el.select('canvas',true).first().dom.getContext('2d');
41980 getImage: function(type)
41982 if(this.is_empty) {
41987 return this.canvasEl().dom.toDataURL('image/'+type, 1);
41990 drawFromImage: function(img_src)
41992 var img = new Image();
41994 img.onload = function(){
41995 this.canvasElCtx().drawImage(img, 0, 0);
42000 this.is_empty = false;
42003 selectImage: function()
42005 this.fileEl().dom.click();
42008 uploadImage: function(e)
42010 var reader = new FileReader();
42012 reader.onload = function(e){
42013 var img = new Image();
42014 img.onload = function(){
42016 this.canvasElCtx().drawImage(img, 0, 0);
42018 img.src = e.target.result;
42021 reader.readAsDataURL(e.target.files[0]);
42024 // Bezier Point Constructor
42025 Point: (function () {
42026 function Point(x, y, time) {
42029 this.time = time || Date.now();
42031 Point.prototype.distanceTo = function (start) {
42032 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42034 Point.prototype.equals = function (other) {
42035 return this.x === other.x && this.y === other.y && this.time === other.time;
42037 Point.prototype.velocityFrom = function (start) {
42038 return this.time !== start.time
42039 ? this.distanceTo(start) / (this.time - start.time)
42046 // Bezier Constructor
42047 Bezier: (function () {
42048 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42049 this.startPoint = startPoint;
42050 this.control2 = control2;
42051 this.control1 = control1;
42052 this.endPoint = endPoint;
42053 this.startWidth = startWidth;
42054 this.endWidth = endWidth;
42056 Bezier.fromPoints = function (points, widths, scope) {
42057 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42058 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42059 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42061 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42062 var dx1 = s1.x - s2.x;
42063 var dy1 = s1.y - s2.y;
42064 var dx2 = s2.x - s3.x;
42065 var dy2 = s2.y - s3.y;
42066 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42067 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42068 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42069 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42070 var dxm = m1.x - m2.x;
42071 var dym = m1.y - m2.y;
42072 var k = l2 / (l1 + l2);
42073 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42074 var tx = s2.x - cm.x;
42075 var ty = s2.y - cm.y;
42077 c1: new scope.Point(m1.x + tx, m1.y + ty),
42078 c2: new scope.Point(m2.x + tx, m2.y + ty)
42081 Bezier.prototype.length = function () {
42086 for (var i = 0; i <= steps; i += 1) {
42088 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42089 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42091 var xdiff = cx - px;
42092 var ydiff = cy - py;
42093 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42100 Bezier.prototype.point = function (t, start, c1, c2, end) {
42101 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42102 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42103 + (3.0 * c2 * (1.0 - t) * t * t)
42104 + (end * t * t * t);
42109 throttleStroke: function(fn, wait) {
42110 if (wait === void 0) { wait = 250; }
42112 var timeout = null;
42116 var later = function () {
42117 previous = Date.now();
42119 result = fn.apply(storedContext, storedArgs);
42121 storedContext = null;
42125 return function wrapper() {
42127 for (var _i = 0; _i < arguments.length; _i++) {
42128 args[_i] = arguments[_i];
42130 var now = Date.now();
42131 var remaining = wait - (now - previous);
42132 storedContext = this;
42134 if (remaining <= 0 || remaining > wait) {
42136 clearTimeout(timeout);
42140 result = fn.apply(storedContext, storedArgs);
42142 storedContext = null;
42146 else if (!timeout) {
42147 timeout = window.setTimeout(later, remaining);