2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = (
9 Roo.each(document.styleSheets[0], 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');
418 this.fireEvent('show', this);
423 * Hide a component - adds 'hidden' class
427 if(!this.getVisibilityEl()){
431 this.getVisibilityEl().addClass('hidden');
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
2064 * @param {Roo.menu.Menu} this
2069 * Fires before this menu is hidden
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){
2292 this.parentMenu = parentMenu;
2296 this.fireEvent("beforeshow", this);
2297 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2300 * Displays this menu at a specific xy position
2301 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2302 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2304 showAt : function(xy, parentMenu, /* private: */_e){
2305 this.parentMenu = parentMenu;
2310 this.fireEvent("beforeshow", this);
2311 //xy = this.el.adjustForConstraints(xy);
2315 this.hideMenuItems();
2316 this.hidden = false;
2317 this.triggerEl.addClass('open');
2318 this.el.addClass('show');
2320 // reassign x when hitting right
2321 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2322 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2325 // reassign y when hitting bottom
2326 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2327 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2330 // but the list may align on trigger left or trigger top... should it be a properity?
2332 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2337 this.fireEvent("show", this);
2343 this.doFocus.defer(50, this);
2347 doFocus : function(){
2349 this.focusEl.focus();
2354 * Hides this menu and optionally all parent menus
2355 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2357 hide : function(deep)
2360 this.hideMenuItems();
2361 if(this.el && this.isVisible()){
2362 this.fireEvent("beforehide", this);
2363 if(this.activeItem){
2364 this.activeItem.deactivate();
2365 this.activeItem = null;
2367 this.triggerEl.removeClass('open');;
2368 this.el.removeClass('show');
2370 this.fireEvent("hide", this);
2372 if(deep === true && this.parentMenu){
2373 this.parentMenu.hide(true);
2377 onTriggerClick : function(e)
2379 Roo.log('trigger click');
2381 var target = e.getTarget();
2383 Roo.log(target.nodeName.toLowerCase());
2385 if(target.nodeName.toLowerCase() === 'i'){
2391 onTriggerPress : function(e)
2393 Roo.log('trigger press');
2394 //Roo.log(e.getTarget());
2395 // Roo.log(this.triggerEl.dom);
2397 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2398 var pel = Roo.get(e.getTarget());
2399 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2400 Roo.log('is treeview or dropdown?');
2404 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2408 if (this.isVisible()) {
2413 this.show(this.triggerEl, false, false);
2416 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2423 hideMenuItems : function()
2425 Roo.log("hide Menu Items");
2429 //$(backdrop).remove()
2430 this.el.select('.open',true).each(function(aa) {
2432 aa.removeClass('open');
2433 //var parent = getParent($(this))
2434 //var relatedTarget = { relatedTarget: this }
2436 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2437 //if (e.isDefaultPrevented()) return
2438 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2441 addxtypeChild : function (tree, cntr) {
2442 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2444 this.menuitems.add(comp);
2456 this.getEl().dom.innerHTML = '';
2457 this.menuitems.clear();
2471 * @class Roo.bootstrap.MenuItem
2472 * @extends Roo.bootstrap.Component
2473 * Bootstrap MenuItem class
2474 * @cfg {String} html the menu label
2475 * @cfg {String} href the link
2476 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2477 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2478 * @cfg {Boolean} active used on sidebars to highlight active itesm
2479 * @cfg {String} fa favicon to show on left of menu item.
2480 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2484 * Create a new MenuItem
2485 * @param {Object} config The config object
2489 Roo.bootstrap.MenuItem = function(config){
2490 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2495 * The raw click event for the entire grid.
2496 * @param {Roo.bootstrap.MenuItem} this
2497 * @param {Roo.EventObject} e
2503 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2507 preventDefault: false,
2508 isContainer : false,
2512 getAutoCreate : function(){
2514 if(this.isContainer){
2517 cls: 'dropdown-menu-item '
2527 cls : 'dropdown-item',
2532 if (this.fa !== false) {
2535 cls : 'fa fa-' + this.fa
2544 cls: 'dropdown-menu-item',
2547 if (this.parent().type == 'treeview') {
2548 cfg.cls = 'treeview-menu';
2551 cfg.cls += ' active';
2556 anc.href = this.href || cfg.cn[0].href ;
2557 ctag.html = this.html || cfg.cn[0].html ;
2561 initEvents: function()
2563 if (this.parent().type == 'treeview') {
2564 this.el.select('a').on('click', this.onClick, this);
2568 this.menu.parentType = this.xtype;
2569 this.menu.triggerEl = this.el;
2570 this.menu = this.addxtype(Roo.apply({}, this.menu));
2574 onClick : function(e)
2576 Roo.log('item on click ');
2578 if(this.preventDefault){
2581 //this.parent().hideMenuItems();
2583 this.fireEvent('click', this, e);
2602 * @class Roo.bootstrap.MenuSeparator
2603 * @extends Roo.bootstrap.Component
2604 * Bootstrap MenuSeparator class
2607 * Create a new MenuItem
2608 * @param {Object} config The config object
2612 Roo.bootstrap.MenuSeparator = function(config){
2613 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2616 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2618 getAutoCreate : function(){
2637 * @class Roo.bootstrap.Modal
2638 * @extends Roo.bootstrap.Component
2639 * Bootstrap Modal class
2640 * @cfg {String} title Title of dialog
2641 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2642 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2643 * @cfg {Boolean} specificTitle default false
2644 * @cfg {Array} buttons Array of buttons or standard button set..
2645 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2646 * @cfg {Boolean} animate default true
2647 * @cfg {Boolean} allow_close default true
2648 * @cfg {Boolean} fitwindow default false
2649 * @cfg {String} size (sm|lg) default empty
2650 * @cfg {Number} max_width set the max width of modal
2654 * Create a new Modal Dialog
2655 * @param {Object} config The config object
2658 Roo.bootstrap.Modal = function(config){
2659 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2664 * The raw btnclick event for the button
2665 * @param {Roo.EventObject} e
2670 * Fire when dialog resize
2671 * @param {Roo.bootstrap.Modal} this
2672 * @param {Roo.EventObject} e
2676 this.buttons = this.buttons || [];
2679 this.tmpl = Roo.factory(this.tmpl);
2684 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2686 title : 'test dialog',
2696 specificTitle: false,
2698 buttonPosition: 'right',
2721 onRender : function(ct, position)
2723 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2726 var cfg = Roo.apply({}, this.getAutoCreate());
2729 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2731 //if (!cfg.name.length) {
2735 cfg.cls += ' ' + this.cls;
2738 cfg.style = this.style;
2740 this.el = Roo.get(document.body).createChild(cfg, position);
2742 //var type = this.el.dom.type;
2745 if(this.tabIndex !== undefined){
2746 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2749 this.dialogEl = this.el.select('.modal-dialog',true).first();
2750 this.bodyEl = this.el.select('.modal-body',true).first();
2751 this.closeEl = this.el.select('.modal-header .close', true).first();
2752 this.headerEl = this.el.select('.modal-header',true).first();
2753 this.titleEl = this.el.select('.modal-title',true).first();
2754 this.footerEl = this.el.select('.modal-footer',true).first();
2756 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2758 //this.el.addClass("x-dlg-modal");
2760 if (this.buttons.length) {
2761 Roo.each(this.buttons, function(bb) {
2762 var b = Roo.apply({}, bb);
2763 b.xns = b.xns || Roo.bootstrap;
2764 b.xtype = b.xtype || 'Button';
2765 if (typeof(b.listeners) == 'undefined') {
2766 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2769 var btn = Roo.factory(b);
2771 btn.render(this.el.select('.modal-footer div').first());
2775 // render the children.
2778 if(typeof(this.items) != 'undefined'){
2779 var items = this.items;
2782 for(var i =0;i < items.length;i++) {
2783 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2787 this.items = nitems;
2789 // where are these used - they used to be body/close/footer
2793 //this.el.addClass([this.fieldClass, this.cls]);
2797 getAutoCreate : function()
2801 html : this.html || ''
2806 cls : 'modal-title',
2810 if(this.specificTitle){
2816 if (this.allow_close && Roo.bootstrap.version == 3) {
2826 if (this.allow_close && Roo.bootstrap.version == 4) {
2836 if(this.size.length){
2837 size = 'modal-' + this.size;
2840 var footer = Roo.bootstrap.version == 3 ?
2842 cls : 'modal-footer',
2846 cls: 'btn-' + this.buttonPosition
2851 { // BS4 uses mr-auto on left buttons....
2852 cls : 'modal-footer'
2863 cls: "modal-dialog " + size,
2866 cls : "modal-content",
2869 cls : 'modal-header',
2874 cls : 'modal-footer',
2878 cls: 'btn-' + this.buttonPosition
2895 modal.cls += ' fade';
2901 getChildContainer : function() {
2906 getButtonContainer : function() {
2908 return Roo.bootstrap.version == 4 ?
2909 this.el.select('.modal-footer',true).first()
2910 : this.el.select('.modal-footer div',true).first();
2913 initEvents : function()
2915 if (this.allow_close) {
2916 this.closeEl.on('click', this.hide, this);
2918 Roo.EventManager.onWindowResize(this.resize, this, true);
2925 this.maskEl.setSize(
2926 Roo.lib.Dom.getViewWidth(true),
2927 Roo.lib.Dom.getViewHeight(true)
2930 if (this.fitwindow) {
2932 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2933 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2938 if(this.max_width !== 0) {
2940 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2943 this.setSize(w, this.height);
2947 if(this.max_height) {
2948 this.setSize(w,Math.min(
2950 Roo.lib.Dom.getViewportHeight(true) - 60
2956 if(!this.fit_content) {
2957 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2961 this.setSize(w, Math.min(
2963 this.headerEl.getHeight() +
2964 this.footerEl.getHeight() +
2965 this.getChildHeight(this.bodyEl.dom.childNodes),
2966 Roo.lib.Dom.getViewportHeight(true) - 60)
2972 setSize : function(w,h)
2983 if (!this.rendered) {
2987 //this.el.setStyle('display', 'block');
2988 this.el.removeClass('hideing');
2989 this.el.dom.style.display='block';
2991 Roo.get(document.body).addClass('modal-open');
2993 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2996 this.el.addClass('show');
2997 this.el.addClass('in');
3000 this.el.addClass('show');
3001 this.el.addClass('in');
3004 // not sure how we can show data in here..
3006 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3009 Roo.get(document.body).addClass("x-body-masked");
3011 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3012 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3013 this.maskEl.dom.style.display = 'block';
3014 this.maskEl.addClass('show');
3019 this.fireEvent('show', this);
3021 // set zindex here - otherwise it appears to be ignored...
3022 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3025 this.items.forEach( function(e) {
3026 e.layout ? e.layout() : false;
3034 if(this.fireEvent("beforehide", this) !== false){
3036 this.maskEl.removeClass('show');
3038 this.maskEl.dom.style.display = '';
3039 Roo.get(document.body).removeClass("x-body-masked");
3040 this.el.removeClass('in');
3041 this.el.select('.modal-dialog', true).first().setStyle('transform','');
3043 if(this.animate){ // why
3044 this.el.addClass('hideing');
3045 this.el.removeClass('show');
3047 if (!this.el.hasClass('hideing')) {
3048 return; // it's been shown again...
3051 this.el.dom.style.display='';
3053 Roo.get(document.body).removeClass('modal-open');
3054 this.el.removeClass('hideing');
3058 this.el.removeClass('show');
3059 this.el.dom.style.display='';
3060 Roo.get(document.body).removeClass('modal-open');
3063 this.fireEvent('hide', this);
3066 isVisible : function()
3069 return this.el.hasClass('show') && !this.el.hasClass('hideing');
3073 addButton : function(str, cb)
3077 var b = Roo.apply({}, { html : str } );
3078 b.xns = b.xns || Roo.bootstrap;
3079 b.xtype = b.xtype || 'Button';
3080 if (typeof(b.listeners) == 'undefined') {
3081 b.listeners = { click : cb.createDelegate(this) };
3084 var btn = Roo.factory(b);
3086 btn.render(this.el.select('.modal-footer div').first());
3092 setDefaultButton : function(btn)
3094 //this.el.select('.modal-footer').()
3098 resizeTo: function(w,h)
3102 this.dialogEl.setWidth(w);
3103 if (this.diff === false) {
3104 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3107 this.bodyEl.setHeight(h - this.diff);
3109 this.fireEvent('resize', this);
3112 setContentSize : function(w, h)
3116 onButtonClick: function(btn,e)
3119 this.fireEvent('btnclick', btn.name, e);
3122 * Set the title of the Dialog
3123 * @param {String} str new Title
3125 setTitle: function(str) {
3126 this.titleEl.dom.innerHTML = str;
3129 * Set the body of the Dialog
3130 * @param {String} str new Title
3132 setBody: function(str) {
3133 this.bodyEl.dom.innerHTML = str;
3136 * Set the body of the Dialog using the template
3137 * @param {Obj} data - apply this data to the template and replace the body contents.
3139 applyBody: function(obj)
3142 Roo.log("Error - using apply Body without a template");
3145 this.tmpl.overwrite(this.bodyEl, obj);
3148 getChildHeight : function(child_nodes)
3152 child_nodes.length == 0
3157 var child_height = 0;
3159 for(var i = 0; i < child_nodes.length; i++) {
3162 * for modal with tabs...
3163 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3165 var layout_childs = child_nodes[i].childNodes;
3167 for(var j = 0; j < layout_childs.length; j++) {
3169 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3171 var layout_body_childs = layout_childs[j].childNodes;
3173 for(var k = 0; k < layout_body_childs.length; k++) {
3175 if(layout_body_childs[k].classList.contains('navbar')) {
3176 child_height += layout_body_childs[k].offsetHeight;
3180 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3182 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3184 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3186 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3187 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3202 child_height += child_nodes[i].offsetHeight;
3203 // Roo.log(child_nodes[i].offsetHeight);
3206 return child_height;
3212 Roo.apply(Roo.bootstrap.Modal, {
3214 * Button config that displays a single OK button
3223 * Button config that displays Yes and No buttons
3239 * Button config that displays OK and Cancel buttons
3254 * Button config that displays Yes, No and Cancel buttons
3278 * messagebox - can be used as a replace
3282 * @class Roo.MessageBox
3283 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3287 Roo.Msg.alert('Status', 'Changes saved successfully.');
3289 // Prompt for user data:
3290 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3292 // process text value...
3296 // Show a dialog using config options:
3298 title:'Save Changes?',
3299 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3300 buttons: Roo.Msg.YESNOCANCEL,
3307 Roo.bootstrap.MessageBox = function(){
3308 var dlg, opt, mask, waitTimer;
3309 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3310 var buttons, activeTextEl, bwidth;
3314 var handleButton = function(button){
3316 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3320 var handleHide = function(){
3322 dlg.el.removeClass(opt.cls);
3325 // Roo.TaskMgr.stop(waitTimer);
3326 // waitTimer = null;
3331 var updateButtons = function(b){
3334 buttons["ok"].hide();
3335 buttons["cancel"].hide();
3336 buttons["yes"].hide();
3337 buttons["no"].hide();
3338 //dlg.footer.dom.style.display = 'none';
3341 dlg.footerEl.dom.style.display = '';
3342 for(var k in buttons){
3343 if(typeof buttons[k] != "function"){
3346 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3347 width += buttons[k].el.getWidth()+15;
3357 var handleEsc = function(d, k, e){
3358 if(opt && opt.closable !== false){
3368 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3369 * @return {Roo.BasicDialog} The BasicDialog element
3371 getDialog : function(){
3373 dlg = new Roo.bootstrap.Modal( {
3376 //constraintoviewport:false,
3378 //collapsible : false,
3383 //buttonAlign:"center",
3384 closeClick : function(){
3385 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3388 handleButton("cancel");
3393 dlg.on("hide", handleHide);
3395 //dlg.addKeyListener(27, handleEsc);
3397 this.buttons = buttons;
3398 var bt = this.buttonText;
3399 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3400 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3401 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3402 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3404 bodyEl = dlg.bodyEl.createChild({
3406 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3407 '<textarea class="roo-mb-textarea"></textarea>' +
3408 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3410 msgEl = bodyEl.dom.firstChild;
3411 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3412 textboxEl.enableDisplayMode();
3413 textboxEl.addKeyListener([10,13], function(){
3414 if(dlg.isVisible() && opt && opt.buttons){
3417 }else if(opt.buttons.yes){
3418 handleButton("yes");
3422 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3423 textareaEl.enableDisplayMode();
3424 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3425 progressEl.enableDisplayMode();
3427 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3428 var pf = progressEl.dom.firstChild;
3430 pp = Roo.get(pf.firstChild);
3431 pp.setHeight(pf.offsetHeight);
3439 * Updates the message box body text
3440 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3441 * the XHTML-compliant non-breaking space character '&#160;')
3442 * @return {Roo.MessageBox} This message box
3444 updateText : function(text)
3446 if(!dlg.isVisible() && !opt.width){
3447 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3448 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3450 msgEl.innerHTML = text || ' ';
3452 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3453 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3455 Math.min(opt.width || cw , this.maxWidth),
3456 Math.max(opt.minWidth || this.minWidth, bwidth)
3459 activeTextEl.setWidth(w);
3461 if(dlg.isVisible()){
3462 dlg.fixedcenter = false;
3464 // to big, make it scroll. = But as usual stupid IE does not support
3467 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3468 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3469 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3471 bodyEl.dom.style.height = '';
3472 bodyEl.dom.style.overflowY = '';
3475 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3477 bodyEl.dom.style.overflowX = '';
3480 dlg.setContentSize(w, bodyEl.getHeight());
3481 if(dlg.isVisible()){
3482 dlg.fixedcenter = true;
3488 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3489 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3490 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3491 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3492 * @return {Roo.MessageBox} This message box
3494 updateProgress : function(value, text){
3496 this.updateText(text);
3499 if (pp) { // weird bug on my firefox - for some reason this is not defined
3500 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3501 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3507 * Returns true if the message box is currently displayed
3508 * @return {Boolean} True if the message box is visible, else false
3510 isVisible : function(){
3511 return dlg && dlg.isVisible();
3515 * Hides the message box if it is displayed
3518 if(this.isVisible()){
3524 * Displays a new message box, or reinitializes an existing message box, based on the config options
3525 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3526 * The following config object properties are supported:
3528 Property Type Description
3529 ---------- --------------- ------------------------------------------------------------------------------------
3530 animEl String/Element An id or Element from which the message box should animate as it opens and
3531 closes (defaults to undefined)
3532 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3533 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3534 closable Boolean False to hide the top-right close button (defaults to true). Note that
3535 progress and wait dialogs will ignore this property and always hide the
3536 close button as they can only be closed programmatically.
3537 cls String A custom CSS class to apply to the message box element
3538 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3539 displayed (defaults to 75)
3540 fn Function A callback function to execute after closing the dialog. The arguments to the
3541 function will be btn (the name of the button that was clicked, if applicable,
3542 e.g. "ok"), and text (the value of the active text field, if applicable).
3543 Progress and wait dialogs will ignore this option since they do not respond to
3544 user actions and can only be closed programmatically, so any required function
3545 should be called by the same code after it closes the dialog.
3546 icon String A CSS class that provides a background image to be used as an icon for
3547 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3548 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3549 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3550 modal Boolean False to allow user interaction with the page while the message box is
3551 displayed (defaults to true)
3552 msg String A string that will replace the existing message box body text (defaults
3553 to the XHTML-compliant non-breaking space character ' ')
3554 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3555 progress Boolean True to display a progress bar (defaults to false)
3556 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3557 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3558 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3559 title String The title text
3560 value String The string value to set into the active textbox element if displayed
3561 wait Boolean True to display a progress bar (defaults to false)
3562 width Number The width of the dialog in pixels
3569 msg: 'Please enter your address:',
3571 buttons: Roo.MessageBox.OKCANCEL,
3574 animEl: 'addAddressBtn'
3577 * @param {Object} config Configuration options
3578 * @return {Roo.MessageBox} This message box
3580 show : function(options)
3583 // this causes nightmares if you show one dialog after another
3584 // especially on callbacks..
3586 if(this.isVisible()){
3589 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3590 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3591 Roo.log("New Dialog Message:" + options.msg )
3592 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3593 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3596 var d = this.getDialog();
3598 d.setTitle(opt.title || " ");
3599 d.closeEl.setDisplayed(opt.closable !== false);
3600 activeTextEl = textboxEl;
3601 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3606 textareaEl.setHeight(typeof opt.multiline == "number" ?
3607 opt.multiline : this.defaultTextHeight);
3608 activeTextEl = textareaEl;
3617 progressEl.setDisplayed(opt.progress === true);
3618 this.updateProgress(0);
3619 activeTextEl.dom.value = opt.value || "";
3621 dlg.setDefaultButton(activeTextEl);
3623 var bs = opt.buttons;
3627 }else if(bs && bs.yes){
3628 db = buttons["yes"];
3630 dlg.setDefaultButton(db);
3632 bwidth = updateButtons(opt.buttons);
3633 this.updateText(opt.msg);
3635 d.el.addClass(opt.cls);
3637 d.proxyDrag = opt.proxyDrag === true;
3638 d.modal = opt.modal !== false;
3639 d.mask = opt.modal !== false ? mask : false;
3641 // force it to the end of the z-index stack so it gets a cursor in FF
3642 document.body.appendChild(dlg.el.dom);
3643 d.animateTarget = null;
3644 d.show(options.animEl);
3650 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3651 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3652 * and closing the message box when the process is complete.
3653 * @param {String} title The title bar text
3654 * @param {String} msg The message box body text
3655 * @return {Roo.MessageBox} This message box
3657 progress : function(title, msg){
3664 minWidth: this.minProgressWidth,
3671 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3672 * If a callback function is passed it will be called after the user clicks the button, and the
3673 * id of the button that was clicked will be passed as the only parameter to the callback
3674 * (could also be the top-right close button).
3675 * @param {String} title The title bar text
3676 * @param {String} msg The message box body text
3677 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3678 * @param {Object} scope (optional) The scope of the callback function
3679 * @return {Roo.MessageBox} This message box
3681 alert : function(title, msg, fn, scope)
3696 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3697 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3698 * You are responsible for closing the message box when the process is complete.
3699 * @param {String} msg The message box body text
3700 * @param {String} title (optional) The title bar text
3701 * @return {Roo.MessageBox} This message box
3703 wait : function(msg, title){
3714 waitTimer = Roo.TaskMgr.start({
3716 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3724 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3725 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3726 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3727 * @param {String} title The title bar text
3728 * @param {String} msg The message box body text
3729 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3730 * @param {Object} scope (optional) The scope of the callback function
3731 * @return {Roo.MessageBox} This message box
3733 confirm : function(title, msg, fn, scope){
3737 buttons: this.YESNO,
3746 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3747 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3748 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3749 * (could also be the top-right close button) and the text that was entered will be passed as the two
3750 * parameters to the callback.
3751 * @param {String} title The title bar text
3752 * @param {String} msg The message box body text
3753 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3754 * @param {Object} scope (optional) The scope of the callback function
3755 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3756 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3757 * @return {Roo.MessageBox} This message box
3759 prompt : function(title, msg, fn, scope, multiline){
3763 buttons: this.OKCANCEL,
3768 multiline: multiline,
3775 * Button config that displays a single OK button
3780 * Button config that displays Yes and No buttons
3783 YESNO : {yes:true, no:true},
3785 * Button config that displays OK and Cancel buttons
3788 OKCANCEL : {ok:true, cancel:true},
3790 * Button config that displays Yes, No and Cancel buttons
3793 YESNOCANCEL : {yes:true, no:true, cancel:true},
3796 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3799 defaultTextHeight : 75,
3801 * The maximum width in pixels of the message box (defaults to 600)
3806 * The minimum width in pixels of the message box (defaults to 100)
3811 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3812 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3815 minProgressWidth : 250,
3817 * An object containing the default button text strings that can be overriden for localized language support.
3818 * Supported properties are: ok, cancel, yes and no.
3819 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3832 * Shorthand for {@link Roo.MessageBox}
3834 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3835 Roo.Msg = Roo.Msg || Roo.MessageBox;
3844 * @class Roo.bootstrap.Navbar
3845 * @extends Roo.bootstrap.Component
3846 * Bootstrap Navbar class
3849 * Create a new Navbar
3850 * @param {Object} config The config object
3854 Roo.bootstrap.Navbar = function(config){
3855 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3859 * @event beforetoggle
3860 * Fire before toggle the menu
3861 * @param {Roo.EventObject} e
3863 "beforetoggle" : true
3867 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3876 getAutoCreate : function(){
3879 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3883 initEvents :function ()
3885 //Roo.log(this.el.select('.navbar-toggle',true));
3886 this.el.select('.navbar-toggle',true).on('click', function() {
3887 if(this.fireEvent('beforetoggle', this) !== false){
3888 var ce = this.el.select('.navbar-collapse',true).first();
3889 ce.toggleClass('in'); // old...
3890 if (ce.hasClass('collapse')) {
3892 ce.removeClass('collapse');
3893 ce.addClass('show');
3894 var h = ce.getHeight();
3896 ce.removeClass('show');
3897 // at this point we should be able to see it..
3898 ce.addClass('collapsing');
3900 ce.setHeight(0); // resize it ...
3901 ce.on('transitionend', function() {
3902 Roo.log('done transition');
3903 ce.removeClass('collapsing');
3904 ce.addClass('show');
3905 ce.removeClass('collapse');
3907 ce.dom.style.height = '';
3908 }, this, { single: true} );
3912 ce.setHeight(ce.getHeight());
3913 ce.removeClass('show');
3914 ce.addClass('collapsing');
3916 ce.on('transitionend', function() {
3917 ce.dom.style.height = '';
3918 ce.removeClass('collapsing');
3919 ce.addClass('collapse');
3920 }, this, { single: true} );
3932 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3934 var size = this.el.getSize();
3935 this.maskEl.setSize(size.width, size.height);
3936 this.maskEl.enableDisplayMode("block");
3945 getChildContainer : function()
3947 if (this.el.select('.collapse').getCount()) {
3948 return this.el.select('.collapse',true).first();
3981 * @class Roo.bootstrap.NavSimplebar
3982 * @extends Roo.bootstrap.Navbar
3983 * Bootstrap Sidebar class
3985 * @cfg {Boolean} inverse is inverted color
3987 * @cfg {String} type (nav | pills | tabs)
3988 * @cfg {Boolean} arrangement stacked | justified
3989 * @cfg {String} align (left | right) alignment
3991 * @cfg {Boolean} main (true|false) main nav bar? default false
3992 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3994 * @cfg {String} tag (header|footer|nav|div) default is nav
3996 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4000 * Create a new Sidebar
4001 * @param {Object} config The config object
4005 Roo.bootstrap.NavSimplebar = function(config){
4006 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4009 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
4025 getAutoCreate : function(){
4029 tag : this.tag || 'div',
4030 cls : 'navbar navbar-expand-lg'
4032 if (['light','white'].indexOf(this.weight) > -1) {
4033 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4035 cfg.cls += ' bg-' + this.weight;
4047 this.type = this.type || 'nav';
4048 if (['tabs','pills'].indexOf(this.type)!==-1) {
4049 cfg.cn[0].cls += ' nav-' + this.type
4053 if (this.type!=='nav') {
4054 Roo.log('nav type must be nav/tabs/pills')
4056 cfg.cn[0].cls += ' navbar-nav'
4062 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4063 cfg.cn[0].cls += ' nav-' + this.arrangement;
4067 if (this.align === 'right') {
4068 cfg.cn[0].cls += ' navbar-right';
4072 cfg.cls += ' navbar-inverse';
4096 * navbar-expand-md fixed-top
4100 * @class Roo.bootstrap.NavHeaderbar
4101 * @extends Roo.bootstrap.NavSimplebar
4102 * Bootstrap Sidebar class
4104 * @cfg {String} brand what is brand
4105 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4106 * @cfg {String} brand_href href of the brand
4107 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4108 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4109 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4110 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4113 * Create a new Sidebar
4114 * @param {Object} config The config object
4118 Roo.bootstrap.NavHeaderbar = function(config){
4119 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4123 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4130 desktopCenter : false,
4133 getAutoCreate : function(){
4136 tag: this.nav || 'nav',
4137 cls: 'navbar navbar-expand-md',
4143 if (this.desktopCenter) {
4144 cn.push({cls : 'container', cn : []});
4152 cls: 'navbar-toggle navbar-toggler',
4153 'data-toggle': 'collapse',
4158 html: 'Toggle navigation'
4162 cls: 'icon-bar navbar-toggler-icon'
4175 cn.push( Roo.bootstrap.version == 4 ? btn : {
4177 cls: 'navbar-header',
4186 cls: 'collapse navbar-collapse',
4190 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4192 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4193 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4195 // tag can override this..
4197 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4200 if (this.brand !== '') {
4201 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4202 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4204 href: this.brand_href ? this.brand_href : '#',
4205 cls: 'navbar-brand',
4213 cfg.cls += ' main-nav';
4221 getHeaderChildContainer : function()
4223 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4224 return this.el.select('.navbar-header',true).first();
4227 return this.getChildContainer();
4231 initEvents : function()
4233 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4235 if (this.autohide) {
4240 Roo.get(document).on('scroll',function(e) {
4241 var ns = Roo.get(document).getScroll().top;
4242 var os = prevScroll;
4246 ft.removeClass('slideDown');
4247 ft.addClass('slideUp');
4250 ft.removeClass('slideUp');
4251 ft.addClass('slideDown');
4272 * @class Roo.bootstrap.NavSidebar
4273 * @extends Roo.bootstrap.Navbar
4274 * Bootstrap Sidebar class
4277 * Create a new Sidebar
4278 * @param {Object} config The config object
4282 Roo.bootstrap.NavSidebar = function(config){
4283 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4286 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4288 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4290 getAutoCreate : function(){
4295 cls: 'sidebar sidebar-nav'
4317 * @class Roo.bootstrap.NavGroup
4318 * @extends Roo.bootstrap.Component
4319 * Bootstrap NavGroup class
4320 * @cfg {String} align (left|right)
4321 * @cfg {Boolean} inverse
4322 * @cfg {String} type (nav|pills|tab) default nav
4323 * @cfg {String} navId - reference Id for navbar.
4327 * Create a new nav group
4328 * @param {Object} config The config object
4331 Roo.bootstrap.NavGroup = function(config){
4332 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4335 Roo.bootstrap.NavGroup.register(this);
4339 * Fires when the active item changes
4340 * @param {Roo.bootstrap.NavGroup} this
4341 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4342 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4349 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4360 getAutoCreate : function()
4362 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4369 if (['tabs','pills'].indexOf(this.type)!==-1) {
4370 cfg.cls += ' nav-' + this.type
4372 if (this.type!=='nav') {
4373 Roo.log('nav type must be nav/tabs/pills')
4375 cfg.cls += ' navbar-nav'
4378 if (this.parent() && this.parent().sidebar) {
4381 cls: 'dashboard-menu sidebar-menu'
4387 if (this.form === true) {
4393 if (this.align === 'right') {
4394 cfg.cls += ' navbar-right ml-md-auto';
4396 cfg.cls += ' navbar-left';
4400 if (this.align === 'right') {
4401 cfg.cls += ' navbar-right ml-md-auto';
4403 cfg.cls += ' mr-auto';
4407 cfg.cls += ' navbar-inverse';
4415 * sets the active Navigation item
4416 * @param {Roo.bootstrap.NavItem} the new current navitem
4418 setActiveItem : function(item)
4421 Roo.each(this.navItems, function(v){
4426 v.setActive(false, true);
4433 item.setActive(true, true);
4434 this.fireEvent('changed', this, item, prev);
4439 * gets the active Navigation item
4440 * @return {Roo.bootstrap.NavItem} the current navitem
4442 getActive : function()
4446 Roo.each(this.navItems, function(v){
4457 indexOfNav : function()
4461 Roo.each(this.navItems, function(v,i){
4472 * adds a Navigation item
4473 * @param {Roo.bootstrap.NavItem} the navitem to add
4475 addItem : function(cfg)
4477 var cn = new Roo.bootstrap.NavItem(cfg);
4479 cn.parentId = this.id;
4480 cn.onRender(this.el, null);
4484 * register a Navigation item
4485 * @param {Roo.bootstrap.NavItem} the navitem to add
4487 register : function(item)
4489 this.navItems.push( item);
4490 item.navId = this.navId;
4495 * clear all the Navigation item
4498 clearAll : function()
4501 this.el.dom.innerHTML = '';
4504 getNavItem: function(tabId)
4507 Roo.each(this.navItems, function(e) {
4508 if (e.tabId == tabId) {
4518 setActiveNext : function()
4520 var i = this.indexOfNav(this.getActive());
4521 if (i > this.navItems.length) {
4524 this.setActiveItem(this.navItems[i+1]);
4526 setActivePrev : function()
4528 var i = this.indexOfNav(this.getActive());
4532 this.setActiveItem(this.navItems[i-1]);
4534 clearWasActive : function(except) {
4535 Roo.each(this.navItems, function(e) {
4536 if (e.tabId != except.tabId && e.was_active) {
4537 e.was_active = false;
4544 getWasActive : function ()
4547 Roo.each(this.navItems, function(e) {
4562 Roo.apply(Roo.bootstrap.NavGroup, {
4566 * register a Navigation Group
4567 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4569 register : function(navgrp)
4571 this.groups[navgrp.navId] = navgrp;
4575 * fetch a Navigation Group based on the navigation ID
4576 * @param {string} the navgroup to add
4577 * @returns {Roo.bootstrap.NavGroup} the navgroup
4579 get: function(navId) {
4580 if (typeof(this.groups[navId]) == 'undefined') {
4582 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4584 return this.groups[navId] ;
4599 * @class Roo.bootstrap.NavItem
4600 * @extends Roo.bootstrap.Component
4601 * Bootstrap Navbar.NavItem class
4602 * @cfg {String} href link to
4603 * @cfg {String} html content of button
4604 * @cfg {String} badge text inside badge
4605 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4606 * @cfg {String} glyphicon DEPRICATED - use fa
4607 * @cfg {String} icon DEPRICATED - use fa
4608 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4609 * @cfg {Boolean} active Is item active
4610 * @cfg {Boolean} disabled Is item disabled
4612 * @cfg {Boolean} preventDefault (true | false) default false
4613 * @cfg {String} tabId the tab that this item activates.
4614 * @cfg {String} tagtype (a|span) render as a href or span?
4615 * @cfg {Boolean} animateRef (true|false) link to element default false
4618 * Create a new Navbar Item
4619 * @param {Object} config The config object
4621 Roo.bootstrap.NavItem = function(config){
4622 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4627 * The raw click event for the entire grid.
4628 * @param {Roo.EventObject} e
4633 * Fires when the active item active state changes
4634 * @param {Roo.bootstrap.NavItem} this
4635 * @param {boolean} state the new state
4641 * Fires when scroll to element
4642 * @param {Roo.bootstrap.NavItem} this
4643 * @param {Object} options
4644 * @param {Roo.EventObject} e
4652 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4661 preventDefault : false,
4668 getAutoCreate : function(){
4677 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4679 if (this.disabled) {
4680 cfg.cls += ' disabled';
4683 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4687 href : this.href || "#",
4688 html: this.html || ''
4691 if (this.tagtype == 'a') {
4692 cfg.cn[0].cls = 'nav-link';
4695 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4698 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4700 if(this.glyphicon) {
4701 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4706 cfg.cn[0].html += " <span class='caret'></span>";
4710 if (this.badge !== '') {
4712 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4720 initEvents: function()
4722 if (typeof (this.menu) != 'undefined') {
4723 this.menu.parentType = this.xtype;
4724 this.menu.triggerEl = this.el;
4725 this.menu = this.addxtype(Roo.apply({}, this.menu));
4728 this.el.select('a',true).on('click', this.onClick, this);
4730 if(this.tagtype == 'span'){
4731 this.el.select('span',true).on('click', this.onClick, this);
4734 // at this point parent should be available..
4735 this.parent().register(this);
4738 onClick : function(e)
4740 if (e.getTarget('.dropdown-menu-item')) {
4741 // did you click on a menu itemm.... - then don't trigger onclick..
4746 this.preventDefault ||
4749 Roo.log("NavItem - prevent Default?");
4753 if (this.disabled) {
4757 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4758 if (tg && tg.transition) {
4759 Roo.log("waiting for the transitionend");
4765 //Roo.log("fire event clicked");
4766 if(this.fireEvent('click', this, e) === false){
4770 if(this.tagtype == 'span'){
4774 //Roo.log(this.href);
4775 var ael = this.el.select('a',true).first();
4778 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4779 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4780 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4781 return; // ignore... - it's a 'hash' to another page.
4783 Roo.log("NavItem - prevent Default?");
4785 this.scrollToElement(e);
4789 var p = this.parent();
4791 if (['tabs','pills'].indexOf(p.type)!==-1) {
4792 if (typeof(p.setActiveItem) !== 'undefined') {
4793 p.setActiveItem(this);
4797 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4798 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4799 // remove the collapsed menu expand...
4800 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4804 isActive: function () {
4807 setActive : function(state, fire, is_was_active)
4809 if (this.active && !state && this.navId) {
4810 this.was_active = true;
4811 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4813 nv.clearWasActive(this);
4817 this.active = state;
4820 this.el.removeClass('active');
4821 } else if (!this.el.hasClass('active')) {
4822 this.el.addClass('active');
4825 this.fireEvent('changed', this, state);
4828 // show a panel if it's registered and related..
4830 if (!this.navId || !this.tabId || !state || is_was_active) {
4834 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4838 var pan = tg.getPanelByName(this.tabId);
4842 // if we can not flip to new panel - go back to old nav highlight..
4843 if (false == tg.showPanel(pan)) {
4844 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4846 var onav = nv.getWasActive();
4848 onav.setActive(true, false, true);
4857 // this should not be here...
4858 setDisabled : function(state)
4860 this.disabled = state;
4862 this.el.removeClass('disabled');
4863 } else if (!this.el.hasClass('disabled')) {
4864 this.el.addClass('disabled');
4870 * Fetch the element to display the tooltip on.
4871 * @return {Roo.Element} defaults to this.el
4873 tooltipEl : function()
4875 return this.el.select('' + this.tagtype + '', true).first();
4878 scrollToElement : function(e)
4880 var c = document.body;
4883 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4885 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4886 c = document.documentElement;
4889 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4895 var o = target.calcOffsetsTo(c);
4902 this.fireEvent('scrollto', this, options, e);
4904 Roo.get(c).scrollTo('top', options.value, true);
4917 * <span> icon </span>
4918 * <span> text </span>
4919 * <span>badge </span>
4923 * @class Roo.bootstrap.NavSidebarItem
4924 * @extends Roo.bootstrap.NavItem
4925 * Bootstrap Navbar.NavSidebarItem class
4926 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4927 * {Boolean} open is the menu open
4928 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4929 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4930 * {String} buttonSize (sm|md|lg)the extra classes for the button
4931 * {Boolean} showArrow show arrow next to the text (default true)
4933 * Create a new Navbar Button
4934 * @param {Object} config The config object
4936 Roo.bootstrap.NavSidebarItem = function(config){
4937 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4942 * The raw click event for the entire grid.
4943 * @param {Roo.EventObject} e
4948 * Fires when the active item active state changes
4949 * @param {Roo.bootstrap.NavSidebarItem} this
4950 * @param {boolean} state the new state
4958 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4960 badgeWeight : 'default',
4966 buttonWeight : 'default',
4972 getAutoCreate : function(){
4977 href : this.href || '#',
4983 if(this.buttonView){
4986 href : this.href || '#',
4987 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5000 cfg.cls += ' active';
5003 if (this.disabled) {
5004 cfg.cls += ' disabled';
5007 cfg.cls += ' open x-open';
5010 if (this.glyphicon || this.icon) {
5011 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
5012 a.cn.push({ tag : 'i', cls : c }) ;
5015 if(!this.buttonView){
5018 html : this.html || ''
5025 if (this.badge !== '') {
5026 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
5032 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5035 a.cls += ' dropdown-toggle treeview' ;
5041 initEvents : function()
5043 if (typeof (this.menu) != 'undefined') {
5044 this.menu.parentType = this.xtype;
5045 this.menu.triggerEl = this.el;
5046 this.menu = this.addxtype(Roo.apply({}, this.menu));
5049 this.el.on('click', this.onClick, this);
5051 if(this.badge !== ''){
5052 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5057 onClick : function(e)
5064 if(this.preventDefault){
5068 this.fireEvent('click', this);
5071 disable : function()
5073 this.setDisabled(true);
5078 this.setDisabled(false);
5081 setDisabled : function(state)
5083 if(this.disabled == state){
5087 this.disabled = state;
5090 this.el.addClass('disabled');
5094 this.el.removeClass('disabled');
5099 setActive : function(state)
5101 if(this.active == state){
5105 this.active = state;
5108 this.el.addClass('active');
5112 this.el.removeClass('active');
5117 isActive: function ()
5122 setBadge : function(str)
5128 this.badgeEl.dom.innerHTML = str;
5145 * @class Roo.bootstrap.Row
5146 * @extends Roo.bootstrap.Component
5147 * Bootstrap Row class (contains columns...)
5151 * @param {Object} config The config object
5154 Roo.bootstrap.Row = function(config){
5155 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5158 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5160 getAutoCreate : function(){
5179 * @class Roo.bootstrap.Element
5180 * @extends Roo.bootstrap.Component
5181 * Bootstrap Element class
5182 * @cfg {String} html contents of the element
5183 * @cfg {String} tag tag of the element
5184 * @cfg {String} cls class of the element
5185 * @cfg {Boolean} preventDefault (true|false) default false
5186 * @cfg {Boolean} clickable (true|false) default false
5189 * Create a new Element
5190 * @param {Object} config The config object
5193 Roo.bootstrap.Element = function(config){
5194 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5200 * When a element is chick
5201 * @param {Roo.bootstrap.Element} this
5202 * @param {Roo.EventObject} e
5208 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5213 preventDefault: false,
5216 getAutoCreate : function(){
5220 // cls: this.cls, double assign in parent class Component.js :: onRender
5227 initEvents: function()
5229 Roo.bootstrap.Element.superclass.initEvents.call(this);
5232 this.el.on('click', this.onClick, this);
5237 onClick : function(e)
5239 if(this.preventDefault){
5243 this.fireEvent('click', this, e);
5246 getValue : function()
5248 return this.el.dom.innerHTML;
5251 setValue : function(value)
5253 this.el.dom.innerHTML = value;
5268 * @class Roo.bootstrap.Pagination
5269 * @extends Roo.bootstrap.Component
5270 * Bootstrap Pagination class
5271 * @cfg {String} size xs | sm | md | lg
5272 * @cfg {Boolean} inverse false | true
5275 * Create a new Pagination
5276 * @param {Object} config The config object
5279 Roo.bootstrap.Pagination = function(config){
5280 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5283 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5289 getAutoCreate : function(){
5295 cfg.cls += ' inverse';
5301 cfg.cls += " " + this.cls;
5319 * @class Roo.bootstrap.PaginationItem
5320 * @extends Roo.bootstrap.Component
5321 * Bootstrap PaginationItem class
5322 * @cfg {String} html text
5323 * @cfg {String} href the link
5324 * @cfg {Boolean} preventDefault (true | false) default true
5325 * @cfg {Boolean} active (true | false) default false
5326 * @cfg {Boolean} disabled default false
5330 * Create a new PaginationItem
5331 * @param {Object} config The config object
5335 Roo.bootstrap.PaginationItem = function(config){
5336 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5341 * The raw click event for the entire grid.
5342 * @param {Roo.EventObject} e
5348 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5352 preventDefault: true,
5357 getAutoCreate : function(){
5363 href : this.href ? this.href : '#',
5364 html : this.html ? this.html : ''
5374 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5378 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5384 initEvents: function() {
5386 this.el.on('click', this.onClick, this);
5389 onClick : function(e)
5391 Roo.log('PaginationItem on click ');
5392 if(this.preventDefault){
5400 this.fireEvent('click', this, e);
5416 * @class Roo.bootstrap.Slider
5417 * @extends Roo.bootstrap.Component
5418 * Bootstrap Slider class
5421 * Create a new Slider
5422 * @param {Object} config The config object
5425 Roo.bootstrap.Slider = function(config){
5426 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5429 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5431 getAutoCreate : function(){
5435 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5439 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5451 * Ext JS Library 1.1.1
5452 * Copyright(c) 2006-2007, Ext JS, LLC.
5454 * Originally Released Under LGPL - original licence link has changed is not relivant.
5457 * <script type="text/javascript">
5462 * @class Roo.grid.ColumnModel
5463 * @extends Roo.util.Observable
5464 * This is the default implementation of a ColumnModel used by the Grid. It defines
5465 * the columns in the grid.
5468 var colModel = new Roo.grid.ColumnModel([
5469 {header: "Ticker", width: 60, sortable: true, locked: true},
5470 {header: "Company Name", width: 150, sortable: true},
5471 {header: "Market Cap.", width: 100, sortable: true},
5472 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5473 {header: "Employees", width: 100, sortable: true, resizable: false}
5478 * The config options listed for this class are options which may appear in each
5479 * individual column definition.
5480 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5482 * @param {Object} config An Array of column config objects. See this class's
5483 * config objects for details.
5485 Roo.grid.ColumnModel = function(config){
5487 * The config passed into the constructor
5489 this.config = config;
5492 // if no id, create one
5493 // if the column does not have a dataIndex mapping,
5494 // map it to the order it is in the config
5495 for(var i = 0, len = config.length; i < len; i++){
5497 if(typeof c.dataIndex == "undefined"){
5500 if(typeof c.renderer == "string"){
5501 c.renderer = Roo.util.Format[c.renderer];
5503 if(typeof c.id == "undefined"){
5506 if(c.editor && c.editor.xtype){
5507 c.editor = Roo.factory(c.editor, Roo.grid);
5509 if(c.editor && c.editor.isFormField){
5510 c.editor = new Roo.grid.GridEditor(c.editor);
5512 this.lookup[c.id] = c;
5516 * The width of columns which have no width specified (defaults to 100)
5519 this.defaultWidth = 100;
5522 * Default sortable of columns which have no sortable specified (defaults to false)
5525 this.defaultSortable = false;
5529 * @event widthchange
5530 * Fires when the width of a column changes.
5531 * @param {ColumnModel} this
5532 * @param {Number} columnIndex The column index
5533 * @param {Number} newWidth The new width
5535 "widthchange": true,
5537 * @event headerchange
5538 * Fires when the text of a header changes.
5539 * @param {ColumnModel} this
5540 * @param {Number} columnIndex The column index
5541 * @param {Number} newText The new header text
5543 "headerchange": true,
5545 * @event hiddenchange
5546 * Fires when a column is hidden or "unhidden".
5547 * @param {ColumnModel} this
5548 * @param {Number} columnIndex The column index
5549 * @param {Boolean} hidden true if hidden, false otherwise
5551 "hiddenchange": true,
5553 * @event columnmoved
5554 * Fires when a column is moved.
5555 * @param {ColumnModel} this
5556 * @param {Number} oldIndex
5557 * @param {Number} newIndex
5559 "columnmoved" : true,
5561 * @event columlockchange
5562 * Fires when a column's locked state is changed
5563 * @param {ColumnModel} this
5564 * @param {Number} colIndex
5565 * @param {Boolean} locked true if locked
5567 "columnlockchange" : true
5569 Roo.grid.ColumnModel.superclass.constructor.call(this);
5571 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5573 * @cfg {String} header The header text to display in the Grid view.
5576 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5577 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5578 * specified, the column's index is used as an index into the Record's data Array.
5581 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5582 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5585 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5586 * Defaults to the value of the {@link #defaultSortable} property.
5587 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5590 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5593 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5596 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5599 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5602 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5603 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5604 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5605 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5608 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5611 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5614 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5617 * @cfg {String} cursor (Optional)
5620 * @cfg {String} tooltip (Optional)
5623 * @cfg {Number} xs (Optional)
5626 * @cfg {Number} sm (Optional)
5629 * @cfg {Number} md (Optional)
5632 * @cfg {Number} lg (Optional)
5635 * Returns the id of the column at the specified index.
5636 * @param {Number} index The column index
5637 * @return {String} the id
5639 getColumnId : function(index){
5640 return this.config[index].id;
5644 * Returns the column for a specified id.
5645 * @param {String} id The column id
5646 * @return {Object} the column
5648 getColumnById : function(id){
5649 return this.lookup[id];
5654 * Returns the column for a specified dataIndex.
5655 * @param {String} dataIndex The column dataIndex
5656 * @return {Object|Boolean} the column or false if not found
5658 getColumnByDataIndex: function(dataIndex){
5659 var index = this.findColumnIndex(dataIndex);
5660 return index > -1 ? this.config[index] : false;
5664 * Returns the index for a specified column id.
5665 * @param {String} id The column id
5666 * @return {Number} the index, or -1 if not found
5668 getIndexById : function(id){
5669 for(var i = 0, len = this.config.length; i < len; i++){
5670 if(this.config[i].id == id){
5678 * Returns the index for a specified column dataIndex.
5679 * @param {String} dataIndex The column dataIndex
5680 * @return {Number} the index, or -1 if not found
5683 findColumnIndex : function(dataIndex){
5684 for(var i = 0, len = this.config.length; i < len; i++){
5685 if(this.config[i].dataIndex == dataIndex){
5693 moveColumn : function(oldIndex, newIndex){
5694 var c = this.config[oldIndex];
5695 this.config.splice(oldIndex, 1);
5696 this.config.splice(newIndex, 0, c);
5697 this.dataMap = null;
5698 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5701 isLocked : function(colIndex){
5702 return this.config[colIndex].locked === true;
5705 setLocked : function(colIndex, value, suppressEvent){
5706 if(this.isLocked(colIndex) == value){
5709 this.config[colIndex].locked = value;
5711 this.fireEvent("columnlockchange", this, colIndex, value);
5715 getTotalLockedWidth : function(){
5717 for(var i = 0; i < this.config.length; i++){
5718 if(this.isLocked(i) && !this.isHidden(i)){
5719 this.totalWidth += this.getColumnWidth(i);
5725 getLockedCount : function(){
5726 for(var i = 0, len = this.config.length; i < len; i++){
5727 if(!this.isLocked(i)){
5732 return this.config.length;
5736 * Returns the number of columns.
5739 getColumnCount : function(visibleOnly){
5740 if(visibleOnly === true){
5742 for(var i = 0, len = this.config.length; i < len; i++){
5743 if(!this.isHidden(i)){
5749 return this.config.length;
5753 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5754 * @param {Function} fn
5755 * @param {Object} scope (optional)
5756 * @return {Array} result
5758 getColumnsBy : function(fn, scope){
5760 for(var i = 0, len = this.config.length; i < len; i++){
5761 var c = this.config[i];
5762 if(fn.call(scope||this, c, i) === true){
5770 * Returns true if the specified column is sortable.
5771 * @param {Number} col The column index
5774 isSortable : function(col){
5775 if(typeof this.config[col].sortable == "undefined"){
5776 return this.defaultSortable;
5778 return this.config[col].sortable;
5782 * Returns the rendering (formatting) function defined for the column.
5783 * @param {Number} col The column index.
5784 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5786 getRenderer : function(col){
5787 if(!this.config[col].renderer){
5788 return Roo.grid.ColumnModel.defaultRenderer;
5790 return this.config[col].renderer;
5794 * Sets the rendering (formatting) function for a column.
5795 * @param {Number} col The column index
5796 * @param {Function} fn The function to use to process the cell's raw data
5797 * to return HTML markup for the grid view. The render function is called with
5798 * the following parameters:<ul>
5799 * <li>Data value.</li>
5800 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5801 * <li>css A CSS style string to apply to the table cell.</li>
5802 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5803 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5804 * <li>Row index</li>
5805 * <li>Column index</li>
5806 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5808 setRenderer : function(col, fn){
5809 this.config[col].renderer = fn;
5813 * Returns the width for the specified column.
5814 * @param {Number} col The column index
5817 getColumnWidth : function(col){
5818 return this.config[col].width * 1 || this.defaultWidth;
5822 * Sets the width for a column.
5823 * @param {Number} col The column index
5824 * @param {Number} width The new width
5826 setColumnWidth : function(col, width, suppressEvent){
5827 this.config[col].width = width;
5828 this.totalWidth = null;
5830 this.fireEvent("widthchange", this, col, width);
5835 * Returns the total width of all columns.
5836 * @param {Boolean} includeHidden True to include hidden column widths
5839 getTotalWidth : function(includeHidden){
5840 if(!this.totalWidth){
5841 this.totalWidth = 0;
5842 for(var i = 0, len = this.config.length; i < len; i++){
5843 if(includeHidden || !this.isHidden(i)){
5844 this.totalWidth += this.getColumnWidth(i);
5848 return this.totalWidth;
5852 * Returns the header for the specified column.
5853 * @param {Number} col The column index
5856 getColumnHeader : function(col){
5857 return this.config[col].header;
5861 * Sets the header for a column.
5862 * @param {Number} col The column index
5863 * @param {String} header The new header
5865 setColumnHeader : function(col, header){
5866 this.config[col].header = header;
5867 this.fireEvent("headerchange", this, col, header);
5871 * Returns the tooltip for the specified column.
5872 * @param {Number} col The column index
5875 getColumnTooltip : function(col){
5876 return this.config[col].tooltip;
5879 * Sets the tooltip for a column.
5880 * @param {Number} col The column index
5881 * @param {String} tooltip The new tooltip
5883 setColumnTooltip : function(col, tooltip){
5884 this.config[col].tooltip = tooltip;
5888 * Returns the dataIndex for the specified column.
5889 * @param {Number} col The column index
5892 getDataIndex : function(col){
5893 return this.config[col].dataIndex;
5897 * Sets the dataIndex for a column.
5898 * @param {Number} col The column index
5899 * @param {Number} dataIndex The new dataIndex
5901 setDataIndex : function(col, dataIndex){
5902 this.config[col].dataIndex = dataIndex;
5908 * Returns true if the cell is editable.
5909 * @param {Number} colIndex The column index
5910 * @param {Number} rowIndex The row index - this is nto actually used..?
5913 isCellEditable : function(colIndex, rowIndex){
5914 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5918 * Returns the editor defined for the cell/column.
5919 * return false or null to disable editing.
5920 * @param {Number} colIndex The column index
5921 * @param {Number} rowIndex The row index
5924 getCellEditor : function(colIndex, rowIndex){
5925 return this.config[colIndex].editor;
5929 * Sets if a column is editable.
5930 * @param {Number} col The column index
5931 * @param {Boolean} editable True if the column is editable
5933 setEditable : function(col, editable){
5934 this.config[col].editable = editable;
5939 * Returns true if the column is hidden.
5940 * @param {Number} colIndex The column index
5943 isHidden : function(colIndex){
5944 return this.config[colIndex].hidden;
5949 * Returns true if the column width cannot be changed
5951 isFixed : function(colIndex){
5952 return this.config[colIndex].fixed;
5956 * Returns true if the column can be resized
5959 isResizable : function(colIndex){
5960 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5963 * Sets if a column is hidden.
5964 * @param {Number} colIndex The column index
5965 * @param {Boolean} hidden True if the column is hidden
5967 setHidden : function(colIndex, hidden){
5968 this.config[colIndex].hidden = hidden;
5969 this.totalWidth = null;
5970 this.fireEvent("hiddenchange", this, colIndex, hidden);
5974 * Sets the editor for a column.
5975 * @param {Number} col The column index
5976 * @param {Object} editor The editor object
5978 setEditor : function(col, editor){
5979 this.config[col].editor = editor;
5983 Roo.grid.ColumnModel.defaultRenderer = function(value)
5985 if(typeof value == "object") {
5988 if(typeof value == "string" && value.length < 1){
5992 return String.format("{0}", value);
5995 // Alias for backwards compatibility
5996 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5999 * Ext JS Library 1.1.1
6000 * Copyright(c) 2006-2007, Ext JS, LLC.
6002 * Originally Released Under LGPL - original licence link has changed is not relivant.
6005 * <script type="text/javascript">
6009 * @class Roo.LoadMask
6010 * A simple utility class for generically masking elements while loading data. If the element being masked has
6011 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6012 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
6013 * element's UpdateManager load indicator and will be destroyed after the initial load.
6015 * Create a new LoadMask
6016 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6017 * @param {Object} config The config object
6019 Roo.LoadMask = function(el, config){
6020 this.el = Roo.get(el);
6021 Roo.apply(this, config);
6023 this.store.on('beforeload', this.onBeforeLoad, this);
6024 this.store.on('load', this.onLoad, this);
6025 this.store.on('loadexception', this.onLoadException, this);
6026 this.removeMask = false;
6028 var um = this.el.getUpdateManager();
6029 um.showLoadIndicator = false; // disable the default indicator
6030 um.on('beforeupdate', this.onBeforeLoad, this);
6031 um.on('update', this.onLoad, this);
6032 um.on('failure', this.onLoad, this);
6033 this.removeMask = true;
6037 Roo.LoadMask.prototype = {
6039 * @cfg {Boolean} removeMask
6040 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6041 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6045 * The text to display in a centered loading message box (defaults to 'Loading...')
6049 * @cfg {String} msgCls
6050 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6052 msgCls : 'x-mask-loading',
6055 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6061 * Disables the mask to prevent it from being displayed
6063 disable : function(){
6064 this.disabled = true;
6068 * Enables the mask so that it can be displayed
6070 enable : function(){
6071 this.disabled = false;
6074 onLoadException : function()
6078 if (typeof(arguments[3]) != 'undefined') {
6079 Roo.MessageBox.alert("Error loading",arguments[3]);
6083 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6084 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6091 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6096 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6100 onBeforeLoad : function(){
6102 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6107 destroy : function(){
6109 this.store.un('beforeload', this.onBeforeLoad, this);
6110 this.store.un('load', this.onLoad, this);
6111 this.store.un('loadexception', this.onLoadException, this);
6113 var um = this.el.getUpdateManager();
6114 um.un('beforeupdate', this.onBeforeLoad, this);
6115 um.un('update', this.onLoad, this);
6116 um.un('failure', this.onLoad, this);
6127 * @class Roo.bootstrap.Table
6128 * @extends Roo.bootstrap.Component
6129 * Bootstrap Table class
6130 * @cfg {String} cls table class
6131 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6132 * @cfg {String} bgcolor Specifies the background color for a table
6133 * @cfg {Number} border Specifies whether the table cells should have borders or not
6134 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6135 * @cfg {Number} cellspacing Specifies the space between cells
6136 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6137 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6138 * @cfg {String} sortable Specifies that the table should be sortable
6139 * @cfg {String} summary Specifies a summary of the content of a table
6140 * @cfg {Number} width Specifies the width of a table
6141 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6143 * @cfg {boolean} striped Should the rows be alternative striped
6144 * @cfg {boolean} bordered Add borders to the table
6145 * @cfg {boolean} hover Add hover highlighting
6146 * @cfg {boolean} condensed Format condensed
6147 * @cfg {boolean} responsive Format condensed
6148 * @cfg {Boolean} loadMask (true|false) default false
6149 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6150 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6151 * @cfg {Boolean} rowSelection (true|false) default false
6152 * @cfg {Boolean} cellSelection (true|false) default false
6153 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6154 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6155 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6156 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6160 * Create a new Table
6161 * @param {Object} config The config object
6164 Roo.bootstrap.Table = function(config){
6165 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6170 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6171 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6172 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6173 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6175 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6177 this.sm.grid = this;
6178 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6179 this.sm = this.selModel;
6180 this.sm.xmodule = this.xmodule || false;
6183 if (this.cm && typeof(this.cm.config) == 'undefined') {
6184 this.colModel = new Roo.grid.ColumnModel(this.cm);
6185 this.cm = this.colModel;
6186 this.cm.xmodule = this.xmodule || false;
6189 this.store= Roo.factory(this.store, Roo.data);
6190 this.ds = this.store;
6191 this.ds.xmodule = this.xmodule || false;
6194 if (this.footer && this.store) {
6195 this.footer.dataSource = this.ds;
6196 this.footer = Roo.factory(this.footer);
6203 * Fires when a cell is clicked
6204 * @param {Roo.bootstrap.Table} this
6205 * @param {Roo.Element} el
6206 * @param {Number} rowIndex
6207 * @param {Number} columnIndex
6208 * @param {Roo.EventObject} e
6212 * @event celldblclick
6213 * Fires when a cell is double clicked
6214 * @param {Roo.bootstrap.Table} this
6215 * @param {Roo.Element} el
6216 * @param {Number} rowIndex
6217 * @param {Number} columnIndex
6218 * @param {Roo.EventObject} e
6220 "celldblclick" : true,
6223 * Fires when a row is clicked
6224 * @param {Roo.bootstrap.Table} this
6225 * @param {Roo.Element} el
6226 * @param {Number} rowIndex
6227 * @param {Roo.EventObject} e
6231 * @event rowdblclick
6232 * Fires when a row is double clicked
6233 * @param {Roo.bootstrap.Table} this
6234 * @param {Roo.Element} el
6235 * @param {Number} rowIndex
6236 * @param {Roo.EventObject} e
6238 "rowdblclick" : true,
6241 * Fires when a mouseover occur
6242 * @param {Roo.bootstrap.Table} this
6243 * @param {Roo.Element} el
6244 * @param {Number} rowIndex
6245 * @param {Number} columnIndex
6246 * @param {Roo.EventObject} e
6251 * Fires when a mouseout occur
6252 * @param {Roo.bootstrap.Table} this
6253 * @param {Roo.Element} el
6254 * @param {Number} rowIndex
6255 * @param {Number} columnIndex
6256 * @param {Roo.EventObject} e
6261 * Fires when a row is rendered, so you can change add a style to it.
6262 * @param {Roo.bootstrap.Table} this
6263 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6267 * @event rowsrendered
6268 * Fires when all the rows have been rendered
6269 * @param {Roo.bootstrap.Table} this
6271 'rowsrendered' : true,
6273 * @event contextmenu
6274 * The raw contextmenu event for the entire grid.
6275 * @param {Roo.EventObject} e
6277 "contextmenu" : true,
6279 * @event rowcontextmenu
6280 * Fires when a row is right clicked
6281 * @param {Roo.bootstrap.Table} this
6282 * @param {Number} rowIndex
6283 * @param {Roo.EventObject} e
6285 "rowcontextmenu" : true,
6287 * @event cellcontextmenu
6288 * Fires when a cell is right clicked
6289 * @param {Roo.bootstrap.Table} this
6290 * @param {Number} rowIndex
6291 * @param {Number} cellIndex
6292 * @param {Roo.EventObject} e
6294 "cellcontextmenu" : true,
6296 * @event headercontextmenu
6297 * Fires when a header is right clicked
6298 * @param {Roo.bootstrap.Table} this
6299 * @param {Number} columnIndex
6300 * @param {Roo.EventObject} e
6302 "headercontextmenu" : true
6306 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6332 rowSelection : false,
6333 cellSelection : false,
6336 // Roo.Element - the tbody
6338 // Roo.Element - thead element
6341 container: false, // used by gridpanel...
6347 auto_hide_footer : false,
6349 getAutoCreate : function()
6351 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6358 if (this.scrollBody) {
6359 cfg.cls += ' table-body-fixed';
6362 cfg.cls += ' table-striped';
6366 cfg.cls += ' table-hover';
6368 if (this.bordered) {
6369 cfg.cls += ' table-bordered';
6371 if (this.condensed) {
6372 cfg.cls += ' table-condensed';
6374 if (this.responsive) {
6375 cfg.cls += ' table-responsive';
6379 cfg.cls+= ' ' +this.cls;
6382 // this lot should be simplifed...
6395 ].forEach(function(k) {
6403 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6406 if(this.store || this.cm){
6407 if(this.headerShow){
6408 cfg.cn.push(this.renderHeader());
6411 cfg.cn.push(this.renderBody());
6413 if(this.footerShow){
6414 cfg.cn.push(this.renderFooter());
6416 // where does this come from?
6417 //cfg.cls+= ' TableGrid';
6420 return { cn : [ cfg ] };
6423 initEvents : function()
6425 if(!this.store || !this.cm){
6428 if (this.selModel) {
6429 this.selModel.initEvents();
6433 //Roo.log('initEvents with ds!!!!');
6435 this.mainBody = this.el.select('tbody', true).first();
6436 this.mainHead = this.el.select('thead', true).first();
6437 this.mainFoot = this.el.select('tfoot', true).first();
6443 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6444 e.on('click', _this.sort, _this);
6447 this.mainBody.on("click", this.onClick, this);
6448 this.mainBody.on("dblclick", this.onDblClick, this);
6450 // why is this done????? = it breaks dialogs??
6451 //this.parent().el.setStyle('position', 'relative');
6455 this.footer.parentId = this.id;
6456 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6459 this.el.select('tfoot tr td').first().addClass('hide');
6464 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6467 this.store.on('load', this.onLoad, this);
6468 this.store.on('beforeload', this.onBeforeLoad, this);
6469 this.store.on('update', this.onUpdate, this);
6470 this.store.on('add', this.onAdd, this);
6471 this.store.on("clear", this.clear, this);
6473 this.el.on("contextmenu", this.onContextMenu, this);
6475 this.mainBody.on('scroll', this.onBodyScroll, this);
6477 this.cm.on("headerchange", this.onHeaderChange, this);
6479 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6483 onContextMenu : function(e, t)
6485 this.processEvent("contextmenu", e);
6488 processEvent : function(name, e)
6490 if (name != 'touchstart' ) {
6491 this.fireEvent(name, e);
6494 var t = e.getTarget();
6496 var cell = Roo.get(t);
6502 if(cell.findParent('tfoot', false, true)){
6506 if(cell.findParent('thead', false, true)){
6508 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6509 cell = Roo.get(t).findParent('th', false, true);
6511 Roo.log("failed to find th in thead?");
6512 Roo.log(e.getTarget());
6517 var cellIndex = cell.dom.cellIndex;
6519 var ename = name == 'touchstart' ? 'click' : name;
6520 this.fireEvent("header" + ename, this, cellIndex, e);
6525 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6526 cell = Roo.get(t).findParent('td', false, true);
6528 Roo.log("failed to find th in tbody?");
6529 Roo.log(e.getTarget());
6534 var row = cell.findParent('tr', false, true);
6535 var cellIndex = cell.dom.cellIndex;
6536 var rowIndex = row.dom.rowIndex - 1;
6540 this.fireEvent("row" + name, this, rowIndex, e);
6544 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6550 onMouseover : function(e, el)
6552 var cell = Roo.get(el);
6558 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6559 cell = cell.findParent('td', false, true);
6562 var row = cell.findParent('tr', false, true);
6563 var cellIndex = cell.dom.cellIndex;
6564 var rowIndex = row.dom.rowIndex - 1; // start from 0
6566 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6570 onMouseout : function(e, el)
6572 var cell = Roo.get(el);
6578 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6579 cell = cell.findParent('td', false, true);
6582 var row = cell.findParent('tr', false, true);
6583 var cellIndex = cell.dom.cellIndex;
6584 var rowIndex = row.dom.rowIndex - 1; // start from 0
6586 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6590 onClick : function(e, el)
6592 var cell = Roo.get(el);
6594 if(!cell || (!this.cellSelection && !this.rowSelection)){
6598 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6599 cell = cell.findParent('td', false, true);
6602 if(!cell || typeof(cell) == 'undefined'){
6606 var row = cell.findParent('tr', false, true);
6608 if(!row || typeof(row) == 'undefined'){
6612 var cellIndex = cell.dom.cellIndex;
6613 var rowIndex = this.getRowIndex(row);
6615 // why??? - should these not be based on SelectionModel?
6616 if(this.cellSelection){
6617 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6620 if(this.rowSelection){
6621 this.fireEvent('rowclick', this, row, rowIndex, e);
6627 onDblClick : function(e,el)
6629 var cell = Roo.get(el);
6631 if(!cell || (!this.cellSelection && !this.rowSelection)){
6635 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6636 cell = cell.findParent('td', false, true);
6639 if(!cell || typeof(cell) == 'undefined'){
6643 var row = cell.findParent('tr', false, true);
6645 if(!row || typeof(row) == 'undefined'){
6649 var cellIndex = cell.dom.cellIndex;
6650 var rowIndex = this.getRowIndex(row);
6652 if(this.cellSelection){
6653 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6656 if(this.rowSelection){
6657 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6661 sort : function(e,el)
6663 var col = Roo.get(el);
6665 if(!col.hasClass('sortable')){
6669 var sort = col.attr('sort');
6672 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6676 this.store.sortInfo = {field : sort, direction : dir};
6679 Roo.log("calling footer first");
6680 this.footer.onClick('first');
6683 this.store.load({ params : { start : 0 } });
6687 renderHeader : function()
6695 this.totalWidth = 0;
6697 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6699 var config = cm.config[i];
6703 cls : 'x-hcol-' + i,
6705 html: cm.getColumnHeader(i)
6710 if(typeof(config.sortable) != 'undefined' && config.sortable){
6712 c.html = '<i class="glyphicon"></i>' + c.html;
6715 if(typeof(config.lgHeader) != 'undefined'){
6716 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6719 if(typeof(config.mdHeader) != 'undefined'){
6720 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6723 if(typeof(config.smHeader) != 'undefined'){
6724 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6727 if(typeof(config.xsHeader) != 'undefined'){
6728 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6735 if(typeof(config.tooltip) != 'undefined'){
6736 c.tooltip = config.tooltip;
6739 if(typeof(config.colspan) != 'undefined'){
6740 c.colspan = config.colspan;
6743 if(typeof(config.hidden) != 'undefined' && config.hidden){
6744 c.style += ' display:none;';
6747 if(typeof(config.dataIndex) != 'undefined'){
6748 c.sort = config.dataIndex;
6753 if(typeof(config.align) != 'undefined' && config.align.length){
6754 c.style += ' text-align:' + config.align + ';';
6757 if(typeof(config.width) != 'undefined'){
6758 c.style += ' width:' + config.width + 'px;';
6759 this.totalWidth += config.width;
6761 this.totalWidth += 100; // assume minimum of 100 per column?
6764 if(typeof(config.cls) != 'undefined'){
6765 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6768 ['xs','sm','md','lg'].map(function(size){
6770 if(typeof(config[size]) == 'undefined'){
6774 if (!config[size]) { // 0 = hidden
6775 c.cls += ' hidden-' + size;
6779 c.cls += ' col-' + size + '-' + config[size];
6789 renderBody : function()
6799 colspan : this.cm.getColumnCount()
6809 renderFooter : function()
6819 colspan : this.cm.getColumnCount()
6833 // Roo.log('ds onload');
6838 var ds = this.store;
6840 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6841 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6842 if (_this.store.sortInfo) {
6844 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6845 e.select('i', true).addClass(['glyphicon-arrow-up']);
6848 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6849 e.select('i', true).addClass(['glyphicon-arrow-down']);
6854 var tbody = this.mainBody;
6856 if(ds.getCount() > 0){
6857 ds.data.each(function(d,rowIndex){
6858 var row = this.renderRow(cm, ds, rowIndex);
6860 tbody.createChild(row);
6864 if(row.cellObjects.length){
6865 Roo.each(row.cellObjects, function(r){
6866 _this.renderCellObject(r);
6873 var tfoot = this.el.select('tfoot', true).first();
6875 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6877 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6879 var total = this.ds.getTotalCount();
6881 if(this.footer.pageSize < total){
6882 this.mainFoot.show();
6886 Roo.each(this.el.select('tbody td', true).elements, function(e){
6887 e.on('mouseover', _this.onMouseover, _this);
6890 Roo.each(this.el.select('tbody td', true).elements, function(e){
6891 e.on('mouseout', _this.onMouseout, _this);
6893 this.fireEvent('rowsrendered', this);
6899 onUpdate : function(ds,record)
6901 this.refreshRow(record);
6905 onRemove : function(ds, record, index, isUpdate){
6906 if(isUpdate !== true){
6907 this.fireEvent("beforerowremoved", this, index, record);
6909 var bt = this.mainBody.dom;
6911 var rows = this.el.select('tbody > tr', true).elements;
6913 if(typeof(rows[index]) != 'undefined'){
6914 bt.removeChild(rows[index].dom);
6917 // if(bt.rows[index]){
6918 // bt.removeChild(bt.rows[index]);
6921 if(isUpdate !== true){
6922 //this.stripeRows(index);
6923 //this.syncRowHeights(index, index);
6925 this.fireEvent("rowremoved", this, index, record);
6929 onAdd : function(ds, records, rowIndex)
6931 //Roo.log('on Add called');
6932 // - note this does not handle multiple adding very well..
6933 var bt = this.mainBody.dom;
6934 for (var i =0 ; i < records.length;i++) {
6935 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6936 //Roo.log(records[i]);
6937 //Roo.log(this.store.getAt(rowIndex+i));
6938 this.insertRow(this.store, rowIndex + i, false);
6945 refreshRow : function(record){
6946 var ds = this.store, index;
6947 if(typeof record == 'number'){
6949 record = ds.getAt(index);
6951 index = ds.indexOf(record);
6953 this.insertRow(ds, index, true);
6955 this.onRemove(ds, record, index+1, true);
6957 //this.syncRowHeights(index, index);
6959 this.fireEvent("rowupdated", this, index, record);
6962 insertRow : function(dm, rowIndex, isUpdate){
6965 this.fireEvent("beforerowsinserted", this, rowIndex);
6967 //var s = this.getScrollState();
6968 var row = this.renderRow(this.cm, this.store, rowIndex);
6969 // insert before rowIndex..
6970 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6974 if(row.cellObjects.length){
6975 Roo.each(row.cellObjects, function(r){
6976 _this.renderCellObject(r);
6981 this.fireEvent("rowsinserted", this, rowIndex);
6982 //this.syncRowHeights(firstRow, lastRow);
6983 //this.stripeRows(firstRow);
6990 getRowDom : function(rowIndex)
6992 var rows = this.el.select('tbody > tr', true).elements;
6994 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6997 // returns the object tree for a tr..
7000 renderRow : function(cm, ds, rowIndex)
7002 var d = ds.getAt(rowIndex);
7006 cls : 'x-row-' + rowIndex,
7010 var cellObjects = [];
7012 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7013 var config = cm.config[i];
7015 var renderer = cm.getRenderer(i);
7019 if(typeof(renderer) !== 'undefined'){
7020 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7022 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7023 // and are rendered into the cells after the row is rendered - using the id for the element.
7025 if(typeof(value) === 'object'){
7035 rowIndex : rowIndex,
7040 this.fireEvent('rowclass', this, rowcfg);
7044 cls : rowcfg.rowClass + ' x-col-' + i,
7046 html: (typeof(value) === 'object') ? '' : value
7053 if(typeof(config.colspan) != 'undefined'){
7054 td.colspan = config.colspan;
7057 if(typeof(config.hidden) != 'undefined' && config.hidden){
7058 td.style += ' display:none;';
7061 if(typeof(config.align) != 'undefined' && config.align.length){
7062 td.style += ' text-align:' + config.align + ';';
7064 if(typeof(config.valign) != 'undefined' && config.valign.length){
7065 td.style += ' vertical-align:' + config.valign + ';';
7068 if(typeof(config.width) != 'undefined'){
7069 td.style += ' width:' + config.width + 'px;';
7072 if(typeof(config.cursor) != 'undefined'){
7073 td.style += ' cursor:' + config.cursor + ';';
7076 if(typeof(config.cls) != 'undefined'){
7077 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7080 ['xs','sm','md','lg'].map(function(size){
7082 if(typeof(config[size]) == 'undefined'){
7086 if (!config[size]) { // 0 = hidden
7087 td.cls += ' hidden-' + size;
7091 td.cls += ' col-' + size + '-' + config[size];
7099 row.cellObjects = cellObjects;
7107 onBeforeLoad : function()
7116 this.el.select('tbody', true).first().dom.innerHTML = '';
7119 * Show or hide a row.
7120 * @param {Number} rowIndex to show or hide
7121 * @param {Boolean} state hide
7123 setRowVisibility : function(rowIndex, state)
7125 var bt = this.mainBody.dom;
7127 var rows = this.el.select('tbody > tr', true).elements;
7129 if(typeof(rows[rowIndex]) == 'undefined'){
7132 rows[rowIndex].dom.style.display = state ? '' : 'none';
7136 getSelectionModel : function(){
7138 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7140 return this.selModel;
7143 * Render the Roo.bootstrap object from renderder
7145 renderCellObject : function(r)
7149 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7151 var t = r.cfg.render(r.container);
7154 Roo.each(r.cfg.cn, function(c){
7156 container: t.getChildContainer(),
7159 _this.renderCellObject(child);
7164 getRowIndex : function(row)
7168 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7179 * Returns the grid's underlying element = used by panel.Grid
7180 * @return {Element} The element
7182 getGridEl : function(){
7186 * Forces a resize - used by panel.Grid
7187 * @return {Element} The element
7189 autoSize : function()
7191 //var ctr = Roo.get(this.container.dom.parentElement);
7192 var ctr = Roo.get(this.el.dom);
7194 var thd = this.getGridEl().select('thead',true).first();
7195 var tbd = this.getGridEl().select('tbody', true).first();
7196 var tfd = this.getGridEl().select('tfoot', true).first();
7198 var cw = ctr.getWidth();
7202 tbd.setSize(ctr.getWidth(),
7203 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7205 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7208 cw = Math.max(cw, this.totalWidth);
7209 this.getGridEl().select('tr',true).setWidth(cw);
7210 // resize 'expandable coloumn?
7212 return; // we doe not have a view in this design..
7215 onBodyScroll: function()
7217 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7219 this.mainHead.setStyle({
7220 'position' : 'relative',
7221 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7227 var scrollHeight = this.mainBody.dom.scrollHeight;
7229 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7231 var height = this.mainBody.getHeight();
7233 if(scrollHeight - height == scrollTop) {
7235 var total = this.ds.getTotalCount();
7237 if(this.footer.cursor + this.footer.pageSize < total){
7239 this.footer.ds.load({
7241 start : this.footer.cursor + this.footer.pageSize,
7242 limit : this.footer.pageSize
7252 onHeaderChange : function()
7254 var header = this.renderHeader();
7255 var table = this.el.select('table', true).first();
7257 this.mainHead.remove();
7258 this.mainHead = table.createChild(header, this.mainBody, false);
7261 onHiddenChange : function(colModel, colIndex, hidden)
7263 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7264 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7266 this.CSS.updateRule(thSelector, "display", "");
7267 this.CSS.updateRule(tdSelector, "display", "");
7270 this.CSS.updateRule(thSelector, "display", "none");
7271 this.CSS.updateRule(tdSelector, "display", "none");
7274 this.onHeaderChange();
7278 setColumnWidth: function(col_index, width)
7280 // width = "md-2 xs-2..."
7281 if(!this.colModel.config[col_index]) {
7285 var w = width.split(" ");
7287 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7289 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7292 for(var j = 0; j < w.length; j++) {
7298 var size_cls = w[j].split("-");
7300 if(!Number.isInteger(size_cls[1] * 1)) {
7304 if(!this.colModel.config[col_index][size_cls[0]]) {
7308 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7312 h_row[0].classList.replace(
7313 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7314 "col-"+size_cls[0]+"-"+size_cls[1]
7317 for(var i = 0; i < rows.length; i++) {
7319 var size_cls = w[j].split("-");
7321 if(!Number.isInteger(size_cls[1] * 1)) {
7325 if(!this.colModel.config[col_index][size_cls[0]]) {
7329 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7333 rows[i].classList.replace(
7334 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7335 "col-"+size_cls[0]+"-"+size_cls[1]
7339 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7354 * @class Roo.bootstrap.TableCell
7355 * @extends Roo.bootstrap.Component
7356 * Bootstrap TableCell class
7357 * @cfg {String} html cell contain text
7358 * @cfg {String} cls cell class
7359 * @cfg {String} tag cell tag (td|th) default td
7360 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7361 * @cfg {String} align Aligns the content in a cell
7362 * @cfg {String} axis Categorizes cells
7363 * @cfg {String} bgcolor Specifies the background color of a cell
7364 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7365 * @cfg {Number} colspan Specifies the number of columns a cell should span
7366 * @cfg {String} headers Specifies one or more header cells a cell is related to
7367 * @cfg {Number} height Sets the height of a cell
7368 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7369 * @cfg {Number} rowspan Sets the number of rows a cell should span
7370 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7371 * @cfg {String} valign Vertical aligns the content in a cell
7372 * @cfg {Number} width Specifies the width of a cell
7375 * Create a new TableCell
7376 * @param {Object} config The config object
7379 Roo.bootstrap.TableCell = function(config){
7380 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7383 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7403 getAutoCreate : function(){
7404 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7424 cfg.align=this.align
7430 cfg.bgcolor=this.bgcolor
7433 cfg.charoff=this.charoff
7436 cfg.colspan=this.colspan
7439 cfg.headers=this.headers
7442 cfg.height=this.height
7445 cfg.nowrap=this.nowrap
7448 cfg.rowspan=this.rowspan
7451 cfg.scope=this.scope
7454 cfg.valign=this.valign
7457 cfg.width=this.width
7476 * @class Roo.bootstrap.TableRow
7477 * @extends Roo.bootstrap.Component
7478 * Bootstrap TableRow class
7479 * @cfg {String} cls row class
7480 * @cfg {String} align Aligns the content in a table row
7481 * @cfg {String} bgcolor Specifies a background color for a table row
7482 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7483 * @cfg {String} valign Vertical aligns the content in a table row
7486 * Create a new TableRow
7487 * @param {Object} config The config object
7490 Roo.bootstrap.TableRow = function(config){
7491 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7494 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7502 getAutoCreate : function(){
7503 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7513 cfg.align = this.align;
7516 cfg.bgcolor = this.bgcolor;
7519 cfg.charoff = this.charoff;
7522 cfg.valign = this.valign;
7540 * @class Roo.bootstrap.TableBody
7541 * @extends Roo.bootstrap.Component
7542 * Bootstrap TableBody class
7543 * @cfg {String} cls element class
7544 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7545 * @cfg {String} align Aligns the content inside the element
7546 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7547 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7550 * Create a new TableBody
7551 * @param {Object} config The config object
7554 Roo.bootstrap.TableBody = function(config){
7555 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7558 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7566 getAutoCreate : function(){
7567 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7581 cfg.align = this.align;
7584 cfg.charoff = this.charoff;
7587 cfg.valign = this.valign;
7594 // initEvents : function()
7601 // this.store = Roo.factory(this.store, Roo.data);
7602 // this.store.on('load', this.onLoad, this);
7604 // this.store.load();
7608 // onLoad: function ()
7610 // this.fireEvent('load', this);
7620 * Ext JS Library 1.1.1
7621 * Copyright(c) 2006-2007, Ext JS, LLC.
7623 * Originally Released Under LGPL - original licence link has changed is not relivant.
7626 * <script type="text/javascript">
7629 // as we use this in bootstrap.
7630 Roo.namespace('Roo.form');
7632 * @class Roo.form.Action
7633 * Internal Class used to handle form actions
7635 * @param {Roo.form.BasicForm} el The form element or its id
7636 * @param {Object} config Configuration options
7641 // define the action interface
7642 Roo.form.Action = function(form, options){
7644 this.options = options || {};
7647 * Client Validation Failed
7650 Roo.form.Action.CLIENT_INVALID = 'client';
7652 * Server Validation Failed
7655 Roo.form.Action.SERVER_INVALID = 'server';
7657 * Connect to Server Failed
7660 Roo.form.Action.CONNECT_FAILURE = 'connect';
7662 * Reading Data from Server Failed
7665 Roo.form.Action.LOAD_FAILURE = 'load';
7667 Roo.form.Action.prototype = {
7669 failureType : undefined,
7670 response : undefined,
7674 run : function(options){
7679 success : function(response){
7684 handleResponse : function(response){
7688 // default connection failure
7689 failure : function(response){
7691 this.response = response;
7692 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7693 this.form.afterAction(this, false);
7696 processResponse : function(response){
7697 this.response = response;
7698 if(!response.responseText){
7701 this.result = this.handleResponse(response);
7705 // utility functions used internally
7706 getUrl : function(appendParams){
7707 var url = this.options.url || this.form.url || this.form.el.dom.action;
7709 var p = this.getParams();
7711 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7717 getMethod : function(){
7718 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7721 getParams : function(){
7722 var bp = this.form.baseParams;
7723 var p = this.options.params;
7725 if(typeof p == "object"){
7726 p = Roo.urlEncode(Roo.applyIf(p, bp));
7727 }else if(typeof p == 'string' && bp){
7728 p += '&' + Roo.urlEncode(bp);
7731 p = Roo.urlEncode(bp);
7736 createCallback : function(){
7738 success: this.success,
7739 failure: this.failure,
7741 timeout: (this.form.timeout*1000),
7742 upload: this.form.fileUpload ? this.success : undefined
7747 Roo.form.Action.Submit = function(form, options){
7748 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7751 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7754 haveProgress : false,
7755 uploadComplete : false,
7757 // uploadProgress indicator.
7758 uploadProgress : function()
7760 if (!this.form.progressUrl) {
7764 if (!this.haveProgress) {
7765 Roo.MessageBox.progress("Uploading", "Uploading");
7767 if (this.uploadComplete) {
7768 Roo.MessageBox.hide();
7772 this.haveProgress = true;
7774 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7776 var c = new Roo.data.Connection();
7778 url : this.form.progressUrl,
7783 success : function(req){
7784 //console.log(data);
7788 rdata = Roo.decode(req.responseText)
7790 Roo.log("Invalid data from server..");
7794 if (!rdata || !rdata.success) {
7796 Roo.MessageBox.alert(Roo.encode(rdata));
7799 var data = rdata.data;
7801 if (this.uploadComplete) {
7802 Roo.MessageBox.hide();
7807 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7808 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7811 this.uploadProgress.defer(2000,this);
7814 failure: function(data) {
7815 Roo.log('progress url failed ');
7826 // run get Values on the form, so it syncs any secondary forms.
7827 this.form.getValues();
7829 var o = this.options;
7830 var method = this.getMethod();
7831 var isPost = method == 'POST';
7832 if(o.clientValidation === false || this.form.isValid()){
7834 if (this.form.progressUrl) {
7835 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7836 (new Date() * 1) + '' + Math.random());
7841 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7842 form:this.form.el.dom,
7843 url:this.getUrl(!isPost),
7845 params:isPost ? this.getParams() : null,
7846 isUpload: this.form.fileUpload
7849 this.uploadProgress();
7851 }else if (o.clientValidation !== false){ // client validation failed
7852 this.failureType = Roo.form.Action.CLIENT_INVALID;
7853 this.form.afterAction(this, false);
7857 success : function(response)
7859 this.uploadComplete= true;
7860 if (this.haveProgress) {
7861 Roo.MessageBox.hide();
7865 var result = this.processResponse(response);
7866 if(result === true || result.success){
7867 this.form.afterAction(this, true);
7871 this.form.markInvalid(result.errors);
7872 this.failureType = Roo.form.Action.SERVER_INVALID;
7874 this.form.afterAction(this, false);
7876 failure : function(response)
7878 this.uploadComplete= true;
7879 if (this.haveProgress) {
7880 Roo.MessageBox.hide();
7883 this.response = response;
7884 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7885 this.form.afterAction(this, false);
7888 handleResponse : function(response){
7889 if(this.form.errorReader){
7890 var rs = this.form.errorReader.read(response);
7893 for(var i = 0, len = rs.records.length; i < len; i++) {
7894 var r = rs.records[i];
7898 if(errors.length < 1){
7902 success : rs.success,
7908 ret = Roo.decode(response.responseText);
7912 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7922 Roo.form.Action.Load = function(form, options){
7923 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7924 this.reader = this.form.reader;
7927 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7932 Roo.Ajax.request(Roo.apply(
7933 this.createCallback(), {
7934 method:this.getMethod(),
7935 url:this.getUrl(false),
7936 params:this.getParams()
7940 success : function(response){
7942 var result = this.processResponse(response);
7943 if(result === true || !result.success || !result.data){
7944 this.failureType = Roo.form.Action.LOAD_FAILURE;
7945 this.form.afterAction(this, false);
7948 this.form.clearInvalid();
7949 this.form.setValues(result.data);
7950 this.form.afterAction(this, true);
7953 handleResponse : function(response){
7954 if(this.form.reader){
7955 var rs = this.form.reader.read(response);
7956 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7958 success : rs.success,
7962 return Roo.decode(response.responseText);
7966 Roo.form.Action.ACTION_TYPES = {
7967 'load' : Roo.form.Action.Load,
7968 'submit' : Roo.form.Action.Submit
7977 * @class Roo.bootstrap.Form
7978 * @extends Roo.bootstrap.Component
7979 * Bootstrap Form class
7980 * @cfg {String} method GET | POST (default POST)
7981 * @cfg {String} labelAlign top | left (default top)
7982 * @cfg {String} align left | right - for navbars
7983 * @cfg {Boolean} loadMask load mask when submit (default true)
7988 * @param {Object} config The config object
7992 Roo.bootstrap.Form = function(config){
7994 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7996 Roo.bootstrap.Form.popover.apply();
8000 * @event clientvalidation
8001 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8002 * @param {Form} this
8003 * @param {Boolean} valid true if the form has passed client-side validation
8005 clientvalidation: true,
8007 * @event beforeaction
8008 * Fires before any action is performed. Return false to cancel the action.
8009 * @param {Form} this
8010 * @param {Action} action The action to be performed
8014 * @event actionfailed
8015 * Fires when an action fails.
8016 * @param {Form} this
8017 * @param {Action} action The action that failed
8019 actionfailed : true,
8021 * @event actioncomplete
8022 * Fires when an action is completed.
8023 * @param {Form} this
8024 * @param {Action} action The action that completed
8026 actioncomplete : true
8030 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
8033 * @cfg {String} method
8034 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8039 * The URL to use for form actions if one isn't supplied in the action options.
8042 * @cfg {Boolean} fileUpload
8043 * Set to true if this form is a file upload.
8047 * @cfg {Object} baseParams
8048 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8052 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8056 * @cfg {Sting} align (left|right) for navbar forms
8061 activeAction : null,
8064 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8065 * element by passing it or its id or mask the form itself by passing in true.
8068 waitMsgTarget : false,
8073 * @cfg {Boolean} errorMask (true|false) default false
8078 * @cfg {Number} maskOffset Default 100
8083 * @cfg {Boolean} maskBody
8087 getAutoCreate : function(){
8091 method : this.method || 'POST',
8092 id : this.id || Roo.id(),
8095 if (this.parent().xtype.match(/^Nav/)) {
8096 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8100 if (this.labelAlign == 'left' ) {
8101 cfg.cls += ' form-horizontal';
8107 initEvents : function()
8109 this.el.on('submit', this.onSubmit, this);
8110 // this was added as random key presses on the form where triggering form submit.
8111 this.el.on('keypress', function(e) {
8112 if (e.getCharCode() != 13) {
8115 // we might need to allow it for textareas.. and some other items.
8116 // check e.getTarget().
8118 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8122 Roo.log("keypress blocked");
8130 onSubmit : function(e){
8135 * Returns true if client-side validation on the form is successful.
8138 isValid : function(){
8139 var items = this.getItems();
8143 items.each(function(f){
8149 Roo.log('invalid field: ' + f.name);
8153 if(!target && f.el.isVisible(true)){
8159 if(this.errorMask && !valid){
8160 Roo.bootstrap.Form.popover.mask(this, target);
8167 * Returns true if any fields in this form have changed since their original load.
8170 isDirty : function(){
8172 var items = this.getItems();
8173 items.each(function(f){
8183 * Performs a predefined action (submit or load) or custom actions you define on this form.
8184 * @param {String} actionName The name of the action type
8185 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8186 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8187 * accept other config options):
8189 Property Type Description
8190 ---------------- --------------- ----------------------------------------------------------------------------------
8191 url String The url for the action (defaults to the form's url)
8192 method String The form method to use (defaults to the form's method, or POST if not defined)
8193 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8194 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8195 validate the form on the client (defaults to false)
8197 * @return {BasicForm} this
8199 doAction : function(action, options){
8200 if(typeof action == 'string'){
8201 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8203 if(this.fireEvent('beforeaction', this, action) !== false){
8204 this.beforeAction(action);
8205 action.run.defer(100, action);
8211 beforeAction : function(action){
8212 var o = action.options;
8217 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8219 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8222 // not really supported yet.. ??
8224 //if(this.waitMsgTarget === true){
8225 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8226 //}else if(this.waitMsgTarget){
8227 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8228 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8230 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8236 afterAction : function(action, success){
8237 this.activeAction = null;
8238 var o = action.options;
8243 Roo.get(document.body).unmask();
8249 //if(this.waitMsgTarget === true){
8250 // this.el.unmask();
8251 //}else if(this.waitMsgTarget){
8252 // this.waitMsgTarget.unmask();
8254 // Roo.MessageBox.updateProgress(1);
8255 // Roo.MessageBox.hide();
8262 Roo.callback(o.success, o.scope, [this, action]);
8263 this.fireEvent('actioncomplete', this, action);
8267 // failure condition..
8268 // we have a scenario where updates need confirming.
8269 // eg. if a locking scenario exists..
8270 // we look for { errors : { needs_confirm : true }} in the response.
8272 (typeof(action.result) != 'undefined') &&
8273 (typeof(action.result.errors) != 'undefined') &&
8274 (typeof(action.result.errors.needs_confirm) != 'undefined')
8277 Roo.log("not supported yet");
8280 Roo.MessageBox.confirm(
8281 "Change requires confirmation",
8282 action.result.errorMsg,
8287 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8297 Roo.callback(o.failure, o.scope, [this, action]);
8298 // show an error message if no failed handler is set..
8299 if (!this.hasListener('actionfailed')) {
8300 Roo.log("need to add dialog support");
8302 Roo.MessageBox.alert("Error",
8303 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8304 action.result.errorMsg :
8305 "Saving Failed, please check your entries or try again"
8310 this.fireEvent('actionfailed', this, action);
8315 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8316 * @param {String} id The value to search for
8319 findField : function(id){
8320 var items = this.getItems();
8321 var field = items.get(id);
8323 items.each(function(f){
8324 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8331 return field || null;
8334 * Mark fields in this form invalid in bulk.
8335 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8336 * @return {BasicForm} this
8338 markInvalid : function(errors){
8339 if(errors instanceof Array){
8340 for(var i = 0, len = errors.length; i < len; i++){
8341 var fieldError = errors[i];
8342 var f = this.findField(fieldError.id);
8344 f.markInvalid(fieldError.msg);
8350 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8351 field.markInvalid(errors[id]);
8355 //Roo.each(this.childForms || [], function (f) {
8356 // f.markInvalid(errors);
8363 * Set values for fields in this form in bulk.
8364 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8365 * @return {BasicForm} this
8367 setValues : function(values){
8368 if(values instanceof Array){ // array of objects
8369 for(var i = 0, len = values.length; i < len; i++){
8371 var f = this.findField(v.id);
8373 f.setValue(v.value);
8374 if(this.trackResetOnLoad){
8375 f.originalValue = f.getValue();
8379 }else{ // object hash
8382 if(typeof values[id] != 'function' && (field = this.findField(id))){
8384 if (field.setFromData &&
8386 field.displayField &&
8387 // combos' with local stores can
8388 // be queried via setValue()
8389 // to set their value..
8390 (field.store && !field.store.isLocal)
8394 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8395 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8396 field.setFromData(sd);
8398 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8400 field.setFromData(values);
8403 field.setValue(values[id]);
8407 if(this.trackResetOnLoad){
8408 field.originalValue = field.getValue();
8414 //Roo.each(this.childForms || [], function (f) {
8415 // f.setValues(values);
8422 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8423 * they are returned as an array.
8424 * @param {Boolean} asString
8427 getValues : function(asString){
8428 //if (this.childForms) {
8429 // copy values from the child forms
8430 // Roo.each(this.childForms, function (f) {
8431 // this.setValues(f.getValues());
8437 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8438 if(asString === true){
8441 return Roo.urlDecode(fs);
8445 * Returns the fields in this form as an object with key/value pairs.
8446 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8449 getFieldValues : function(with_hidden)
8451 var items = this.getItems();
8453 items.each(function(f){
8459 var v = f.getValue();
8461 if (f.inputType =='radio') {
8462 if (typeof(ret[f.getName()]) == 'undefined') {
8463 ret[f.getName()] = ''; // empty..
8466 if (!f.el.dom.checked) {
8474 if(f.xtype == 'MoneyField'){
8475 ret[f.currencyName] = f.getCurrency();
8478 // not sure if this supported any more..
8479 if ((typeof(v) == 'object') && f.getRawValue) {
8480 v = f.getRawValue() ; // dates..
8482 // combo boxes where name != hiddenName...
8483 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8484 ret[f.name] = f.getRawValue();
8486 ret[f.getName()] = v;
8493 * Clears all invalid messages in this form.
8494 * @return {BasicForm} this
8496 clearInvalid : function(){
8497 var items = this.getItems();
8499 items.each(function(f){
8508 * @return {BasicForm} this
8511 var items = this.getItems();
8512 items.each(function(f){
8516 Roo.each(this.childForms || [], function (f) {
8524 getItems : function()
8526 var r=new Roo.util.MixedCollection(false, function(o){
8527 return o.id || (o.id = Roo.id());
8529 var iter = function(el) {
8536 Roo.each(el.items,function(e) {
8545 hideFields : function(items)
8547 Roo.each(items, function(i){
8549 var f = this.findField(i);
8560 showFields : function(items)
8562 Roo.each(items, function(i){
8564 var f = this.findField(i);
8577 Roo.apply(Roo.bootstrap.Form, {
8604 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8605 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8606 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8607 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8610 this.maskEl.top.enableDisplayMode("block");
8611 this.maskEl.left.enableDisplayMode("block");
8612 this.maskEl.bottom.enableDisplayMode("block");
8613 this.maskEl.right.enableDisplayMode("block");
8615 this.toolTip = new Roo.bootstrap.Tooltip({
8616 cls : 'roo-form-error-popover',
8618 'left' : ['r-l', [-2,0], 'right'],
8619 'right' : ['l-r', [2,0], 'left'],
8620 'bottom' : ['tl-bl', [0,2], 'top'],
8621 'top' : [ 'bl-tl', [0,-2], 'bottom']
8625 this.toolTip.render(Roo.get(document.body));
8627 this.toolTip.el.enableDisplayMode("block");
8629 Roo.get(document.body).on('click', function(){
8633 Roo.get(document.body).on('touchstart', function(){
8637 this.isApplied = true
8640 mask : function(form, target)
8644 this.target = target;
8646 if(!this.form.errorMask || !target.el){
8650 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8652 Roo.log(scrollable);
8654 var ot = this.target.el.calcOffsetsTo(scrollable);
8656 var scrollTo = ot[1] - this.form.maskOffset;
8658 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8660 scrollable.scrollTo('top', scrollTo);
8662 var box = this.target.el.getBox();
8664 var zIndex = Roo.bootstrap.Modal.zIndex++;
8667 this.maskEl.top.setStyle('position', 'absolute');
8668 this.maskEl.top.setStyle('z-index', zIndex);
8669 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8670 this.maskEl.top.setLeft(0);
8671 this.maskEl.top.setTop(0);
8672 this.maskEl.top.show();
8674 this.maskEl.left.setStyle('position', 'absolute');
8675 this.maskEl.left.setStyle('z-index', zIndex);
8676 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8677 this.maskEl.left.setLeft(0);
8678 this.maskEl.left.setTop(box.y - this.padding);
8679 this.maskEl.left.show();
8681 this.maskEl.bottom.setStyle('position', 'absolute');
8682 this.maskEl.bottom.setStyle('z-index', zIndex);
8683 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8684 this.maskEl.bottom.setLeft(0);
8685 this.maskEl.bottom.setTop(box.bottom + this.padding);
8686 this.maskEl.bottom.show();
8688 this.maskEl.right.setStyle('position', 'absolute');
8689 this.maskEl.right.setStyle('z-index', zIndex);
8690 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8691 this.maskEl.right.setLeft(box.right + this.padding);
8692 this.maskEl.right.setTop(box.y - this.padding);
8693 this.maskEl.right.show();
8695 this.toolTip.bindEl = this.target.el;
8697 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8699 var tip = this.target.blankText;
8701 if(this.target.getValue() !== '' ) {
8703 if (this.target.invalidText.length) {
8704 tip = this.target.invalidText;
8705 } else if (this.target.regexText.length){
8706 tip = this.target.regexText;
8710 this.toolTip.show(tip);
8712 this.intervalID = window.setInterval(function() {
8713 Roo.bootstrap.Form.popover.unmask();
8716 window.onwheel = function(){ return false;};
8718 (function(){ this.isMasked = true; }).defer(500, this);
8724 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8728 this.maskEl.top.setStyle('position', 'absolute');
8729 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8730 this.maskEl.top.hide();
8732 this.maskEl.left.setStyle('position', 'absolute');
8733 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8734 this.maskEl.left.hide();
8736 this.maskEl.bottom.setStyle('position', 'absolute');
8737 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8738 this.maskEl.bottom.hide();
8740 this.maskEl.right.setStyle('position', 'absolute');
8741 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8742 this.maskEl.right.hide();
8744 this.toolTip.hide();
8746 this.toolTip.el.hide();
8748 window.onwheel = function(){ return true;};
8750 if(this.intervalID){
8751 window.clearInterval(this.intervalID);
8752 this.intervalID = false;
8755 this.isMasked = false;
8765 * Ext JS Library 1.1.1
8766 * Copyright(c) 2006-2007, Ext JS, LLC.
8768 * Originally Released Under LGPL - original licence link has changed is not relivant.
8771 * <script type="text/javascript">
8774 * @class Roo.form.VTypes
8775 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8778 Roo.form.VTypes = function(){
8779 // closure these in so they are only created once.
8780 var alpha = /^[a-zA-Z_]+$/;
8781 var alphanum = /^[a-zA-Z0-9_]+$/;
8782 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8783 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8785 // All these messages and functions are configurable
8788 * The function used to validate email addresses
8789 * @param {String} value The email address
8791 'email' : function(v){
8792 return email.test(v);
8795 * The error text to display when the email validation function returns false
8798 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8800 * The keystroke filter mask to be applied on email input
8803 'emailMask' : /[a-z0-9_\.\-@]/i,
8806 * The function used to validate URLs
8807 * @param {String} value The URL
8809 'url' : function(v){
8813 * The error text to display when the url validation function returns false
8816 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8819 * The function used to validate alpha values
8820 * @param {String} value The value
8822 'alpha' : function(v){
8823 return alpha.test(v);
8826 * The error text to display when the alpha validation function returns false
8829 'alphaText' : 'This field should only contain letters and _',
8831 * The keystroke filter mask to be applied on alpha input
8834 'alphaMask' : /[a-z_]/i,
8837 * The function used to validate alphanumeric values
8838 * @param {String} value The value
8840 'alphanum' : function(v){
8841 return alphanum.test(v);
8844 * The error text to display when the alphanumeric validation function returns false
8847 'alphanumText' : 'This field should only contain letters, numbers and _',
8849 * The keystroke filter mask to be applied on alphanumeric input
8852 'alphanumMask' : /[a-z0-9_]/i
8862 * @class Roo.bootstrap.Input
8863 * @extends Roo.bootstrap.Component
8864 * Bootstrap Input class
8865 * @cfg {Boolean} disabled is it disabled
8866 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8867 * @cfg {String} name name of the input
8868 * @cfg {string} fieldLabel - the label associated
8869 * @cfg {string} placeholder - placeholder to put in text.
8870 * @cfg {string} before - input group add on before
8871 * @cfg {string} after - input group add on after
8872 * @cfg {string} size - (lg|sm) or leave empty..
8873 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8874 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8875 * @cfg {Number} md colspan out of 12 for computer-sized screens
8876 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8877 * @cfg {string} value default value of the input
8878 * @cfg {Number} labelWidth set the width of label
8879 * @cfg {Number} labellg set the width of label (1-12)
8880 * @cfg {Number} labelmd set the width of label (1-12)
8881 * @cfg {Number} labelsm set the width of label (1-12)
8882 * @cfg {Number} labelxs set the width of label (1-12)
8883 * @cfg {String} labelAlign (top|left)
8884 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8885 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8886 * @cfg {String} indicatorpos (left|right) default left
8887 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8888 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8890 * @cfg {String} align (left|center|right) Default left
8891 * @cfg {Boolean} forceFeedback (true|false) Default false
8894 * Create a new Input
8895 * @param {Object} config The config object
8898 Roo.bootstrap.Input = function(config){
8900 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8905 * Fires when this field receives input focus.
8906 * @param {Roo.form.Field} this
8911 * Fires when this field loses input focus.
8912 * @param {Roo.form.Field} this
8917 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8918 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8919 * @param {Roo.form.Field} this
8920 * @param {Roo.EventObject} e The event object
8925 * Fires just before the field blurs if the field value has changed.
8926 * @param {Roo.form.Field} this
8927 * @param {Mixed} newValue The new value
8928 * @param {Mixed} oldValue The original value
8933 * Fires after the field has been marked as invalid.
8934 * @param {Roo.form.Field} this
8935 * @param {String} msg The validation message
8940 * Fires after the field has been validated with no errors.
8941 * @param {Roo.form.Field} this
8946 * Fires after the key up
8947 * @param {Roo.form.Field} this
8948 * @param {Roo.EventObject} e The event Object
8954 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8956 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8957 automatic validation (defaults to "keyup").
8959 validationEvent : "keyup",
8961 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8963 validateOnBlur : true,
8965 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8967 validationDelay : 250,
8969 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8971 focusClass : "x-form-focus", // not needed???
8975 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8977 invalidClass : "has-warning",
8980 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8982 validClass : "has-success",
8985 * @cfg {Boolean} hasFeedback (true|false) default true
8990 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8992 invalidFeedbackClass : "glyphicon-warning-sign",
8995 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8997 validFeedbackClass : "glyphicon-ok",
9000 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9002 selectOnFocus : false,
9005 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9009 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9014 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9016 disableKeyFilter : false,
9019 * @cfg {Boolean} disabled True to disable the field (defaults to false).
9023 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9027 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9029 blankText : "Please complete this mandatory field",
9032 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9036 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9038 maxLength : Number.MAX_VALUE,
9040 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9042 minLengthText : "The minimum length for this field is {0}",
9044 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9046 maxLengthText : "The maximum length for this field is {0}",
9050 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9051 * If available, this function will be called only after the basic validators all return true, and will be passed the
9052 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9056 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9057 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9058 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9062 * @cfg {String} regexText -- Depricated - use Invalid Text
9067 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9073 autocomplete: false,
9092 formatedValue : false,
9093 forceFeedback : false,
9095 indicatorpos : 'left',
9105 parentLabelAlign : function()
9108 while (parent.parent()) {
9109 parent = parent.parent();
9110 if (typeof(parent.labelAlign) !='undefined') {
9111 return parent.labelAlign;
9118 getAutoCreate : function()
9120 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9126 if(this.inputType != 'hidden'){
9127 cfg.cls = 'form-group' //input-group
9133 type : this.inputType,
9135 cls : 'form-control',
9136 placeholder : this.placeholder || '',
9137 autocomplete : this.autocomplete || 'new-password'
9140 if(this.capture.length){
9141 input.capture = this.capture;
9144 if(this.accept.length){
9145 input.accept = this.accept + "/*";
9149 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9152 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9153 input.maxLength = this.maxLength;
9156 if (this.disabled) {
9157 input.disabled=true;
9160 if (this.readOnly) {
9161 input.readonly=true;
9165 input.name = this.name;
9169 input.cls += ' input-' + this.size;
9173 ['xs','sm','md','lg'].map(function(size){
9174 if (settings[size]) {
9175 cfg.cls += ' col-' + size + '-' + settings[size];
9179 var inputblock = input;
9183 cls: 'glyphicon form-control-feedback'
9186 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9189 cls : 'has-feedback',
9197 if (this.before || this.after) {
9200 cls : 'input-group',
9204 if (this.before && typeof(this.before) == 'string') {
9206 inputblock.cn.push({
9208 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9212 if (this.before && typeof(this.before) == 'object') {
9213 this.before = Roo.factory(this.before);
9215 inputblock.cn.push({
9217 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9218 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9222 inputblock.cn.push(input);
9224 if (this.after && typeof(this.after) == 'string') {
9225 inputblock.cn.push({
9227 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9231 if (this.after && typeof(this.after) == 'object') {
9232 this.after = Roo.factory(this.after);
9234 inputblock.cn.push({
9236 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9237 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9241 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9242 inputblock.cls += ' has-feedback';
9243 inputblock.cn.push(feedback);
9248 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9249 tooltip : 'This field is required'
9251 if (Roo.bootstrap.version == 4) {
9254 style : 'display-none'
9257 if (align ==='left' && this.fieldLabel.length) {
9259 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
9266 cls : 'control-label col-form-label',
9267 html : this.fieldLabel
9278 var labelCfg = cfg.cn[1];
9279 var contentCfg = cfg.cn[2];
9281 if(this.indicatorpos == 'right'){
9286 cls : 'control-label col-form-label',
9290 html : this.fieldLabel
9304 labelCfg = cfg.cn[0];
9305 contentCfg = cfg.cn[1];
9309 if(this.labelWidth > 12){
9310 labelCfg.style = "width: " + this.labelWidth + 'px';
9313 if(this.labelWidth < 13 && this.labelmd == 0){
9314 this.labelmd = this.labelWidth;
9317 if(this.labellg > 0){
9318 labelCfg.cls += ' col-lg-' + this.labellg;
9319 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9322 if(this.labelmd > 0){
9323 labelCfg.cls += ' col-md-' + this.labelmd;
9324 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9327 if(this.labelsm > 0){
9328 labelCfg.cls += ' col-sm-' + this.labelsm;
9329 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9332 if(this.labelxs > 0){
9333 labelCfg.cls += ' col-xs-' + this.labelxs;
9334 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9338 } else if ( this.fieldLabel.length) {
9343 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9344 tooltip : 'This field is required'
9348 //cls : 'input-group-addon',
9349 html : this.fieldLabel
9357 if(this.indicatorpos == 'right'){
9362 //cls : 'input-group-addon',
9363 html : this.fieldLabel
9368 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9369 tooltip : 'This field is required'
9389 if (this.parentType === 'Navbar' && this.parent().bar) {
9390 cfg.cls += ' navbar-form';
9393 if (this.parentType === 'NavGroup') {
9394 cfg.cls += ' navbar-form';
9402 * return the real input element.
9404 inputEl: function ()
9406 return this.el.select('input.form-control',true).first();
9409 tooltipEl : function()
9411 return this.inputEl();
9414 indicatorEl : function()
9416 if (Roo.bootstrap.version == 4) {
9417 return false; // not enabled in v4 yet.
9420 var indicator = this.el.select('i.roo-required-indicator',true).first();
9430 setDisabled : function(v)
9432 var i = this.inputEl().dom;
9434 i.removeAttribute('disabled');
9438 i.setAttribute('disabled','true');
9440 initEvents : function()
9443 this.inputEl().on("keydown" , this.fireKey, this);
9444 this.inputEl().on("focus", this.onFocus, this);
9445 this.inputEl().on("blur", this.onBlur, this);
9447 this.inputEl().relayEvent('keyup', this);
9449 this.indicator = this.indicatorEl();
9452 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9455 // reference to original value for reset
9456 this.originalValue = this.getValue();
9457 //Roo.form.TextField.superclass.initEvents.call(this);
9458 if(this.validationEvent == 'keyup'){
9459 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9460 this.inputEl().on('keyup', this.filterValidation, this);
9462 else if(this.validationEvent !== false){
9463 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9466 if(this.selectOnFocus){
9467 this.on("focus", this.preFocus, this);
9470 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9471 this.inputEl().on("keypress", this.filterKeys, this);
9473 this.inputEl().relayEvent('keypress', this);
9476 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9477 this.el.on("click", this.autoSize, this);
9480 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9481 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9484 if (typeof(this.before) == 'object') {
9485 this.before.render(this.el.select('.roo-input-before',true).first());
9487 if (typeof(this.after) == 'object') {
9488 this.after.render(this.el.select('.roo-input-after',true).first());
9491 this.inputEl().on('change', this.onChange, this);
9494 filterValidation : function(e){
9495 if(!e.isNavKeyPress()){
9496 this.validationTask.delay(this.validationDelay);
9500 * Validates the field value
9501 * @return {Boolean} True if the value is valid, else false
9503 validate : function(){
9504 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9505 if(this.disabled || this.validateValue(this.getRawValue())){
9516 * Validates a value according to the field's validation rules and marks the field as invalid
9517 * if the validation fails
9518 * @param {Mixed} value The value to validate
9519 * @return {Boolean} True if the value is valid, else false
9521 validateValue : function(value)
9523 if(this.getVisibilityEl().hasClass('hidden')){
9527 if(value.length < 1) { // if it's blank
9528 if(this.allowBlank){
9534 if(value.length < this.minLength){
9537 if(value.length > this.maxLength){
9541 var vt = Roo.form.VTypes;
9542 if(!vt[this.vtype](value, this)){
9546 if(typeof this.validator == "function"){
9547 var msg = this.validator(value);
9551 if (typeof(msg) == 'string') {
9552 this.invalidText = msg;
9556 if(this.regex && !this.regex.test(value)){
9564 fireKey : function(e){
9565 //Roo.log('field ' + e.getKey());
9566 if(e.isNavKeyPress()){
9567 this.fireEvent("specialkey", this, e);
9570 focus : function (selectText){
9572 this.inputEl().focus();
9573 if(selectText === true){
9574 this.inputEl().dom.select();
9580 onFocus : function(){
9581 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9582 // this.el.addClass(this.focusClass);
9585 this.hasFocus = true;
9586 this.startValue = this.getValue();
9587 this.fireEvent("focus", this);
9591 beforeBlur : Roo.emptyFn,
9595 onBlur : function(){
9597 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9598 //this.el.removeClass(this.focusClass);
9600 this.hasFocus = false;
9601 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9604 var v = this.getValue();
9605 if(String(v) !== String(this.startValue)){
9606 this.fireEvent('change', this, v, this.startValue);
9608 this.fireEvent("blur", this);
9611 onChange : function(e)
9613 var v = this.getValue();
9614 if(String(v) !== String(this.startValue)){
9615 this.fireEvent('change', this, v, this.startValue);
9621 * Resets the current field value to the originally loaded value and clears any validation messages
9624 this.setValue(this.originalValue);
9628 * Returns the name of the field
9629 * @return {Mixed} name The name field
9631 getName: function(){
9635 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9636 * @return {Mixed} value The field value
9638 getValue : function(){
9640 var v = this.inputEl().getValue();
9645 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9646 * @return {Mixed} value The field value
9648 getRawValue : function(){
9649 var v = this.inputEl().getValue();
9655 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9656 * @param {Mixed} value The value to set
9658 setRawValue : function(v){
9659 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9662 selectText : function(start, end){
9663 var v = this.getRawValue();
9665 start = start === undefined ? 0 : start;
9666 end = end === undefined ? v.length : end;
9667 var d = this.inputEl().dom;
9668 if(d.setSelectionRange){
9669 d.setSelectionRange(start, end);
9670 }else if(d.createTextRange){
9671 var range = d.createTextRange();
9672 range.moveStart("character", start);
9673 range.moveEnd("character", v.length-end);
9680 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9681 * @param {Mixed} value The value to set
9683 setValue : function(v){
9686 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9692 processValue : function(value){
9693 if(this.stripCharsRe){
9694 var newValue = value.replace(this.stripCharsRe, '');
9695 if(newValue !== value){
9696 this.setRawValue(newValue);
9703 preFocus : function(){
9705 if(this.selectOnFocus){
9706 this.inputEl().dom.select();
9709 filterKeys : function(e){
9711 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9714 var c = e.getCharCode(), cc = String.fromCharCode(c);
9715 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9718 if(!this.maskRe.test(cc)){
9723 * Clear any invalid styles/messages for this field
9725 clearInvalid : function(){
9727 if(!this.el || this.preventMark){ // not rendered
9732 this.el.removeClass(this.invalidClass);
9734 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9736 var feedback = this.el.select('.form-control-feedback', true).first();
9739 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9745 this.indicator.removeClass('visible');
9746 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9749 this.fireEvent('valid', this);
9753 * Mark this field as valid
9755 markValid : function()
9757 if(!this.el || this.preventMark){ // not rendered...
9761 this.el.removeClass([this.invalidClass, this.validClass]);
9763 var feedback = this.el.select('.form-control-feedback', true).first();
9766 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9770 this.indicator.removeClass('visible');
9771 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9778 if(this.allowBlank && !this.getRawValue().length){
9782 this.el.addClass(this.validClass);
9784 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9786 var feedback = this.el.select('.form-control-feedback', true).first();
9789 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9790 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9795 this.fireEvent('valid', this);
9799 * Mark this field as invalid
9800 * @param {String} msg The validation message
9802 markInvalid : function(msg)
9804 if(!this.el || this.preventMark){ // not rendered
9808 this.el.removeClass([this.invalidClass, this.validClass]);
9810 var feedback = this.el.select('.form-control-feedback', true).first();
9813 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9820 if(this.allowBlank && !this.getRawValue().length){
9825 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9826 this.indicator.addClass('visible');
9829 this.el.addClass(this.invalidClass);
9831 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9833 var feedback = this.el.select('.form-control-feedback', true).first();
9836 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9838 if(this.getValue().length || this.forceFeedback){
9839 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9846 this.fireEvent('invalid', this, msg);
9849 SafariOnKeyDown : function(event)
9851 // this is a workaround for a password hang bug on chrome/ webkit.
9852 if (this.inputEl().dom.type != 'password') {
9856 var isSelectAll = false;
9858 if(this.inputEl().dom.selectionEnd > 0){
9859 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9861 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9862 event.preventDefault();
9867 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9869 event.preventDefault();
9870 // this is very hacky as keydown always get's upper case.
9872 var cc = String.fromCharCode(event.getCharCode());
9873 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9877 adjustWidth : function(tag, w){
9878 tag = tag.toLowerCase();
9879 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9880 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9884 if(tag == 'textarea'){
9887 }else if(Roo.isOpera){
9891 if(tag == 'textarea'){
9899 setFieldLabel : function(v)
9905 if(this.indicatorEl()){
9906 var ar = this.el.select('label > span',true);
9908 if (ar.elements.length) {
9909 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9910 this.fieldLabel = v;
9914 var br = this.el.select('label',true);
9916 if(br.elements.length) {
9917 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9918 this.fieldLabel = v;
9922 Roo.log('Cannot Found any of label > span || label in input');
9926 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9927 this.fieldLabel = v;
9942 * @class Roo.bootstrap.TextArea
9943 * @extends Roo.bootstrap.Input
9944 * Bootstrap TextArea class
9945 * @cfg {Number} cols Specifies the visible width of a text area
9946 * @cfg {Number} rows Specifies the visible number of lines in a text area
9947 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9948 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9949 * @cfg {string} html text
9952 * Create a new TextArea
9953 * @param {Object} config The config object
9956 Roo.bootstrap.TextArea = function(config){
9957 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9961 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9971 getAutoCreate : function(){
9973 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9979 if(this.inputType != 'hidden'){
9980 cfg.cls = 'form-group' //input-group
9988 value : this.value || '',
9989 html: this.html || '',
9990 cls : 'form-control',
9991 placeholder : this.placeholder || ''
9995 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9996 input.maxLength = this.maxLength;
10000 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10004 input.cols = this.cols;
10007 if (this.readOnly) {
10008 input.readonly = true;
10012 input.name = this.name;
10016 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10020 ['xs','sm','md','lg'].map(function(size){
10021 if (settings[size]) {
10022 cfg.cls += ' col-' + size + '-' + settings[size];
10026 var inputblock = input;
10028 if(this.hasFeedback && !this.allowBlank){
10032 cls: 'glyphicon form-control-feedback'
10036 cls : 'has-feedback',
10045 if (this.before || this.after) {
10048 cls : 'input-group',
10052 inputblock.cn.push({
10054 cls : 'input-group-addon',
10059 inputblock.cn.push(input);
10061 if(this.hasFeedback && !this.allowBlank){
10062 inputblock.cls += ' has-feedback';
10063 inputblock.cn.push(feedback);
10067 inputblock.cn.push({
10069 cls : 'input-group-addon',
10076 if (align ==='left' && this.fieldLabel.length) {
10081 cls : 'control-label',
10082 html : this.fieldLabel
10093 if(this.labelWidth > 12){
10094 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10097 if(this.labelWidth < 13 && this.labelmd == 0){
10098 this.labelmd = this.labelWidth;
10101 if(this.labellg > 0){
10102 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10103 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10106 if(this.labelmd > 0){
10107 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10108 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10111 if(this.labelsm > 0){
10112 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10113 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10116 if(this.labelxs > 0){
10117 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10118 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10121 } else if ( this.fieldLabel.length) {
10126 //cls : 'input-group-addon',
10127 html : this.fieldLabel
10145 if (this.disabled) {
10146 input.disabled=true;
10153 * return the real textarea element.
10155 inputEl: function ()
10157 return this.el.select('textarea.form-control',true).first();
10161 * Clear any invalid styles/messages for this field
10163 clearInvalid : function()
10166 if(!this.el || this.preventMark){ // not rendered
10170 var label = this.el.select('label', true).first();
10171 var icon = this.el.select('i.fa-star', true).first();
10177 this.el.removeClass(this.invalidClass);
10179 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10181 var feedback = this.el.select('.form-control-feedback', true).first();
10184 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10189 this.fireEvent('valid', this);
10193 * Mark this field as valid
10195 markValid : function()
10197 if(!this.el || this.preventMark){ // not rendered
10201 this.el.removeClass([this.invalidClass, this.validClass]);
10203 var feedback = this.el.select('.form-control-feedback', true).first();
10206 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10209 if(this.disabled || this.allowBlank){
10213 var label = this.el.select('label', true).first();
10214 var icon = this.el.select('i.fa-star', true).first();
10220 this.el.addClass(this.validClass);
10222 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10224 var feedback = this.el.select('.form-control-feedback', true).first();
10227 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10228 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10233 this.fireEvent('valid', this);
10237 * Mark this field as invalid
10238 * @param {String} msg The validation message
10240 markInvalid : function(msg)
10242 if(!this.el || this.preventMark){ // not rendered
10246 this.el.removeClass([this.invalidClass, this.validClass]);
10248 var feedback = this.el.select('.form-control-feedback', true).first();
10251 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10254 if(this.disabled || this.allowBlank){
10258 var label = this.el.select('label', true).first();
10259 var icon = this.el.select('i.fa-star', true).first();
10261 if(!this.getValue().length && label && !icon){
10262 this.el.createChild({
10264 cls : 'text-danger fa fa-lg fa-star',
10265 tooltip : 'This field is required',
10266 style : 'margin-right:5px;'
10270 this.el.addClass(this.invalidClass);
10272 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10274 var feedback = this.el.select('.form-control-feedback', true).first();
10277 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10279 if(this.getValue().length || this.forceFeedback){
10280 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10287 this.fireEvent('invalid', this, msg);
10295 * trigger field - base class for combo..
10300 * @class Roo.bootstrap.TriggerField
10301 * @extends Roo.bootstrap.Input
10302 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10303 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10304 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10305 * for which you can provide a custom implementation. For example:
10307 var trigger = new Roo.bootstrap.TriggerField();
10308 trigger.onTriggerClick = myTriggerFn;
10309 trigger.applyTo('my-field');
10312 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10313 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10314 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10315 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10316 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10319 * Create a new TriggerField.
10320 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10321 * to the base TextField)
10323 Roo.bootstrap.TriggerField = function(config){
10324 this.mimicing = false;
10325 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10328 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10330 * @cfg {String} triggerClass A CSS class to apply to the trigger
10333 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10338 * @cfg {Boolean} removable (true|false) special filter default false
10342 /** @cfg {Boolean} grow @hide */
10343 /** @cfg {Number} growMin @hide */
10344 /** @cfg {Number} growMax @hide */
10350 autoSize: Roo.emptyFn,
10354 deferHeight : true,
10357 actionMode : 'wrap',
10362 getAutoCreate : function(){
10364 var align = this.labelAlign || this.parentLabelAlign();
10369 cls: 'form-group' //input-group
10376 type : this.inputType,
10377 cls : 'form-control',
10378 autocomplete: 'new-password',
10379 placeholder : this.placeholder || ''
10383 input.name = this.name;
10386 input.cls += ' input-' + this.size;
10389 if (this.disabled) {
10390 input.disabled=true;
10393 var inputblock = input;
10395 if(this.hasFeedback && !this.allowBlank){
10399 cls: 'glyphicon form-control-feedback'
10402 if(this.removable && !this.editable && !this.tickable){
10404 cls : 'has-feedback',
10410 cls : 'roo-combo-removable-btn close'
10417 cls : 'has-feedback',
10426 if(this.removable && !this.editable && !this.tickable){
10428 cls : 'roo-removable',
10434 cls : 'roo-combo-removable-btn close'
10441 if (this.before || this.after) {
10444 cls : 'input-group',
10448 inputblock.cn.push({
10450 cls : 'input-group-addon input-group-prepend input-group-text',
10455 inputblock.cn.push(input);
10457 if(this.hasFeedback && !this.allowBlank){
10458 inputblock.cls += ' has-feedback';
10459 inputblock.cn.push(feedback);
10463 inputblock.cn.push({
10465 cls : 'input-group-addon input-group-append input-group-text',
10474 var ibwrap = inputblock;
10479 cls: 'roo-select2-choices',
10483 cls: 'roo-select2-search-field',
10495 cls: 'roo-select2-container input-group',
10500 cls: 'form-hidden-field'
10506 if(!this.multiple && this.showToggleBtn){
10512 if (this.caret != false) {
10515 cls: 'fa fa-' + this.caret
10522 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10527 cls: 'combobox-clear',
10541 combobox.cls += ' roo-select2-container-multi';
10545 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10546 tooltip : 'This field is required'
10548 if (Roo.bootstrap.version == 4) {
10551 style : 'display:none'
10556 if (align ==='left' && this.fieldLabel.length) {
10558 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10565 cls : 'control-label',
10566 html : this.fieldLabel
10578 var labelCfg = cfg.cn[1];
10579 var contentCfg = cfg.cn[2];
10581 if(this.indicatorpos == 'right'){
10586 cls : 'control-label',
10590 html : this.fieldLabel
10604 labelCfg = cfg.cn[0];
10605 contentCfg = cfg.cn[1];
10608 if(this.labelWidth > 12){
10609 labelCfg.style = "width: " + this.labelWidth + 'px';
10612 if(this.labelWidth < 13 && this.labelmd == 0){
10613 this.labelmd = this.labelWidth;
10616 if(this.labellg > 0){
10617 labelCfg.cls += ' col-lg-' + this.labellg;
10618 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10621 if(this.labelmd > 0){
10622 labelCfg.cls += ' col-md-' + this.labelmd;
10623 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10626 if(this.labelsm > 0){
10627 labelCfg.cls += ' col-sm-' + this.labelsm;
10628 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10631 if(this.labelxs > 0){
10632 labelCfg.cls += ' col-xs-' + this.labelxs;
10633 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10636 } else if ( this.fieldLabel.length) {
10637 // Roo.log(" label");
10642 //cls : 'input-group-addon',
10643 html : this.fieldLabel
10651 if(this.indicatorpos == 'right'){
10659 html : this.fieldLabel
10673 // Roo.log(" no label && no align");
10680 ['xs','sm','md','lg'].map(function(size){
10681 if (settings[size]) {
10682 cfg.cls += ' col-' + size + '-' + settings[size];
10693 onResize : function(w, h){
10694 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10695 // if(typeof w == 'number'){
10696 // var x = w - this.trigger.getWidth();
10697 // this.inputEl().setWidth(this.adjustWidth('input', x));
10698 // this.trigger.setStyle('left', x+'px');
10703 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10706 getResizeEl : function(){
10707 return this.inputEl();
10711 getPositionEl : function(){
10712 return this.inputEl();
10716 alignErrorIcon : function(){
10717 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10721 initEvents : function(){
10725 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10726 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10727 if(!this.multiple && this.showToggleBtn){
10728 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10729 if(this.hideTrigger){
10730 this.trigger.setDisplayed(false);
10732 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10736 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10739 if(this.removable && !this.editable && !this.tickable){
10740 var close = this.closeTriggerEl();
10743 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10744 close.on('click', this.removeBtnClick, this, close);
10748 //this.trigger.addClassOnOver('x-form-trigger-over');
10749 //this.trigger.addClassOnClick('x-form-trigger-click');
10752 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10756 closeTriggerEl : function()
10758 var close = this.el.select('.roo-combo-removable-btn', true).first();
10759 return close ? close : false;
10762 removeBtnClick : function(e, h, el)
10764 e.preventDefault();
10766 if(this.fireEvent("remove", this) !== false){
10768 this.fireEvent("afterremove", this)
10772 createList : function()
10774 this.list = Roo.get(document.body).createChild({
10775 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10776 cls: 'typeahead typeahead-long dropdown-menu',
10777 style: 'display:none'
10780 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10785 initTrigger : function(){
10790 onDestroy : function(){
10792 this.trigger.removeAllListeners();
10793 // this.trigger.remove();
10796 // this.wrap.remove();
10798 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10802 onFocus : function(){
10803 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10805 if(!this.mimicing){
10806 this.wrap.addClass('x-trigger-wrap-focus');
10807 this.mimicing = true;
10808 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10809 if(this.monitorTab){
10810 this.el.on("keydown", this.checkTab, this);
10817 checkTab : function(e){
10818 if(e.getKey() == e.TAB){
10819 this.triggerBlur();
10824 onBlur : function(){
10829 mimicBlur : function(e, t){
10831 if(!this.wrap.contains(t) && this.validateBlur()){
10832 this.triggerBlur();
10838 triggerBlur : function(){
10839 this.mimicing = false;
10840 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10841 if(this.monitorTab){
10842 this.el.un("keydown", this.checkTab, this);
10844 //this.wrap.removeClass('x-trigger-wrap-focus');
10845 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10849 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10850 validateBlur : function(e, t){
10855 onDisable : function(){
10856 this.inputEl().dom.disabled = true;
10857 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10859 // this.wrap.addClass('x-item-disabled');
10864 onEnable : function(){
10865 this.inputEl().dom.disabled = false;
10866 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10868 // this.el.removeClass('x-item-disabled');
10873 onShow : function(){
10874 var ae = this.getActionEl();
10877 ae.dom.style.display = '';
10878 ae.dom.style.visibility = 'visible';
10884 onHide : function(){
10885 var ae = this.getActionEl();
10886 ae.dom.style.display = 'none';
10890 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10891 * by an implementing function.
10893 * @param {EventObject} e
10895 onTriggerClick : Roo.emptyFn
10899 * Ext JS Library 1.1.1
10900 * Copyright(c) 2006-2007, Ext JS, LLC.
10902 * Originally Released Under LGPL - original licence link has changed is not relivant.
10905 * <script type="text/javascript">
10910 * @class Roo.data.SortTypes
10912 * Defines the default sorting (casting?) comparison functions used when sorting data.
10914 Roo.data.SortTypes = {
10916 * Default sort that does nothing
10917 * @param {Mixed} s The value being converted
10918 * @return {Mixed} The comparison value
10920 none : function(s){
10925 * The regular expression used to strip tags
10929 stripTagsRE : /<\/?[^>]+>/gi,
10932 * Strips all HTML tags to sort on text only
10933 * @param {Mixed} s The value being converted
10934 * @return {String} The comparison value
10936 asText : function(s){
10937 return String(s).replace(this.stripTagsRE, "");
10941 * Strips all HTML tags to sort on text only - Case insensitive
10942 * @param {Mixed} s The value being converted
10943 * @return {String} The comparison value
10945 asUCText : function(s){
10946 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10950 * Case insensitive string
10951 * @param {Mixed} s The value being converted
10952 * @return {String} The comparison value
10954 asUCString : function(s) {
10955 return String(s).toUpperCase();
10960 * @param {Mixed} s The value being converted
10961 * @return {Number} The comparison value
10963 asDate : function(s) {
10967 if(s instanceof Date){
10968 return s.getTime();
10970 return Date.parse(String(s));
10975 * @param {Mixed} s The value being converted
10976 * @return {Float} The comparison value
10978 asFloat : function(s) {
10979 var val = parseFloat(String(s).replace(/,/g, ""));
10988 * @param {Mixed} s The value being converted
10989 * @return {Number} The comparison value
10991 asInt : function(s) {
10992 var val = parseInt(String(s).replace(/,/g, ""));
11000 * Ext JS Library 1.1.1
11001 * Copyright(c) 2006-2007, Ext JS, LLC.
11003 * Originally Released Under LGPL - original licence link has changed is not relivant.
11006 * <script type="text/javascript">
11010 * @class Roo.data.Record
11011 * Instances of this class encapsulate both record <em>definition</em> information, and record
11012 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11013 * to access Records cached in an {@link Roo.data.Store} object.<br>
11015 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11016 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11019 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11021 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11022 * {@link #create}. The parameters are the same.
11023 * @param {Array} data An associative Array of data values keyed by the field name.
11024 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11025 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11026 * not specified an integer id is generated.
11028 Roo.data.Record = function(data, id){
11029 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11034 * Generate a constructor for a specific record layout.
11035 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11036 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11037 * Each field definition object may contain the following properties: <ul>
11038 * <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,
11039 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11040 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11041 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11042 * is being used, then this is a string containing the javascript expression to reference the data relative to
11043 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11044 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11045 * this may be omitted.</p></li>
11046 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11047 * <ul><li>auto (Default, implies no conversion)</li>
11052 * <li>date</li></ul></p></li>
11053 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11054 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11055 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11056 * by the Reader into an object that will be stored in the Record. It is passed the
11057 * following parameters:<ul>
11058 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11060 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11062 * <br>usage:<br><pre><code>
11063 var TopicRecord = Roo.data.Record.create(
11064 {name: 'title', mapping: 'topic_title'},
11065 {name: 'author', mapping: 'username'},
11066 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11067 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11068 {name: 'lastPoster', mapping: 'user2'},
11069 {name: 'excerpt', mapping: 'post_text'}
11072 var myNewRecord = new TopicRecord({
11073 title: 'Do my job please',
11076 lastPost: new Date(),
11077 lastPoster: 'Animal',
11078 excerpt: 'No way dude!'
11080 myStore.add(myNewRecord);
11085 Roo.data.Record.create = function(o){
11086 var f = function(){
11087 f.superclass.constructor.apply(this, arguments);
11089 Roo.extend(f, Roo.data.Record);
11090 var p = f.prototype;
11091 p.fields = new Roo.util.MixedCollection(false, function(field){
11094 for(var i = 0, len = o.length; i < len; i++){
11095 p.fields.add(new Roo.data.Field(o[i]));
11097 f.getField = function(name){
11098 return p.fields.get(name);
11103 Roo.data.Record.AUTO_ID = 1000;
11104 Roo.data.Record.EDIT = 'edit';
11105 Roo.data.Record.REJECT = 'reject';
11106 Roo.data.Record.COMMIT = 'commit';
11108 Roo.data.Record.prototype = {
11110 * Readonly flag - true if this record has been modified.
11119 join : function(store){
11120 this.store = store;
11124 * Set the named field to the specified value.
11125 * @param {String} name The name of the field to set.
11126 * @param {Object} value The value to set the field to.
11128 set : function(name, value){
11129 if(this.data[name] == value){
11133 if(!this.modified){
11134 this.modified = {};
11136 if(typeof this.modified[name] == 'undefined'){
11137 this.modified[name] = this.data[name];
11139 this.data[name] = value;
11140 if(!this.editing && this.store){
11141 this.store.afterEdit(this);
11146 * Get the value of the named field.
11147 * @param {String} name The name of the field to get the value of.
11148 * @return {Object} The value of the field.
11150 get : function(name){
11151 return this.data[name];
11155 beginEdit : function(){
11156 this.editing = true;
11157 this.modified = {};
11161 cancelEdit : function(){
11162 this.editing = false;
11163 delete this.modified;
11167 endEdit : function(){
11168 this.editing = false;
11169 if(this.dirty && this.store){
11170 this.store.afterEdit(this);
11175 * Usually called by the {@link Roo.data.Store} which owns the Record.
11176 * Rejects all changes made to the Record since either creation, or the last commit operation.
11177 * Modified fields are reverted to their original values.
11179 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11180 * of reject operations.
11182 reject : function(){
11183 var m = this.modified;
11185 if(typeof m[n] != "function"){
11186 this.data[n] = m[n];
11189 this.dirty = false;
11190 delete this.modified;
11191 this.editing = false;
11193 this.store.afterReject(this);
11198 * Usually called by the {@link Roo.data.Store} which owns the Record.
11199 * Commits all changes made to the Record since either creation, or the last commit operation.
11201 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11202 * of commit operations.
11204 commit : function(){
11205 this.dirty = false;
11206 delete this.modified;
11207 this.editing = false;
11209 this.store.afterCommit(this);
11214 hasError : function(){
11215 return this.error != null;
11219 clearError : function(){
11224 * Creates a copy of this record.
11225 * @param {String} id (optional) A new record id if you don't want to use this record's id
11228 copy : function(newId) {
11229 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11233 * Ext JS Library 1.1.1
11234 * Copyright(c) 2006-2007, Ext JS, LLC.
11236 * Originally Released Under LGPL - original licence link has changed is not relivant.
11239 * <script type="text/javascript">
11245 * @class Roo.data.Store
11246 * @extends Roo.util.Observable
11247 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11248 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11250 * 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
11251 * has no knowledge of the format of the data returned by the Proxy.<br>
11253 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11254 * instances from the data object. These records are cached and made available through accessor functions.
11256 * Creates a new Store.
11257 * @param {Object} config A config object containing the objects needed for the Store to access data,
11258 * and read the data into Records.
11260 Roo.data.Store = function(config){
11261 this.data = new Roo.util.MixedCollection(false);
11262 this.data.getKey = function(o){
11265 this.baseParams = {};
11267 this.paramNames = {
11272 "multisort" : "_multisort"
11275 if(config && config.data){
11276 this.inlineData = config.data;
11277 delete config.data;
11280 Roo.apply(this, config);
11282 if(this.reader){ // reader passed
11283 this.reader = Roo.factory(this.reader, Roo.data);
11284 this.reader.xmodule = this.xmodule || false;
11285 if(!this.recordType){
11286 this.recordType = this.reader.recordType;
11288 if(this.reader.onMetaChange){
11289 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11293 if(this.recordType){
11294 this.fields = this.recordType.prototype.fields;
11296 this.modified = [];
11300 * @event datachanged
11301 * Fires when the data cache has changed, and a widget which is using this Store
11302 * as a Record cache should refresh its view.
11303 * @param {Store} this
11305 datachanged : true,
11307 * @event metachange
11308 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11309 * @param {Store} this
11310 * @param {Object} meta The JSON metadata
11315 * Fires when Records have been added to the Store
11316 * @param {Store} this
11317 * @param {Roo.data.Record[]} records The array of Records added
11318 * @param {Number} index The index at which the record(s) were added
11323 * Fires when a Record has been removed from the Store
11324 * @param {Store} this
11325 * @param {Roo.data.Record} record The Record that was removed
11326 * @param {Number} index The index at which the record was removed
11331 * Fires when a Record has been updated
11332 * @param {Store} this
11333 * @param {Roo.data.Record} record The Record that was updated
11334 * @param {String} operation The update operation being performed. Value may be one of:
11336 Roo.data.Record.EDIT
11337 Roo.data.Record.REJECT
11338 Roo.data.Record.COMMIT
11344 * Fires when the data cache has been cleared.
11345 * @param {Store} this
11349 * @event beforeload
11350 * Fires before a request is made for a new data object. If the beforeload handler returns false
11351 * the load action will be canceled.
11352 * @param {Store} this
11353 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11357 * @event beforeloadadd
11358 * Fires after a new set of Records has been loaded.
11359 * @param {Store} this
11360 * @param {Roo.data.Record[]} records The Records that were loaded
11361 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11363 beforeloadadd : true,
11366 * Fires after a new set of Records has been loaded, before they are added to the store.
11367 * @param {Store} this
11368 * @param {Roo.data.Record[]} records The Records that were loaded
11369 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11370 * @params {Object} return from reader
11374 * @event loadexception
11375 * Fires if an exception occurs in the Proxy during loading.
11376 * Called with the signature of the Proxy's "loadexception" event.
11377 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11380 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11381 * @param {Object} load options
11382 * @param {Object} jsonData from your request (normally this contains the Exception)
11384 loadexception : true
11388 this.proxy = Roo.factory(this.proxy, Roo.data);
11389 this.proxy.xmodule = this.xmodule || false;
11390 this.relayEvents(this.proxy, ["loadexception"]);
11392 this.sortToggle = {};
11393 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11395 Roo.data.Store.superclass.constructor.call(this);
11397 if(this.inlineData){
11398 this.loadData(this.inlineData);
11399 delete this.inlineData;
11403 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11405 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11406 * without a remote query - used by combo/forms at present.
11410 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11413 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11416 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11417 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11420 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11421 * on any HTTP request
11424 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11427 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11431 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11432 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11434 remoteSort : false,
11437 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11438 * loaded or when a record is removed. (defaults to false).
11440 pruneModifiedRecords : false,
11443 lastOptions : null,
11446 * Add Records to the Store and fires the add event.
11447 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11449 add : function(records){
11450 records = [].concat(records);
11451 for(var i = 0, len = records.length; i < len; i++){
11452 records[i].join(this);
11454 var index = this.data.length;
11455 this.data.addAll(records);
11456 this.fireEvent("add", this, records, index);
11460 * Remove a Record from the Store and fires the remove event.
11461 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11463 remove : function(record){
11464 var index = this.data.indexOf(record);
11465 this.data.removeAt(index);
11467 if(this.pruneModifiedRecords){
11468 this.modified.remove(record);
11470 this.fireEvent("remove", this, record, index);
11474 * Remove all Records from the Store and fires the clear event.
11476 removeAll : function(){
11478 if(this.pruneModifiedRecords){
11479 this.modified = [];
11481 this.fireEvent("clear", this);
11485 * Inserts Records to the Store at the given index and fires the add event.
11486 * @param {Number} index The start index at which to insert the passed Records.
11487 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11489 insert : function(index, records){
11490 records = [].concat(records);
11491 for(var i = 0, len = records.length; i < len; i++){
11492 this.data.insert(index, records[i]);
11493 records[i].join(this);
11495 this.fireEvent("add", this, records, index);
11499 * Get the index within the cache of the passed Record.
11500 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11501 * @return {Number} The index of the passed Record. Returns -1 if not found.
11503 indexOf : function(record){
11504 return this.data.indexOf(record);
11508 * Get the index within the cache of the Record with the passed id.
11509 * @param {String} id The id of the Record to find.
11510 * @return {Number} The index of the Record. Returns -1 if not found.
11512 indexOfId : function(id){
11513 return this.data.indexOfKey(id);
11517 * Get the Record with the specified id.
11518 * @param {String} id The id of the Record to find.
11519 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11521 getById : function(id){
11522 return this.data.key(id);
11526 * Get the Record at the specified index.
11527 * @param {Number} index The index of the Record to find.
11528 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11530 getAt : function(index){
11531 return this.data.itemAt(index);
11535 * Returns a range of Records between specified indices.
11536 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11537 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11538 * @return {Roo.data.Record[]} An array of Records
11540 getRange : function(start, end){
11541 return this.data.getRange(start, end);
11545 storeOptions : function(o){
11546 o = Roo.apply({}, o);
11549 this.lastOptions = o;
11553 * Loads the Record cache from the configured Proxy using the configured Reader.
11555 * If using remote paging, then the first load call must specify the <em>start</em>
11556 * and <em>limit</em> properties in the options.params property to establish the initial
11557 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11559 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11560 * and this call will return before the new data has been loaded. Perform any post-processing
11561 * in a callback function, or in a "load" event handler.</strong>
11563 * @param {Object} options An object containing properties which control loading options:<ul>
11564 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11565 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11566 * passed the following arguments:<ul>
11567 * <li>r : Roo.data.Record[]</li>
11568 * <li>options: Options object from the load call</li>
11569 * <li>success: Boolean success indicator</li></ul></li>
11570 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11571 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11574 load : function(options){
11575 options = options || {};
11576 if(this.fireEvent("beforeload", this, options) !== false){
11577 this.storeOptions(options);
11578 var p = Roo.apply(options.params || {}, this.baseParams);
11579 // if meta was not loaded from remote source.. try requesting it.
11580 if (!this.reader.metaFromRemote) {
11581 p._requestMeta = 1;
11583 if(this.sortInfo && this.remoteSort){
11584 var pn = this.paramNames;
11585 p[pn["sort"]] = this.sortInfo.field;
11586 p[pn["dir"]] = this.sortInfo.direction;
11588 if (this.multiSort) {
11589 var pn = this.paramNames;
11590 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11593 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11598 * Reloads the Record cache from the configured Proxy using the configured Reader and
11599 * the options from the last load operation performed.
11600 * @param {Object} options (optional) An object containing properties which may override the options
11601 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11602 * the most recently used options are reused).
11604 reload : function(options){
11605 this.load(Roo.applyIf(options||{}, this.lastOptions));
11609 // Called as a callback by the Reader during a load operation.
11610 loadRecords : function(o, options, success){
11611 if(!o || success === false){
11612 if(success !== false){
11613 this.fireEvent("load", this, [], options, o);
11615 if(options.callback){
11616 options.callback.call(options.scope || this, [], options, false);
11620 // if data returned failure - throw an exception.
11621 if (o.success === false) {
11622 // show a message if no listener is registered.
11623 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11624 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11626 // loadmask wil be hooked into this..
11627 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11630 var r = o.records, t = o.totalRecords || r.length;
11632 this.fireEvent("beforeloadadd", this, r, options, o);
11634 if(!options || options.add !== true){
11635 if(this.pruneModifiedRecords){
11636 this.modified = [];
11638 for(var i = 0, len = r.length; i < len; i++){
11642 this.data = this.snapshot;
11643 delete this.snapshot;
11646 this.data.addAll(r);
11647 this.totalLength = t;
11649 this.fireEvent("datachanged", this);
11651 this.totalLength = Math.max(t, this.data.length+r.length);
11655 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11657 var e = new Roo.data.Record({});
11659 e.set(this.parent.displayField, this.parent.emptyTitle);
11660 e.set(this.parent.valueField, '');
11665 this.fireEvent("load", this, r, options, o);
11666 if(options.callback){
11667 options.callback.call(options.scope || this, r, options, true);
11673 * Loads data from a passed data block. A Reader which understands the format of the data
11674 * must have been configured in the constructor.
11675 * @param {Object} data The data block from which to read the Records. The format of the data expected
11676 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11677 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11679 loadData : function(o, append){
11680 var r = this.reader.readRecords(o);
11681 this.loadRecords(r, {add: append}, true);
11685 * Gets the number of cached records.
11687 * <em>If using paging, this may not be the total size of the dataset. If the data object
11688 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11689 * the data set size</em>
11691 getCount : function(){
11692 return this.data.length || 0;
11696 * Gets the total number of records in the dataset as returned by the server.
11698 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11699 * the dataset size</em>
11701 getTotalCount : function(){
11702 return this.totalLength || 0;
11706 * Returns the sort state of the Store as an object with two properties:
11708 field {String} The name of the field by which the Records are sorted
11709 direction {String} The sort order, "ASC" or "DESC"
11712 getSortState : function(){
11713 return this.sortInfo;
11717 applySort : function(){
11718 if(this.sortInfo && !this.remoteSort){
11719 var s = this.sortInfo, f = s.field;
11720 var st = this.fields.get(f).sortType;
11721 var fn = function(r1, r2){
11722 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11723 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11725 this.data.sort(s.direction, fn);
11726 if(this.snapshot && this.snapshot != this.data){
11727 this.snapshot.sort(s.direction, fn);
11733 * Sets the default sort column and order to be used by the next load operation.
11734 * @param {String} fieldName The name of the field to sort by.
11735 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11737 setDefaultSort : function(field, dir){
11738 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11742 * Sort the Records.
11743 * If remote sorting is used, the sort is performed on the server, and the cache is
11744 * reloaded. If local sorting is used, the cache is sorted internally.
11745 * @param {String} fieldName The name of the field to sort by.
11746 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11748 sort : function(fieldName, dir){
11749 var f = this.fields.get(fieldName);
11751 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11753 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11754 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11759 this.sortToggle[f.name] = dir;
11760 this.sortInfo = {field: f.name, direction: dir};
11761 if(!this.remoteSort){
11763 this.fireEvent("datachanged", this);
11765 this.load(this.lastOptions);
11770 * Calls the specified function for each of the Records in the cache.
11771 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11772 * Returning <em>false</em> aborts and exits the iteration.
11773 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11775 each : function(fn, scope){
11776 this.data.each(fn, scope);
11780 * Gets all records modified since the last commit. Modified records are persisted across load operations
11781 * (e.g., during paging).
11782 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11784 getModifiedRecords : function(){
11785 return this.modified;
11789 createFilterFn : function(property, value, anyMatch){
11790 if(!value.exec){ // not a regex
11791 value = String(value);
11792 if(value.length == 0){
11795 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11797 return function(r){
11798 return value.test(r.data[property]);
11803 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11804 * @param {String} property A field on your records
11805 * @param {Number} start The record index to start at (defaults to 0)
11806 * @param {Number} end The last record index to include (defaults to length - 1)
11807 * @return {Number} The sum
11809 sum : function(property, start, end){
11810 var rs = this.data.items, v = 0;
11811 start = start || 0;
11812 end = (end || end === 0) ? end : rs.length-1;
11814 for(var i = start; i <= end; i++){
11815 v += (rs[i].data[property] || 0);
11821 * Filter the records by a specified property.
11822 * @param {String} field A field on your records
11823 * @param {String/RegExp} value Either a string that the field
11824 * should start with or a RegExp to test against the field
11825 * @param {Boolean} anyMatch True to match any part not just the beginning
11827 filter : function(property, value, anyMatch){
11828 var fn = this.createFilterFn(property, value, anyMatch);
11829 return fn ? this.filterBy(fn) : this.clearFilter();
11833 * Filter by a function. The specified function will be called with each
11834 * record in this data source. If the function returns true the record is included,
11835 * otherwise it is filtered.
11836 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11837 * @param {Object} scope (optional) The scope of the function (defaults to this)
11839 filterBy : function(fn, scope){
11840 this.snapshot = this.snapshot || this.data;
11841 this.data = this.queryBy(fn, scope||this);
11842 this.fireEvent("datachanged", this);
11846 * Query the records by a specified property.
11847 * @param {String} field A field on your records
11848 * @param {String/RegExp} value Either a string that the field
11849 * should start with or a RegExp to test against the field
11850 * @param {Boolean} anyMatch True to match any part not just the beginning
11851 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11853 query : function(property, value, anyMatch){
11854 var fn = this.createFilterFn(property, value, anyMatch);
11855 return fn ? this.queryBy(fn) : this.data.clone();
11859 * Query by a function. The specified function will be called with each
11860 * record in this data source. If the function returns true the record is included
11862 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11863 * @param {Object} scope (optional) The scope of the function (defaults to this)
11864 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11866 queryBy : function(fn, scope){
11867 var data = this.snapshot || this.data;
11868 return data.filterBy(fn, scope||this);
11872 * Collects unique values for a particular dataIndex from this store.
11873 * @param {String} dataIndex The property to collect
11874 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11875 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11876 * @return {Array} An array of the unique values
11878 collect : function(dataIndex, allowNull, bypassFilter){
11879 var d = (bypassFilter === true && this.snapshot) ?
11880 this.snapshot.items : this.data.items;
11881 var v, sv, r = [], l = {};
11882 for(var i = 0, len = d.length; i < len; i++){
11883 v = d[i].data[dataIndex];
11885 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11894 * Revert to a view of the Record cache with no filtering applied.
11895 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11897 clearFilter : function(suppressEvent){
11898 if(this.snapshot && this.snapshot != this.data){
11899 this.data = this.snapshot;
11900 delete this.snapshot;
11901 if(suppressEvent !== true){
11902 this.fireEvent("datachanged", this);
11908 afterEdit : function(record){
11909 if(this.modified.indexOf(record) == -1){
11910 this.modified.push(record);
11912 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11916 afterReject : function(record){
11917 this.modified.remove(record);
11918 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11922 afterCommit : function(record){
11923 this.modified.remove(record);
11924 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11928 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11929 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11931 commitChanges : function(){
11932 var m = this.modified.slice(0);
11933 this.modified = [];
11934 for(var i = 0, len = m.length; i < len; i++){
11940 * Cancel outstanding changes on all changed records.
11942 rejectChanges : function(){
11943 var m = this.modified.slice(0);
11944 this.modified = [];
11945 for(var i = 0, len = m.length; i < len; i++){
11950 onMetaChange : function(meta, rtype, o){
11951 this.recordType = rtype;
11952 this.fields = rtype.prototype.fields;
11953 delete this.snapshot;
11954 this.sortInfo = meta.sortInfo || this.sortInfo;
11955 this.modified = [];
11956 this.fireEvent('metachange', this, this.reader.meta);
11959 moveIndex : function(data, type)
11961 var index = this.indexOf(data);
11963 var newIndex = index + type;
11967 this.insert(newIndex, data);
11972 * Ext JS Library 1.1.1
11973 * Copyright(c) 2006-2007, Ext JS, LLC.
11975 * Originally Released Under LGPL - original licence link has changed is not relivant.
11978 * <script type="text/javascript">
11982 * @class Roo.data.SimpleStore
11983 * @extends Roo.data.Store
11984 * Small helper class to make creating Stores from Array data easier.
11985 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11986 * @cfg {Array} fields An array of field definition objects, or field name strings.
11987 * @cfg {Array} data The multi-dimensional array of data
11989 * @param {Object} config
11991 Roo.data.SimpleStore = function(config){
11992 Roo.data.SimpleStore.superclass.constructor.call(this, {
11994 reader: new Roo.data.ArrayReader({
11997 Roo.data.Record.create(config.fields)
11999 proxy : new Roo.data.MemoryProxy(config.data)
12003 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12005 * Ext JS Library 1.1.1
12006 * Copyright(c) 2006-2007, Ext JS, LLC.
12008 * Originally Released Under LGPL - original licence link has changed is not relivant.
12011 * <script type="text/javascript">
12016 * @extends Roo.data.Store
12017 * @class Roo.data.JsonStore
12018 * Small helper class to make creating Stores for JSON data easier. <br/>
12020 var store = new Roo.data.JsonStore({
12021 url: 'get-images.php',
12023 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12026 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12027 * JsonReader and HttpProxy (unless inline data is provided).</b>
12028 * @cfg {Array} fields An array of field definition objects, or field name strings.
12030 * @param {Object} config
12032 Roo.data.JsonStore = function(c){
12033 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12034 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12035 reader: new Roo.data.JsonReader(c, c.fields)
12038 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12040 * Ext JS Library 1.1.1
12041 * Copyright(c) 2006-2007, Ext JS, LLC.
12043 * Originally Released Under LGPL - original licence link has changed is not relivant.
12046 * <script type="text/javascript">
12050 Roo.data.Field = function(config){
12051 if(typeof config == "string"){
12052 config = {name: config};
12054 Roo.apply(this, config);
12057 this.type = "auto";
12060 var st = Roo.data.SortTypes;
12061 // named sortTypes are supported, here we look them up
12062 if(typeof this.sortType == "string"){
12063 this.sortType = st[this.sortType];
12066 // set default sortType for strings and dates
12067 if(!this.sortType){
12070 this.sortType = st.asUCString;
12073 this.sortType = st.asDate;
12076 this.sortType = st.none;
12081 var stripRe = /[\$,%]/g;
12083 // prebuilt conversion function for this field, instead of
12084 // switching every time we're reading a value
12086 var cv, dateFormat = this.dateFormat;
12091 cv = function(v){ return v; };
12094 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12098 return v !== undefined && v !== null && v !== '' ?
12099 parseInt(String(v).replace(stripRe, ""), 10) : '';
12104 return v !== undefined && v !== null && v !== '' ?
12105 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12110 cv = function(v){ return v === true || v === "true" || v == 1; };
12117 if(v instanceof Date){
12121 if(dateFormat == "timestamp"){
12122 return new Date(v*1000);
12124 return Date.parseDate(v, dateFormat);
12126 var parsed = Date.parse(v);
12127 return parsed ? new Date(parsed) : null;
12136 Roo.data.Field.prototype = {
12144 * Ext JS Library 1.1.1
12145 * Copyright(c) 2006-2007, Ext JS, LLC.
12147 * Originally Released Under LGPL - original licence link has changed is not relivant.
12150 * <script type="text/javascript">
12153 // Base class for reading structured data from a data source. This class is intended to be
12154 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12157 * @class Roo.data.DataReader
12158 * Base class for reading structured data from a data source. This class is intended to be
12159 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12162 Roo.data.DataReader = function(meta, recordType){
12166 this.recordType = recordType instanceof Array ?
12167 Roo.data.Record.create(recordType) : recordType;
12170 Roo.data.DataReader.prototype = {
12172 * Create an empty record
12173 * @param {Object} data (optional) - overlay some values
12174 * @return {Roo.data.Record} record created.
12176 newRow : function(d) {
12178 this.recordType.prototype.fields.each(function(c) {
12180 case 'int' : da[c.name] = 0; break;
12181 case 'date' : da[c.name] = new Date(); break;
12182 case 'float' : da[c.name] = 0.0; break;
12183 case 'boolean' : da[c.name] = false; break;
12184 default : da[c.name] = ""; break;
12188 return new this.recordType(Roo.apply(da, d));
12193 * Ext JS Library 1.1.1
12194 * Copyright(c) 2006-2007, Ext JS, LLC.
12196 * Originally Released Under LGPL - original licence link has changed is not relivant.
12199 * <script type="text/javascript">
12203 * @class Roo.data.DataProxy
12204 * @extends Roo.data.Observable
12205 * This class is an abstract base class for implementations which provide retrieval of
12206 * unformatted data objects.<br>
12208 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12209 * (of the appropriate type which knows how to parse the data object) to provide a block of
12210 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12212 * Custom implementations must implement the load method as described in
12213 * {@link Roo.data.HttpProxy#load}.
12215 Roo.data.DataProxy = function(){
12218 * @event beforeload
12219 * Fires before a network request is made to retrieve a data object.
12220 * @param {Object} This DataProxy object.
12221 * @param {Object} params The params parameter to the load function.
12226 * Fires before the load method's callback is called.
12227 * @param {Object} This DataProxy object.
12228 * @param {Object} o The data object.
12229 * @param {Object} arg The callback argument object passed to the load function.
12233 * @event loadexception
12234 * Fires if an Exception occurs during data retrieval.
12235 * @param {Object} This DataProxy object.
12236 * @param {Object} o The data object.
12237 * @param {Object} arg The callback argument object passed to the load function.
12238 * @param {Object} e The Exception.
12240 loadexception : true
12242 Roo.data.DataProxy.superclass.constructor.call(this);
12245 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12248 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12252 * Ext JS Library 1.1.1
12253 * Copyright(c) 2006-2007, Ext JS, LLC.
12255 * Originally Released Under LGPL - original licence link has changed is not relivant.
12258 * <script type="text/javascript">
12261 * @class Roo.data.MemoryProxy
12262 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12263 * to the Reader when its load method is called.
12265 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12267 Roo.data.MemoryProxy = function(data){
12271 Roo.data.MemoryProxy.superclass.constructor.call(this);
12275 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12278 * Load data from the requested source (in this case an in-memory
12279 * data object passed to the constructor), read the data object into
12280 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12281 * process that block using the passed callback.
12282 * @param {Object} params This parameter is not used by the MemoryProxy class.
12283 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12284 * object into a block of Roo.data.Records.
12285 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12286 * The function must be passed <ul>
12287 * <li>The Record block object</li>
12288 * <li>The "arg" argument from the load function</li>
12289 * <li>A boolean success indicator</li>
12291 * @param {Object} scope The scope in which to call the callback
12292 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12294 load : function(params, reader, callback, scope, arg){
12295 params = params || {};
12298 result = reader.readRecords(this.data);
12300 this.fireEvent("loadexception", this, arg, null, e);
12301 callback.call(scope, null, arg, false);
12304 callback.call(scope, result, arg, true);
12308 update : function(params, records){
12313 * Ext JS Library 1.1.1
12314 * Copyright(c) 2006-2007, Ext JS, LLC.
12316 * Originally Released Under LGPL - original licence link has changed is not relivant.
12319 * <script type="text/javascript">
12322 * @class Roo.data.HttpProxy
12323 * @extends Roo.data.DataProxy
12324 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12325 * configured to reference a certain URL.<br><br>
12327 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12328 * from which the running page was served.<br><br>
12330 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12332 * Be aware that to enable the browser to parse an XML document, the server must set
12333 * the Content-Type header in the HTTP response to "text/xml".
12335 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12336 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12337 * will be used to make the request.
12339 Roo.data.HttpProxy = function(conn){
12340 Roo.data.HttpProxy.superclass.constructor.call(this);
12341 // is conn a conn config or a real conn?
12343 this.useAjax = !conn || !conn.events;
12347 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12348 // thse are take from connection...
12351 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12354 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12355 * extra parameters to each request made by this object. (defaults to undefined)
12358 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12359 * to each request made by this object. (defaults to undefined)
12362 * @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)
12365 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12368 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12374 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12378 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12379 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12380 * a finer-grained basis than the DataProxy events.
12382 getConnection : function(){
12383 return this.useAjax ? Roo.Ajax : this.conn;
12387 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12388 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12389 * process that block using the passed callback.
12390 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12391 * for the request to the remote server.
12392 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12393 * object into a block of Roo.data.Records.
12394 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12395 * The function must be passed <ul>
12396 * <li>The Record block object</li>
12397 * <li>The "arg" argument from the load function</li>
12398 * <li>A boolean success indicator</li>
12400 * @param {Object} scope The scope in which to call the callback
12401 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12403 load : function(params, reader, callback, scope, arg){
12404 if(this.fireEvent("beforeload", this, params) !== false){
12406 params : params || {},
12408 callback : callback,
12413 callback : this.loadResponse,
12417 Roo.applyIf(o, this.conn);
12418 if(this.activeRequest){
12419 Roo.Ajax.abort(this.activeRequest);
12421 this.activeRequest = Roo.Ajax.request(o);
12423 this.conn.request(o);
12426 callback.call(scope||this, null, arg, false);
12431 loadResponse : function(o, success, response){
12432 delete this.activeRequest;
12434 this.fireEvent("loadexception", this, o, response);
12435 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12440 result = o.reader.read(response);
12442 this.fireEvent("loadexception", this, o, response, e);
12443 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12447 this.fireEvent("load", this, o, o.request.arg);
12448 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12452 update : function(dataSet){
12457 updateResponse : function(dataSet){
12462 * Ext JS Library 1.1.1
12463 * Copyright(c) 2006-2007, Ext JS, LLC.
12465 * Originally Released Under LGPL - original licence link has changed is not relivant.
12468 * <script type="text/javascript">
12472 * @class Roo.data.ScriptTagProxy
12473 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12474 * other than the originating domain of the running page.<br><br>
12476 * <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
12477 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12479 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12480 * source code that is used as the source inside a <script> tag.<br><br>
12482 * In order for the browser to process the returned data, the server must wrap the data object
12483 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12484 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12485 * depending on whether the callback name was passed:
12488 boolean scriptTag = false;
12489 String cb = request.getParameter("callback");
12492 response.setContentType("text/javascript");
12494 response.setContentType("application/x-json");
12496 Writer out = response.getWriter();
12498 out.write(cb + "(");
12500 out.print(dataBlock.toJsonString());
12507 * @param {Object} config A configuration object.
12509 Roo.data.ScriptTagProxy = function(config){
12510 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12511 Roo.apply(this, config);
12512 this.head = document.getElementsByTagName("head")[0];
12515 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12517 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12519 * @cfg {String} url The URL from which to request the data object.
12522 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12526 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12527 * the server the name of the callback function set up by the load call to process the returned data object.
12528 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12529 * javascript output which calls this named function passing the data object as its only parameter.
12531 callbackParam : "callback",
12533 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12534 * name to the request.
12539 * Load data from the configured URL, read the data object into
12540 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12541 * process that block using the passed callback.
12542 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12543 * for the request to the remote server.
12544 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12545 * object into a block of Roo.data.Records.
12546 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12547 * The function must be passed <ul>
12548 * <li>The Record block object</li>
12549 * <li>The "arg" argument from the load function</li>
12550 * <li>A boolean success indicator</li>
12552 * @param {Object} scope The scope in which to call the callback
12553 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12555 load : function(params, reader, callback, scope, arg){
12556 if(this.fireEvent("beforeload", this, params) !== false){
12558 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12560 var url = this.url;
12561 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12563 url += "&_dc=" + (new Date().getTime());
12565 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12568 cb : "stcCallback"+transId,
12569 scriptId : "stcScript"+transId,
12573 callback : callback,
12579 window[trans.cb] = function(o){
12580 conn.handleResponse(o, trans);
12583 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12585 if(this.autoAbort !== false){
12589 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12591 var script = document.createElement("script");
12592 script.setAttribute("src", url);
12593 script.setAttribute("type", "text/javascript");
12594 script.setAttribute("id", trans.scriptId);
12595 this.head.appendChild(script);
12597 this.trans = trans;
12599 callback.call(scope||this, null, arg, false);
12604 isLoading : function(){
12605 return this.trans ? true : false;
12609 * Abort the current server request.
12611 abort : function(){
12612 if(this.isLoading()){
12613 this.destroyTrans(this.trans);
12618 destroyTrans : function(trans, isLoaded){
12619 this.head.removeChild(document.getElementById(trans.scriptId));
12620 clearTimeout(trans.timeoutId);
12622 window[trans.cb] = undefined;
12624 delete window[trans.cb];
12627 // if hasn't been loaded, wait for load to remove it to prevent script error
12628 window[trans.cb] = function(){
12629 window[trans.cb] = undefined;
12631 delete window[trans.cb];
12638 handleResponse : function(o, trans){
12639 this.trans = false;
12640 this.destroyTrans(trans, true);
12643 result = trans.reader.readRecords(o);
12645 this.fireEvent("loadexception", this, o, trans.arg, e);
12646 trans.callback.call(trans.scope||window, null, trans.arg, false);
12649 this.fireEvent("load", this, o, trans.arg);
12650 trans.callback.call(trans.scope||window, result, trans.arg, true);
12654 handleFailure : function(trans){
12655 this.trans = false;
12656 this.destroyTrans(trans, false);
12657 this.fireEvent("loadexception", this, null, trans.arg);
12658 trans.callback.call(trans.scope||window, null, trans.arg, false);
12662 * Ext JS Library 1.1.1
12663 * Copyright(c) 2006-2007, Ext JS, LLC.
12665 * Originally Released Under LGPL - original licence link has changed is not relivant.
12668 * <script type="text/javascript">
12672 * @class Roo.data.JsonReader
12673 * @extends Roo.data.DataReader
12674 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12675 * based on mappings in a provided Roo.data.Record constructor.
12677 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12678 * in the reply previously.
12683 var RecordDef = Roo.data.Record.create([
12684 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12685 {name: 'occupation'} // This field will use "occupation" as the mapping.
12687 var myReader = new Roo.data.JsonReader({
12688 totalProperty: "results", // The property which contains the total dataset size (optional)
12689 root: "rows", // The property which contains an Array of row objects
12690 id: "id" // The property within each row object that provides an ID for the record (optional)
12694 * This would consume a JSON file like this:
12696 { 'results': 2, 'rows': [
12697 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12698 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12701 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12702 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12703 * paged from the remote server.
12704 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12705 * @cfg {String} root name of the property which contains the Array of row objects.
12706 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12707 * @cfg {Array} fields Array of field definition objects
12709 * Create a new JsonReader
12710 * @param {Object} meta Metadata configuration options
12711 * @param {Object} recordType Either an Array of field definition objects,
12712 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12714 Roo.data.JsonReader = function(meta, recordType){
12717 // set some defaults:
12718 Roo.applyIf(meta, {
12719 totalProperty: 'total',
12720 successProperty : 'success',
12725 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12727 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12730 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12731 * Used by Store query builder to append _requestMeta to params.
12734 metaFromRemote : false,
12736 * This method is only used by a DataProxy which has retrieved data from a remote server.
12737 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12738 * @return {Object} data A data block which is used by an Roo.data.Store object as
12739 * a cache of Roo.data.Records.
12741 read : function(response){
12742 var json = response.responseText;
12744 var o = /* eval:var:o */ eval("("+json+")");
12746 throw {message: "JsonReader.read: Json object not found"};
12752 this.metaFromRemote = true;
12753 this.meta = o.metaData;
12754 this.recordType = Roo.data.Record.create(o.metaData.fields);
12755 this.onMetaChange(this.meta, this.recordType, o);
12757 return this.readRecords(o);
12760 // private function a store will implement
12761 onMetaChange : function(meta, recordType, o){
12768 simpleAccess: function(obj, subsc) {
12775 getJsonAccessor: function(){
12777 return function(expr) {
12779 return(re.test(expr))
12780 ? new Function("obj", "return obj." + expr)
12785 return Roo.emptyFn;
12790 * Create a data block containing Roo.data.Records from an XML document.
12791 * @param {Object} o An object which contains an Array of row objects in the property specified
12792 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12793 * which contains the total size of the dataset.
12794 * @return {Object} data A data block which is used by an Roo.data.Store object as
12795 * a cache of Roo.data.Records.
12797 readRecords : function(o){
12799 * After any data loads, the raw JSON data is available for further custom processing.
12803 var s = this.meta, Record = this.recordType,
12804 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12806 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12808 if(s.totalProperty) {
12809 this.getTotal = this.getJsonAccessor(s.totalProperty);
12811 if(s.successProperty) {
12812 this.getSuccess = this.getJsonAccessor(s.successProperty);
12814 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12816 var g = this.getJsonAccessor(s.id);
12817 this.getId = function(rec) {
12819 return (r === undefined || r === "") ? null : r;
12822 this.getId = function(){return null;};
12825 for(var jj = 0; jj < fl; jj++){
12827 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12828 this.ef[jj] = this.getJsonAccessor(map);
12832 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12833 if(s.totalProperty){
12834 var vt = parseInt(this.getTotal(o), 10);
12839 if(s.successProperty){
12840 var vs = this.getSuccess(o);
12841 if(vs === false || vs === 'false'){
12846 for(var i = 0; i < c; i++){
12849 var id = this.getId(n);
12850 for(var j = 0; j < fl; j++){
12852 var v = this.ef[j](n);
12854 Roo.log('missing convert for ' + f.name);
12858 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12860 var record = new Record(values, id);
12862 records[i] = record;
12868 totalRecords : totalRecords
12873 * Ext JS Library 1.1.1
12874 * Copyright(c) 2006-2007, Ext JS, LLC.
12876 * Originally Released Under LGPL - original licence link has changed is not relivant.
12879 * <script type="text/javascript">
12883 * @class Roo.data.ArrayReader
12884 * @extends Roo.data.DataReader
12885 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12886 * Each element of that Array represents a row of data fields. The
12887 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12888 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12892 var RecordDef = Roo.data.Record.create([
12893 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12894 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12896 var myReader = new Roo.data.ArrayReader({
12897 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12901 * This would consume an Array like this:
12903 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12905 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12907 * Create a new JsonReader
12908 * @param {Object} meta Metadata configuration options.
12909 * @param {Object} recordType Either an Array of field definition objects
12910 * as specified to {@link Roo.data.Record#create},
12911 * or an {@link Roo.data.Record} object
12912 * created using {@link Roo.data.Record#create}.
12914 Roo.data.ArrayReader = function(meta, recordType){
12915 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12918 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12920 * Create a data block containing Roo.data.Records from an XML document.
12921 * @param {Object} o An Array of row objects which represents the dataset.
12922 * @return {Object} data A data block which is used by an Roo.data.Store object as
12923 * a cache of Roo.data.Records.
12925 readRecords : function(o){
12926 var sid = this.meta ? this.meta.id : null;
12927 var recordType = this.recordType, fields = recordType.prototype.fields;
12930 for(var i = 0; i < root.length; i++){
12933 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12934 for(var j = 0, jlen = fields.length; j < jlen; j++){
12935 var f = fields.items[j];
12936 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12937 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12939 values[f.name] = v;
12941 var record = new recordType(values, id);
12943 records[records.length] = record;
12947 totalRecords : records.length
12956 * @class Roo.bootstrap.ComboBox
12957 * @extends Roo.bootstrap.TriggerField
12958 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12959 * @cfg {Boolean} append (true|false) default false
12960 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12961 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12962 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12963 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12964 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12965 * @cfg {Boolean} animate default true
12966 * @cfg {Boolean} emptyResultText only for touch device
12967 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12968 * @cfg {String} emptyTitle default ''
12970 * Create a new ComboBox.
12971 * @param {Object} config Configuration options
12973 Roo.bootstrap.ComboBox = function(config){
12974 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12978 * Fires when the dropdown list is expanded
12979 * @param {Roo.bootstrap.ComboBox} combo This combo box
12984 * Fires when the dropdown list is collapsed
12985 * @param {Roo.bootstrap.ComboBox} combo This combo box
12989 * @event beforeselect
12990 * Fires before a list item is selected. Return false to cancel the selection.
12991 * @param {Roo.bootstrap.ComboBox} combo This combo box
12992 * @param {Roo.data.Record} record The data record returned from the underlying store
12993 * @param {Number} index The index of the selected item in the dropdown list
12995 'beforeselect' : true,
12998 * Fires when a list item is selected
12999 * @param {Roo.bootstrap.ComboBox} combo This combo box
13000 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13001 * @param {Number} index The index of the selected item in the dropdown list
13005 * @event beforequery
13006 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13007 * The event object passed has these properties:
13008 * @param {Roo.bootstrap.ComboBox} combo This combo box
13009 * @param {String} query The query
13010 * @param {Boolean} forceAll true to force "all" query
13011 * @param {Boolean} cancel true to cancel the query
13012 * @param {Object} e The query event object
13014 'beforequery': true,
13017 * Fires when the 'add' icon is pressed (add a listener to enable add button)
13018 * @param {Roo.bootstrap.ComboBox} combo This combo box
13023 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13024 * @param {Roo.bootstrap.ComboBox} combo This combo box
13025 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13030 * Fires when the remove value from the combobox array
13031 * @param {Roo.bootstrap.ComboBox} combo This combo box
13035 * @event afterremove
13036 * Fires when the remove value from the combobox array
13037 * @param {Roo.bootstrap.ComboBox} combo This combo box
13039 'afterremove' : true,
13041 * @event specialfilter
13042 * Fires when specialfilter
13043 * @param {Roo.bootstrap.ComboBox} combo This combo box
13045 'specialfilter' : true,
13048 * Fires when tick the element
13049 * @param {Roo.bootstrap.ComboBox} combo This combo box
13053 * @event touchviewdisplay
13054 * Fires when touch view require special display (default is using displayField)
13055 * @param {Roo.bootstrap.ComboBox} combo This combo box
13056 * @param {Object} cfg set html .
13058 'touchviewdisplay' : true
13063 this.tickItems = [];
13065 this.selectedIndex = -1;
13066 if(this.mode == 'local'){
13067 if(config.queryDelay === undefined){
13068 this.queryDelay = 10;
13070 if(config.minChars === undefined){
13076 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13079 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13080 * rendering into an Roo.Editor, defaults to false)
13083 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13084 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13087 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13090 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13091 * the dropdown list (defaults to undefined, with no header element)
13095 * @cfg {String/Roo.Template} tpl The template to use to render the output
13099 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13101 listWidth: undefined,
13103 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13104 * mode = 'remote' or 'text' if mode = 'local')
13106 displayField: undefined,
13109 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13110 * mode = 'remote' or 'value' if mode = 'local').
13111 * Note: use of a valueField requires the user make a selection
13112 * in order for a value to be mapped.
13114 valueField: undefined,
13116 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13121 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13122 * field's data value (defaults to the underlying DOM element's name)
13124 hiddenName: undefined,
13126 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13130 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13132 selectedClass: 'active',
13135 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13139 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13140 * anchor positions (defaults to 'tl-bl')
13142 listAlign: 'tl-bl?',
13144 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13148 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13149 * query specified by the allQuery config option (defaults to 'query')
13151 triggerAction: 'query',
13153 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13154 * (defaults to 4, does not apply if editable = false)
13158 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13159 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13163 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13164 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13168 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13169 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13173 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13174 * when editable = true (defaults to false)
13176 selectOnFocus:false,
13178 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13180 queryParam: 'query',
13182 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13183 * when mode = 'remote' (defaults to 'Loading...')
13185 loadingText: 'Loading...',
13187 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13191 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13195 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13196 * traditional select (defaults to true)
13200 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13204 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13208 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13209 * listWidth has a higher value)
13213 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13214 * allow the user to set arbitrary text into the field (defaults to false)
13216 forceSelection:false,
13218 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13219 * if typeAhead = true (defaults to 250)
13221 typeAheadDelay : 250,
13223 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13224 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13226 valueNotFoundText : undefined,
13228 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13230 blockFocus : false,
13233 * @cfg {Boolean} disableClear Disable showing of clear button.
13235 disableClear : false,
13237 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13239 alwaysQuery : false,
13242 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13247 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13249 invalidClass : "has-warning",
13252 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13254 validClass : "has-success",
13257 * @cfg {Boolean} specialFilter (true|false) special filter default false
13259 specialFilter : false,
13262 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13264 mobileTouchView : true,
13267 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13269 useNativeIOS : false,
13272 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13274 mobile_restrict_height : false,
13276 ios_options : false,
13288 btnPosition : 'right',
13289 triggerList : true,
13290 showToggleBtn : true,
13292 emptyResultText: 'Empty',
13293 triggerText : 'Select',
13296 // element that contains real text value.. (when hidden is used..)
13298 getAutoCreate : function()
13303 * Render classic select for iso
13306 if(Roo.isIOS && this.useNativeIOS){
13307 cfg = this.getAutoCreateNativeIOS();
13315 if(Roo.isTouch && this.mobileTouchView){
13316 cfg = this.getAutoCreateTouchView();
13323 if(!this.tickable){
13324 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13329 * ComboBox with tickable selections
13332 var align = this.labelAlign || this.parentLabelAlign();
13335 cls : 'form-group roo-combobox-tickable' //input-group
13338 var btn_text_select = '';
13339 var btn_text_done = '';
13340 var btn_text_cancel = '';
13342 if (this.btn_text_show) {
13343 btn_text_select = 'Select';
13344 btn_text_done = 'Done';
13345 btn_text_cancel = 'Cancel';
13350 cls : 'tickable-buttons',
13355 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13356 //html : this.triggerText
13357 html: btn_text_select
13363 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13365 html: btn_text_done
13371 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13373 html: btn_text_cancel
13379 buttons.cn.unshift({
13381 cls: 'roo-select2-search-field-input'
13387 Roo.each(buttons.cn, function(c){
13389 c.cls += ' btn-' + _this.size;
13392 if (_this.disabled) {
13399 style : 'display: contents',
13404 cls: 'form-hidden-field'
13408 cls: 'roo-select2-choices',
13412 cls: 'roo-select2-search-field',
13423 cls: 'roo-select2-container input-group roo-select2-container-multi',
13429 // cls: 'typeahead typeahead-long dropdown-menu',
13430 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13435 if(this.hasFeedback && !this.allowBlank){
13439 cls: 'glyphicon form-control-feedback'
13442 combobox.cn.push(feedback);
13447 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13448 tooltip : 'This field is required'
13450 if (Roo.bootstrap.version == 4) {
13453 style : 'display:none'
13456 if (align ==='left' && this.fieldLabel.length) {
13458 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13465 cls : 'control-label col-form-label',
13466 html : this.fieldLabel
13478 var labelCfg = cfg.cn[1];
13479 var contentCfg = cfg.cn[2];
13482 if(this.indicatorpos == 'right'){
13488 cls : 'control-label col-form-label',
13492 html : this.fieldLabel
13508 labelCfg = cfg.cn[0];
13509 contentCfg = cfg.cn[1];
13513 if(this.labelWidth > 12){
13514 labelCfg.style = "width: " + this.labelWidth + 'px';
13517 if(this.labelWidth < 13 && this.labelmd == 0){
13518 this.labelmd = this.labelWidth;
13521 if(this.labellg > 0){
13522 labelCfg.cls += ' col-lg-' + this.labellg;
13523 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13526 if(this.labelmd > 0){
13527 labelCfg.cls += ' col-md-' + this.labelmd;
13528 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13531 if(this.labelsm > 0){
13532 labelCfg.cls += ' col-sm-' + this.labelsm;
13533 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13536 if(this.labelxs > 0){
13537 labelCfg.cls += ' col-xs-' + this.labelxs;
13538 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13542 } else if ( this.fieldLabel.length) {
13543 // Roo.log(" label");
13548 //cls : 'input-group-addon',
13549 html : this.fieldLabel
13554 if(this.indicatorpos == 'right'){
13558 //cls : 'input-group-addon',
13559 html : this.fieldLabel
13569 // Roo.log(" no label && no align");
13576 ['xs','sm','md','lg'].map(function(size){
13577 if (settings[size]) {
13578 cfg.cls += ' col-' + size + '-' + settings[size];
13586 _initEventsCalled : false,
13589 initEvents: function()
13591 if (this._initEventsCalled) { // as we call render... prevent looping...
13594 this._initEventsCalled = true;
13597 throw "can not find store for combo";
13600 this.indicator = this.indicatorEl();
13602 this.store = Roo.factory(this.store, Roo.data);
13603 this.store.parent = this;
13605 // if we are building from html. then this element is so complex, that we can not really
13606 // use the rendered HTML.
13607 // so we have to trash and replace the previous code.
13608 if (Roo.XComponent.build_from_html) {
13609 // remove this element....
13610 var e = this.el.dom, k=0;
13611 while (e ) { e = e.previousSibling; ++k;}
13616 this.rendered = false;
13618 this.render(this.parent().getChildContainer(true), k);
13621 if(Roo.isIOS && this.useNativeIOS){
13622 this.initIOSView();
13630 if(Roo.isTouch && this.mobileTouchView){
13631 this.initTouchView();
13636 this.initTickableEvents();
13640 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13642 if(this.hiddenName){
13644 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13646 this.hiddenField.dom.value =
13647 this.hiddenValue !== undefined ? this.hiddenValue :
13648 this.value !== undefined ? this.value : '';
13650 // prevent input submission
13651 this.el.dom.removeAttribute('name');
13652 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13657 // this.el.dom.setAttribute('autocomplete', 'off');
13660 var cls = 'x-combo-list';
13662 //this.list = new Roo.Layer({
13663 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13669 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13670 _this.list.setWidth(lw);
13673 this.list.on('mouseover', this.onViewOver, this);
13674 this.list.on('mousemove', this.onViewMove, this);
13675 this.list.on('scroll', this.onViewScroll, this);
13678 this.list.swallowEvent('mousewheel');
13679 this.assetHeight = 0;
13682 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13683 this.assetHeight += this.header.getHeight();
13686 this.innerList = this.list.createChild({cls:cls+'-inner'});
13687 this.innerList.on('mouseover', this.onViewOver, this);
13688 this.innerList.on('mousemove', this.onViewMove, this);
13689 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13691 if(this.allowBlank && !this.pageSize && !this.disableClear){
13692 this.footer = this.list.createChild({cls:cls+'-ft'});
13693 this.pageTb = new Roo.Toolbar(this.footer);
13697 this.footer = this.list.createChild({cls:cls+'-ft'});
13698 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13699 {pageSize: this.pageSize});
13703 if (this.pageTb && this.allowBlank && !this.disableClear) {
13705 this.pageTb.add(new Roo.Toolbar.Fill(), {
13706 cls: 'x-btn-icon x-btn-clear',
13708 handler: function()
13711 _this.clearValue();
13712 _this.onSelect(false, -1);
13717 this.assetHeight += this.footer.getHeight();
13722 this.tpl = Roo.bootstrap.version == 4 ?
13723 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
13724 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13727 this.view = new Roo.View(this.list, this.tpl, {
13728 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13730 //this.view.wrapEl.setDisplayed(false);
13731 this.view.on('click', this.onViewClick, this);
13734 this.store.on('beforeload', this.onBeforeLoad, this);
13735 this.store.on('load', this.onLoad, this);
13736 this.store.on('loadexception', this.onLoadException, this);
13738 if(this.resizable){
13739 this.resizer = new Roo.Resizable(this.list, {
13740 pinned:true, handles:'se'
13742 this.resizer.on('resize', function(r, w, h){
13743 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13744 this.listWidth = w;
13745 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13746 this.restrictHeight();
13748 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13751 if(!this.editable){
13752 this.editable = true;
13753 this.setEditable(false);
13758 if (typeof(this.events.add.listeners) != 'undefined') {
13760 this.addicon = this.wrap.createChild(
13761 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13763 this.addicon.on('click', function(e) {
13764 this.fireEvent('add', this);
13767 if (typeof(this.events.edit.listeners) != 'undefined') {
13769 this.editicon = this.wrap.createChild(
13770 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13771 if (this.addicon) {
13772 this.editicon.setStyle('margin-left', '40px');
13774 this.editicon.on('click', function(e) {
13776 // we fire even if inothing is selected..
13777 this.fireEvent('edit', this, this.lastData );
13783 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13784 "up" : function(e){
13785 this.inKeyMode = true;
13789 "down" : function(e){
13790 if(!this.isExpanded()){
13791 this.onTriggerClick();
13793 this.inKeyMode = true;
13798 "enter" : function(e){
13799 // this.onViewClick();
13803 if(this.fireEvent("specialkey", this, e)){
13804 this.onViewClick(false);
13810 "esc" : function(e){
13814 "tab" : function(e){
13817 if(this.fireEvent("specialkey", this, e)){
13818 this.onViewClick(false);
13826 doRelay : function(foo, bar, hname){
13827 if(hname == 'down' || this.scope.isExpanded()){
13828 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13837 this.queryDelay = Math.max(this.queryDelay || 10,
13838 this.mode == 'local' ? 10 : 250);
13841 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13843 if(this.typeAhead){
13844 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13846 if(this.editable !== false){
13847 this.inputEl().on("keyup", this.onKeyUp, this);
13849 if(this.forceSelection){
13850 this.inputEl().on('blur', this.doForce, this);
13854 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13855 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13859 initTickableEvents: function()
13863 if(this.hiddenName){
13865 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13867 this.hiddenField.dom.value =
13868 this.hiddenValue !== undefined ? this.hiddenValue :
13869 this.value !== undefined ? this.value : '';
13871 // prevent input submission
13872 this.el.dom.removeAttribute('name');
13873 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13878 // this.list = this.el.select('ul.dropdown-menu',true).first();
13880 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13881 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13882 if(this.triggerList){
13883 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13886 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13887 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13889 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13890 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13892 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13893 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13895 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13896 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13897 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13900 this.cancelBtn.hide();
13905 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13906 _this.list.setWidth(lw);
13909 this.list.on('mouseover', this.onViewOver, this);
13910 this.list.on('mousemove', this.onViewMove, this);
13912 this.list.on('scroll', this.onViewScroll, this);
13915 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13916 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13919 this.view = new Roo.View(this.list, this.tpl, {
13924 selectedClass: this.selectedClass
13927 //this.view.wrapEl.setDisplayed(false);
13928 this.view.on('click', this.onViewClick, this);
13932 this.store.on('beforeload', this.onBeforeLoad, this);
13933 this.store.on('load', this.onLoad, this);
13934 this.store.on('loadexception', this.onLoadException, this);
13937 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13938 "up" : function(e){
13939 this.inKeyMode = true;
13943 "down" : function(e){
13944 this.inKeyMode = true;
13948 "enter" : function(e){
13949 if(this.fireEvent("specialkey", this, e)){
13950 this.onViewClick(false);
13956 "esc" : function(e){
13957 this.onTickableFooterButtonClick(e, false, false);
13960 "tab" : function(e){
13961 this.fireEvent("specialkey", this, e);
13963 this.onTickableFooterButtonClick(e, false, false);
13970 doRelay : function(e, fn, key){
13971 if(this.scope.isExpanded()){
13972 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13981 this.queryDelay = Math.max(this.queryDelay || 10,
13982 this.mode == 'local' ? 10 : 250);
13985 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13987 if(this.typeAhead){
13988 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13991 if(this.editable !== false){
13992 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13995 this.indicator = this.indicatorEl();
13997 if(this.indicator){
13998 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13999 this.indicator.hide();
14004 onDestroy : function(){
14006 this.view.setStore(null);
14007 this.view.el.removeAllListeners();
14008 this.view.el.remove();
14009 this.view.purgeListeners();
14012 this.list.dom.innerHTML = '';
14016 this.store.un('beforeload', this.onBeforeLoad, this);
14017 this.store.un('load', this.onLoad, this);
14018 this.store.un('loadexception', this.onLoadException, this);
14020 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14024 fireKey : function(e){
14025 if(e.isNavKeyPress() && !this.list.isVisible()){
14026 this.fireEvent("specialkey", this, e);
14031 onResize: function(w, h){
14032 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14034 // if(typeof w != 'number'){
14035 // // we do not handle it!?!?
14038 // var tw = this.trigger.getWidth();
14039 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14040 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14042 // this.inputEl().setWidth( this.adjustWidth('input', x));
14044 // //this.trigger.setStyle('left', x+'px');
14046 // if(this.list && this.listWidth === undefined){
14047 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14048 // this.list.setWidth(lw);
14049 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14057 * Allow or prevent the user from directly editing the field text. If false is passed,
14058 * the user will only be able to select from the items defined in the dropdown list. This method
14059 * is the runtime equivalent of setting the 'editable' config option at config time.
14060 * @param {Boolean} value True to allow the user to directly edit the field text
14062 setEditable : function(value){
14063 if(value == this.editable){
14066 this.editable = value;
14068 this.inputEl().dom.setAttribute('readOnly', true);
14069 this.inputEl().on('mousedown', this.onTriggerClick, this);
14070 this.inputEl().addClass('x-combo-noedit');
14072 this.inputEl().dom.setAttribute('readOnly', false);
14073 this.inputEl().un('mousedown', this.onTriggerClick, this);
14074 this.inputEl().removeClass('x-combo-noedit');
14080 onBeforeLoad : function(combo,opts){
14081 if(!this.hasFocus){
14085 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14087 this.restrictHeight();
14088 this.selectedIndex = -1;
14092 onLoad : function(){
14094 this.hasQuery = false;
14096 if(!this.hasFocus){
14100 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14101 this.loading.hide();
14104 if(this.store.getCount() > 0){
14107 this.restrictHeight();
14108 if(this.lastQuery == this.allQuery){
14109 if(this.editable && !this.tickable){
14110 this.inputEl().dom.select();
14114 !this.selectByValue(this.value, true) &&
14117 !this.store.lastOptions ||
14118 typeof(this.store.lastOptions.add) == 'undefined' ||
14119 this.store.lastOptions.add != true
14122 this.select(0, true);
14125 if(this.autoFocus){
14128 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14129 this.taTask.delay(this.typeAheadDelay);
14133 this.onEmptyResults();
14139 onLoadException : function()
14141 this.hasQuery = false;
14143 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14144 this.loading.hide();
14147 if(this.tickable && this.editable){
14152 // only causes errors at present
14153 //Roo.log(this.store.reader.jsonData);
14154 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14156 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14162 onTypeAhead : function(){
14163 if(this.store.getCount() > 0){
14164 var r = this.store.getAt(0);
14165 var newValue = r.data[this.displayField];
14166 var len = newValue.length;
14167 var selStart = this.getRawValue().length;
14169 if(selStart != len){
14170 this.setRawValue(newValue);
14171 this.selectText(selStart, newValue.length);
14177 onSelect : function(record, index){
14179 if(this.fireEvent('beforeselect', this, record, index) !== false){
14181 this.setFromData(index > -1 ? record.data : false);
14184 this.fireEvent('select', this, record, index);
14189 * Returns the currently selected field value or empty string if no value is set.
14190 * @return {String} value The selected value
14192 getValue : function()
14194 if(Roo.isIOS && this.useNativeIOS){
14195 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14199 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14202 if(this.valueField){
14203 return typeof this.value != 'undefined' ? this.value : '';
14205 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14209 getRawValue : function()
14211 if(Roo.isIOS && this.useNativeIOS){
14212 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14215 var v = this.inputEl().getValue();
14221 * Clears any text/value currently set in the field
14223 clearValue : function(){
14225 if(this.hiddenField){
14226 this.hiddenField.dom.value = '';
14229 this.setRawValue('');
14230 this.lastSelectionText = '';
14231 this.lastData = false;
14233 var close = this.closeTriggerEl();
14244 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14245 * will be displayed in the field. If the value does not match the data value of an existing item,
14246 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14247 * Otherwise the field will be blank (although the value will still be set).
14248 * @param {String} value The value to match
14250 setValue : function(v)
14252 if(Roo.isIOS && this.useNativeIOS){
14253 this.setIOSValue(v);
14263 if(this.valueField){
14264 var r = this.findRecord(this.valueField, v);
14266 text = r.data[this.displayField];
14267 }else if(this.valueNotFoundText !== undefined){
14268 text = this.valueNotFoundText;
14271 this.lastSelectionText = text;
14272 if(this.hiddenField){
14273 this.hiddenField.dom.value = v;
14275 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14278 var close = this.closeTriggerEl();
14281 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14287 * @property {Object} the last set data for the element
14292 * Sets the value of the field based on a object which is related to the record format for the store.
14293 * @param {Object} value the value to set as. or false on reset?
14295 setFromData : function(o){
14302 var dv = ''; // display value
14303 var vv = ''; // value value..
14305 if (this.displayField) {
14306 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14308 // this is an error condition!!!
14309 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14312 if(this.valueField){
14313 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14316 var close = this.closeTriggerEl();
14319 if(dv.length || vv * 1 > 0){
14321 this.blockFocus=true;
14327 if(this.hiddenField){
14328 this.hiddenField.dom.value = vv;
14330 this.lastSelectionText = dv;
14331 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14335 // no hidden field.. - we store the value in 'value', but still display
14336 // display field!!!!
14337 this.lastSelectionText = dv;
14338 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14345 reset : function(){
14346 // overridden so that last data is reset..
14353 this.setValue(this.originalValue);
14354 //this.clearInvalid();
14355 this.lastData = false;
14357 this.view.clearSelections();
14363 findRecord : function(prop, value){
14365 if(this.store.getCount() > 0){
14366 this.store.each(function(r){
14367 if(r.data[prop] == value){
14377 getName: function()
14379 // returns hidden if it's set..
14380 if (!this.rendered) {return ''};
14381 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14385 onViewMove : function(e, t){
14386 this.inKeyMode = false;
14390 onViewOver : function(e, t){
14391 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14394 var item = this.view.findItemFromChild(t);
14397 var index = this.view.indexOf(item);
14398 this.select(index, false);
14403 onViewClick : function(view, doFocus, el, e)
14405 var index = this.view.getSelectedIndexes()[0];
14407 var r = this.store.getAt(index);
14411 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14418 Roo.each(this.tickItems, function(v,k){
14420 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14422 _this.tickItems.splice(k, 1);
14424 if(typeof(e) == 'undefined' && view == false){
14425 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14437 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14438 this.tickItems.push(r.data);
14441 if(typeof(e) == 'undefined' && view == false){
14442 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14449 this.onSelect(r, index);
14451 if(doFocus !== false && !this.blockFocus){
14452 this.inputEl().focus();
14457 restrictHeight : function(){
14458 //this.innerList.dom.style.height = '';
14459 //var inner = this.innerList.dom;
14460 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14461 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14462 //this.list.beginUpdate();
14463 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14464 this.list.alignTo(this.inputEl(), this.listAlign);
14465 this.list.alignTo(this.inputEl(), this.listAlign);
14466 //this.list.endUpdate();
14470 onEmptyResults : function(){
14472 if(this.tickable && this.editable){
14473 this.hasFocus = false;
14474 this.restrictHeight();
14482 * Returns true if the dropdown list is expanded, else false.
14484 isExpanded : function(){
14485 return this.list.isVisible();
14489 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14490 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14491 * @param {String} value The data value of the item to select
14492 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14493 * selected item if it is not currently in view (defaults to true)
14494 * @return {Boolean} True if the value matched an item in the list, else false
14496 selectByValue : function(v, scrollIntoView){
14497 if(v !== undefined && v !== null){
14498 var r = this.findRecord(this.valueField || this.displayField, v);
14500 this.select(this.store.indexOf(r), scrollIntoView);
14508 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14509 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14510 * @param {Number} index The zero-based index of the list item to select
14511 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14512 * selected item if it is not currently in view (defaults to true)
14514 select : function(index, scrollIntoView){
14515 this.selectedIndex = index;
14516 this.view.select(index);
14517 if(scrollIntoView !== false){
14518 var el = this.view.getNode(index);
14520 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14523 this.list.scrollChildIntoView(el, false);
14529 selectNext : function(){
14530 var ct = this.store.getCount();
14532 if(this.selectedIndex == -1){
14534 }else if(this.selectedIndex < ct-1){
14535 this.select(this.selectedIndex+1);
14541 selectPrev : function(){
14542 var ct = this.store.getCount();
14544 if(this.selectedIndex == -1){
14546 }else if(this.selectedIndex != 0){
14547 this.select(this.selectedIndex-1);
14553 onKeyUp : function(e){
14554 if(this.editable !== false && !e.isSpecialKey()){
14555 this.lastKey = e.getKey();
14556 this.dqTask.delay(this.queryDelay);
14561 validateBlur : function(){
14562 return !this.list || !this.list.isVisible();
14566 initQuery : function(){
14568 var v = this.getRawValue();
14570 if(this.tickable && this.editable){
14571 v = this.tickableInputEl().getValue();
14578 doForce : function(){
14579 if(this.inputEl().dom.value.length > 0){
14580 this.inputEl().dom.value =
14581 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14587 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14588 * query allowing the query action to be canceled if needed.
14589 * @param {String} query The SQL query to execute
14590 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14591 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14592 * saved in the current store (defaults to false)
14594 doQuery : function(q, forceAll){
14596 if(q === undefined || q === null){
14601 forceAll: forceAll,
14605 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14610 forceAll = qe.forceAll;
14611 if(forceAll === true || (q.length >= this.minChars)){
14613 this.hasQuery = true;
14615 if(this.lastQuery != q || this.alwaysQuery){
14616 this.lastQuery = q;
14617 if(this.mode == 'local'){
14618 this.selectedIndex = -1;
14620 this.store.clearFilter();
14623 if(this.specialFilter){
14624 this.fireEvent('specialfilter', this);
14629 this.store.filter(this.displayField, q);
14632 this.store.fireEvent("datachanged", this.store);
14639 this.store.baseParams[this.queryParam] = q;
14641 var options = {params : this.getParams(q)};
14644 options.add = true;
14645 options.params.start = this.page * this.pageSize;
14648 this.store.load(options);
14651 * this code will make the page width larger, at the beginning, the list not align correctly,
14652 * we should expand the list on onLoad
14653 * so command out it
14658 this.selectedIndex = -1;
14663 this.loadNext = false;
14667 getParams : function(q){
14669 //p[this.queryParam] = q;
14673 p.limit = this.pageSize;
14679 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14681 collapse : function(){
14682 if(!this.isExpanded()){
14688 this.hasFocus = false;
14692 this.cancelBtn.hide();
14693 this.trigger.show();
14696 this.tickableInputEl().dom.value = '';
14697 this.tickableInputEl().blur();
14702 Roo.get(document).un('mousedown', this.collapseIf, this);
14703 Roo.get(document).un('mousewheel', this.collapseIf, this);
14704 if (!this.editable) {
14705 Roo.get(document).un('keydown', this.listKeyPress, this);
14707 this.fireEvent('collapse', this);
14713 collapseIf : function(e){
14714 var in_combo = e.within(this.el);
14715 var in_list = e.within(this.list);
14716 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14718 if (in_combo || in_list || is_list) {
14719 //e.stopPropagation();
14724 this.onTickableFooterButtonClick(e, false, false);
14732 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14734 expand : function(){
14736 if(this.isExpanded() || !this.hasFocus){
14740 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14741 this.list.setWidth(lw);
14747 this.restrictHeight();
14751 this.tickItems = Roo.apply([], this.item);
14754 this.cancelBtn.show();
14755 this.trigger.hide();
14758 this.tickableInputEl().focus();
14763 Roo.get(document).on('mousedown', this.collapseIf, this);
14764 Roo.get(document).on('mousewheel', this.collapseIf, this);
14765 if (!this.editable) {
14766 Roo.get(document).on('keydown', this.listKeyPress, this);
14769 this.fireEvent('expand', this);
14773 // Implements the default empty TriggerField.onTriggerClick function
14774 onTriggerClick : function(e)
14776 Roo.log('trigger click');
14778 if(this.disabled || !this.triggerList){
14783 this.loadNext = false;
14785 if(this.isExpanded()){
14787 if (!this.blockFocus) {
14788 this.inputEl().focus();
14792 this.hasFocus = true;
14793 if(this.triggerAction == 'all') {
14794 this.doQuery(this.allQuery, true);
14796 this.doQuery(this.getRawValue());
14798 if (!this.blockFocus) {
14799 this.inputEl().focus();
14804 onTickableTriggerClick : function(e)
14811 this.loadNext = false;
14812 this.hasFocus = true;
14814 if(this.triggerAction == 'all') {
14815 this.doQuery(this.allQuery, true);
14817 this.doQuery(this.getRawValue());
14821 onSearchFieldClick : function(e)
14823 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14824 this.onTickableFooterButtonClick(e, false, false);
14828 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14833 this.loadNext = false;
14834 this.hasFocus = true;
14836 if(this.triggerAction == 'all') {
14837 this.doQuery(this.allQuery, true);
14839 this.doQuery(this.getRawValue());
14843 listKeyPress : function(e)
14845 //Roo.log('listkeypress');
14846 // scroll to first matching element based on key pres..
14847 if (e.isSpecialKey()) {
14850 var k = String.fromCharCode(e.getKey()).toUpperCase();
14853 var csel = this.view.getSelectedNodes();
14854 var cselitem = false;
14856 var ix = this.view.indexOf(csel[0]);
14857 cselitem = this.store.getAt(ix);
14858 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14864 this.store.each(function(v) {
14866 // start at existing selection.
14867 if (cselitem.id == v.id) {
14873 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14874 match = this.store.indexOf(v);
14880 if (match === false) {
14881 return true; // no more action?
14884 this.view.select(match);
14885 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14886 sn.scrollIntoView(sn.dom.parentNode, false);
14889 onViewScroll : function(e, t){
14891 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){
14895 this.hasQuery = true;
14897 this.loading = this.list.select('.loading', true).first();
14899 if(this.loading === null){
14900 this.list.createChild({
14902 cls: 'loading roo-select2-more-results roo-select2-active',
14903 html: 'Loading more results...'
14906 this.loading = this.list.select('.loading', true).first();
14908 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14910 this.loading.hide();
14913 this.loading.show();
14918 this.loadNext = true;
14920 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14925 addItem : function(o)
14927 var dv = ''; // display value
14929 if (this.displayField) {
14930 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14932 // this is an error condition!!!
14933 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14940 var choice = this.choices.createChild({
14942 cls: 'roo-select2-search-choice',
14951 cls: 'roo-select2-search-choice-close fa fa-times',
14956 }, this.searchField);
14958 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14960 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14968 this.inputEl().dom.value = '';
14973 onRemoveItem : function(e, _self, o)
14975 e.preventDefault();
14977 this.lastItem = Roo.apply([], this.item);
14979 var index = this.item.indexOf(o.data) * 1;
14982 Roo.log('not this item?!');
14986 this.item.splice(index, 1);
14991 this.fireEvent('remove', this, e);
14997 syncValue : function()
14999 if(!this.item.length){
15006 Roo.each(this.item, function(i){
15007 if(_this.valueField){
15008 value.push(i[_this.valueField]);
15015 this.value = value.join(',');
15017 if(this.hiddenField){
15018 this.hiddenField.dom.value = this.value;
15021 this.store.fireEvent("datachanged", this.store);
15026 clearItem : function()
15028 if(!this.multiple){
15034 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15042 if(this.tickable && !Roo.isTouch){
15043 this.view.refresh();
15047 inputEl: function ()
15049 if(Roo.isIOS && this.useNativeIOS){
15050 return this.el.select('select.roo-ios-select', true).first();
15053 if(Roo.isTouch && this.mobileTouchView){
15054 return this.el.select('input.form-control',true).first();
15058 return this.searchField;
15061 return this.el.select('input.form-control',true).first();
15064 onTickableFooterButtonClick : function(e, btn, el)
15066 e.preventDefault();
15068 this.lastItem = Roo.apply([], this.item);
15070 if(btn && btn.name == 'cancel'){
15071 this.tickItems = Roo.apply([], this.item);
15080 Roo.each(this.tickItems, function(o){
15088 validate : function()
15090 if(this.getVisibilityEl().hasClass('hidden')){
15094 var v = this.getRawValue();
15097 v = this.getValue();
15100 if(this.disabled || this.allowBlank || v.length){
15105 this.markInvalid();
15109 tickableInputEl : function()
15111 if(!this.tickable || !this.editable){
15112 return this.inputEl();
15115 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15119 getAutoCreateTouchView : function()
15124 cls: 'form-group' //input-group
15130 type : this.inputType,
15131 cls : 'form-control x-combo-noedit',
15132 autocomplete: 'new-password',
15133 placeholder : this.placeholder || '',
15138 input.name = this.name;
15142 input.cls += ' input-' + this.size;
15145 if (this.disabled) {
15146 input.disabled = true;
15157 inputblock.cls += ' input-group';
15159 inputblock.cn.unshift({
15161 cls : 'input-group-addon input-group-prepend input-group-text',
15166 if(this.removable && !this.multiple){
15167 inputblock.cls += ' roo-removable';
15169 inputblock.cn.push({
15172 cls : 'roo-combo-removable-btn close'
15176 if(this.hasFeedback && !this.allowBlank){
15178 inputblock.cls += ' has-feedback';
15180 inputblock.cn.push({
15182 cls: 'glyphicon form-control-feedback'
15189 inputblock.cls += (this.before) ? '' : ' input-group';
15191 inputblock.cn.push({
15193 cls : 'input-group-addon input-group-append input-group-text',
15199 var ibwrap = inputblock;
15204 cls: 'roo-select2-choices',
15208 cls: 'roo-select2-search-field',
15221 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15226 cls: 'form-hidden-field'
15232 if(!this.multiple && this.showToggleBtn){
15239 if (this.caret != false) {
15242 cls: 'fa fa-' + this.caret
15249 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15254 cls: 'combobox-clear',
15268 combobox.cls += ' roo-select2-container-multi';
15271 var align = this.labelAlign || this.parentLabelAlign();
15273 if (align ==='left' && this.fieldLabel.length) {
15278 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15279 tooltip : 'This field is required'
15283 cls : 'control-label col-form-label',
15284 html : this.fieldLabel
15295 var labelCfg = cfg.cn[1];
15296 var contentCfg = cfg.cn[2];
15299 if(this.indicatorpos == 'right'){
15304 cls : 'control-label col-form-label',
15308 html : this.fieldLabel
15312 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15313 tooltip : 'This field is required'
15326 labelCfg = cfg.cn[0];
15327 contentCfg = cfg.cn[1];
15332 if(this.labelWidth > 12){
15333 labelCfg.style = "width: " + this.labelWidth + 'px';
15336 if(this.labelWidth < 13 && this.labelmd == 0){
15337 this.labelmd = this.labelWidth;
15340 if(this.labellg > 0){
15341 labelCfg.cls += ' col-lg-' + this.labellg;
15342 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15345 if(this.labelmd > 0){
15346 labelCfg.cls += ' col-md-' + this.labelmd;
15347 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15350 if(this.labelsm > 0){
15351 labelCfg.cls += ' col-sm-' + this.labelsm;
15352 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15355 if(this.labelxs > 0){
15356 labelCfg.cls += ' col-xs-' + this.labelxs;
15357 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15361 } else if ( this.fieldLabel.length) {
15365 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15366 tooltip : 'This field is required'
15370 cls : 'control-label',
15371 html : this.fieldLabel
15382 if(this.indicatorpos == 'right'){
15386 cls : 'control-label',
15387 html : this.fieldLabel,
15391 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15392 tooltip : 'This field is required'
15409 var settings = this;
15411 ['xs','sm','md','lg'].map(function(size){
15412 if (settings[size]) {
15413 cfg.cls += ' col-' + size + '-' + settings[size];
15420 initTouchView : function()
15422 this.renderTouchView();
15424 this.touchViewEl.on('scroll', function(){
15425 this.el.dom.scrollTop = 0;
15428 this.originalValue = this.getValue();
15430 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15432 this.inputEl().on("click", this.showTouchView, this);
15433 if (this.triggerEl) {
15434 this.triggerEl.on("click", this.showTouchView, this);
15438 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15439 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15441 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15443 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15444 this.store.on('load', this.onTouchViewLoad, this);
15445 this.store.on('loadexception', this.onTouchViewLoadException, this);
15447 if(this.hiddenName){
15449 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15451 this.hiddenField.dom.value =
15452 this.hiddenValue !== undefined ? this.hiddenValue :
15453 this.value !== undefined ? this.value : '';
15455 this.el.dom.removeAttribute('name');
15456 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15460 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15461 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15464 if(this.removable && !this.multiple){
15465 var close = this.closeTriggerEl();
15467 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15468 close.on('click', this.removeBtnClick, this, close);
15472 * fix the bug in Safari iOS8
15474 this.inputEl().on("focus", function(e){
15475 document.activeElement.blur();
15478 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15485 renderTouchView : function()
15487 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15488 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15490 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15491 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15493 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15494 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15495 this.touchViewBodyEl.setStyle('overflow', 'auto');
15497 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15498 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15500 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15501 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15505 showTouchView : function()
15511 this.touchViewHeaderEl.hide();
15513 if(this.modalTitle.length){
15514 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15515 this.touchViewHeaderEl.show();
15518 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15519 this.touchViewEl.show();
15521 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15523 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15524 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15526 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15528 if(this.modalTitle.length){
15529 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15532 this.touchViewBodyEl.setHeight(bodyHeight);
15536 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15538 this.touchViewEl.addClass('in');
15541 if(this._touchViewMask){
15542 Roo.get(document.body).addClass("x-body-masked");
15543 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15544 this._touchViewMask.setStyle('z-index', 10000);
15545 this._touchViewMask.addClass('show');
15548 this.doTouchViewQuery();
15552 hideTouchView : function()
15554 this.touchViewEl.removeClass('in');
15558 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15560 this.touchViewEl.setStyle('display', 'none');
15563 if(this._touchViewMask){
15564 this._touchViewMask.removeClass('show');
15565 Roo.get(document.body).removeClass("x-body-masked");
15569 setTouchViewValue : function()
15576 Roo.each(this.tickItems, function(o){
15581 this.hideTouchView();
15584 doTouchViewQuery : function()
15593 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15597 if(!this.alwaysQuery || this.mode == 'local'){
15598 this.onTouchViewLoad();
15605 onTouchViewBeforeLoad : function(combo,opts)
15611 onTouchViewLoad : function()
15613 if(this.store.getCount() < 1){
15614 this.onTouchViewEmptyResults();
15618 this.clearTouchView();
15620 var rawValue = this.getRawValue();
15622 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15624 this.tickItems = [];
15626 this.store.data.each(function(d, rowIndex){
15627 var row = this.touchViewListGroup.createChild(template);
15629 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15630 row.addClass(d.data.cls);
15633 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15636 html : d.data[this.displayField]
15639 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15640 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15643 row.removeClass('selected');
15644 if(!this.multiple && this.valueField &&
15645 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15648 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15649 row.addClass('selected');
15652 if(this.multiple && this.valueField &&
15653 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15657 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15658 this.tickItems.push(d.data);
15661 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15665 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15667 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15669 if(this.modalTitle.length){
15670 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15673 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15675 if(this.mobile_restrict_height && listHeight < bodyHeight){
15676 this.touchViewBodyEl.setHeight(listHeight);
15681 if(firstChecked && listHeight > bodyHeight){
15682 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15687 onTouchViewLoadException : function()
15689 this.hideTouchView();
15692 onTouchViewEmptyResults : function()
15694 this.clearTouchView();
15696 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15698 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15702 clearTouchView : function()
15704 this.touchViewListGroup.dom.innerHTML = '';
15707 onTouchViewClick : function(e, el, o)
15709 e.preventDefault();
15712 var rowIndex = o.rowIndex;
15714 var r = this.store.getAt(rowIndex);
15716 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15718 if(!this.multiple){
15719 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15720 c.dom.removeAttribute('checked');
15723 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15725 this.setFromData(r.data);
15727 var close = this.closeTriggerEl();
15733 this.hideTouchView();
15735 this.fireEvent('select', this, r, rowIndex);
15740 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15741 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15742 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15746 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15747 this.addItem(r.data);
15748 this.tickItems.push(r.data);
15752 getAutoCreateNativeIOS : function()
15755 cls: 'form-group' //input-group,
15760 cls : 'roo-ios-select'
15764 combobox.name = this.name;
15767 if (this.disabled) {
15768 combobox.disabled = true;
15771 var settings = this;
15773 ['xs','sm','md','lg'].map(function(size){
15774 if (settings[size]) {
15775 cfg.cls += ' col-' + size + '-' + settings[size];
15785 initIOSView : function()
15787 this.store.on('load', this.onIOSViewLoad, this);
15792 onIOSViewLoad : function()
15794 if(this.store.getCount() < 1){
15798 this.clearIOSView();
15800 if(this.allowBlank) {
15802 var default_text = '-- SELECT --';
15804 if(this.placeholder.length){
15805 default_text = this.placeholder;
15808 if(this.emptyTitle.length){
15809 default_text += ' - ' + this.emptyTitle + ' -';
15812 var opt = this.inputEl().createChild({
15815 html : default_text
15819 o[this.valueField] = 0;
15820 o[this.displayField] = default_text;
15822 this.ios_options.push({
15829 this.store.data.each(function(d, rowIndex){
15833 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15834 html = d.data[this.displayField];
15839 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15840 value = d.data[this.valueField];
15849 if(this.value == d.data[this.valueField]){
15850 option['selected'] = true;
15853 var opt = this.inputEl().createChild(option);
15855 this.ios_options.push({
15862 this.inputEl().on('change', function(){
15863 this.fireEvent('select', this);
15868 clearIOSView: function()
15870 this.inputEl().dom.innerHTML = '';
15872 this.ios_options = [];
15875 setIOSValue: function(v)
15879 if(!this.ios_options){
15883 Roo.each(this.ios_options, function(opts){
15885 opts.el.dom.removeAttribute('selected');
15887 if(opts.data[this.valueField] != v){
15891 opts.el.dom.setAttribute('selected', true);
15897 * @cfg {Boolean} grow
15901 * @cfg {Number} growMin
15905 * @cfg {Number} growMax
15914 Roo.apply(Roo.bootstrap.ComboBox, {
15918 cls: 'modal-header',
15940 cls: 'list-group-item',
15944 cls: 'roo-combobox-list-group-item-value'
15948 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15962 listItemCheckbox : {
15964 cls: 'list-group-item',
15968 cls: 'roo-combobox-list-group-item-value'
15972 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15988 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15993 cls: 'modal-footer',
16001 cls: 'col-xs-6 text-left',
16004 cls: 'btn btn-danger roo-touch-view-cancel',
16010 cls: 'col-xs-6 text-right',
16013 cls: 'btn btn-success roo-touch-view-ok',
16024 Roo.apply(Roo.bootstrap.ComboBox, {
16026 touchViewTemplate : {
16028 cls: 'modal fade roo-combobox-touch-view',
16032 cls: 'modal-dialog',
16033 style : 'position:fixed', // we have to fix position....
16037 cls: 'modal-content',
16039 Roo.bootstrap.ComboBox.header,
16040 Roo.bootstrap.ComboBox.body,
16041 Roo.bootstrap.ComboBox.footer
16050 * Ext JS Library 1.1.1
16051 * Copyright(c) 2006-2007, Ext JS, LLC.
16053 * Originally Released Under LGPL - original licence link has changed is not relivant.
16056 * <script type="text/javascript">
16061 * @extends Roo.util.Observable
16062 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16063 * This class also supports single and multi selection modes. <br>
16064 * Create a data model bound view:
16066 var store = new Roo.data.Store(...);
16068 var view = new Roo.View({
16070 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16072 singleSelect: true,
16073 selectedClass: "ydataview-selected",
16077 // listen for node click?
16078 view.on("click", function(vw, index, node, e){
16079 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16083 dataModel.load("foobar.xml");
16085 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16087 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16088 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16090 * Note: old style constructor is still suported (container, template, config)
16093 * Create a new View
16094 * @param {Object} config The config object
16097 Roo.View = function(config, depreciated_tpl, depreciated_config){
16099 this.parent = false;
16101 if (typeof(depreciated_tpl) == 'undefined') {
16102 // new way.. - universal constructor.
16103 Roo.apply(this, config);
16104 this.el = Roo.get(this.el);
16107 this.el = Roo.get(config);
16108 this.tpl = depreciated_tpl;
16109 Roo.apply(this, depreciated_config);
16111 this.wrapEl = this.el.wrap().wrap();
16112 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16115 if(typeof(this.tpl) == "string"){
16116 this.tpl = new Roo.Template(this.tpl);
16118 // support xtype ctors..
16119 this.tpl = new Roo.factory(this.tpl, Roo);
16123 this.tpl.compile();
16128 * @event beforeclick
16129 * Fires before a click is processed. Returns false to cancel the default action.
16130 * @param {Roo.View} this
16131 * @param {Number} index The index of the target node
16132 * @param {HTMLElement} node The target node
16133 * @param {Roo.EventObject} e The raw event object
16135 "beforeclick" : true,
16138 * Fires when a template node is clicked.
16139 * @param {Roo.View} this
16140 * @param {Number} index The index of the target node
16141 * @param {HTMLElement} node The target node
16142 * @param {Roo.EventObject} e The raw event object
16147 * Fires when a template node is double clicked.
16148 * @param {Roo.View} this
16149 * @param {Number} index The index of the target node
16150 * @param {HTMLElement} node The target node
16151 * @param {Roo.EventObject} e The raw event object
16155 * @event contextmenu
16156 * Fires when a template node is right clicked.
16157 * @param {Roo.View} this
16158 * @param {Number} index The index of the target node
16159 * @param {HTMLElement} node The target node
16160 * @param {Roo.EventObject} e The raw event object
16162 "contextmenu" : true,
16164 * @event selectionchange
16165 * Fires when the selected nodes change.
16166 * @param {Roo.View} this
16167 * @param {Array} selections Array of the selected nodes
16169 "selectionchange" : true,
16172 * @event beforeselect
16173 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16174 * @param {Roo.View} this
16175 * @param {HTMLElement} node The node to be selected
16176 * @param {Array} selections Array of currently selected nodes
16178 "beforeselect" : true,
16180 * @event preparedata
16181 * Fires on every row to render, to allow you to change the data.
16182 * @param {Roo.View} this
16183 * @param {Object} data to be rendered (change this)
16185 "preparedata" : true
16193 "click": this.onClick,
16194 "dblclick": this.onDblClick,
16195 "contextmenu": this.onContextMenu,
16199 this.selections = [];
16201 this.cmp = new Roo.CompositeElementLite([]);
16203 this.store = Roo.factory(this.store, Roo.data);
16204 this.setStore(this.store, true);
16207 if ( this.footer && this.footer.xtype) {
16209 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16211 this.footer.dataSource = this.store;
16212 this.footer.container = fctr;
16213 this.footer = Roo.factory(this.footer, Roo);
16214 fctr.insertFirst(this.el);
16216 // this is a bit insane - as the paging toolbar seems to detach the el..
16217 // dom.parentNode.parentNode.parentNode
16218 // they get detached?
16222 Roo.View.superclass.constructor.call(this);
16227 Roo.extend(Roo.View, Roo.util.Observable, {
16230 * @cfg {Roo.data.Store} store Data store to load data from.
16235 * @cfg {String|Roo.Element} el The container element.
16240 * @cfg {String|Roo.Template} tpl The template used by this View
16244 * @cfg {String} dataName the named area of the template to use as the data area
16245 * Works with domtemplates roo-name="name"
16249 * @cfg {String} selectedClass The css class to add to selected nodes
16251 selectedClass : "x-view-selected",
16253 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16258 * @cfg {String} text to display on mask (default Loading)
16262 * @cfg {Boolean} multiSelect Allow multiple selection
16264 multiSelect : false,
16266 * @cfg {Boolean} singleSelect Allow single selection
16268 singleSelect: false,
16271 * @cfg {Boolean} toggleSelect - selecting
16273 toggleSelect : false,
16276 * @cfg {Boolean} tickable - selecting
16281 * Returns the element this view is bound to.
16282 * @return {Roo.Element}
16284 getEl : function(){
16285 return this.wrapEl;
16291 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16293 refresh : function(){
16294 //Roo.log('refresh');
16297 // if we are using something like 'domtemplate', then
16298 // the what gets used is:
16299 // t.applySubtemplate(NAME, data, wrapping data..)
16300 // the outer template then get' applied with
16301 // the store 'extra data'
16302 // and the body get's added to the
16303 // roo-name="data" node?
16304 // <span class='roo-tpl-{name}'></span> ?????
16308 this.clearSelections();
16309 this.el.update("");
16311 var records = this.store.getRange();
16312 if(records.length < 1) {
16314 // is this valid?? = should it render a template??
16316 this.el.update(this.emptyText);
16320 if (this.dataName) {
16321 this.el.update(t.apply(this.store.meta)); //????
16322 el = this.el.child('.roo-tpl-' + this.dataName);
16325 for(var i = 0, len = records.length; i < len; i++){
16326 var data = this.prepareData(records[i].data, i, records[i]);
16327 this.fireEvent("preparedata", this, data, i, records[i]);
16329 var d = Roo.apply({}, data);
16332 Roo.apply(d, {'roo-id' : Roo.id()});
16336 Roo.each(this.parent.item, function(item){
16337 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16340 Roo.apply(d, {'roo-data-checked' : 'checked'});
16344 html[html.length] = Roo.util.Format.trim(
16346 t.applySubtemplate(this.dataName, d, this.store.meta) :
16353 el.update(html.join(""));
16354 this.nodes = el.dom.childNodes;
16355 this.updateIndexes(0);
16360 * Function to override to reformat the data that is sent to
16361 * the template for each node.
16362 * DEPRICATED - use the preparedata event handler.
16363 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16364 * a JSON object for an UpdateManager bound view).
16366 prepareData : function(data, index, record)
16368 this.fireEvent("preparedata", this, data, index, record);
16372 onUpdate : function(ds, record){
16373 // Roo.log('on update');
16374 this.clearSelections();
16375 var index = this.store.indexOf(record);
16376 var n = this.nodes[index];
16377 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16378 n.parentNode.removeChild(n);
16379 this.updateIndexes(index, index);
16385 onAdd : function(ds, records, index)
16387 //Roo.log(['on Add', ds, records, index] );
16388 this.clearSelections();
16389 if(this.nodes.length == 0){
16393 var n = this.nodes[index];
16394 for(var i = 0, len = records.length; i < len; i++){
16395 var d = this.prepareData(records[i].data, i, records[i]);
16397 this.tpl.insertBefore(n, d);
16400 this.tpl.append(this.el, d);
16403 this.updateIndexes(index);
16406 onRemove : function(ds, record, index){
16407 // Roo.log('onRemove');
16408 this.clearSelections();
16409 var el = this.dataName ?
16410 this.el.child('.roo-tpl-' + this.dataName) :
16413 el.dom.removeChild(this.nodes[index]);
16414 this.updateIndexes(index);
16418 * Refresh an individual node.
16419 * @param {Number} index
16421 refreshNode : function(index){
16422 this.onUpdate(this.store, this.store.getAt(index));
16425 updateIndexes : function(startIndex, endIndex){
16426 var ns = this.nodes;
16427 startIndex = startIndex || 0;
16428 endIndex = endIndex || ns.length - 1;
16429 for(var i = startIndex; i <= endIndex; i++){
16430 ns[i].nodeIndex = i;
16435 * Changes the data store this view uses and refresh the view.
16436 * @param {Store} store
16438 setStore : function(store, initial){
16439 if(!initial && this.store){
16440 this.store.un("datachanged", this.refresh);
16441 this.store.un("add", this.onAdd);
16442 this.store.un("remove", this.onRemove);
16443 this.store.un("update", this.onUpdate);
16444 this.store.un("clear", this.refresh);
16445 this.store.un("beforeload", this.onBeforeLoad);
16446 this.store.un("load", this.onLoad);
16447 this.store.un("loadexception", this.onLoad);
16451 store.on("datachanged", this.refresh, this);
16452 store.on("add", this.onAdd, this);
16453 store.on("remove", this.onRemove, this);
16454 store.on("update", this.onUpdate, this);
16455 store.on("clear", this.refresh, this);
16456 store.on("beforeload", this.onBeforeLoad, this);
16457 store.on("load", this.onLoad, this);
16458 store.on("loadexception", this.onLoad, this);
16466 * onbeforeLoad - masks the loading area.
16469 onBeforeLoad : function(store,opts)
16471 //Roo.log('onBeforeLoad');
16473 this.el.update("");
16475 this.el.mask(this.mask ? this.mask : "Loading" );
16477 onLoad : function ()
16484 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16485 * @param {HTMLElement} node
16486 * @return {HTMLElement} The template node
16488 findItemFromChild : function(node){
16489 var el = this.dataName ?
16490 this.el.child('.roo-tpl-' + this.dataName,true) :
16493 if(!node || node.parentNode == el){
16496 var p = node.parentNode;
16497 while(p && p != el){
16498 if(p.parentNode == el){
16507 onClick : function(e){
16508 var item = this.findItemFromChild(e.getTarget());
16510 var index = this.indexOf(item);
16511 if(this.onItemClick(item, index, e) !== false){
16512 this.fireEvent("click", this, index, item, e);
16515 this.clearSelections();
16520 onContextMenu : function(e){
16521 var item = this.findItemFromChild(e.getTarget());
16523 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16528 onDblClick : function(e){
16529 var item = this.findItemFromChild(e.getTarget());
16531 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16535 onItemClick : function(item, index, e)
16537 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16540 if (this.toggleSelect) {
16541 var m = this.isSelected(item) ? 'unselect' : 'select';
16544 _t[m](item, true, false);
16547 if(this.multiSelect || this.singleSelect){
16548 if(this.multiSelect && e.shiftKey && this.lastSelection){
16549 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16551 this.select(item, this.multiSelect && e.ctrlKey);
16552 this.lastSelection = item;
16555 if(!this.tickable){
16556 e.preventDefault();
16564 * Get the number of selected nodes.
16567 getSelectionCount : function(){
16568 return this.selections.length;
16572 * Get the currently selected nodes.
16573 * @return {Array} An array of HTMLElements
16575 getSelectedNodes : function(){
16576 return this.selections;
16580 * Get the indexes of the selected nodes.
16583 getSelectedIndexes : function(){
16584 var indexes = [], s = this.selections;
16585 for(var i = 0, len = s.length; i < len; i++){
16586 indexes.push(s[i].nodeIndex);
16592 * Clear all selections
16593 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16595 clearSelections : function(suppressEvent){
16596 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16597 this.cmp.elements = this.selections;
16598 this.cmp.removeClass(this.selectedClass);
16599 this.selections = [];
16600 if(!suppressEvent){
16601 this.fireEvent("selectionchange", this, this.selections);
16607 * Returns true if the passed node is selected
16608 * @param {HTMLElement/Number} node The node or node index
16609 * @return {Boolean}
16611 isSelected : function(node){
16612 var s = this.selections;
16616 node = this.getNode(node);
16617 return s.indexOf(node) !== -1;
16622 * @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
16623 * @param {Boolean} keepExisting (optional) true to keep existing selections
16624 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16626 select : function(nodeInfo, keepExisting, suppressEvent){
16627 if(nodeInfo instanceof Array){
16629 this.clearSelections(true);
16631 for(var i = 0, len = nodeInfo.length; i < len; i++){
16632 this.select(nodeInfo[i], true, true);
16636 var node = this.getNode(nodeInfo);
16637 if(!node || this.isSelected(node)){
16638 return; // already selected.
16641 this.clearSelections(true);
16644 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16645 Roo.fly(node).addClass(this.selectedClass);
16646 this.selections.push(node);
16647 if(!suppressEvent){
16648 this.fireEvent("selectionchange", this, this.selections);
16656 * @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
16657 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16658 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16660 unselect : function(nodeInfo, keepExisting, suppressEvent)
16662 if(nodeInfo instanceof Array){
16663 Roo.each(this.selections, function(s) {
16664 this.unselect(s, nodeInfo);
16668 var node = this.getNode(nodeInfo);
16669 if(!node || !this.isSelected(node)){
16670 //Roo.log("not selected");
16671 return; // not selected.
16675 Roo.each(this.selections, function(s) {
16677 Roo.fly(node).removeClass(this.selectedClass);
16684 this.selections= ns;
16685 this.fireEvent("selectionchange", this, this.selections);
16689 * Gets a template node.
16690 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16691 * @return {HTMLElement} The node or null if it wasn't found
16693 getNode : function(nodeInfo){
16694 if(typeof nodeInfo == "string"){
16695 return document.getElementById(nodeInfo);
16696 }else if(typeof nodeInfo == "number"){
16697 return this.nodes[nodeInfo];
16703 * Gets a range template nodes.
16704 * @param {Number} startIndex
16705 * @param {Number} endIndex
16706 * @return {Array} An array of nodes
16708 getNodes : function(start, end){
16709 var ns = this.nodes;
16710 start = start || 0;
16711 end = typeof end == "undefined" ? ns.length - 1 : end;
16714 for(var i = start; i <= end; i++){
16718 for(var i = start; i >= end; i--){
16726 * Finds the index of the passed node
16727 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16728 * @return {Number} The index of the node or -1
16730 indexOf : function(node){
16731 node = this.getNode(node);
16732 if(typeof node.nodeIndex == "number"){
16733 return node.nodeIndex;
16735 var ns = this.nodes;
16736 for(var i = 0, len = ns.length; i < len; i++){
16747 * based on jquery fullcalendar
16751 Roo.bootstrap = Roo.bootstrap || {};
16753 * @class Roo.bootstrap.Calendar
16754 * @extends Roo.bootstrap.Component
16755 * Bootstrap Calendar class
16756 * @cfg {Boolean} loadMask (true|false) default false
16757 * @cfg {Object} header generate the user specific header of the calendar, default false
16760 * Create a new Container
16761 * @param {Object} config The config object
16766 Roo.bootstrap.Calendar = function(config){
16767 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16771 * Fires when a date is selected
16772 * @param {DatePicker} this
16773 * @param {Date} date The selected date
16777 * @event monthchange
16778 * Fires when the displayed month changes
16779 * @param {DatePicker} this
16780 * @param {Date} date The selected month
16782 'monthchange': true,
16784 * @event evententer
16785 * Fires when mouse over an event
16786 * @param {Calendar} this
16787 * @param {event} Event
16789 'evententer': true,
16791 * @event eventleave
16792 * Fires when the mouse leaves an
16793 * @param {Calendar} this
16796 'eventleave': true,
16798 * @event eventclick
16799 * Fires when the mouse click an
16800 * @param {Calendar} this
16809 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16812 * @cfg {Number} startDay
16813 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16821 getAutoCreate : function(){
16824 var fc_button = function(name, corner, style, content ) {
16825 return Roo.apply({},{
16827 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16829 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16832 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16843 style : 'width:100%',
16850 cls : 'fc-header-left',
16852 fc_button('prev', 'left', 'arrow', '‹' ),
16853 fc_button('next', 'right', 'arrow', '›' ),
16854 { tag: 'span', cls: 'fc-header-space' },
16855 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16863 cls : 'fc-header-center',
16867 cls: 'fc-header-title',
16870 html : 'month / year'
16878 cls : 'fc-header-right',
16880 /* fc_button('month', 'left', '', 'month' ),
16881 fc_button('week', '', '', 'week' ),
16882 fc_button('day', 'right', '', 'day' )
16894 header = this.header;
16897 var cal_heads = function() {
16899 // fixme - handle this.
16901 for (var i =0; i < Date.dayNames.length; i++) {
16902 var d = Date.dayNames[i];
16905 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16906 html : d.substring(0,3)
16910 ret[0].cls += ' fc-first';
16911 ret[6].cls += ' fc-last';
16914 var cal_cell = function(n) {
16917 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16922 cls: 'fc-day-number',
16926 cls: 'fc-day-content',
16930 style: 'position: relative;' // height: 17px;
16942 var cal_rows = function() {
16945 for (var r = 0; r < 6; r++) {
16952 for (var i =0; i < Date.dayNames.length; i++) {
16953 var d = Date.dayNames[i];
16954 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16957 row.cn[0].cls+=' fc-first';
16958 row.cn[0].cn[0].style = 'min-height:90px';
16959 row.cn[6].cls+=' fc-last';
16963 ret[0].cls += ' fc-first';
16964 ret[4].cls += ' fc-prev-last';
16965 ret[5].cls += ' fc-last';
16972 cls: 'fc-border-separate',
16973 style : 'width:100%',
16981 cls : 'fc-first fc-last',
16999 cls : 'fc-content',
17000 style : "position: relative;",
17003 cls : 'fc-view fc-view-month fc-grid',
17004 style : 'position: relative',
17005 unselectable : 'on',
17008 cls : 'fc-event-container',
17009 style : 'position:absolute;z-index:8;top:0;left:0;'
17027 initEvents : function()
17030 throw "can not find store for calendar";
17036 style: "text-align:center",
17040 style: "background-color:white;width:50%;margin:250 auto",
17044 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17055 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17057 var size = this.el.select('.fc-content', true).first().getSize();
17058 this.maskEl.setSize(size.width, size.height);
17059 this.maskEl.enableDisplayMode("block");
17060 if(!this.loadMask){
17061 this.maskEl.hide();
17064 this.store = Roo.factory(this.store, Roo.data);
17065 this.store.on('load', this.onLoad, this);
17066 this.store.on('beforeload', this.onBeforeLoad, this);
17070 this.cells = this.el.select('.fc-day',true);
17071 //Roo.log(this.cells);
17072 this.textNodes = this.el.query('.fc-day-number');
17073 this.cells.addClassOnOver('fc-state-hover');
17075 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17076 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17077 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17078 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17080 this.on('monthchange', this.onMonthChange, this);
17082 this.update(new Date().clearTime());
17085 resize : function() {
17086 var sz = this.el.getSize();
17088 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17089 this.el.select('.fc-day-content div',true).setHeight(34);
17094 showPrevMonth : function(e){
17095 this.update(this.activeDate.add("mo", -1));
17097 showToday : function(e){
17098 this.update(new Date().clearTime());
17101 showNextMonth : function(e){
17102 this.update(this.activeDate.add("mo", 1));
17106 showPrevYear : function(){
17107 this.update(this.activeDate.add("y", -1));
17111 showNextYear : function(){
17112 this.update(this.activeDate.add("y", 1));
17117 update : function(date)
17119 var vd = this.activeDate;
17120 this.activeDate = date;
17121 // if(vd && this.el){
17122 // var t = date.getTime();
17123 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17124 // Roo.log('using add remove');
17126 // this.fireEvent('monthchange', this, date);
17128 // this.cells.removeClass("fc-state-highlight");
17129 // this.cells.each(function(c){
17130 // if(c.dateValue == t){
17131 // c.addClass("fc-state-highlight");
17132 // setTimeout(function(){
17133 // try{c.dom.firstChild.focus();}catch(e){}
17143 var days = date.getDaysInMonth();
17145 var firstOfMonth = date.getFirstDateOfMonth();
17146 var startingPos = firstOfMonth.getDay()-this.startDay;
17148 if(startingPos < this.startDay){
17152 var pm = date.add(Date.MONTH, -1);
17153 var prevStart = pm.getDaysInMonth()-startingPos;
17155 this.cells = this.el.select('.fc-day',true);
17156 this.textNodes = this.el.query('.fc-day-number');
17157 this.cells.addClassOnOver('fc-state-hover');
17159 var cells = this.cells.elements;
17160 var textEls = this.textNodes;
17162 Roo.each(cells, function(cell){
17163 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17166 days += startingPos;
17168 // convert everything to numbers so it's fast
17169 var day = 86400000;
17170 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17173 //Roo.log(prevStart);
17175 var today = new Date().clearTime().getTime();
17176 var sel = date.clearTime().getTime();
17177 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17178 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17179 var ddMatch = this.disabledDatesRE;
17180 var ddText = this.disabledDatesText;
17181 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17182 var ddaysText = this.disabledDaysText;
17183 var format = this.format;
17185 var setCellClass = function(cal, cell){
17189 //Roo.log('set Cell Class');
17191 var t = d.getTime();
17195 cell.dateValue = t;
17197 cell.className += " fc-today";
17198 cell.className += " fc-state-highlight";
17199 cell.title = cal.todayText;
17202 // disable highlight in other month..
17203 //cell.className += " fc-state-highlight";
17208 cell.className = " fc-state-disabled";
17209 cell.title = cal.minText;
17213 cell.className = " fc-state-disabled";
17214 cell.title = cal.maxText;
17218 if(ddays.indexOf(d.getDay()) != -1){
17219 cell.title = ddaysText;
17220 cell.className = " fc-state-disabled";
17223 if(ddMatch && format){
17224 var fvalue = d.dateFormat(format);
17225 if(ddMatch.test(fvalue)){
17226 cell.title = ddText.replace("%0", fvalue);
17227 cell.className = " fc-state-disabled";
17231 if (!cell.initialClassName) {
17232 cell.initialClassName = cell.dom.className;
17235 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17240 for(; i < startingPos; i++) {
17241 textEls[i].innerHTML = (++prevStart);
17242 d.setDate(d.getDate()+1);
17244 cells[i].className = "fc-past fc-other-month";
17245 setCellClass(this, cells[i]);
17250 for(; i < days; i++){
17251 intDay = i - startingPos + 1;
17252 textEls[i].innerHTML = (intDay);
17253 d.setDate(d.getDate()+1);
17255 cells[i].className = ''; // "x-date-active";
17256 setCellClass(this, cells[i]);
17260 for(; i < 42; i++) {
17261 textEls[i].innerHTML = (++extraDays);
17262 d.setDate(d.getDate()+1);
17264 cells[i].className = "fc-future fc-other-month";
17265 setCellClass(this, cells[i]);
17268 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17270 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17272 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17273 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17275 if(totalRows != 6){
17276 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17277 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17280 this.fireEvent('monthchange', this, date);
17284 if(!this.internalRender){
17285 var main = this.el.dom.firstChild;
17286 var w = main.offsetWidth;
17287 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17288 Roo.fly(main).setWidth(w);
17289 this.internalRender = true;
17290 // opera does not respect the auto grow header center column
17291 // then, after it gets a width opera refuses to recalculate
17292 // without a second pass
17293 if(Roo.isOpera && !this.secondPass){
17294 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17295 this.secondPass = true;
17296 this.update.defer(10, this, [date]);
17303 findCell : function(dt) {
17304 dt = dt.clearTime().getTime();
17306 this.cells.each(function(c){
17307 //Roo.log("check " +c.dateValue + '?=' + dt);
17308 if(c.dateValue == dt){
17318 findCells : function(ev) {
17319 var s = ev.start.clone().clearTime().getTime();
17321 var e= ev.end.clone().clearTime().getTime();
17324 this.cells.each(function(c){
17325 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17327 if(c.dateValue > e){
17330 if(c.dateValue < s){
17339 // findBestRow: function(cells)
17343 // for (var i =0 ; i < cells.length;i++) {
17344 // ret = Math.max(cells[i].rows || 0,ret);
17351 addItem : function(ev)
17353 // look for vertical location slot in
17354 var cells = this.findCells(ev);
17356 // ev.row = this.findBestRow(cells);
17358 // work out the location.
17362 for(var i =0; i < cells.length; i++) {
17364 cells[i].row = cells[0].row;
17367 cells[i].row = cells[i].row + 1;
17377 if (crow.start.getY() == cells[i].getY()) {
17379 crow.end = cells[i];
17396 cells[0].events.push(ev);
17398 this.calevents.push(ev);
17401 clearEvents: function() {
17403 if(!this.calevents){
17407 Roo.each(this.cells.elements, function(c){
17413 Roo.each(this.calevents, function(e) {
17414 Roo.each(e.els, function(el) {
17415 el.un('mouseenter' ,this.onEventEnter, this);
17416 el.un('mouseleave' ,this.onEventLeave, this);
17421 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17427 renderEvents: function()
17431 this.cells.each(function(c) {
17440 if(c.row != c.events.length){
17441 r = 4 - (4 - (c.row - c.events.length));
17444 c.events = ev.slice(0, r);
17445 c.more = ev.slice(r);
17447 if(c.more.length && c.more.length == 1){
17448 c.events.push(c.more.pop());
17451 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17455 this.cells.each(function(c) {
17457 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17460 for (var e = 0; e < c.events.length; e++){
17461 var ev = c.events[e];
17462 var rows = ev.rows;
17464 for(var i = 0; i < rows.length; i++) {
17466 // how many rows should it span..
17469 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17470 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17472 unselectable : "on",
17475 cls: 'fc-event-inner',
17479 // cls: 'fc-event-time',
17480 // html : cells.length > 1 ? '' : ev.time
17484 cls: 'fc-event-title',
17485 html : String.format('{0}', ev.title)
17492 cls: 'ui-resizable-handle ui-resizable-e',
17493 html : '  '
17500 cfg.cls += ' fc-event-start';
17502 if ((i+1) == rows.length) {
17503 cfg.cls += ' fc-event-end';
17506 var ctr = _this.el.select('.fc-event-container',true).first();
17507 var cg = ctr.createChild(cfg);
17509 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17510 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17512 var r = (c.more.length) ? 1 : 0;
17513 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17514 cg.setWidth(ebox.right - sbox.x -2);
17516 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17517 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17518 cg.on('click', _this.onEventClick, _this, ev);
17529 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17530 style : 'position: absolute',
17531 unselectable : "on",
17534 cls: 'fc-event-inner',
17538 cls: 'fc-event-title',
17546 cls: 'ui-resizable-handle ui-resizable-e',
17547 html : '  '
17553 var ctr = _this.el.select('.fc-event-container',true).first();
17554 var cg = ctr.createChild(cfg);
17556 var sbox = c.select('.fc-day-content',true).first().getBox();
17557 var ebox = c.select('.fc-day-content',true).first().getBox();
17559 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17560 cg.setWidth(ebox.right - sbox.x -2);
17562 cg.on('click', _this.onMoreEventClick, _this, c.more);
17572 onEventEnter: function (e, el,event,d) {
17573 this.fireEvent('evententer', this, el, event);
17576 onEventLeave: function (e, el,event,d) {
17577 this.fireEvent('eventleave', this, el, event);
17580 onEventClick: function (e, el,event,d) {
17581 this.fireEvent('eventclick', this, el, event);
17584 onMonthChange: function () {
17588 onMoreEventClick: function(e, el, more)
17592 this.calpopover.placement = 'right';
17593 this.calpopover.setTitle('More');
17595 this.calpopover.setContent('');
17597 var ctr = this.calpopover.el.select('.popover-content', true).first();
17599 Roo.each(more, function(m){
17601 cls : 'fc-event-hori fc-event-draggable',
17604 var cg = ctr.createChild(cfg);
17606 cg.on('click', _this.onEventClick, _this, m);
17609 this.calpopover.show(el);
17614 onLoad: function ()
17616 this.calevents = [];
17619 if(this.store.getCount() > 0){
17620 this.store.data.each(function(d){
17623 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17624 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17625 time : d.data.start_time,
17626 title : d.data.title,
17627 description : d.data.description,
17628 venue : d.data.venue
17633 this.renderEvents();
17635 if(this.calevents.length && this.loadMask){
17636 this.maskEl.hide();
17640 onBeforeLoad: function()
17642 this.clearEvents();
17644 this.maskEl.show();
17658 * @class Roo.bootstrap.Popover
17659 * @extends Roo.bootstrap.Component
17660 * Bootstrap Popover class
17661 * @cfg {String} html contents of the popover (or false to use children..)
17662 * @cfg {String} title of popover (or false to hide)
17663 * @cfg {String} placement how it is placed
17664 * @cfg {String} trigger click || hover (or false to trigger manually)
17665 * @cfg {String} over what (parent or false to trigger manually.)
17666 * @cfg {Number} delay - delay before showing
17669 * Create a new Popover
17670 * @param {Object} config The config object
17673 Roo.bootstrap.Popover = function(config){
17674 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17680 * After the popover show
17682 * @param {Roo.bootstrap.Popover} this
17687 * After the popover hide
17689 * @param {Roo.bootstrap.Popover} this
17695 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17697 title: 'Fill in a title',
17700 placement : 'right',
17701 trigger : 'hover', // hover
17707 can_build_overlaid : false,
17709 getChildContainer : function()
17711 return this.el.select('.popover-content',true).first();
17714 getAutoCreate : function(){
17717 cls : 'popover roo-dynamic',
17718 style: 'display:block',
17724 cls : 'popover-inner',
17728 cls: 'popover-title popover-header',
17732 cls : 'popover-content popover-body',
17743 setTitle: function(str)
17746 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17748 setContent: function(str)
17751 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17753 // as it get's added to the bottom of the page.
17754 onRender : function(ct, position)
17756 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17758 var cfg = Roo.apply({}, this.getAutoCreate());
17762 cfg.cls += ' ' + this.cls;
17765 cfg.style = this.style;
17767 //Roo.log("adding to ");
17768 this.el = Roo.get(document.body).createChild(cfg, position);
17769 // Roo.log(this.el);
17774 initEvents : function()
17776 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17777 this.el.enableDisplayMode('block');
17779 if (this.over === false) {
17782 if (this.triggers === false) {
17785 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17786 var triggers = this.trigger ? this.trigger.split(' ') : [];
17787 Roo.each(triggers, function(trigger) {
17789 if (trigger == 'click') {
17790 on_el.on('click', this.toggle, this);
17791 } else if (trigger != 'manual') {
17792 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17793 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17795 on_el.on(eventIn ,this.enter, this);
17796 on_el.on(eventOut, this.leave, this);
17807 toggle : function () {
17808 this.hoverState == 'in' ? this.leave() : this.enter();
17811 enter : function () {
17813 clearTimeout(this.timeout);
17815 this.hoverState = 'in';
17817 if (!this.delay || !this.delay.show) {
17822 this.timeout = setTimeout(function () {
17823 if (_t.hoverState == 'in') {
17826 }, this.delay.show)
17829 leave : function() {
17830 clearTimeout(this.timeout);
17832 this.hoverState = 'out';
17834 if (!this.delay || !this.delay.hide) {
17839 this.timeout = setTimeout(function () {
17840 if (_t.hoverState == 'out') {
17843 }, this.delay.hide)
17846 show : function (on_el)
17849 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17853 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17854 if (this.html !== false) {
17855 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17857 this.el.removeClass([
17858 'fade','top','bottom', 'left', 'right','in',
17859 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17861 if (!this.title.length) {
17862 this.el.select('.popover-title',true).hide();
17865 var placement = typeof this.placement == 'function' ?
17866 this.placement.call(this, this.el, on_el) :
17869 var autoToken = /\s?auto?\s?/i;
17870 var autoPlace = autoToken.test(placement);
17872 placement = placement.replace(autoToken, '') || 'top';
17876 //this.el.setXY([0,0]);
17878 this.el.dom.style.display='block';
17879 this.el.addClass(placement);
17881 //this.el.appendTo(on_el);
17883 var p = this.getPosition();
17884 var box = this.el.getBox();
17889 var align = Roo.bootstrap.Popover.alignment[placement];
17892 this.el.alignTo(on_el, align[0],align[1]);
17893 //var arrow = this.el.select('.arrow',true).first();
17894 //arrow.set(align[2],
17896 this.el.addClass('in');
17899 if (this.el.hasClass('fade')) {
17903 this.hoverState = 'in';
17905 this.fireEvent('show', this);
17910 this.el.setXY([0,0]);
17911 this.el.removeClass('in');
17913 this.hoverState = null;
17915 this.fireEvent('hide', this);
17920 Roo.bootstrap.Popover.alignment = {
17921 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17922 'right' : ['l-r', [10,0], 'left bs-popover-left'],
17923 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17924 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17935 * @class Roo.bootstrap.Progress
17936 * @extends Roo.bootstrap.Component
17937 * Bootstrap Progress class
17938 * @cfg {Boolean} striped striped of the progress bar
17939 * @cfg {Boolean} active animated of the progress bar
17943 * Create a new Progress
17944 * @param {Object} config The config object
17947 Roo.bootstrap.Progress = function(config){
17948 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17951 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17956 getAutoCreate : function(){
17964 cfg.cls += ' progress-striped';
17968 cfg.cls += ' active';
17987 * @class Roo.bootstrap.ProgressBar
17988 * @extends Roo.bootstrap.Component
17989 * Bootstrap ProgressBar class
17990 * @cfg {Number} aria_valuenow aria-value now
17991 * @cfg {Number} aria_valuemin aria-value min
17992 * @cfg {Number} aria_valuemax aria-value max
17993 * @cfg {String} label label for the progress bar
17994 * @cfg {String} panel (success | info | warning | danger )
17995 * @cfg {String} role role of the progress bar
17996 * @cfg {String} sr_only text
18000 * Create a new ProgressBar
18001 * @param {Object} config The config object
18004 Roo.bootstrap.ProgressBar = function(config){
18005 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18008 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
18012 aria_valuemax : 100,
18018 getAutoCreate : function()
18023 cls: 'progress-bar',
18024 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18036 cfg.role = this.role;
18039 if(this.aria_valuenow){
18040 cfg['aria-valuenow'] = this.aria_valuenow;
18043 if(this.aria_valuemin){
18044 cfg['aria-valuemin'] = this.aria_valuemin;
18047 if(this.aria_valuemax){
18048 cfg['aria-valuemax'] = this.aria_valuemax;
18051 if(this.label && !this.sr_only){
18052 cfg.html = this.label;
18056 cfg.cls += ' progress-bar-' + this.panel;
18062 update : function(aria_valuenow)
18064 this.aria_valuenow = aria_valuenow;
18066 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18081 * @class Roo.bootstrap.TabGroup
18082 * @extends Roo.bootstrap.Column
18083 * Bootstrap Column class
18084 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18085 * @cfg {Boolean} carousel true to make the group behave like a carousel
18086 * @cfg {Boolean} bullets show bullets for the panels
18087 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18088 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18089 * @cfg {Boolean} showarrow (true|false) show arrow default true
18092 * Create a new TabGroup
18093 * @param {Object} config The config object
18096 Roo.bootstrap.TabGroup = function(config){
18097 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18099 this.navId = Roo.id();
18102 Roo.bootstrap.TabGroup.register(this);
18106 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18109 transition : false,
18114 slideOnTouch : false,
18117 getAutoCreate : function()
18119 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18121 cfg.cls += ' tab-content';
18123 if (this.carousel) {
18124 cfg.cls += ' carousel slide';
18127 cls : 'carousel-inner',
18131 if(this.bullets && !Roo.isTouch){
18134 cls : 'carousel-bullets',
18138 if(this.bullets_cls){
18139 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18146 cfg.cn[0].cn.push(bullets);
18149 if(this.showarrow){
18150 cfg.cn[0].cn.push({
18152 class : 'carousel-arrow',
18156 class : 'carousel-prev',
18160 class : 'fa fa-chevron-left'
18166 class : 'carousel-next',
18170 class : 'fa fa-chevron-right'
18183 initEvents: function()
18185 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18186 // this.el.on("touchstart", this.onTouchStart, this);
18189 if(this.autoslide){
18192 this.slideFn = window.setInterval(function() {
18193 _this.showPanelNext();
18197 if(this.showarrow){
18198 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18199 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18205 // onTouchStart : function(e, el, o)
18207 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18211 // this.showPanelNext();
18215 getChildContainer : function()
18217 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18221 * register a Navigation item
18222 * @param {Roo.bootstrap.NavItem} the navitem to add
18224 register : function(item)
18226 this.tabs.push( item);
18227 item.navId = this.navId; // not really needed..
18232 getActivePanel : function()
18235 Roo.each(this.tabs, function(t) {
18245 getPanelByName : function(n)
18248 Roo.each(this.tabs, function(t) {
18249 if (t.tabId == n) {
18257 indexOfPanel : function(p)
18260 Roo.each(this.tabs, function(t,i) {
18261 if (t.tabId == p.tabId) {
18270 * show a specific panel
18271 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18272 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18274 showPanel : function (pan)
18276 if(this.transition || typeof(pan) == 'undefined'){
18277 Roo.log("waiting for the transitionend");
18281 if (typeof(pan) == 'number') {
18282 pan = this.tabs[pan];
18285 if (typeof(pan) == 'string') {
18286 pan = this.getPanelByName(pan);
18289 var cur = this.getActivePanel();
18292 Roo.log('pan or acitve pan is undefined');
18296 if (pan.tabId == this.getActivePanel().tabId) {
18300 if (false === cur.fireEvent('beforedeactivate')) {
18304 if(this.bullets > 0 && !Roo.isTouch){
18305 this.setActiveBullet(this.indexOfPanel(pan));
18308 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18310 this.transition = true;
18311 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18312 var lr = dir == 'next' ? 'left' : 'right';
18313 pan.el.addClass(dir); // or prev
18314 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18315 cur.el.addClass(lr); // or right
18316 pan.el.addClass(lr);
18319 cur.el.on('transitionend', function() {
18320 Roo.log("trans end?");
18322 pan.el.removeClass([lr,dir]);
18323 pan.setActive(true);
18325 cur.el.removeClass([lr]);
18326 cur.setActive(false);
18328 _this.transition = false;
18330 }, this, { single: true } );
18335 cur.setActive(false);
18336 pan.setActive(true);
18341 showPanelNext : function()
18343 var i = this.indexOfPanel(this.getActivePanel());
18345 if (i >= this.tabs.length - 1 && !this.autoslide) {
18349 if (i >= this.tabs.length - 1 && this.autoslide) {
18353 this.showPanel(this.tabs[i+1]);
18356 showPanelPrev : function()
18358 var i = this.indexOfPanel(this.getActivePanel());
18360 if (i < 1 && !this.autoslide) {
18364 if (i < 1 && this.autoslide) {
18365 i = this.tabs.length;
18368 this.showPanel(this.tabs[i-1]);
18372 addBullet: function()
18374 if(!this.bullets || Roo.isTouch){
18377 var ctr = this.el.select('.carousel-bullets',true).first();
18378 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18379 var bullet = ctr.createChild({
18380 cls : 'bullet bullet-' + i
18381 },ctr.dom.lastChild);
18386 bullet.on('click', (function(e, el, o, ii, t){
18388 e.preventDefault();
18390 this.showPanel(ii);
18392 if(this.autoslide && this.slideFn){
18393 clearInterval(this.slideFn);
18394 this.slideFn = window.setInterval(function() {
18395 _this.showPanelNext();
18399 }).createDelegate(this, [i, bullet], true));
18404 setActiveBullet : function(i)
18410 Roo.each(this.el.select('.bullet', true).elements, function(el){
18411 el.removeClass('selected');
18414 var bullet = this.el.select('.bullet-' + i, true).first();
18420 bullet.addClass('selected');
18431 Roo.apply(Roo.bootstrap.TabGroup, {
18435 * register a Navigation Group
18436 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18438 register : function(navgrp)
18440 this.groups[navgrp.navId] = navgrp;
18444 * fetch a Navigation Group based on the navigation ID
18445 * if one does not exist , it will get created.
18446 * @param {string} the navgroup to add
18447 * @returns {Roo.bootstrap.NavGroup} the navgroup
18449 get: function(navId) {
18450 if (typeof(this.groups[navId]) == 'undefined') {
18451 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18453 return this.groups[navId] ;
18468 * @class Roo.bootstrap.TabPanel
18469 * @extends Roo.bootstrap.Component
18470 * Bootstrap TabPanel class
18471 * @cfg {Boolean} active panel active
18472 * @cfg {String} html panel content
18473 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18474 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18475 * @cfg {String} href click to link..
18479 * Create a new TabPanel
18480 * @param {Object} config The config object
18483 Roo.bootstrap.TabPanel = function(config){
18484 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18488 * Fires when the active status changes
18489 * @param {Roo.bootstrap.TabPanel} this
18490 * @param {Boolean} state the new state
18495 * @event beforedeactivate
18496 * Fires before a tab is de-activated - can be used to do validation on a form.
18497 * @param {Roo.bootstrap.TabPanel} this
18498 * @return {Boolean} false if there is an error
18501 'beforedeactivate': true
18504 this.tabId = this.tabId || Roo.id();
18508 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18516 getAutoCreate : function(){
18519 // item is needed for carousel - not sure if it has any effect otherwise
18520 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18521 html: this.html || ''
18525 cfg.cls += ' active';
18529 cfg.tabId = this.tabId;
18536 initEvents: function()
18538 var p = this.parent();
18540 this.navId = this.navId || p.navId;
18542 if (typeof(this.navId) != 'undefined') {
18543 // not really needed.. but just in case.. parent should be a NavGroup.
18544 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18548 var i = tg.tabs.length - 1;
18550 if(this.active && tg.bullets > 0 && i < tg.bullets){
18551 tg.setActiveBullet(i);
18555 this.el.on('click', this.onClick, this);
18558 this.el.on("touchstart", this.onTouchStart, this);
18559 this.el.on("touchmove", this.onTouchMove, this);
18560 this.el.on("touchend", this.onTouchEnd, this);
18565 onRender : function(ct, position)
18567 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18570 setActive : function(state)
18572 Roo.log("panel - set active " + this.tabId + "=" + state);
18574 this.active = state;
18576 this.el.removeClass('active');
18578 } else if (!this.el.hasClass('active')) {
18579 this.el.addClass('active');
18582 this.fireEvent('changed', this, state);
18585 onClick : function(e)
18587 e.preventDefault();
18589 if(!this.href.length){
18593 window.location.href = this.href;
18602 onTouchStart : function(e)
18604 this.swiping = false;
18606 this.startX = e.browserEvent.touches[0].clientX;
18607 this.startY = e.browserEvent.touches[0].clientY;
18610 onTouchMove : function(e)
18612 this.swiping = true;
18614 this.endX = e.browserEvent.touches[0].clientX;
18615 this.endY = e.browserEvent.touches[0].clientY;
18618 onTouchEnd : function(e)
18625 var tabGroup = this.parent();
18627 if(this.endX > this.startX){ // swiping right
18628 tabGroup.showPanelPrev();
18632 if(this.startX > this.endX){ // swiping left
18633 tabGroup.showPanelNext();
18652 * @class Roo.bootstrap.DateField
18653 * @extends Roo.bootstrap.Input
18654 * Bootstrap DateField class
18655 * @cfg {Number} weekStart default 0
18656 * @cfg {String} viewMode default empty, (months|years)
18657 * @cfg {String} minViewMode default empty, (months|years)
18658 * @cfg {Number} startDate default -Infinity
18659 * @cfg {Number} endDate default Infinity
18660 * @cfg {Boolean} todayHighlight default false
18661 * @cfg {Boolean} todayBtn default false
18662 * @cfg {Boolean} calendarWeeks default false
18663 * @cfg {Object} daysOfWeekDisabled default empty
18664 * @cfg {Boolean} singleMode default false (true | false)
18666 * @cfg {Boolean} keyboardNavigation default true
18667 * @cfg {String} language default en
18670 * Create a new DateField
18671 * @param {Object} config The config object
18674 Roo.bootstrap.DateField = function(config){
18675 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18679 * Fires when this field show.
18680 * @param {Roo.bootstrap.DateField} this
18681 * @param {Mixed} date The date value
18686 * Fires when this field hide.
18687 * @param {Roo.bootstrap.DateField} this
18688 * @param {Mixed} date The date value
18693 * Fires when select a date.
18694 * @param {Roo.bootstrap.DateField} this
18695 * @param {Mixed} date The date value
18699 * @event beforeselect
18700 * Fires when before select a date.
18701 * @param {Roo.bootstrap.DateField} this
18702 * @param {Mixed} date The date value
18704 beforeselect : true
18708 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18711 * @cfg {String} format
18712 * The default date format string which can be overriden for localization support. The format must be
18713 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18717 * @cfg {String} altFormats
18718 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18719 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18721 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18729 todayHighlight : false,
18735 keyboardNavigation: true,
18737 calendarWeeks: false,
18739 startDate: -Infinity,
18743 daysOfWeekDisabled: [],
18747 singleMode : false,
18749 UTCDate: function()
18751 return new Date(Date.UTC.apply(Date, arguments));
18754 UTCToday: function()
18756 var today = new Date();
18757 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18760 getDate: function() {
18761 var d = this.getUTCDate();
18762 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18765 getUTCDate: function() {
18769 setDate: function(d) {
18770 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18773 setUTCDate: function(d) {
18775 this.setValue(this.formatDate(this.date));
18778 onRender: function(ct, position)
18781 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18783 this.language = this.language || 'en';
18784 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18785 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18787 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18788 this.format = this.format || 'm/d/y';
18789 this.isInline = false;
18790 this.isInput = true;
18791 this.component = this.el.select('.add-on', true).first() || false;
18792 this.component = (this.component && this.component.length === 0) ? false : this.component;
18793 this.hasInput = this.component && this.inputEl().length;
18795 if (typeof(this.minViewMode === 'string')) {
18796 switch (this.minViewMode) {
18798 this.minViewMode = 1;
18801 this.minViewMode = 2;
18804 this.minViewMode = 0;
18809 if (typeof(this.viewMode === 'string')) {
18810 switch (this.viewMode) {
18823 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18825 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18827 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18829 this.picker().on('mousedown', this.onMousedown, this);
18830 this.picker().on('click', this.onClick, this);
18832 this.picker().addClass('datepicker-dropdown');
18834 this.startViewMode = this.viewMode;
18836 if(this.singleMode){
18837 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18838 v.setVisibilityMode(Roo.Element.DISPLAY);
18842 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18843 v.setStyle('width', '189px');
18847 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18848 if(!this.calendarWeeks){
18853 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18854 v.attr('colspan', function(i, val){
18855 return parseInt(val) + 1;
18860 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18862 this.setStartDate(this.startDate);
18863 this.setEndDate(this.endDate);
18865 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18872 if(this.isInline) {
18877 picker : function()
18879 return this.pickerEl;
18880 // return this.el.select('.datepicker', true).first();
18883 fillDow: function()
18885 var dowCnt = this.weekStart;
18894 if(this.calendarWeeks){
18902 while (dowCnt < this.weekStart + 7) {
18906 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18910 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18913 fillMonths: function()
18916 var months = this.picker().select('>.datepicker-months td', true).first();
18918 months.dom.innerHTML = '';
18924 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18927 months.createChild(month);
18934 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;
18936 if (this.date < this.startDate) {
18937 this.viewDate = new Date(this.startDate);
18938 } else if (this.date > this.endDate) {
18939 this.viewDate = new Date(this.endDate);
18941 this.viewDate = new Date(this.date);
18949 var d = new Date(this.viewDate),
18950 year = d.getUTCFullYear(),
18951 month = d.getUTCMonth(),
18952 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18953 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18954 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18955 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18956 currentDate = this.date && this.date.valueOf(),
18957 today = this.UTCToday();
18959 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18961 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18963 // this.picker.select('>tfoot th.today').
18964 // .text(dates[this.language].today)
18965 // .toggle(this.todayBtn !== false);
18967 this.updateNavArrows();
18970 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18972 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18974 prevMonth.setUTCDate(day);
18976 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18978 var nextMonth = new Date(prevMonth);
18980 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18982 nextMonth = nextMonth.valueOf();
18984 var fillMonths = false;
18986 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18988 while(prevMonth.valueOf() <= nextMonth) {
18991 if (prevMonth.getUTCDay() === this.weekStart) {
18993 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19001 if(this.calendarWeeks){
19002 // ISO 8601: First week contains first thursday.
19003 // ISO also states week starts on Monday, but we can be more abstract here.
19005 // Start of current week: based on weekstart/current date
19006 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19007 // Thursday of this week
19008 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19009 // First Thursday of year, year from thursday
19010 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19011 // Calendar week: ms between thursdays, div ms per day, div 7 days
19012 calWeek = (th - yth) / 864e5 / 7 + 1;
19014 fillMonths.cn.push({
19022 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19024 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19027 if (this.todayHighlight &&
19028 prevMonth.getUTCFullYear() == today.getFullYear() &&
19029 prevMonth.getUTCMonth() == today.getMonth() &&
19030 prevMonth.getUTCDate() == today.getDate()) {
19031 clsName += ' today';
19034 if (currentDate && prevMonth.valueOf() === currentDate) {
19035 clsName += ' active';
19038 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19039 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19040 clsName += ' disabled';
19043 fillMonths.cn.push({
19045 cls: 'day ' + clsName,
19046 html: prevMonth.getDate()
19049 prevMonth.setDate(prevMonth.getDate()+1);
19052 var currentYear = this.date && this.date.getUTCFullYear();
19053 var currentMonth = this.date && this.date.getUTCMonth();
19055 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19057 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19058 v.removeClass('active');
19060 if(currentYear === year && k === currentMonth){
19061 v.addClass('active');
19064 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19065 v.addClass('disabled');
19071 year = parseInt(year/10, 10) * 10;
19073 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19075 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19078 for (var i = -1; i < 11; i++) {
19079 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19081 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19089 showMode: function(dir)
19092 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19095 Roo.each(this.picker().select('>div',true).elements, function(v){
19096 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19099 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19104 if(this.isInline) {
19108 this.picker().removeClass(['bottom', 'top']);
19110 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19112 * place to the top of element!
19116 this.picker().addClass('top');
19117 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19122 this.picker().addClass('bottom');
19124 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19127 parseDate : function(value)
19129 if(!value || value instanceof Date){
19132 var v = Date.parseDate(value, this.format);
19133 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19134 v = Date.parseDate(value, 'Y-m-d');
19136 if(!v && this.altFormats){
19137 if(!this.altFormatsArray){
19138 this.altFormatsArray = this.altFormats.split("|");
19140 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19141 v = Date.parseDate(value, this.altFormatsArray[i]);
19147 formatDate : function(date, fmt)
19149 return (!date || !(date instanceof Date)) ?
19150 date : date.dateFormat(fmt || this.format);
19153 onFocus : function()
19155 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19159 onBlur : function()
19161 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19163 var d = this.inputEl().getValue();
19170 showPopup : function()
19172 this.picker().show();
19176 this.fireEvent('showpopup', this, this.date);
19179 hidePopup : function()
19181 if(this.isInline) {
19184 this.picker().hide();
19185 this.viewMode = this.startViewMode;
19188 this.fireEvent('hidepopup', this, this.date);
19192 onMousedown: function(e)
19194 e.stopPropagation();
19195 e.preventDefault();
19200 Roo.bootstrap.DateField.superclass.keyup.call(this);
19204 setValue: function(v)
19206 if(this.fireEvent('beforeselect', this, v) !== false){
19207 var d = new Date(this.parseDate(v) ).clearTime();
19209 if(isNaN(d.getTime())){
19210 this.date = this.viewDate = '';
19211 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19215 v = this.formatDate(d);
19217 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19219 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19223 this.fireEvent('select', this, this.date);
19227 getValue: function()
19229 return this.formatDate(this.date);
19232 fireKey: function(e)
19234 if (!this.picker().isVisible()){
19235 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19241 var dateChanged = false,
19243 newDate, newViewDate;
19248 e.preventDefault();
19252 if (!this.keyboardNavigation) {
19255 dir = e.keyCode == 37 ? -1 : 1;
19258 newDate = this.moveYear(this.date, dir);
19259 newViewDate = this.moveYear(this.viewDate, dir);
19260 } else if (e.shiftKey){
19261 newDate = this.moveMonth(this.date, dir);
19262 newViewDate = this.moveMonth(this.viewDate, dir);
19264 newDate = new Date(this.date);
19265 newDate.setUTCDate(this.date.getUTCDate() + dir);
19266 newViewDate = new Date(this.viewDate);
19267 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19269 if (this.dateWithinRange(newDate)){
19270 this.date = newDate;
19271 this.viewDate = newViewDate;
19272 this.setValue(this.formatDate(this.date));
19274 e.preventDefault();
19275 dateChanged = true;
19280 if (!this.keyboardNavigation) {
19283 dir = e.keyCode == 38 ? -1 : 1;
19285 newDate = this.moveYear(this.date, dir);
19286 newViewDate = this.moveYear(this.viewDate, dir);
19287 } else if (e.shiftKey){
19288 newDate = this.moveMonth(this.date, dir);
19289 newViewDate = this.moveMonth(this.viewDate, dir);
19291 newDate = new Date(this.date);
19292 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19293 newViewDate = new Date(this.viewDate);
19294 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19296 if (this.dateWithinRange(newDate)){
19297 this.date = newDate;
19298 this.viewDate = newViewDate;
19299 this.setValue(this.formatDate(this.date));
19301 e.preventDefault();
19302 dateChanged = true;
19306 this.setValue(this.formatDate(this.date));
19308 e.preventDefault();
19311 this.setValue(this.formatDate(this.date));
19325 onClick: function(e)
19327 e.stopPropagation();
19328 e.preventDefault();
19330 var target = e.getTarget();
19332 if(target.nodeName.toLowerCase() === 'i'){
19333 target = Roo.get(target).dom.parentNode;
19336 var nodeName = target.nodeName;
19337 var className = target.className;
19338 var html = target.innerHTML;
19339 //Roo.log(nodeName);
19341 switch(nodeName.toLowerCase()) {
19343 switch(className) {
19349 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19350 switch(this.viewMode){
19352 this.viewDate = this.moveMonth(this.viewDate, dir);
19356 this.viewDate = this.moveYear(this.viewDate, dir);
19362 var date = new Date();
19363 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19365 this.setValue(this.formatDate(this.date));
19372 if (className.indexOf('disabled') < 0) {
19373 this.viewDate.setUTCDate(1);
19374 if (className.indexOf('month') > -1) {
19375 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19377 var year = parseInt(html, 10) || 0;
19378 this.viewDate.setUTCFullYear(year);
19382 if(this.singleMode){
19383 this.setValue(this.formatDate(this.viewDate));
19394 //Roo.log(className);
19395 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19396 var day = parseInt(html, 10) || 1;
19397 var year = this.viewDate.getUTCFullYear(),
19398 month = this.viewDate.getUTCMonth();
19400 if (className.indexOf('old') > -1) {
19407 } else if (className.indexOf('new') > -1) {
19415 //Roo.log([year,month,day]);
19416 this.date = this.UTCDate(year, month, day,0,0,0,0);
19417 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19419 //Roo.log(this.formatDate(this.date));
19420 this.setValue(this.formatDate(this.date));
19427 setStartDate: function(startDate)
19429 this.startDate = startDate || -Infinity;
19430 if (this.startDate !== -Infinity) {
19431 this.startDate = this.parseDate(this.startDate);
19434 this.updateNavArrows();
19437 setEndDate: function(endDate)
19439 this.endDate = endDate || Infinity;
19440 if (this.endDate !== Infinity) {
19441 this.endDate = this.parseDate(this.endDate);
19444 this.updateNavArrows();
19447 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19449 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19450 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19451 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19453 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19454 return parseInt(d, 10);
19457 this.updateNavArrows();
19460 updateNavArrows: function()
19462 if(this.singleMode){
19466 var d = new Date(this.viewDate),
19467 year = d.getUTCFullYear(),
19468 month = d.getUTCMonth();
19470 Roo.each(this.picker().select('.prev', true).elements, function(v){
19472 switch (this.viewMode) {
19475 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19481 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19488 Roo.each(this.picker().select('.next', true).elements, function(v){
19490 switch (this.viewMode) {
19493 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19499 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19507 moveMonth: function(date, dir)
19512 var new_date = new Date(date.valueOf()),
19513 day = new_date.getUTCDate(),
19514 month = new_date.getUTCMonth(),
19515 mag = Math.abs(dir),
19517 dir = dir > 0 ? 1 : -1;
19520 // If going back one month, make sure month is not current month
19521 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19523 return new_date.getUTCMonth() == month;
19525 // If going forward one month, make sure month is as expected
19526 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19528 return new_date.getUTCMonth() != new_month;
19530 new_month = month + dir;
19531 new_date.setUTCMonth(new_month);
19532 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19533 if (new_month < 0 || new_month > 11) {
19534 new_month = (new_month + 12) % 12;
19537 // For magnitudes >1, move one month at a time...
19538 for (var i=0; i<mag; i++) {
19539 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19540 new_date = this.moveMonth(new_date, dir);
19542 // ...then reset the day, keeping it in the new month
19543 new_month = new_date.getUTCMonth();
19544 new_date.setUTCDate(day);
19546 return new_month != new_date.getUTCMonth();
19549 // Common date-resetting loop -- if date is beyond end of month, make it
19552 new_date.setUTCDate(--day);
19553 new_date.setUTCMonth(new_month);
19558 moveYear: function(date, dir)
19560 return this.moveMonth(date, dir*12);
19563 dateWithinRange: function(date)
19565 return date >= this.startDate && date <= this.endDate;
19571 this.picker().remove();
19574 validateValue : function(value)
19576 if(this.getVisibilityEl().hasClass('hidden')){
19580 if(value.length < 1) {
19581 if(this.allowBlank){
19587 if(value.length < this.minLength){
19590 if(value.length > this.maxLength){
19594 var vt = Roo.form.VTypes;
19595 if(!vt[this.vtype](value, this)){
19599 if(typeof this.validator == "function"){
19600 var msg = this.validator(value);
19606 if(this.regex && !this.regex.test(value)){
19610 if(typeof(this.parseDate(value)) == 'undefined'){
19614 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19618 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19628 this.date = this.viewDate = '';
19630 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19635 Roo.apply(Roo.bootstrap.DateField, {
19646 html: '<i class="fa fa-arrow-left"/>'
19656 html: '<i class="fa fa-arrow-right"/>'
19698 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19699 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19700 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19701 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19702 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19715 navFnc: 'FullYear',
19720 navFnc: 'FullYear',
19725 Roo.apply(Roo.bootstrap.DateField, {
19729 cls: 'datepicker dropdown-menu roo-dynamic',
19733 cls: 'datepicker-days',
19737 cls: 'table-condensed',
19739 Roo.bootstrap.DateField.head,
19743 Roo.bootstrap.DateField.footer
19750 cls: 'datepicker-months',
19754 cls: 'table-condensed',
19756 Roo.bootstrap.DateField.head,
19757 Roo.bootstrap.DateField.content,
19758 Roo.bootstrap.DateField.footer
19765 cls: 'datepicker-years',
19769 cls: 'table-condensed',
19771 Roo.bootstrap.DateField.head,
19772 Roo.bootstrap.DateField.content,
19773 Roo.bootstrap.DateField.footer
19792 * @class Roo.bootstrap.TimeField
19793 * @extends Roo.bootstrap.Input
19794 * Bootstrap DateField class
19798 * Create a new TimeField
19799 * @param {Object} config The config object
19802 Roo.bootstrap.TimeField = function(config){
19803 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19807 * Fires when this field show.
19808 * @param {Roo.bootstrap.DateField} thisthis
19809 * @param {Mixed} date The date value
19814 * Fires when this field hide.
19815 * @param {Roo.bootstrap.DateField} this
19816 * @param {Mixed} date The date value
19821 * Fires when select a date.
19822 * @param {Roo.bootstrap.DateField} this
19823 * @param {Mixed} date The date value
19829 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19832 * @cfg {String} format
19833 * The default time format string which can be overriden for localization support. The format must be
19834 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19838 onRender: function(ct, position)
19841 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19843 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19845 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19847 this.pop = this.picker().select('>.datepicker-time',true).first();
19848 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19850 this.picker().on('mousedown', this.onMousedown, this);
19851 this.picker().on('click', this.onClick, this);
19853 this.picker().addClass('datepicker-dropdown');
19858 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19859 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19860 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19861 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19862 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19863 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19867 fireKey: function(e){
19868 if (!this.picker().isVisible()){
19869 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19875 e.preventDefault();
19883 this.onTogglePeriod();
19886 this.onIncrementMinutes();
19889 this.onDecrementMinutes();
19898 onClick: function(e) {
19899 e.stopPropagation();
19900 e.preventDefault();
19903 picker : function()
19905 return this.el.select('.datepicker', true).first();
19908 fillTime: function()
19910 var time = this.pop.select('tbody', true).first();
19912 time.dom.innerHTML = '';
19927 cls: 'hours-up glyphicon glyphicon-chevron-up'
19947 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19968 cls: 'timepicker-hour',
19983 cls: 'timepicker-minute',
19998 cls: 'btn btn-primary period',
20020 cls: 'hours-down glyphicon glyphicon-chevron-down'
20040 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20058 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20065 var hours = this.time.getHours();
20066 var minutes = this.time.getMinutes();
20079 hours = hours - 12;
20083 hours = '0' + hours;
20087 minutes = '0' + minutes;
20090 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20091 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20092 this.pop.select('button', true).first().dom.innerHTML = period;
20098 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20100 var cls = ['bottom'];
20102 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20109 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20114 this.picker().addClass(cls.join('-'));
20118 Roo.each(cls, function(c){
20120 _this.picker().setTop(_this.inputEl().getHeight());
20124 _this.picker().setTop(0 - _this.picker().getHeight());
20129 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20133 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20140 onFocus : function()
20142 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20146 onBlur : function()
20148 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20154 this.picker().show();
20159 this.fireEvent('show', this, this.date);
20164 this.picker().hide();
20167 this.fireEvent('hide', this, this.date);
20170 setTime : function()
20173 this.setValue(this.time.format(this.format));
20175 this.fireEvent('select', this, this.date);
20180 onMousedown: function(e){
20181 e.stopPropagation();
20182 e.preventDefault();
20185 onIncrementHours: function()
20187 Roo.log('onIncrementHours');
20188 this.time = this.time.add(Date.HOUR, 1);
20193 onDecrementHours: function()
20195 Roo.log('onDecrementHours');
20196 this.time = this.time.add(Date.HOUR, -1);
20200 onIncrementMinutes: function()
20202 Roo.log('onIncrementMinutes');
20203 this.time = this.time.add(Date.MINUTE, 1);
20207 onDecrementMinutes: function()
20209 Roo.log('onDecrementMinutes');
20210 this.time = this.time.add(Date.MINUTE, -1);
20214 onTogglePeriod: function()
20216 Roo.log('onTogglePeriod');
20217 this.time = this.time.add(Date.HOUR, 12);
20224 Roo.apply(Roo.bootstrap.TimeField, {
20254 cls: 'btn btn-info ok',
20266 Roo.apply(Roo.bootstrap.TimeField, {
20270 cls: 'datepicker dropdown-menu',
20274 cls: 'datepicker-time',
20278 cls: 'table-condensed',
20280 Roo.bootstrap.TimeField.content,
20281 Roo.bootstrap.TimeField.footer
20300 * @class Roo.bootstrap.MonthField
20301 * @extends Roo.bootstrap.Input
20302 * Bootstrap MonthField class
20304 * @cfg {String} language default en
20307 * Create a new MonthField
20308 * @param {Object} config The config object
20311 Roo.bootstrap.MonthField = function(config){
20312 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20317 * Fires when this field show.
20318 * @param {Roo.bootstrap.MonthField} this
20319 * @param {Mixed} date The date value
20324 * Fires when this field hide.
20325 * @param {Roo.bootstrap.MonthField} this
20326 * @param {Mixed} date The date value
20331 * Fires when select a date.
20332 * @param {Roo.bootstrap.MonthField} this
20333 * @param {String} oldvalue The old value
20334 * @param {String} newvalue The new value
20340 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20342 onRender: function(ct, position)
20345 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20347 this.language = this.language || 'en';
20348 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20349 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20351 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20352 this.isInline = false;
20353 this.isInput = true;
20354 this.component = this.el.select('.add-on', true).first() || false;
20355 this.component = (this.component && this.component.length === 0) ? false : this.component;
20356 this.hasInput = this.component && this.inputEL().length;
20358 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20360 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20362 this.picker().on('mousedown', this.onMousedown, this);
20363 this.picker().on('click', this.onClick, this);
20365 this.picker().addClass('datepicker-dropdown');
20367 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20368 v.setStyle('width', '189px');
20375 if(this.isInline) {
20381 setValue: function(v, suppressEvent)
20383 var o = this.getValue();
20385 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20389 if(suppressEvent !== true){
20390 this.fireEvent('select', this, o, v);
20395 getValue: function()
20400 onClick: function(e)
20402 e.stopPropagation();
20403 e.preventDefault();
20405 var target = e.getTarget();
20407 if(target.nodeName.toLowerCase() === 'i'){
20408 target = Roo.get(target).dom.parentNode;
20411 var nodeName = target.nodeName;
20412 var className = target.className;
20413 var html = target.innerHTML;
20415 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20419 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20421 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20427 picker : function()
20429 return this.pickerEl;
20432 fillMonths: function()
20435 var months = this.picker().select('>.datepicker-months td', true).first();
20437 months.dom.innerHTML = '';
20443 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20446 months.createChild(month);
20455 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20456 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20459 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20460 e.removeClass('active');
20462 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20463 e.addClass('active');
20470 if(this.isInline) {
20474 this.picker().removeClass(['bottom', 'top']);
20476 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20478 * place to the top of element!
20482 this.picker().addClass('top');
20483 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20488 this.picker().addClass('bottom');
20490 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20493 onFocus : function()
20495 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20499 onBlur : function()
20501 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20503 var d = this.inputEl().getValue();
20512 this.picker().show();
20513 this.picker().select('>.datepicker-months', true).first().show();
20517 this.fireEvent('show', this, this.date);
20522 if(this.isInline) {
20525 this.picker().hide();
20526 this.fireEvent('hide', this, this.date);
20530 onMousedown: function(e)
20532 e.stopPropagation();
20533 e.preventDefault();
20538 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20542 fireKey: function(e)
20544 if (!this.picker().isVisible()){
20545 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20556 e.preventDefault();
20560 dir = e.keyCode == 37 ? -1 : 1;
20562 this.vIndex = this.vIndex + dir;
20564 if(this.vIndex < 0){
20568 if(this.vIndex > 11){
20572 if(isNaN(this.vIndex)){
20576 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20582 dir = e.keyCode == 38 ? -1 : 1;
20584 this.vIndex = this.vIndex + dir * 4;
20586 if(this.vIndex < 0){
20590 if(this.vIndex > 11){
20594 if(isNaN(this.vIndex)){
20598 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20603 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20604 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20608 e.preventDefault();
20611 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20612 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20628 this.picker().remove();
20633 Roo.apply(Roo.bootstrap.MonthField, {
20652 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20653 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20658 Roo.apply(Roo.bootstrap.MonthField, {
20662 cls: 'datepicker dropdown-menu roo-dynamic',
20666 cls: 'datepicker-months',
20670 cls: 'table-condensed',
20672 Roo.bootstrap.DateField.content
20692 * @class Roo.bootstrap.CheckBox
20693 * @extends Roo.bootstrap.Input
20694 * Bootstrap CheckBox class
20696 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20697 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20698 * @cfg {String} boxLabel The text that appears beside the checkbox
20699 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20700 * @cfg {Boolean} checked initnal the element
20701 * @cfg {Boolean} inline inline the element (default false)
20702 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20703 * @cfg {String} tooltip label tooltip
20706 * Create a new CheckBox
20707 * @param {Object} config The config object
20710 Roo.bootstrap.CheckBox = function(config){
20711 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20716 * Fires when the element is checked or unchecked.
20717 * @param {Roo.bootstrap.CheckBox} this This input
20718 * @param {Boolean} checked The new checked value
20723 * Fires when the element is click.
20724 * @param {Roo.bootstrap.CheckBox} this This input
20731 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20733 inputType: 'checkbox',
20742 getAutoCreate : function()
20744 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20750 cfg.cls = 'form-group ' + this.inputType; //input-group
20753 cfg.cls += ' ' + this.inputType + '-inline';
20759 type : this.inputType,
20760 value : this.inputValue,
20761 cls : 'roo-' + this.inputType, //'form-box',
20762 placeholder : this.placeholder || ''
20766 if(this.inputType != 'radio'){
20770 cls : 'roo-hidden-value',
20771 value : this.checked ? this.inputValue : this.valueOff
20776 if (this.weight) { // Validity check?
20777 cfg.cls += " " + this.inputType + "-" + this.weight;
20780 if (this.disabled) {
20781 input.disabled=true;
20785 input.checked = this.checked;
20790 input.name = this.name;
20792 if(this.inputType != 'radio'){
20793 hidden.name = this.name;
20794 input.name = '_hidden_' + this.name;
20799 input.cls += ' input-' + this.size;
20804 ['xs','sm','md','lg'].map(function(size){
20805 if (settings[size]) {
20806 cfg.cls += ' col-' + size + '-' + settings[size];
20810 var inputblock = input;
20812 if (this.before || this.after) {
20815 cls : 'input-group',
20820 inputblock.cn.push({
20822 cls : 'input-group-addon',
20827 inputblock.cn.push(input);
20829 if(this.inputType != 'radio'){
20830 inputblock.cn.push(hidden);
20834 inputblock.cn.push({
20836 cls : 'input-group-addon',
20843 if (align ==='left' && this.fieldLabel.length) {
20844 // Roo.log("left and has label");
20849 cls : 'control-label',
20850 html : this.fieldLabel
20860 if(this.labelWidth > 12){
20861 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20864 if(this.labelWidth < 13 && this.labelmd == 0){
20865 this.labelmd = this.labelWidth;
20868 if(this.labellg > 0){
20869 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20870 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20873 if(this.labelmd > 0){
20874 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20875 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20878 if(this.labelsm > 0){
20879 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20880 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20883 if(this.labelxs > 0){
20884 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20885 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20888 } else if ( this.fieldLabel.length) {
20889 // Roo.log(" label");
20893 tag: this.boxLabel ? 'span' : 'label',
20895 cls: 'control-label box-input-label',
20896 //cls : 'input-group-addon',
20897 html : this.fieldLabel
20906 // Roo.log(" no label && no align");
20907 cfg.cn = [ inputblock ] ;
20913 var boxLabelCfg = {
20915 //'for': id, // box label is handled by onclick - so no for...
20917 html: this.boxLabel
20921 boxLabelCfg.tooltip = this.tooltip;
20924 cfg.cn.push(boxLabelCfg);
20927 if(this.inputType != 'radio'){
20928 cfg.cn.push(hidden);
20936 * return the real input element.
20938 inputEl: function ()
20940 return this.el.select('input.roo-' + this.inputType,true).first();
20942 hiddenEl: function ()
20944 return this.el.select('input.roo-hidden-value',true).first();
20947 labelEl: function()
20949 return this.el.select('label.control-label',true).first();
20951 /* depricated... */
20955 return this.labelEl();
20958 boxLabelEl: function()
20960 return this.el.select('label.box-label',true).first();
20963 initEvents : function()
20965 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20967 this.inputEl().on('click', this.onClick, this);
20969 if (this.boxLabel) {
20970 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20973 this.startValue = this.getValue();
20976 Roo.bootstrap.CheckBox.register(this);
20980 onClick : function(e)
20982 if(this.fireEvent('click', this, e) !== false){
20983 this.setChecked(!this.checked);
20988 setChecked : function(state,suppressEvent)
20990 this.startValue = this.getValue();
20992 if(this.inputType == 'radio'){
20994 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20995 e.dom.checked = false;
20998 this.inputEl().dom.checked = true;
21000 this.inputEl().dom.value = this.inputValue;
21002 if(suppressEvent !== true){
21003 this.fireEvent('check', this, true);
21011 this.checked = state;
21013 this.inputEl().dom.checked = state;
21016 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21018 if(suppressEvent !== true){
21019 this.fireEvent('check', this, state);
21025 getValue : function()
21027 if(this.inputType == 'radio'){
21028 return this.getGroupValue();
21031 return this.hiddenEl().dom.value;
21035 getGroupValue : function()
21037 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21041 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21044 setValue : function(v,suppressEvent)
21046 if(this.inputType == 'radio'){
21047 this.setGroupValue(v, suppressEvent);
21051 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21056 setGroupValue : function(v, suppressEvent)
21058 this.startValue = this.getValue();
21060 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21061 e.dom.checked = false;
21063 if(e.dom.value == v){
21064 e.dom.checked = true;
21068 if(suppressEvent !== true){
21069 this.fireEvent('check', this, true);
21077 validate : function()
21079 if(this.getVisibilityEl().hasClass('hidden')){
21085 (this.inputType == 'radio' && this.validateRadio()) ||
21086 (this.inputType == 'checkbox' && this.validateCheckbox())
21092 this.markInvalid();
21096 validateRadio : function()
21098 if(this.getVisibilityEl().hasClass('hidden')){
21102 if(this.allowBlank){
21108 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21109 if(!e.dom.checked){
21121 validateCheckbox : function()
21124 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21125 //return (this.getValue() == this.inputValue) ? true : false;
21128 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21136 for(var i in group){
21137 if(group[i].el.isVisible(true)){
21145 for(var i in group){
21150 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21157 * Mark this field as valid
21159 markValid : function()
21163 this.fireEvent('valid', this);
21165 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21168 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21175 if(this.inputType == 'radio'){
21176 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21177 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21178 e.findParent('.form-group', false, true).addClass(_this.validClass);
21185 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21186 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21190 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21196 for(var i in group){
21197 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21198 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21203 * Mark this field as invalid
21204 * @param {String} msg The validation message
21206 markInvalid : function(msg)
21208 if(this.allowBlank){
21214 this.fireEvent('invalid', this, msg);
21216 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21219 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21223 label.markInvalid();
21226 if(this.inputType == 'radio'){
21227 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21228 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21229 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21236 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21237 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21241 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21247 for(var i in group){
21248 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21249 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21254 clearInvalid : function()
21256 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21258 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21260 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21262 if (label && label.iconEl) {
21263 label.iconEl.removeClass(label.validClass);
21264 label.iconEl.removeClass(label.invalidClass);
21268 disable : function()
21270 if(this.inputType != 'radio'){
21271 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21278 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21279 _this.getActionEl().addClass(this.disabledClass);
21280 e.dom.disabled = true;
21284 this.disabled = true;
21285 this.fireEvent("disable", this);
21289 enable : function()
21291 if(this.inputType != 'radio'){
21292 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21299 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21300 _this.getActionEl().removeClass(this.disabledClass);
21301 e.dom.disabled = false;
21305 this.disabled = false;
21306 this.fireEvent("enable", this);
21310 setBoxLabel : function(v)
21315 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21321 Roo.apply(Roo.bootstrap.CheckBox, {
21326 * register a CheckBox Group
21327 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21329 register : function(checkbox)
21331 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21332 this.groups[checkbox.groupId] = {};
21335 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21339 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21343 * fetch a CheckBox Group based on the group ID
21344 * @param {string} the group ID
21345 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21347 get: function(groupId) {
21348 if (typeof(this.groups[groupId]) == 'undefined') {
21352 return this.groups[groupId] ;
21365 * @class Roo.bootstrap.Radio
21366 * @extends Roo.bootstrap.Component
21367 * Bootstrap Radio class
21368 * @cfg {String} boxLabel - the label associated
21369 * @cfg {String} value - the value of radio
21372 * Create a new Radio
21373 * @param {Object} config The config object
21375 Roo.bootstrap.Radio = function(config){
21376 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21380 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21386 getAutoCreate : function()
21390 cls : 'form-group radio',
21395 html : this.boxLabel
21403 initEvents : function()
21405 this.parent().register(this);
21407 this.el.on('click', this.onClick, this);
21411 onClick : function(e)
21413 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21414 this.setChecked(true);
21418 setChecked : function(state, suppressEvent)
21420 this.parent().setValue(this.value, suppressEvent);
21424 setBoxLabel : function(v)
21429 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21444 * @class Roo.bootstrap.SecurePass
21445 * @extends Roo.bootstrap.Input
21446 * Bootstrap SecurePass class
21450 * Create a new SecurePass
21451 * @param {Object} config The config object
21454 Roo.bootstrap.SecurePass = function (config) {
21455 // these go here, so the translation tool can replace them..
21457 PwdEmpty: "Please type a password, and then retype it to confirm.",
21458 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21459 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21460 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21461 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21462 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21463 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21464 TooWeak: "Your password is Too Weak."
21466 this.meterLabel = "Password strength:";
21467 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21468 this.meterClass = [
21469 "roo-password-meter-tooweak",
21470 "roo-password-meter-weak",
21471 "roo-password-meter-medium",
21472 "roo-password-meter-strong",
21473 "roo-password-meter-grey"
21478 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21481 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21483 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21485 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21486 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21487 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21488 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21489 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21490 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21491 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21501 * @cfg {String/Object} Label for the strength meter (defaults to
21502 * 'Password strength:')
21507 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21508 * ['Weak', 'Medium', 'Strong'])
21511 pwdStrengths: false,
21524 initEvents: function ()
21526 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21528 if (this.el.is('input[type=password]') && Roo.isSafari) {
21529 this.el.on('keydown', this.SafariOnKeyDown, this);
21532 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21535 onRender: function (ct, position)
21537 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21538 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21539 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21541 this.trigger.createChild({
21546 cls: 'roo-password-meter-grey col-xs-12',
21549 //width: this.meterWidth + 'px'
21553 cls: 'roo-password-meter-text'
21559 if (this.hideTrigger) {
21560 this.trigger.setDisplayed(false);
21562 this.setSize(this.width || '', this.height || '');
21565 onDestroy: function ()
21567 if (this.trigger) {
21568 this.trigger.removeAllListeners();
21569 this.trigger.remove();
21572 this.wrap.remove();
21574 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21577 checkStrength: function ()
21579 var pwd = this.inputEl().getValue();
21580 if (pwd == this._lastPwd) {
21585 if (this.ClientSideStrongPassword(pwd)) {
21587 } else if (this.ClientSideMediumPassword(pwd)) {
21589 } else if (this.ClientSideWeakPassword(pwd)) {
21595 Roo.log('strength1: ' + strength);
21597 //var pm = this.trigger.child('div/div/div').dom;
21598 var pm = this.trigger.child('div/div');
21599 pm.removeClass(this.meterClass);
21600 pm.addClass(this.meterClass[strength]);
21603 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21605 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21607 this._lastPwd = pwd;
21611 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21613 this._lastPwd = '';
21615 var pm = this.trigger.child('div/div');
21616 pm.removeClass(this.meterClass);
21617 pm.addClass('roo-password-meter-grey');
21620 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21623 this.inputEl().dom.type='password';
21626 validateValue: function (value)
21629 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21632 if (value.length == 0) {
21633 if (this.allowBlank) {
21634 this.clearInvalid();
21638 this.markInvalid(this.errors.PwdEmpty);
21639 this.errorMsg = this.errors.PwdEmpty;
21647 if ('[\x21-\x7e]*'.match(value)) {
21648 this.markInvalid(this.errors.PwdBadChar);
21649 this.errorMsg = this.errors.PwdBadChar;
21652 if (value.length < 6) {
21653 this.markInvalid(this.errors.PwdShort);
21654 this.errorMsg = this.errors.PwdShort;
21657 if (value.length > 16) {
21658 this.markInvalid(this.errors.PwdLong);
21659 this.errorMsg = this.errors.PwdLong;
21663 if (this.ClientSideStrongPassword(value)) {
21665 } else if (this.ClientSideMediumPassword(value)) {
21667 } else if (this.ClientSideWeakPassword(value)) {
21674 if (strength < 2) {
21675 //this.markInvalid(this.errors.TooWeak);
21676 this.errorMsg = this.errors.TooWeak;
21681 console.log('strength2: ' + strength);
21683 //var pm = this.trigger.child('div/div/div').dom;
21685 var pm = this.trigger.child('div/div');
21686 pm.removeClass(this.meterClass);
21687 pm.addClass(this.meterClass[strength]);
21689 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21691 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21693 this.errorMsg = '';
21697 CharacterSetChecks: function (type)
21700 this.fResult = false;
21703 isctype: function (character, type)
21706 case this.kCapitalLetter:
21707 if (character >= 'A' && character <= 'Z') {
21712 case this.kSmallLetter:
21713 if (character >= 'a' && character <= 'z') {
21719 if (character >= '0' && character <= '9') {
21724 case this.kPunctuation:
21725 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21736 IsLongEnough: function (pwd, size)
21738 return !(pwd == null || isNaN(size) || pwd.length < size);
21741 SpansEnoughCharacterSets: function (word, nb)
21743 if (!this.IsLongEnough(word, nb))
21748 var characterSetChecks = new Array(
21749 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21750 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21753 for (var index = 0; index < word.length; ++index) {
21754 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21755 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21756 characterSetChecks[nCharSet].fResult = true;
21763 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21764 if (characterSetChecks[nCharSet].fResult) {
21769 if (nCharSets < nb) {
21775 ClientSideStrongPassword: function (pwd)
21777 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21780 ClientSideMediumPassword: function (pwd)
21782 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21785 ClientSideWeakPassword: function (pwd)
21787 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21790 })//<script type="text/javascript">
21793 * Based Ext JS Library 1.1.1
21794 * Copyright(c) 2006-2007, Ext JS, LLC.
21800 * @class Roo.HtmlEditorCore
21801 * @extends Roo.Component
21802 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21804 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21807 Roo.HtmlEditorCore = function(config){
21810 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21815 * @event initialize
21816 * Fires when the editor is fully initialized (including the iframe)
21817 * @param {Roo.HtmlEditorCore} this
21822 * Fires when the editor is first receives the focus. Any insertion must wait
21823 * until after this event.
21824 * @param {Roo.HtmlEditorCore} this
21828 * @event beforesync
21829 * Fires before the textarea is updated with content from the editor iframe. Return false
21830 * to cancel the sync.
21831 * @param {Roo.HtmlEditorCore} this
21832 * @param {String} html
21836 * @event beforepush
21837 * Fires before the iframe editor is updated with content from the textarea. Return false
21838 * to cancel the push.
21839 * @param {Roo.HtmlEditorCore} this
21840 * @param {String} html
21845 * Fires when the textarea is updated with content from the editor iframe.
21846 * @param {Roo.HtmlEditorCore} this
21847 * @param {String} html
21852 * Fires when the iframe editor is updated with content from the textarea.
21853 * @param {Roo.HtmlEditorCore} this
21854 * @param {String} html
21859 * @event editorevent
21860 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21861 * @param {Roo.HtmlEditorCore} this
21867 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21869 // defaults : white / black...
21870 this.applyBlacklists();
21877 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21881 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21887 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21892 * @cfg {Number} height (in pixels)
21896 * @cfg {Number} width (in pixels)
21901 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21904 stylesheets: false,
21909 // private properties
21910 validationEvent : false,
21912 initialized : false,
21914 sourceEditMode : false,
21915 onFocus : Roo.emptyFn,
21917 hideMode:'offsets',
21921 // blacklist + whitelisted elements..
21928 * Protected method that will not generally be called directly. It
21929 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21930 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21932 getDocMarkup : function(){
21936 // inherit styels from page...??
21937 if (this.stylesheets === false) {
21939 Roo.get(document.head).select('style').each(function(node) {
21940 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21943 Roo.get(document.head).select('link').each(function(node) {
21944 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21947 } else if (!this.stylesheets.length) {
21949 st = '<style type="text/css">' +
21950 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21953 st = '<style type="text/css">' +
21958 st += '<style type="text/css">' +
21959 'IMG { cursor: pointer } ' +
21962 var cls = 'roo-htmleditor-body';
21964 if(this.bodyCls.length){
21965 cls += ' ' + this.bodyCls;
21968 return '<html><head>' + st +
21969 //<style type="text/css">' +
21970 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21972 ' </head><body class="' + cls + '"></body></html>';
21976 onRender : function(ct, position)
21979 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21980 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21983 this.el.dom.style.border = '0 none';
21984 this.el.dom.setAttribute('tabIndex', -1);
21985 this.el.addClass('x-hidden hide');
21989 if(Roo.isIE){ // fix IE 1px bogus margin
21990 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21994 this.frameId = Roo.id();
21998 var iframe = this.owner.wrap.createChild({
22000 cls: 'form-control', // bootstrap..
22002 name: this.frameId,
22003 frameBorder : 'no',
22004 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
22009 this.iframe = iframe.dom;
22011 this.assignDocWin();
22013 this.doc.designMode = 'on';
22016 this.doc.write(this.getDocMarkup());
22020 var task = { // must defer to wait for browser to be ready
22022 //console.log("run task?" + this.doc.readyState);
22023 this.assignDocWin();
22024 if(this.doc.body || this.doc.readyState == 'complete'){
22026 this.doc.designMode="on";
22030 Roo.TaskMgr.stop(task);
22031 this.initEditor.defer(10, this);
22038 Roo.TaskMgr.start(task);
22043 onResize : function(w, h)
22045 Roo.log('resize: ' +w + ',' + h );
22046 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22050 if(typeof w == 'number'){
22052 this.iframe.style.width = w + 'px';
22054 if(typeof h == 'number'){
22056 this.iframe.style.height = h + 'px';
22058 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22065 * Toggles the editor between standard and source edit mode.
22066 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22068 toggleSourceEdit : function(sourceEditMode){
22070 this.sourceEditMode = sourceEditMode === true;
22072 if(this.sourceEditMode){
22074 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22077 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22078 //this.iframe.className = '';
22081 //this.setSize(this.owner.wrap.getSize());
22082 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22089 * Protected method that will not generally be called directly. If you need/want
22090 * custom HTML cleanup, this is the method you should override.
22091 * @param {String} html The HTML to be cleaned
22092 * return {String} The cleaned HTML
22094 cleanHtml : function(html){
22095 html = String(html);
22096 if(html.length > 5){
22097 if(Roo.isSafari){ // strip safari nonsense
22098 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22101 if(html == ' '){
22108 * HTML Editor -> Textarea
22109 * Protected method that will not generally be called directly. Syncs the contents
22110 * of the editor iframe with the textarea.
22112 syncValue : function(){
22113 if(this.initialized){
22114 var bd = (this.doc.body || this.doc.documentElement);
22115 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22116 var html = bd.innerHTML;
22118 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22119 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22121 html = '<div style="'+m[0]+'">' + html + '</div>';
22124 html = this.cleanHtml(html);
22125 // fix up the special chars.. normaly like back quotes in word...
22126 // however we do not want to do this with chinese..
22127 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22128 var cc = b.charCodeAt();
22130 (cc >= 0x4E00 && cc < 0xA000 ) ||
22131 (cc >= 0x3400 && cc < 0x4E00 ) ||
22132 (cc >= 0xf900 && cc < 0xfb00 )
22138 if(this.owner.fireEvent('beforesync', this, html) !== false){
22139 this.el.dom.value = html;
22140 this.owner.fireEvent('sync', this, html);
22146 * Protected method that will not generally be called directly. Pushes the value of the textarea
22147 * into the iframe editor.
22149 pushValue : function(){
22150 if(this.initialized){
22151 var v = this.el.dom.value.trim();
22153 // if(v.length < 1){
22157 if(this.owner.fireEvent('beforepush', this, v) !== false){
22158 var d = (this.doc.body || this.doc.documentElement);
22160 this.cleanUpPaste();
22161 this.el.dom.value = d.innerHTML;
22162 this.owner.fireEvent('push', this, v);
22168 deferFocus : function(){
22169 this.focus.defer(10, this);
22173 focus : function(){
22174 if(this.win && !this.sourceEditMode){
22181 assignDocWin: function()
22183 var iframe = this.iframe;
22186 this.doc = iframe.contentWindow.document;
22187 this.win = iframe.contentWindow;
22189 // if (!Roo.get(this.frameId)) {
22192 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22193 // this.win = Roo.get(this.frameId).dom.contentWindow;
22195 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22199 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22200 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22205 initEditor : function(){
22206 //console.log("INIT EDITOR");
22207 this.assignDocWin();
22211 this.doc.designMode="on";
22213 this.doc.write(this.getDocMarkup());
22216 var dbody = (this.doc.body || this.doc.documentElement);
22217 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22218 // this copies styles from the containing element into thsi one..
22219 // not sure why we need all of this..
22220 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22222 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22223 //ss['background-attachment'] = 'fixed'; // w3c
22224 dbody.bgProperties = 'fixed'; // ie
22225 //Roo.DomHelper.applyStyles(dbody, ss);
22226 Roo.EventManager.on(this.doc, {
22227 //'mousedown': this.onEditorEvent,
22228 'mouseup': this.onEditorEvent,
22229 'dblclick': this.onEditorEvent,
22230 'click': this.onEditorEvent,
22231 'keyup': this.onEditorEvent,
22236 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22238 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22239 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22241 this.initialized = true;
22243 this.owner.fireEvent('initialize', this);
22248 onDestroy : function(){
22254 //for (var i =0; i < this.toolbars.length;i++) {
22255 // // fixme - ask toolbars for heights?
22256 // this.toolbars[i].onDestroy();
22259 //this.wrap.dom.innerHTML = '';
22260 //this.wrap.remove();
22265 onFirstFocus : function(){
22267 this.assignDocWin();
22270 this.activated = true;
22273 if(Roo.isGecko){ // prevent silly gecko errors
22275 var s = this.win.getSelection();
22276 if(!s.focusNode || s.focusNode.nodeType != 3){
22277 var r = s.getRangeAt(0);
22278 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22283 this.execCmd('useCSS', true);
22284 this.execCmd('styleWithCSS', false);
22287 this.owner.fireEvent('activate', this);
22291 adjustFont: function(btn){
22292 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22293 //if(Roo.isSafari){ // safari
22296 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22297 if(Roo.isSafari){ // safari
22298 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22299 v = (v < 10) ? 10 : v;
22300 v = (v > 48) ? 48 : v;
22301 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22306 v = Math.max(1, v+adjust);
22308 this.execCmd('FontSize', v );
22311 onEditorEvent : function(e)
22313 this.owner.fireEvent('editorevent', this, e);
22314 // this.updateToolbar();
22315 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22318 insertTag : function(tg)
22320 // could be a bit smarter... -> wrap the current selected tRoo..
22321 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22323 range = this.createRange(this.getSelection());
22324 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22325 wrappingNode.appendChild(range.extractContents());
22326 range.insertNode(wrappingNode);
22333 this.execCmd("formatblock", tg);
22337 insertText : function(txt)
22341 var range = this.createRange();
22342 range.deleteContents();
22343 //alert(Sender.getAttribute('label'));
22345 range.insertNode(this.doc.createTextNode(txt));
22351 * Executes a Midas editor command on the editor document and performs necessary focus and
22352 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22353 * @param {String} cmd The Midas command
22354 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22356 relayCmd : function(cmd, value){
22358 this.execCmd(cmd, value);
22359 this.owner.fireEvent('editorevent', this);
22360 //this.updateToolbar();
22361 this.owner.deferFocus();
22365 * Executes a Midas editor command directly on the editor document.
22366 * For visual commands, you should use {@link #relayCmd} instead.
22367 * <b>This should only be called after the editor is initialized.</b>
22368 * @param {String} cmd The Midas command
22369 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22371 execCmd : function(cmd, value){
22372 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22379 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22381 * @param {String} text | dom node..
22383 insertAtCursor : function(text)
22386 if(!this.activated){
22392 var r = this.doc.selection.createRange();
22403 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22407 // from jquery ui (MIT licenced)
22409 var win = this.win;
22411 if (win.getSelection && win.getSelection().getRangeAt) {
22412 range = win.getSelection().getRangeAt(0);
22413 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22414 range.insertNode(node);
22415 } else if (win.document.selection && win.document.selection.createRange) {
22416 // no firefox support
22417 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22418 win.document.selection.createRange().pasteHTML(txt);
22420 // no firefox support
22421 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22422 this.execCmd('InsertHTML', txt);
22431 mozKeyPress : function(e){
22433 var c = e.getCharCode(), cmd;
22436 c = String.fromCharCode(c).toLowerCase();
22450 this.cleanUpPaste.defer(100, this);
22458 e.preventDefault();
22466 fixKeys : function(){ // load time branching for fastest keydown performance
22468 return function(e){
22469 var k = e.getKey(), r;
22472 r = this.doc.selection.createRange();
22475 r.pasteHTML('    ');
22482 r = this.doc.selection.createRange();
22484 var target = r.parentElement();
22485 if(!target || target.tagName.toLowerCase() != 'li'){
22487 r.pasteHTML('<br />');
22493 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22494 this.cleanUpPaste.defer(100, this);
22500 }else if(Roo.isOpera){
22501 return function(e){
22502 var k = e.getKey();
22506 this.execCmd('InsertHTML','    ');
22509 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22510 this.cleanUpPaste.defer(100, this);
22515 }else if(Roo.isSafari){
22516 return function(e){
22517 var k = e.getKey();
22521 this.execCmd('InsertText','\t');
22525 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22526 this.cleanUpPaste.defer(100, this);
22534 getAllAncestors: function()
22536 var p = this.getSelectedNode();
22539 a.push(p); // push blank onto stack..
22540 p = this.getParentElement();
22544 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22548 a.push(this.doc.body);
22552 lastSelNode : false,
22555 getSelection : function()
22557 this.assignDocWin();
22558 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22561 getSelectedNode: function()
22563 // this may only work on Gecko!!!
22565 // should we cache this!!!!
22570 var range = this.createRange(this.getSelection()).cloneRange();
22573 var parent = range.parentElement();
22575 var testRange = range.duplicate();
22576 testRange.moveToElementText(parent);
22577 if (testRange.inRange(range)) {
22580 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22583 parent = parent.parentElement;
22588 // is ancestor a text element.
22589 var ac = range.commonAncestorContainer;
22590 if (ac.nodeType == 3) {
22591 ac = ac.parentNode;
22594 var ar = ac.childNodes;
22597 var other_nodes = [];
22598 var has_other_nodes = false;
22599 for (var i=0;i<ar.length;i++) {
22600 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22603 // fullly contained node.
22605 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22610 // probably selected..
22611 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22612 other_nodes.push(ar[i]);
22616 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22621 has_other_nodes = true;
22623 if (!nodes.length && other_nodes.length) {
22624 nodes= other_nodes;
22626 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22632 createRange: function(sel)
22634 // this has strange effects when using with
22635 // top toolbar - not sure if it's a great idea.
22636 //this.editor.contentWindow.focus();
22637 if (typeof sel != "undefined") {
22639 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22641 return this.doc.createRange();
22644 return this.doc.createRange();
22647 getParentElement: function()
22650 this.assignDocWin();
22651 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22653 var range = this.createRange(sel);
22656 var p = range.commonAncestorContainer;
22657 while (p.nodeType == 3) { // text node
22668 * Range intersection.. the hard stuff...
22672 * [ -- selected range --- ]
22676 * if end is before start or hits it. fail.
22677 * if start is after end or hits it fail.
22679 * if either hits (but other is outside. - then it's not
22685 // @see http://www.thismuchiknow.co.uk/?p=64.
22686 rangeIntersectsNode : function(range, node)
22688 var nodeRange = node.ownerDocument.createRange();
22690 nodeRange.selectNode(node);
22692 nodeRange.selectNodeContents(node);
22695 var rangeStartRange = range.cloneRange();
22696 rangeStartRange.collapse(true);
22698 var rangeEndRange = range.cloneRange();
22699 rangeEndRange.collapse(false);
22701 var nodeStartRange = nodeRange.cloneRange();
22702 nodeStartRange.collapse(true);
22704 var nodeEndRange = nodeRange.cloneRange();
22705 nodeEndRange.collapse(false);
22707 return rangeStartRange.compareBoundaryPoints(
22708 Range.START_TO_START, nodeEndRange) == -1 &&
22709 rangeEndRange.compareBoundaryPoints(
22710 Range.START_TO_START, nodeStartRange) == 1;
22714 rangeCompareNode : function(range, node)
22716 var nodeRange = node.ownerDocument.createRange();
22718 nodeRange.selectNode(node);
22720 nodeRange.selectNodeContents(node);
22724 range.collapse(true);
22726 nodeRange.collapse(true);
22728 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22729 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22731 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22733 var nodeIsBefore = ss == 1;
22734 var nodeIsAfter = ee == -1;
22736 if (nodeIsBefore && nodeIsAfter) {
22739 if (!nodeIsBefore && nodeIsAfter) {
22740 return 1; //right trailed.
22743 if (nodeIsBefore && !nodeIsAfter) {
22744 return 2; // left trailed.
22750 // private? - in a new class?
22751 cleanUpPaste : function()
22753 // cleans up the whole document..
22754 Roo.log('cleanuppaste');
22756 this.cleanUpChildren(this.doc.body);
22757 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22758 if (clean != this.doc.body.innerHTML) {
22759 this.doc.body.innerHTML = clean;
22764 cleanWordChars : function(input) {// change the chars to hex code
22765 var he = Roo.HtmlEditorCore;
22767 var output = input;
22768 Roo.each(he.swapCodes, function(sw) {
22769 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22771 output = output.replace(swapper, sw[1]);
22778 cleanUpChildren : function (n)
22780 if (!n.childNodes.length) {
22783 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22784 this.cleanUpChild(n.childNodes[i]);
22791 cleanUpChild : function (node)
22794 //console.log(node);
22795 if (node.nodeName == "#text") {
22796 // clean up silly Windows -- stuff?
22799 if (node.nodeName == "#comment") {
22800 node.parentNode.removeChild(node);
22801 // clean up silly Windows -- stuff?
22804 var lcname = node.tagName.toLowerCase();
22805 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22806 // whitelist of tags..
22808 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22810 node.parentNode.removeChild(node);
22815 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22817 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22818 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22820 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22821 // remove_keep_children = true;
22824 if (remove_keep_children) {
22825 this.cleanUpChildren(node);
22826 // inserts everything just before this node...
22827 while (node.childNodes.length) {
22828 var cn = node.childNodes[0];
22829 node.removeChild(cn);
22830 node.parentNode.insertBefore(cn, node);
22832 node.parentNode.removeChild(node);
22836 if (!node.attributes || !node.attributes.length) {
22837 this.cleanUpChildren(node);
22841 function cleanAttr(n,v)
22844 if (v.match(/^\./) || v.match(/^\//)) {
22847 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22850 if (v.match(/^#/)) {
22853 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22854 node.removeAttribute(n);
22858 var cwhite = this.cwhite;
22859 var cblack = this.cblack;
22861 function cleanStyle(n,v)
22863 if (v.match(/expression/)) { //XSS?? should we even bother..
22864 node.removeAttribute(n);
22868 var parts = v.split(/;/);
22871 Roo.each(parts, function(p) {
22872 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22876 var l = p.split(':').shift().replace(/\s+/g,'');
22877 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22879 if ( cwhite.length && cblack.indexOf(l) > -1) {
22880 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22881 //node.removeAttribute(n);
22885 // only allow 'c whitelisted system attributes'
22886 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22887 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22888 //node.removeAttribute(n);
22898 if (clean.length) {
22899 node.setAttribute(n, clean.join(';'));
22901 node.removeAttribute(n);
22907 for (var i = node.attributes.length-1; i > -1 ; i--) {
22908 var a = node.attributes[i];
22911 if (a.name.toLowerCase().substr(0,2)=='on') {
22912 node.removeAttribute(a.name);
22915 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22916 node.removeAttribute(a.name);
22919 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22920 cleanAttr(a.name,a.value); // fixme..
22923 if (a.name == 'style') {
22924 cleanStyle(a.name,a.value);
22927 /// clean up MS crap..
22928 // tecnically this should be a list of valid class'es..
22931 if (a.name == 'class') {
22932 if (a.value.match(/^Mso/)) {
22933 node.className = '';
22936 if (a.value.match(/^body$/)) {
22937 node.className = '';
22948 this.cleanUpChildren(node);
22954 * Clean up MS wordisms...
22956 cleanWord : function(node)
22961 this.cleanWord(this.doc.body);
22964 if (node.nodeName == "#text") {
22965 // clean up silly Windows -- stuff?
22968 if (node.nodeName == "#comment") {
22969 node.parentNode.removeChild(node);
22970 // clean up silly Windows -- stuff?
22974 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22975 node.parentNode.removeChild(node);
22979 // remove - but keep children..
22980 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22981 while (node.childNodes.length) {
22982 var cn = node.childNodes[0];
22983 node.removeChild(cn);
22984 node.parentNode.insertBefore(cn, node);
22986 node.parentNode.removeChild(node);
22987 this.iterateChildren(node, this.cleanWord);
22991 if (node.className.length) {
22993 var cn = node.className.split(/\W+/);
22995 Roo.each(cn, function(cls) {
22996 if (cls.match(/Mso[a-zA-Z]+/)) {
23001 node.className = cna.length ? cna.join(' ') : '';
23003 node.removeAttribute("class");
23007 if (node.hasAttribute("lang")) {
23008 node.removeAttribute("lang");
23011 if (node.hasAttribute("style")) {
23013 var styles = node.getAttribute("style").split(";");
23015 Roo.each(styles, function(s) {
23016 if (!s.match(/:/)) {
23019 var kv = s.split(":");
23020 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23023 // what ever is left... we allow.
23026 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23027 if (!nstyle.length) {
23028 node.removeAttribute('style');
23031 this.iterateChildren(node, this.cleanWord);
23037 * iterateChildren of a Node, calling fn each time, using this as the scole..
23038 * @param {DomNode} node node to iterate children of.
23039 * @param {Function} fn method of this class to call on each item.
23041 iterateChildren : function(node, fn)
23043 if (!node.childNodes.length) {
23046 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23047 fn.call(this, node.childNodes[i])
23053 * cleanTableWidths.
23055 * Quite often pasting from word etc.. results in tables with column and widths.
23056 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23059 cleanTableWidths : function(node)
23064 this.cleanTableWidths(this.doc.body);
23069 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23072 Roo.log(node.tagName);
23073 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23074 this.iterateChildren(node, this.cleanTableWidths);
23077 if (node.hasAttribute('width')) {
23078 node.removeAttribute('width');
23082 if (node.hasAttribute("style")) {
23085 var styles = node.getAttribute("style").split(";");
23087 Roo.each(styles, function(s) {
23088 if (!s.match(/:/)) {
23091 var kv = s.split(":");
23092 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23095 // what ever is left... we allow.
23098 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23099 if (!nstyle.length) {
23100 node.removeAttribute('style');
23104 this.iterateChildren(node, this.cleanTableWidths);
23112 domToHTML : function(currentElement, depth, nopadtext) {
23114 depth = depth || 0;
23115 nopadtext = nopadtext || false;
23117 if (!currentElement) {
23118 return this.domToHTML(this.doc.body);
23121 //Roo.log(currentElement);
23123 var allText = false;
23124 var nodeName = currentElement.nodeName;
23125 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23127 if (nodeName == '#text') {
23129 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23134 if (nodeName != 'BODY') {
23137 // Prints the node tagName, such as <A>, <IMG>, etc
23140 for(i = 0; i < currentElement.attributes.length;i++) {
23142 var aname = currentElement.attributes.item(i).name;
23143 if (!currentElement.attributes.item(i).value.length) {
23146 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23149 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23158 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23161 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23166 // Traverse the tree
23168 var currentElementChild = currentElement.childNodes.item(i);
23169 var allText = true;
23170 var innerHTML = '';
23172 while (currentElementChild) {
23173 // Formatting code (indent the tree so it looks nice on the screen)
23174 var nopad = nopadtext;
23175 if (lastnode == 'SPAN') {
23179 if (currentElementChild.nodeName == '#text') {
23180 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23181 toadd = nopadtext ? toadd : toadd.trim();
23182 if (!nopad && toadd.length > 80) {
23183 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23185 innerHTML += toadd;
23188 currentElementChild = currentElement.childNodes.item(i);
23194 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23196 // Recursively traverse the tree structure of the child node
23197 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23198 lastnode = currentElementChild.nodeName;
23200 currentElementChild=currentElement.childNodes.item(i);
23206 // The remaining code is mostly for formatting the tree
23207 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23212 ret+= "</"+tagName+">";
23218 applyBlacklists : function()
23220 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23221 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23225 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23226 if (b.indexOf(tag) > -1) {
23229 this.white.push(tag);
23233 Roo.each(w, function(tag) {
23234 if (b.indexOf(tag) > -1) {
23237 if (this.white.indexOf(tag) > -1) {
23240 this.white.push(tag);
23245 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23246 if (w.indexOf(tag) > -1) {
23249 this.black.push(tag);
23253 Roo.each(b, function(tag) {
23254 if (w.indexOf(tag) > -1) {
23257 if (this.black.indexOf(tag) > -1) {
23260 this.black.push(tag);
23265 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23266 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23270 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23271 if (b.indexOf(tag) > -1) {
23274 this.cwhite.push(tag);
23278 Roo.each(w, function(tag) {
23279 if (b.indexOf(tag) > -1) {
23282 if (this.cwhite.indexOf(tag) > -1) {
23285 this.cwhite.push(tag);
23290 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23291 if (w.indexOf(tag) > -1) {
23294 this.cblack.push(tag);
23298 Roo.each(b, function(tag) {
23299 if (w.indexOf(tag) > -1) {
23302 if (this.cblack.indexOf(tag) > -1) {
23305 this.cblack.push(tag);
23310 setStylesheets : function(stylesheets)
23312 if(typeof(stylesheets) == 'string'){
23313 Roo.get(this.iframe.contentDocument.head).createChild({
23315 rel : 'stylesheet',
23324 Roo.each(stylesheets, function(s) {
23329 Roo.get(_this.iframe.contentDocument.head).createChild({
23331 rel : 'stylesheet',
23340 removeStylesheets : function()
23344 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23349 setStyle : function(style)
23351 Roo.get(this.iframe.contentDocument.head).createChild({
23360 // hide stuff that is not compatible
23374 * @event specialkey
23378 * @cfg {String} fieldClass @hide
23381 * @cfg {String} focusClass @hide
23384 * @cfg {String} autoCreate @hide
23387 * @cfg {String} inputType @hide
23390 * @cfg {String} invalidClass @hide
23393 * @cfg {String} invalidText @hide
23396 * @cfg {String} msgFx @hide
23399 * @cfg {String} validateOnBlur @hide
23403 Roo.HtmlEditorCore.white = [
23404 'area', 'br', 'img', 'input', 'hr', 'wbr',
23406 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23407 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23408 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23409 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23410 'table', 'ul', 'xmp',
23412 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23415 'dir', 'menu', 'ol', 'ul', 'dl',
23421 Roo.HtmlEditorCore.black = [
23422 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23424 'base', 'basefont', 'bgsound', 'blink', 'body',
23425 'frame', 'frameset', 'head', 'html', 'ilayer',
23426 'iframe', 'layer', 'link', 'meta', 'object',
23427 'script', 'style' ,'title', 'xml' // clean later..
23429 Roo.HtmlEditorCore.clean = [
23430 'script', 'style', 'title', 'xml'
23432 Roo.HtmlEditorCore.remove = [
23437 Roo.HtmlEditorCore.ablack = [
23441 Roo.HtmlEditorCore.aclean = [
23442 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23446 Roo.HtmlEditorCore.pwhite= [
23447 'http', 'https', 'mailto'
23450 // white listed style attributes.
23451 Roo.HtmlEditorCore.cwhite= [
23452 // 'text-align', /// default is to allow most things..
23458 // black listed style attributes.
23459 Roo.HtmlEditorCore.cblack= [
23460 // 'font-size' -- this can be set by the project
23464 Roo.HtmlEditorCore.swapCodes =[
23483 * @class Roo.bootstrap.HtmlEditor
23484 * @extends Roo.bootstrap.TextArea
23485 * Bootstrap HtmlEditor class
23488 * Create a new HtmlEditor
23489 * @param {Object} config The config object
23492 Roo.bootstrap.HtmlEditor = function(config){
23493 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23494 if (!this.toolbars) {
23495 this.toolbars = [];
23498 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23501 * @event initialize
23502 * Fires when the editor is fully initialized (including the iframe)
23503 * @param {HtmlEditor} this
23508 * Fires when the editor is first receives the focus. Any insertion must wait
23509 * until after this event.
23510 * @param {HtmlEditor} this
23514 * @event beforesync
23515 * Fires before the textarea is updated with content from the editor iframe. Return false
23516 * to cancel the sync.
23517 * @param {HtmlEditor} this
23518 * @param {String} html
23522 * @event beforepush
23523 * Fires before the iframe editor is updated with content from the textarea. Return false
23524 * to cancel the push.
23525 * @param {HtmlEditor} this
23526 * @param {String} html
23531 * Fires when the textarea is updated with content from the editor iframe.
23532 * @param {HtmlEditor} this
23533 * @param {String} html
23538 * Fires when the iframe editor is updated with content from the textarea.
23539 * @param {HtmlEditor} this
23540 * @param {String} html
23544 * @event editmodechange
23545 * Fires when the editor switches edit modes
23546 * @param {HtmlEditor} this
23547 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23549 editmodechange: true,
23551 * @event editorevent
23552 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23553 * @param {HtmlEditor} this
23557 * @event firstfocus
23558 * Fires when on first focus - needed by toolbars..
23559 * @param {HtmlEditor} this
23564 * Auto save the htmlEditor value as a file into Events
23565 * @param {HtmlEditor} this
23569 * @event savedpreview
23570 * preview the saved version of htmlEditor
23571 * @param {HtmlEditor} this
23578 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23582 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23587 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23592 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23597 * @cfg {Number} height (in pixels)
23601 * @cfg {Number} width (in pixels)
23606 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23609 stylesheets: false,
23614 // private properties
23615 validationEvent : false,
23617 initialized : false,
23620 onFocus : Roo.emptyFn,
23622 hideMode:'offsets',
23624 tbContainer : false,
23628 toolbarContainer :function() {
23629 return this.wrap.select('.x-html-editor-tb',true).first();
23633 * Protected method that will not generally be called directly. It
23634 * is called when the editor creates its toolbar. Override this method if you need to
23635 * add custom toolbar buttons.
23636 * @param {HtmlEditor} editor
23638 createToolbar : function(){
23639 Roo.log('renewing');
23640 Roo.log("create toolbars");
23642 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23643 this.toolbars[0].render(this.toolbarContainer());
23647 // if (!editor.toolbars || !editor.toolbars.length) {
23648 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23651 // for (var i =0 ; i < editor.toolbars.length;i++) {
23652 // editor.toolbars[i] = Roo.factory(
23653 // typeof(editor.toolbars[i]) == 'string' ?
23654 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23655 // Roo.bootstrap.HtmlEditor);
23656 // editor.toolbars[i].init(editor);
23662 onRender : function(ct, position)
23664 // Roo.log("Call onRender: " + this.xtype);
23666 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23668 this.wrap = this.inputEl().wrap({
23669 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23672 this.editorcore.onRender(ct, position);
23674 if (this.resizable) {
23675 this.resizeEl = new Roo.Resizable(this.wrap, {
23679 minHeight : this.height,
23680 height: this.height,
23681 handles : this.resizable,
23684 resize : function(r, w, h) {
23685 _t.onResize(w,h); // -something
23691 this.createToolbar(this);
23694 if(!this.width && this.resizable){
23695 this.setSize(this.wrap.getSize());
23697 if (this.resizeEl) {
23698 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23699 // should trigger onReize..
23705 onResize : function(w, h)
23707 Roo.log('resize: ' +w + ',' + h );
23708 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23712 if(this.inputEl() ){
23713 if(typeof w == 'number'){
23714 var aw = w - this.wrap.getFrameWidth('lr');
23715 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23718 if(typeof h == 'number'){
23719 var tbh = -11; // fixme it needs to tool bar size!
23720 for (var i =0; i < this.toolbars.length;i++) {
23721 // fixme - ask toolbars for heights?
23722 tbh += this.toolbars[i].el.getHeight();
23723 //if (this.toolbars[i].footer) {
23724 // tbh += this.toolbars[i].footer.el.getHeight();
23732 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23733 ah -= 5; // knock a few pixes off for look..
23734 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23738 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23739 this.editorcore.onResize(ew,eh);
23744 * Toggles the editor between standard and source edit mode.
23745 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23747 toggleSourceEdit : function(sourceEditMode)
23749 this.editorcore.toggleSourceEdit(sourceEditMode);
23751 if(this.editorcore.sourceEditMode){
23752 Roo.log('editor - showing textarea');
23755 // Roo.log(this.syncValue());
23757 this.inputEl().removeClass(['hide', 'x-hidden']);
23758 this.inputEl().dom.removeAttribute('tabIndex');
23759 this.inputEl().focus();
23761 Roo.log('editor - hiding textarea');
23763 // Roo.log(this.pushValue());
23766 this.inputEl().addClass(['hide', 'x-hidden']);
23767 this.inputEl().dom.setAttribute('tabIndex', -1);
23768 //this.deferFocus();
23771 if(this.resizable){
23772 this.setSize(this.wrap.getSize());
23775 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23778 // private (for BoxComponent)
23779 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23781 // private (for BoxComponent)
23782 getResizeEl : function(){
23786 // private (for BoxComponent)
23787 getPositionEl : function(){
23792 initEvents : function(){
23793 this.originalValue = this.getValue();
23797 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23800 // markInvalid : Roo.emptyFn,
23802 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23805 // clearInvalid : Roo.emptyFn,
23807 setValue : function(v){
23808 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23809 this.editorcore.pushValue();
23814 deferFocus : function(){
23815 this.focus.defer(10, this);
23819 focus : function(){
23820 this.editorcore.focus();
23826 onDestroy : function(){
23832 for (var i =0; i < this.toolbars.length;i++) {
23833 // fixme - ask toolbars for heights?
23834 this.toolbars[i].onDestroy();
23837 this.wrap.dom.innerHTML = '';
23838 this.wrap.remove();
23843 onFirstFocus : function(){
23844 //Roo.log("onFirstFocus");
23845 this.editorcore.onFirstFocus();
23846 for (var i =0; i < this.toolbars.length;i++) {
23847 this.toolbars[i].onFirstFocus();
23853 syncValue : function()
23855 this.editorcore.syncValue();
23858 pushValue : function()
23860 this.editorcore.pushValue();
23864 // hide stuff that is not compatible
23878 * @event specialkey
23882 * @cfg {String} fieldClass @hide
23885 * @cfg {String} focusClass @hide
23888 * @cfg {String} autoCreate @hide
23891 * @cfg {String} inputType @hide
23894 * @cfg {String} invalidClass @hide
23897 * @cfg {String} invalidText @hide
23900 * @cfg {String} msgFx @hide
23903 * @cfg {String} validateOnBlur @hide
23912 Roo.namespace('Roo.bootstrap.htmleditor');
23914 * @class Roo.bootstrap.HtmlEditorToolbar1
23919 new Roo.bootstrap.HtmlEditor({
23922 new Roo.bootstrap.HtmlEditorToolbar1({
23923 disable : { fonts: 1 , format: 1, ..., ... , ...],
23929 * @cfg {Object} disable List of elements to disable..
23930 * @cfg {Array} btns List of additional buttons.
23934 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23937 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23940 Roo.apply(this, config);
23942 // default disabled, based on 'good practice'..
23943 this.disable = this.disable || {};
23944 Roo.applyIf(this.disable, {
23947 specialElements : true
23949 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23951 this.editor = config.editor;
23952 this.editorcore = config.editor.editorcore;
23954 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23956 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23957 // dont call parent... till later.
23959 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23964 editorcore : false,
23969 "h1","h2","h3","h4","h5","h6",
23971 "abbr", "acronym", "address", "cite", "samp", "var",
23975 onRender : function(ct, position)
23977 // Roo.log("Call onRender: " + this.xtype);
23979 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23981 this.el.dom.style.marginBottom = '0';
23983 var editorcore = this.editorcore;
23984 var editor= this.editor;
23987 var btn = function(id,cmd , toggle, handler, html){
23989 var event = toggle ? 'toggle' : 'click';
23994 xns: Roo.bootstrap,
23998 enableToggle:toggle !== false,
24000 pressed : toggle ? false : null,
24003 a.listeners[toggle ? 'toggle' : 'click'] = function() {
24004 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
24010 // var cb_box = function...
24015 xns: Roo.bootstrap,
24020 xns: Roo.bootstrap,
24024 Roo.each(this.formats, function(f) {
24025 style.menu.items.push({
24027 xns: Roo.bootstrap,
24028 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24033 editorcore.insertTag(this.tagname);
24040 children.push(style);
24042 btn('bold',false,true);
24043 btn('italic',false,true);
24044 btn('align-left', 'justifyleft',true);
24045 btn('align-center', 'justifycenter',true);
24046 btn('align-right' , 'justifyright',true);
24047 btn('link', false, false, function(btn) {
24048 //Roo.log("create link?");
24049 var url = prompt(this.createLinkText, this.defaultLinkValue);
24050 if(url && url != 'http:/'+'/'){
24051 this.editorcore.relayCmd('createlink', url);
24054 btn('list','insertunorderedlist',true);
24055 btn('pencil', false,true, function(btn){
24057 this.toggleSourceEdit(btn.pressed);
24060 if (this.editor.btns.length > 0) {
24061 for (var i = 0; i<this.editor.btns.length; i++) {
24062 children.push(this.editor.btns[i]);
24070 xns: Roo.bootstrap,
24075 xns: Roo.bootstrap,
24080 cog.menu.items.push({
24082 xns: Roo.bootstrap,
24083 html : Clean styles,
24088 editorcore.insertTag(this.tagname);
24097 this.xtype = 'NavSimplebar';
24099 for(var i=0;i< children.length;i++) {
24101 this.buttons.add(this.addxtypeChild(children[i]));
24105 editor.on('editorevent', this.updateToolbar, this);
24107 onBtnClick : function(id)
24109 this.editorcore.relayCmd(id);
24110 this.editorcore.focus();
24114 * Protected method that will not generally be called directly. It triggers
24115 * a toolbar update by reading the markup state of the current selection in the editor.
24117 updateToolbar: function(){
24119 if(!this.editorcore.activated){
24120 this.editor.onFirstFocus(); // is this neeed?
24124 var btns = this.buttons;
24125 var doc = this.editorcore.doc;
24126 btns.get('bold').setActive(doc.queryCommandState('bold'));
24127 btns.get('italic').setActive(doc.queryCommandState('italic'));
24128 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24130 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24131 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24132 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24134 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24135 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24138 var ans = this.editorcore.getAllAncestors();
24139 if (this.formatCombo) {
24142 var store = this.formatCombo.store;
24143 this.formatCombo.setValue("");
24144 for (var i =0; i < ans.length;i++) {
24145 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24147 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24155 // hides menus... - so this cant be on a menu...
24156 Roo.bootstrap.MenuMgr.hideAll();
24158 Roo.bootstrap.MenuMgr.hideAll();
24159 //this.editorsyncValue();
24161 onFirstFocus: function() {
24162 this.buttons.each(function(item){
24166 toggleSourceEdit : function(sourceEditMode){
24169 if(sourceEditMode){
24170 Roo.log("disabling buttons");
24171 this.buttons.each( function(item){
24172 if(item.cmd != 'pencil'){
24178 Roo.log("enabling buttons");
24179 if(this.editorcore.initialized){
24180 this.buttons.each( function(item){
24186 Roo.log("calling toggole on editor");
24187 // tell the editor that it's been pressed..
24188 this.editor.toggleSourceEdit(sourceEditMode);
24198 * @class Roo.bootstrap.Table.AbstractSelectionModel
24199 * @extends Roo.util.Observable
24200 * Abstract base class for grid SelectionModels. It provides the interface that should be
24201 * implemented by descendant classes. This class should not be directly instantiated.
24204 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24205 this.locked = false;
24206 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24210 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24211 /** @ignore Called by the grid automatically. Do not call directly. */
24212 init : function(grid){
24218 * Locks the selections.
24221 this.locked = true;
24225 * Unlocks the selections.
24227 unlock : function(){
24228 this.locked = false;
24232 * Returns true if the selections are locked.
24233 * @return {Boolean}
24235 isLocked : function(){
24236 return this.locked;
24240 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24241 * @class Roo.bootstrap.Table.RowSelectionModel
24242 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24243 * It supports multiple selections and keyboard selection/navigation.
24245 * @param {Object} config
24248 Roo.bootstrap.Table.RowSelectionModel = function(config){
24249 Roo.apply(this, config);
24250 this.selections = new Roo.util.MixedCollection(false, function(o){
24255 this.lastActive = false;
24259 * @event selectionchange
24260 * Fires when the selection changes
24261 * @param {SelectionModel} this
24263 "selectionchange" : true,
24265 * @event afterselectionchange
24266 * Fires after the selection changes (eg. by key press or clicking)
24267 * @param {SelectionModel} this
24269 "afterselectionchange" : true,
24271 * @event beforerowselect
24272 * Fires when a row is selected being selected, return false to cancel.
24273 * @param {SelectionModel} this
24274 * @param {Number} rowIndex The selected index
24275 * @param {Boolean} keepExisting False if other selections will be cleared
24277 "beforerowselect" : true,
24280 * Fires when a row is selected.
24281 * @param {SelectionModel} this
24282 * @param {Number} rowIndex The selected index
24283 * @param {Roo.data.Record} r The record
24285 "rowselect" : true,
24287 * @event rowdeselect
24288 * Fires when a row is deselected.
24289 * @param {SelectionModel} this
24290 * @param {Number} rowIndex The selected index
24292 "rowdeselect" : true
24294 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24295 this.locked = false;
24298 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24300 * @cfg {Boolean} singleSelect
24301 * True to allow selection of only one row at a time (defaults to false)
24303 singleSelect : false,
24306 initEvents : function()
24309 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24310 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24311 //}else{ // allow click to work like normal
24312 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24314 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24315 this.grid.on("rowclick", this.handleMouseDown, this);
24317 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24318 "up" : function(e){
24320 this.selectPrevious(e.shiftKey);
24321 }else if(this.last !== false && this.lastActive !== false){
24322 var last = this.last;
24323 this.selectRange(this.last, this.lastActive-1);
24324 this.grid.getView().focusRow(this.lastActive);
24325 if(last !== false){
24329 this.selectFirstRow();
24331 this.fireEvent("afterselectionchange", this);
24333 "down" : function(e){
24335 this.selectNext(e.shiftKey);
24336 }else if(this.last !== false && this.lastActive !== false){
24337 var last = this.last;
24338 this.selectRange(this.last, this.lastActive+1);
24339 this.grid.getView().focusRow(this.lastActive);
24340 if(last !== false){
24344 this.selectFirstRow();
24346 this.fireEvent("afterselectionchange", this);
24350 this.grid.store.on('load', function(){
24351 this.selections.clear();
24354 var view = this.grid.view;
24355 view.on("refresh", this.onRefresh, this);
24356 view.on("rowupdated", this.onRowUpdated, this);
24357 view.on("rowremoved", this.onRemove, this);
24362 onRefresh : function()
24364 var ds = this.grid.store, i, v = this.grid.view;
24365 var s = this.selections;
24366 s.each(function(r){
24367 if((i = ds.indexOfId(r.id)) != -1){
24376 onRemove : function(v, index, r){
24377 this.selections.remove(r);
24381 onRowUpdated : function(v, index, r){
24382 if(this.isSelected(r)){
24383 v.onRowSelect(index);
24389 * @param {Array} records The records to select
24390 * @param {Boolean} keepExisting (optional) True to keep existing selections
24392 selectRecords : function(records, keepExisting)
24395 this.clearSelections();
24397 var ds = this.grid.store;
24398 for(var i = 0, len = records.length; i < len; i++){
24399 this.selectRow(ds.indexOf(records[i]), true);
24404 * Gets the number of selected rows.
24407 getCount : function(){
24408 return this.selections.length;
24412 * Selects the first row in the grid.
24414 selectFirstRow : function(){
24419 * Select the last row.
24420 * @param {Boolean} keepExisting (optional) True to keep existing selections
24422 selectLastRow : function(keepExisting){
24423 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24424 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24428 * Selects the row immediately following the last selected row.
24429 * @param {Boolean} keepExisting (optional) True to keep existing selections
24431 selectNext : function(keepExisting)
24433 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24434 this.selectRow(this.last+1, keepExisting);
24435 this.grid.getView().focusRow(this.last);
24440 * Selects the row that precedes the last selected row.
24441 * @param {Boolean} keepExisting (optional) True to keep existing selections
24443 selectPrevious : function(keepExisting){
24445 this.selectRow(this.last-1, keepExisting);
24446 this.grid.getView().focusRow(this.last);
24451 * Returns the selected records
24452 * @return {Array} Array of selected records
24454 getSelections : function(){
24455 return [].concat(this.selections.items);
24459 * Returns the first selected record.
24462 getSelected : function(){
24463 return this.selections.itemAt(0);
24468 * Clears all selections.
24470 clearSelections : function(fast)
24476 var ds = this.grid.store;
24477 var s = this.selections;
24478 s.each(function(r){
24479 this.deselectRow(ds.indexOfId(r.id));
24483 this.selections.clear();
24490 * Selects all rows.
24492 selectAll : function(){
24496 this.selections.clear();
24497 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24498 this.selectRow(i, true);
24503 * Returns True if there is a selection.
24504 * @return {Boolean}
24506 hasSelection : function(){
24507 return this.selections.length > 0;
24511 * Returns True if the specified row is selected.
24512 * @param {Number/Record} record The record or index of the record to check
24513 * @return {Boolean}
24515 isSelected : function(index){
24516 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24517 return (r && this.selections.key(r.id) ? true : false);
24521 * Returns True if the specified record id is selected.
24522 * @param {String} id The id of record to check
24523 * @return {Boolean}
24525 isIdSelected : function(id){
24526 return (this.selections.key(id) ? true : false);
24531 handleMouseDBClick : function(e, t){
24535 handleMouseDown : function(e, t)
24537 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24538 if(this.isLocked() || rowIndex < 0 ){
24541 if(e.shiftKey && this.last !== false){
24542 var last = this.last;
24543 this.selectRange(last, rowIndex, e.ctrlKey);
24544 this.last = last; // reset the last
24548 var isSelected = this.isSelected(rowIndex);
24549 //Roo.log("select row:" + rowIndex);
24551 this.deselectRow(rowIndex);
24553 this.selectRow(rowIndex, true);
24557 if(e.button !== 0 && isSelected){
24558 alert('rowIndex 2: ' + rowIndex);
24559 view.focusRow(rowIndex);
24560 }else if(e.ctrlKey && isSelected){
24561 this.deselectRow(rowIndex);
24562 }else if(!isSelected){
24563 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24564 view.focusRow(rowIndex);
24568 this.fireEvent("afterselectionchange", this);
24571 handleDragableRowClick : function(grid, rowIndex, e)
24573 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24574 this.selectRow(rowIndex, false);
24575 grid.view.focusRow(rowIndex);
24576 this.fireEvent("afterselectionchange", this);
24581 * Selects multiple rows.
24582 * @param {Array} rows Array of the indexes of the row to select
24583 * @param {Boolean} keepExisting (optional) True to keep existing selections
24585 selectRows : function(rows, keepExisting){
24587 this.clearSelections();
24589 for(var i = 0, len = rows.length; i < len; i++){
24590 this.selectRow(rows[i], true);
24595 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24596 * @param {Number} startRow The index of the first row in the range
24597 * @param {Number} endRow The index of the last row in the range
24598 * @param {Boolean} keepExisting (optional) True to retain existing selections
24600 selectRange : function(startRow, endRow, keepExisting){
24605 this.clearSelections();
24607 if(startRow <= endRow){
24608 for(var i = startRow; i <= endRow; i++){
24609 this.selectRow(i, true);
24612 for(var i = startRow; i >= endRow; i--){
24613 this.selectRow(i, true);
24619 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24620 * @param {Number} startRow The index of the first row in the range
24621 * @param {Number} endRow The index of the last row in the range
24623 deselectRange : function(startRow, endRow, preventViewNotify){
24627 for(var i = startRow; i <= endRow; i++){
24628 this.deselectRow(i, preventViewNotify);
24634 * @param {Number} row The index of the row to select
24635 * @param {Boolean} keepExisting (optional) True to keep existing selections
24637 selectRow : function(index, keepExisting, preventViewNotify)
24639 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24642 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24643 if(!keepExisting || this.singleSelect){
24644 this.clearSelections();
24647 var r = this.grid.store.getAt(index);
24648 //console.log('selectRow - record id :' + r.id);
24650 this.selections.add(r);
24651 this.last = this.lastActive = index;
24652 if(!preventViewNotify){
24653 var proxy = new Roo.Element(
24654 this.grid.getRowDom(index)
24656 proxy.addClass('bg-info info');
24658 this.fireEvent("rowselect", this, index, r);
24659 this.fireEvent("selectionchange", this);
24665 * @param {Number} row The index of the row to deselect
24667 deselectRow : function(index, preventViewNotify)
24672 if(this.last == index){
24675 if(this.lastActive == index){
24676 this.lastActive = false;
24679 var r = this.grid.store.getAt(index);
24684 this.selections.remove(r);
24685 //.console.log('deselectRow - record id :' + r.id);
24686 if(!preventViewNotify){
24688 var proxy = new Roo.Element(
24689 this.grid.getRowDom(index)
24691 proxy.removeClass('bg-info info');
24693 this.fireEvent("rowdeselect", this, index);
24694 this.fireEvent("selectionchange", this);
24698 restoreLast : function(){
24700 this.last = this._last;
24705 acceptsNav : function(row, col, cm){
24706 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24710 onEditorKey : function(field, e){
24711 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24716 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24718 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24720 }else if(k == e.ENTER && !e.ctrlKey){
24724 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24726 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24728 }else if(k == e.ESC){
24732 g.startEditing(newCell[0], newCell[1]);
24738 * Ext JS Library 1.1.1
24739 * Copyright(c) 2006-2007, Ext JS, LLC.
24741 * Originally Released Under LGPL - original licence link has changed is not relivant.
24744 * <script type="text/javascript">
24748 * @class Roo.bootstrap.PagingToolbar
24749 * @extends Roo.bootstrap.NavSimplebar
24750 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24752 * Create a new PagingToolbar
24753 * @param {Object} config The config object
24754 * @param {Roo.data.Store} store
24756 Roo.bootstrap.PagingToolbar = function(config)
24758 // old args format still supported... - xtype is prefered..
24759 // created from xtype...
24761 this.ds = config.dataSource;
24763 if (config.store && !this.ds) {
24764 this.store= Roo.factory(config.store, Roo.data);
24765 this.ds = this.store;
24766 this.ds.xmodule = this.xmodule || false;
24769 this.toolbarItems = [];
24770 if (config.items) {
24771 this.toolbarItems = config.items;
24774 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24779 this.bind(this.ds);
24782 if (Roo.bootstrap.version == 4) {
24783 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24785 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24790 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24792 * @cfg {Roo.data.Store} dataSource
24793 * The underlying data store providing the paged data
24796 * @cfg {String/HTMLElement/Element} container
24797 * container The id or element that will contain the toolbar
24800 * @cfg {Boolean} displayInfo
24801 * True to display the displayMsg (defaults to false)
24804 * @cfg {Number} pageSize
24805 * The number of records to display per page (defaults to 20)
24809 * @cfg {String} displayMsg
24810 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24812 displayMsg : 'Displaying {0} - {1} of {2}',
24814 * @cfg {String} emptyMsg
24815 * The message to display when no records are found (defaults to "No data to display")
24817 emptyMsg : 'No data to display',
24819 * Customizable piece of the default paging text (defaults to "Page")
24822 beforePageText : "Page",
24824 * Customizable piece of the default paging text (defaults to "of %0")
24827 afterPageText : "of {0}",
24829 * Customizable piece of the default paging text (defaults to "First Page")
24832 firstText : "First Page",
24834 * Customizable piece of the default paging text (defaults to "Previous Page")
24837 prevText : "Previous Page",
24839 * Customizable piece of the default paging text (defaults to "Next Page")
24842 nextText : "Next Page",
24844 * Customizable piece of the default paging text (defaults to "Last Page")
24847 lastText : "Last Page",
24849 * Customizable piece of the default paging text (defaults to "Refresh")
24852 refreshText : "Refresh",
24856 onRender : function(ct, position)
24858 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24859 this.navgroup.parentId = this.id;
24860 this.navgroup.onRender(this.el, null);
24861 // add the buttons to the navgroup
24863 if(this.displayInfo){
24864 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24865 this.displayEl = this.el.select('.x-paging-info', true).first();
24866 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24867 // this.displayEl = navel.el.select('span',true).first();
24873 Roo.each(_this.buttons, function(e){ // this might need to use render????
24874 Roo.factory(e).render(_this.el);
24878 Roo.each(_this.toolbarItems, function(e) {
24879 _this.navgroup.addItem(e);
24883 this.first = this.navgroup.addItem({
24884 tooltip: this.firstText,
24885 cls: "prev btn-outline-secondary",
24886 html : ' <i class="fa fa-step-backward"></i>',
24888 preventDefault: true,
24889 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24892 this.prev = this.navgroup.addItem({
24893 tooltip: this.prevText,
24894 cls: "prev btn-outline-secondary",
24895 html : ' <i class="fa fa-backward"></i>',
24897 preventDefault: true,
24898 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24900 //this.addSeparator();
24903 var field = this.navgroup.addItem( {
24905 cls : 'x-paging-position btn-outline-secondary',
24907 html : this.beforePageText +
24908 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24909 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24912 this.field = field.el.select('input', true).first();
24913 this.field.on("keydown", this.onPagingKeydown, this);
24914 this.field.on("focus", function(){this.dom.select();});
24917 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24918 //this.field.setHeight(18);
24919 //this.addSeparator();
24920 this.next = this.navgroup.addItem({
24921 tooltip: this.nextText,
24922 cls: "next btn-outline-secondary",
24923 html : ' <i class="fa fa-forward"></i>',
24925 preventDefault: true,
24926 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24928 this.last = this.navgroup.addItem({
24929 tooltip: this.lastText,
24930 html : ' <i class="fa fa-step-forward"></i>',
24931 cls: "next btn-outline-secondary",
24933 preventDefault: true,
24934 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24936 //this.addSeparator();
24937 this.loading = this.navgroup.addItem({
24938 tooltip: this.refreshText,
24939 cls: "btn-outline-secondary",
24940 html : ' <i class="fa fa-refresh"></i>',
24941 preventDefault: true,
24942 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24948 updateInfo : function(){
24949 if(this.displayEl){
24950 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24951 var msg = count == 0 ?
24955 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24957 this.displayEl.update(msg);
24962 onLoad : function(ds, r, o)
24964 this.cursor = o.params.start ? o.params.start : 0;
24966 var d = this.getPageData(),
24971 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24972 this.field.dom.value = ap;
24973 this.first.setDisabled(ap == 1);
24974 this.prev.setDisabled(ap == 1);
24975 this.next.setDisabled(ap == ps);
24976 this.last.setDisabled(ap == ps);
24977 this.loading.enable();
24982 getPageData : function(){
24983 var total = this.ds.getTotalCount();
24986 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24987 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24992 onLoadError : function(){
24993 this.loading.enable();
24997 onPagingKeydown : function(e){
24998 var k = e.getKey();
24999 var d = this.getPageData();
25001 var v = this.field.dom.value, pageNum;
25002 if(!v || isNaN(pageNum = parseInt(v, 10))){
25003 this.field.dom.value = d.activePage;
25006 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25007 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25010 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))
25012 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25013 this.field.dom.value = pageNum;
25014 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25017 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25019 var v = this.field.dom.value, pageNum;
25020 var increment = (e.shiftKey) ? 10 : 1;
25021 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25024 if(!v || isNaN(pageNum = parseInt(v, 10))) {
25025 this.field.dom.value = d.activePage;
25028 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25030 this.field.dom.value = parseInt(v, 10) + increment;
25031 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25032 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25039 beforeLoad : function(){
25041 this.loading.disable();
25046 onClick : function(which){
25055 ds.load({params:{start: 0, limit: this.pageSize}});
25058 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25061 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25064 var total = ds.getTotalCount();
25065 var extra = total % this.pageSize;
25066 var lastStart = extra ? (total - extra) : total-this.pageSize;
25067 ds.load({params:{start: lastStart, limit: this.pageSize}});
25070 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25076 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25077 * @param {Roo.data.Store} store The data store to unbind
25079 unbind : function(ds){
25080 ds.un("beforeload", this.beforeLoad, this);
25081 ds.un("load", this.onLoad, this);
25082 ds.un("loadexception", this.onLoadError, this);
25083 ds.un("remove", this.updateInfo, this);
25084 ds.un("add", this.updateInfo, this);
25085 this.ds = undefined;
25089 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25090 * @param {Roo.data.Store} store The data store to bind
25092 bind : function(ds){
25093 ds.on("beforeload", this.beforeLoad, this);
25094 ds.on("load", this.onLoad, this);
25095 ds.on("loadexception", this.onLoadError, this);
25096 ds.on("remove", this.updateInfo, this);
25097 ds.on("add", this.updateInfo, this);
25108 * @class Roo.bootstrap.MessageBar
25109 * @extends Roo.bootstrap.Component
25110 * Bootstrap MessageBar class
25111 * @cfg {String} html contents of the MessageBar
25112 * @cfg {String} weight (info | success | warning | danger) default info
25113 * @cfg {String} beforeClass insert the bar before the given class
25114 * @cfg {Boolean} closable (true | false) default false
25115 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25118 * Create a new Element
25119 * @param {Object} config The config object
25122 Roo.bootstrap.MessageBar = function(config){
25123 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25126 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25132 beforeClass: 'bootstrap-sticky-wrap',
25134 getAutoCreate : function(){
25138 cls: 'alert alert-dismissable alert-' + this.weight,
25143 html: this.html || ''
25149 cfg.cls += ' alert-messages-fixed';
25163 onRender : function(ct, position)
25165 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25168 var cfg = Roo.apply({}, this.getAutoCreate());
25172 cfg.cls += ' ' + this.cls;
25175 cfg.style = this.style;
25177 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25179 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25182 this.el.select('>button.close').on('click', this.hide, this);
25188 if (!this.rendered) {
25194 this.fireEvent('show', this);
25200 if (!this.rendered) {
25206 this.fireEvent('hide', this);
25209 update : function()
25211 // var e = this.el.dom.firstChild;
25213 // if(this.closable){
25214 // e = e.nextSibling;
25217 // e.data = this.html || '';
25219 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25235 * @class Roo.bootstrap.Graph
25236 * @extends Roo.bootstrap.Component
25237 * Bootstrap Graph class
25241 @cfg {String} graphtype bar | vbar | pie
25242 @cfg {number} g_x coodinator | centre x (pie)
25243 @cfg {number} g_y coodinator | centre y (pie)
25244 @cfg {number} g_r radius (pie)
25245 @cfg {number} g_height height of the chart (respected by all elements in the set)
25246 @cfg {number} g_width width of the chart (respected by all elements in the set)
25247 @cfg {Object} title The title of the chart
25250 -opts (object) options for the chart
25252 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25253 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25255 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.
25256 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25258 o stretch (boolean)
25260 -opts (object) options for the pie
25263 o startAngle (number)
25264 o endAngle (number)
25268 * Create a new Input
25269 * @param {Object} config The config object
25272 Roo.bootstrap.Graph = function(config){
25273 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25279 * The img click event for the img.
25280 * @param {Roo.EventObject} e
25286 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25297 //g_colors: this.colors,
25304 getAutoCreate : function(){
25315 onRender : function(ct,position){
25318 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25320 if (typeof(Raphael) == 'undefined') {
25321 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25325 this.raphael = Raphael(this.el.dom);
25327 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25328 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25329 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25330 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25332 r.text(160, 10, "Single Series Chart").attr(txtattr);
25333 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25334 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25335 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25337 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25338 r.barchart(330, 10, 300, 220, data1);
25339 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25340 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25343 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25344 // r.barchart(30, 30, 560, 250, xdata, {
25345 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25346 // axis : "0 0 1 1",
25347 // axisxlabels : xdata
25348 // //yvalues : cols,
25351 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25353 // this.load(null,xdata,{
25354 // axis : "0 0 1 1",
25355 // axisxlabels : xdata
25360 load : function(graphtype,xdata,opts)
25362 this.raphael.clear();
25364 graphtype = this.graphtype;
25369 var r = this.raphael,
25370 fin = function () {
25371 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25373 fout = function () {
25374 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25376 pfin = function() {
25377 this.sector.stop();
25378 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25381 this.label[0].stop();
25382 this.label[0].attr({ r: 7.5 });
25383 this.label[1].attr({ "font-weight": 800 });
25386 pfout = function() {
25387 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25390 this.label[0].animate({ r: 5 }, 500, "bounce");
25391 this.label[1].attr({ "font-weight": 400 });
25397 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25400 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25403 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25404 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25406 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25413 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25418 setTitle: function(o)
25423 initEvents: function() {
25426 this.el.on('click', this.onClick, this);
25430 onClick : function(e)
25432 Roo.log('img onclick');
25433 this.fireEvent('click', this, e);
25445 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25448 * @class Roo.bootstrap.dash.NumberBox
25449 * @extends Roo.bootstrap.Component
25450 * Bootstrap NumberBox class
25451 * @cfg {String} headline Box headline
25452 * @cfg {String} content Box content
25453 * @cfg {String} icon Box icon
25454 * @cfg {String} footer Footer text
25455 * @cfg {String} fhref Footer href
25458 * Create a new NumberBox
25459 * @param {Object} config The config object
25463 Roo.bootstrap.dash.NumberBox = function(config){
25464 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25468 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25477 getAutoCreate : function(){
25481 cls : 'small-box ',
25489 cls : 'roo-headline',
25490 html : this.headline
25494 cls : 'roo-content',
25495 html : this.content
25509 cls : 'ion ' + this.icon
25518 cls : 'small-box-footer',
25519 href : this.fhref || '#',
25523 cfg.cn.push(footer);
25530 onRender : function(ct,position){
25531 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25538 setHeadline: function (value)
25540 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25543 setFooter: function (value, href)
25545 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25548 this.el.select('a.small-box-footer',true).first().attr('href', href);
25553 setContent: function (value)
25555 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25558 initEvents: function()
25572 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25575 * @class Roo.bootstrap.dash.TabBox
25576 * @extends Roo.bootstrap.Component
25577 * Bootstrap TabBox class
25578 * @cfg {String} title Title of the TabBox
25579 * @cfg {String} icon Icon of the TabBox
25580 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25581 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25584 * Create a new TabBox
25585 * @param {Object} config The config object
25589 Roo.bootstrap.dash.TabBox = function(config){
25590 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25595 * When a pane is added
25596 * @param {Roo.bootstrap.dash.TabPane} pane
25600 * @event activatepane
25601 * When a pane is activated
25602 * @param {Roo.bootstrap.dash.TabPane} pane
25604 "activatepane" : true
25612 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25617 tabScrollable : false,
25619 getChildContainer : function()
25621 return this.el.select('.tab-content', true).first();
25624 getAutoCreate : function(){
25628 cls: 'pull-left header',
25636 cls: 'fa ' + this.icon
25642 cls: 'nav nav-tabs pull-right',
25648 if(this.tabScrollable){
25655 cls: 'nav nav-tabs pull-right',
25666 cls: 'nav-tabs-custom',
25671 cls: 'tab-content no-padding',
25679 initEvents : function()
25681 //Roo.log('add add pane handler');
25682 this.on('addpane', this.onAddPane, this);
25685 * Updates the box title
25686 * @param {String} html to set the title to.
25688 setTitle : function(value)
25690 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25692 onAddPane : function(pane)
25694 this.panes.push(pane);
25695 //Roo.log('addpane');
25697 // tabs are rendere left to right..
25698 if(!this.showtabs){
25702 var ctr = this.el.select('.nav-tabs', true).first();
25705 var existing = ctr.select('.nav-tab',true);
25706 var qty = existing.getCount();;
25709 var tab = ctr.createChild({
25711 cls : 'nav-tab' + (qty ? '' : ' active'),
25719 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25722 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25724 pane.el.addClass('active');
25729 onTabClick : function(ev,un,ob,pane)
25731 //Roo.log('tab - prev default');
25732 ev.preventDefault();
25735 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25736 pane.tab.addClass('active');
25737 //Roo.log(pane.title);
25738 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25739 // technically we should have a deactivate event.. but maybe add later.
25740 // and it should not de-activate the selected tab...
25741 this.fireEvent('activatepane', pane);
25742 pane.el.addClass('active');
25743 pane.fireEvent('activate');
25748 getActivePane : function()
25751 Roo.each(this.panes, function(p) {
25752 if(p.el.hasClass('active')){
25773 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25775 * @class Roo.bootstrap.TabPane
25776 * @extends Roo.bootstrap.Component
25777 * Bootstrap TabPane class
25778 * @cfg {Boolean} active (false | true) Default false
25779 * @cfg {String} title title of panel
25783 * Create a new TabPane
25784 * @param {Object} config The config object
25787 Roo.bootstrap.dash.TabPane = function(config){
25788 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25794 * When a pane is activated
25795 * @param {Roo.bootstrap.dash.TabPane} pane
25802 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25807 // the tabBox that this is attached to.
25810 getAutoCreate : function()
25818 cfg.cls += ' active';
25823 initEvents : function()
25825 //Roo.log('trigger add pane handler');
25826 this.parent().fireEvent('addpane', this)
25830 * Updates the tab title
25831 * @param {String} html to set the title to.
25833 setTitle: function(str)
25839 this.tab.select('a', true).first().dom.innerHTML = str;
25856 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25859 * @class Roo.bootstrap.menu.Menu
25860 * @extends Roo.bootstrap.Component
25861 * Bootstrap Menu class - container for Menu
25862 * @cfg {String} html Text of the menu
25863 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25864 * @cfg {String} icon Font awesome icon
25865 * @cfg {String} pos Menu align to (top | bottom) default bottom
25869 * Create a new Menu
25870 * @param {Object} config The config object
25874 Roo.bootstrap.menu.Menu = function(config){
25875 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25879 * @event beforeshow
25880 * Fires before this menu is displayed
25881 * @param {Roo.bootstrap.menu.Menu} this
25885 * @event beforehide
25886 * Fires before this menu is hidden
25887 * @param {Roo.bootstrap.menu.Menu} this
25892 * Fires after this menu is displayed
25893 * @param {Roo.bootstrap.menu.Menu} this
25898 * Fires after this menu is hidden
25899 * @param {Roo.bootstrap.menu.Menu} this
25904 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25905 * @param {Roo.bootstrap.menu.Menu} this
25906 * @param {Roo.EventObject} e
25913 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25917 weight : 'default',
25922 getChildContainer : function() {
25923 if(this.isSubMenu){
25927 return this.el.select('ul.dropdown-menu', true).first();
25930 getAutoCreate : function()
25935 cls : 'roo-menu-text',
25943 cls : 'fa ' + this.icon
25954 cls : 'dropdown-button btn btn-' + this.weight,
25959 cls : 'dropdown-toggle btn btn-' + this.weight,
25969 cls : 'dropdown-menu'
25975 if(this.pos == 'top'){
25976 cfg.cls += ' dropup';
25979 if(this.isSubMenu){
25982 cls : 'dropdown-menu'
25989 onRender : function(ct, position)
25991 this.isSubMenu = ct.hasClass('dropdown-submenu');
25993 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25996 initEvents : function()
25998 if(this.isSubMenu){
26002 this.hidden = true;
26004 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26005 this.triggerEl.on('click', this.onTriggerPress, this);
26007 this.buttonEl = this.el.select('button.dropdown-button', true).first();
26008 this.buttonEl.on('click', this.onClick, this);
26014 if(this.isSubMenu){
26018 return this.el.select('ul.dropdown-menu', true).first();
26021 onClick : function(e)
26023 this.fireEvent("click", this, e);
26026 onTriggerPress : function(e)
26028 if (this.isVisible()) {
26035 isVisible : function(){
26036 return !this.hidden;
26041 this.fireEvent("beforeshow", this);
26043 this.hidden = false;
26044 this.el.addClass('open');
26046 Roo.get(document).on("mouseup", this.onMouseUp, this);
26048 this.fireEvent("show", this);
26055 this.fireEvent("beforehide", this);
26057 this.hidden = true;
26058 this.el.removeClass('open');
26060 Roo.get(document).un("mouseup", this.onMouseUp);
26062 this.fireEvent("hide", this);
26065 onMouseUp : function()
26079 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26082 * @class Roo.bootstrap.menu.Item
26083 * @extends Roo.bootstrap.Component
26084 * Bootstrap MenuItem class
26085 * @cfg {Boolean} submenu (true | false) default false
26086 * @cfg {String} html text of the item
26087 * @cfg {String} href the link
26088 * @cfg {Boolean} disable (true | false) default false
26089 * @cfg {Boolean} preventDefault (true | false) default true
26090 * @cfg {String} icon Font awesome icon
26091 * @cfg {String} pos Submenu align to (left | right) default right
26095 * Create a new Item
26096 * @param {Object} config The config object
26100 Roo.bootstrap.menu.Item = function(config){
26101 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26105 * Fires when the mouse is hovering over this menu
26106 * @param {Roo.bootstrap.menu.Item} this
26107 * @param {Roo.EventObject} e
26112 * Fires when the mouse exits this menu
26113 * @param {Roo.bootstrap.menu.Item} this
26114 * @param {Roo.EventObject} e
26120 * The raw click event for the entire grid.
26121 * @param {Roo.EventObject} e
26127 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26132 preventDefault: true,
26137 getAutoCreate : function()
26142 cls : 'roo-menu-item-text',
26150 cls : 'fa ' + this.icon
26159 href : this.href || '#',
26166 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26170 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26172 if(this.pos == 'left'){
26173 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26180 initEvents : function()
26182 this.el.on('mouseover', this.onMouseOver, this);
26183 this.el.on('mouseout', this.onMouseOut, this);
26185 this.el.select('a', true).first().on('click', this.onClick, this);
26189 onClick : function(e)
26191 if(this.preventDefault){
26192 e.preventDefault();
26195 this.fireEvent("click", this, e);
26198 onMouseOver : function(e)
26200 if(this.submenu && this.pos == 'left'){
26201 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26204 this.fireEvent("mouseover", this, e);
26207 onMouseOut : function(e)
26209 this.fireEvent("mouseout", this, e);
26221 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26224 * @class Roo.bootstrap.menu.Separator
26225 * @extends Roo.bootstrap.Component
26226 * Bootstrap Separator class
26229 * Create a new Separator
26230 * @param {Object} config The config object
26234 Roo.bootstrap.menu.Separator = function(config){
26235 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26238 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26240 getAutoCreate : function(){
26261 * @class Roo.bootstrap.Tooltip
26262 * Bootstrap Tooltip class
26263 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26264 * to determine which dom element triggers the tooltip.
26266 * It needs to add support for additional attributes like tooltip-position
26269 * Create a new Toolti
26270 * @param {Object} config The config object
26273 Roo.bootstrap.Tooltip = function(config){
26274 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26276 this.alignment = Roo.bootstrap.Tooltip.alignment;
26278 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26279 this.alignment = config.alignment;
26284 Roo.apply(Roo.bootstrap.Tooltip, {
26286 * @function init initialize tooltip monitoring.
26290 currentTip : false,
26291 currentRegion : false,
26297 Roo.get(document).on('mouseover', this.enter ,this);
26298 Roo.get(document).on('mouseout', this.leave, this);
26301 this.currentTip = new Roo.bootstrap.Tooltip();
26304 enter : function(ev)
26306 var dom = ev.getTarget();
26308 //Roo.log(['enter',dom]);
26309 var el = Roo.fly(dom);
26310 if (this.currentEl) {
26312 //Roo.log(this.currentEl);
26313 //Roo.log(this.currentEl.contains(dom));
26314 if (this.currentEl == el) {
26317 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26323 if (this.currentTip.el) {
26324 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26328 if(!el || el.dom == document){
26334 // you can not look for children, as if el is the body.. then everythign is the child..
26335 if (!el.attr('tooltip')) { //
26336 if (!el.select("[tooltip]").elements.length) {
26339 // is the mouse over this child...?
26340 bindEl = el.select("[tooltip]").first();
26341 var xy = ev.getXY();
26342 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26343 //Roo.log("not in region.");
26346 //Roo.log("child element over..");
26349 this.currentEl = bindEl;
26350 this.currentTip.bind(bindEl);
26351 this.currentRegion = Roo.lib.Region.getRegion(dom);
26352 this.currentTip.enter();
26355 leave : function(ev)
26357 var dom = ev.getTarget();
26358 //Roo.log(['leave',dom]);
26359 if (!this.currentEl) {
26364 if (dom != this.currentEl.dom) {
26367 var xy = ev.getXY();
26368 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26371 // only activate leave if mouse cursor is outside... bounding box..
26376 if (this.currentTip) {
26377 this.currentTip.leave();
26379 //Roo.log('clear currentEl');
26380 this.currentEl = false;
26385 'left' : ['r-l', [-2,0], 'right'],
26386 'right' : ['l-r', [2,0], 'left'],
26387 'bottom' : ['t-b', [0,2], 'top'],
26388 'top' : [ 'b-t', [0,-2], 'bottom']
26394 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26399 delay : null, // can be { show : 300 , hide: 500}
26403 hoverState : null, //???
26405 placement : 'bottom',
26409 getAutoCreate : function(){
26416 cls : 'tooltip-arrow'
26419 cls : 'tooltip-inner'
26426 bind : function(el)
26432 enter : function () {
26434 if (this.timeout != null) {
26435 clearTimeout(this.timeout);
26438 this.hoverState = 'in';
26439 //Roo.log("enter - show");
26440 if (!this.delay || !this.delay.show) {
26445 this.timeout = setTimeout(function () {
26446 if (_t.hoverState == 'in') {
26449 }, this.delay.show);
26453 clearTimeout(this.timeout);
26455 this.hoverState = 'out';
26456 if (!this.delay || !this.delay.hide) {
26462 this.timeout = setTimeout(function () {
26463 //Roo.log("leave - timeout");
26465 if (_t.hoverState == 'out') {
26467 Roo.bootstrap.Tooltip.currentEl = false;
26472 show : function (msg)
26475 this.render(document.body);
26478 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26480 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26482 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26484 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26486 var placement = typeof this.placement == 'function' ?
26487 this.placement.call(this, this.el, on_el) :
26490 var autoToken = /\s?auto?\s?/i;
26491 var autoPlace = autoToken.test(placement);
26493 placement = placement.replace(autoToken, '') || 'top';
26497 //this.el.setXY([0,0]);
26499 //this.el.dom.style.display='block';
26501 //this.el.appendTo(on_el);
26503 var p = this.getPosition();
26504 var box = this.el.getBox();
26510 var align = this.alignment[placement];
26512 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26514 if(placement == 'top' || placement == 'bottom'){
26516 placement = 'right';
26519 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26520 placement = 'left';
26523 var scroll = Roo.select('body', true).first().getScroll();
26525 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26529 align = this.alignment[placement];
26532 this.el.alignTo(this.bindEl, align[0],align[1]);
26533 //var arrow = this.el.select('.arrow',true).first();
26534 //arrow.set(align[2],
26536 this.el.addClass(placement);
26538 this.el.addClass('in fade');
26540 this.hoverState = null;
26542 if (this.el.hasClass('fade')) {
26553 //this.el.setXY([0,0]);
26554 this.el.removeClass('in');
26570 * @class Roo.bootstrap.LocationPicker
26571 * @extends Roo.bootstrap.Component
26572 * Bootstrap LocationPicker class
26573 * @cfg {Number} latitude Position when init default 0
26574 * @cfg {Number} longitude Position when init default 0
26575 * @cfg {Number} zoom default 15
26576 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26577 * @cfg {Boolean} mapTypeControl default false
26578 * @cfg {Boolean} disableDoubleClickZoom default false
26579 * @cfg {Boolean} scrollwheel default true
26580 * @cfg {Boolean} streetViewControl default false
26581 * @cfg {Number} radius default 0
26582 * @cfg {String} locationName
26583 * @cfg {Boolean} draggable default true
26584 * @cfg {Boolean} enableAutocomplete default false
26585 * @cfg {Boolean} enableReverseGeocode default true
26586 * @cfg {String} markerTitle
26589 * Create a new LocationPicker
26590 * @param {Object} config The config object
26594 Roo.bootstrap.LocationPicker = function(config){
26596 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26601 * Fires when the picker initialized.
26602 * @param {Roo.bootstrap.LocationPicker} this
26603 * @param {Google Location} location
26607 * @event positionchanged
26608 * Fires when the picker position changed.
26609 * @param {Roo.bootstrap.LocationPicker} this
26610 * @param {Google Location} location
26612 positionchanged : true,
26615 * Fires when the map resize.
26616 * @param {Roo.bootstrap.LocationPicker} this
26621 * Fires when the map show.
26622 * @param {Roo.bootstrap.LocationPicker} this
26627 * Fires when the map hide.
26628 * @param {Roo.bootstrap.LocationPicker} this
26633 * Fires when click the map.
26634 * @param {Roo.bootstrap.LocationPicker} this
26635 * @param {Map event} e
26639 * @event mapRightClick
26640 * Fires when right click the map.
26641 * @param {Roo.bootstrap.LocationPicker} this
26642 * @param {Map event} e
26644 mapRightClick : true,
26646 * @event markerClick
26647 * Fires when click the marker.
26648 * @param {Roo.bootstrap.LocationPicker} this
26649 * @param {Map event} e
26651 markerClick : true,
26653 * @event markerRightClick
26654 * Fires when right click the marker.
26655 * @param {Roo.bootstrap.LocationPicker} this
26656 * @param {Map event} e
26658 markerRightClick : true,
26660 * @event OverlayViewDraw
26661 * Fires when OverlayView Draw
26662 * @param {Roo.bootstrap.LocationPicker} this
26664 OverlayViewDraw : true,
26666 * @event OverlayViewOnAdd
26667 * Fires when OverlayView Draw
26668 * @param {Roo.bootstrap.LocationPicker} this
26670 OverlayViewOnAdd : true,
26672 * @event OverlayViewOnRemove
26673 * Fires when OverlayView Draw
26674 * @param {Roo.bootstrap.LocationPicker} this
26676 OverlayViewOnRemove : true,
26678 * @event OverlayViewShow
26679 * Fires when OverlayView Draw
26680 * @param {Roo.bootstrap.LocationPicker} this
26681 * @param {Pixel} cpx
26683 OverlayViewShow : true,
26685 * @event OverlayViewHide
26686 * Fires when OverlayView Draw
26687 * @param {Roo.bootstrap.LocationPicker} this
26689 OverlayViewHide : true,
26691 * @event loadexception
26692 * Fires when load google lib failed.
26693 * @param {Roo.bootstrap.LocationPicker} this
26695 loadexception : true
26700 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26702 gMapContext: false,
26708 mapTypeControl: false,
26709 disableDoubleClickZoom: false,
26711 streetViewControl: false,
26715 enableAutocomplete: false,
26716 enableReverseGeocode: true,
26719 getAutoCreate: function()
26724 cls: 'roo-location-picker'
26730 initEvents: function(ct, position)
26732 if(!this.el.getWidth() || this.isApplied()){
26736 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26741 initial: function()
26743 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26744 this.fireEvent('loadexception', this);
26748 if(!this.mapTypeId){
26749 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26752 this.gMapContext = this.GMapContext();
26754 this.initOverlayView();
26756 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26760 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26761 _this.setPosition(_this.gMapContext.marker.position);
26764 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26765 _this.fireEvent('mapClick', this, event);
26769 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26770 _this.fireEvent('mapRightClick', this, event);
26774 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26775 _this.fireEvent('markerClick', this, event);
26779 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26780 _this.fireEvent('markerRightClick', this, event);
26784 this.setPosition(this.gMapContext.location);
26786 this.fireEvent('initial', this, this.gMapContext.location);
26789 initOverlayView: function()
26793 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26797 _this.fireEvent('OverlayViewDraw', _this);
26802 _this.fireEvent('OverlayViewOnAdd', _this);
26805 onRemove: function()
26807 _this.fireEvent('OverlayViewOnRemove', _this);
26810 show: function(cpx)
26812 _this.fireEvent('OverlayViewShow', _this, cpx);
26817 _this.fireEvent('OverlayViewHide', _this);
26823 fromLatLngToContainerPixel: function(event)
26825 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26828 isApplied: function()
26830 return this.getGmapContext() == false ? false : true;
26833 getGmapContext: function()
26835 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26838 GMapContext: function()
26840 var position = new google.maps.LatLng(this.latitude, this.longitude);
26842 var _map = new google.maps.Map(this.el.dom, {
26845 mapTypeId: this.mapTypeId,
26846 mapTypeControl: this.mapTypeControl,
26847 disableDoubleClickZoom: this.disableDoubleClickZoom,
26848 scrollwheel: this.scrollwheel,
26849 streetViewControl: this.streetViewControl,
26850 locationName: this.locationName,
26851 draggable: this.draggable,
26852 enableAutocomplete: this.enableAutocomplete,
26853 enableReverseGeocode: this.enableReverseGeocode
26856 var _marker = new google.maps.Marker({
26857 position: position,
26859 title: this.markerTitle,
26860 draggable: this.draggable
26867 location: position,
26868 radius: this.radius,
26869 locationName: this.locationName,
26870 addressComponents: {
26871 formatted_address: null,
26872 addressLine1: null,
26873 addressLine2: null,
26875 streetNumber: null,
26879 stateOrProvince: null
26882 domContainer: this.el.dom,
26883 geodecoder: new google.maps.Geocoder()
26887 drawCircle: function(center, radius, options)
26889 if (this.gMapContext.circle != null) {
26890 this.gMapContext.circle.setMap(null);
26894 options = Roo.apply({}, options, {
26895 strokeColor: "#0000FF",
26896 strokeOpacity: .35,
26898 fillColor: "#0000FF",
26902 options.map = this.gMapContext.map;
26903 options.radius = radius;
26904 options.center = center;
26905 this.gMapContext.circle = new google.maps.Circle(options);
26906 return this.gMapContext.circle;
26912 setPosition: function(location)
26914 this.gMapContext.location = location;
26915 this.gMapContext.marker.setPosition(location);
26916 this.gMapContext.map.panTo(location);
26917 this.drawCircle(location, this.gMapContext.radius, {});
26921 if (this.gMapContext.settings.enableReverseGeocode) {
26922 this.gMapContext.geodecoder.geocode({
26923 latLng: this.gMapContext.location
26924 }, function(results, status) {
26926 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26927 _this.gMapContext.locationName = results[0].formatted_address;
26928 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26930 _this.fireEvent('positionchanged', this, location);
26937 this.fireEvent('positionchanged', this, location);
26942 google.maps.event.trigger(this.gMapContext.map, "resize");
26944 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26946 this.fireEvent('resize', this);
26949 setPositionByLatLng: function(latitude, longitude)
26951 this.setPosition(new google.maps.LatLng(latitude, longitude));
26954 getCurrentPosition: function()
26957 latitude: this.gMapContext.location.lat(),
26958 longitude: this.gMapContext.location.lng()
26962 getAddressName: function()
26964 return this.gMapContext.locationName;
26967 getAddressComponents: function()
26969 return this.gMapContext.addressComponents;
26972 address_component_from_google_geocode: function(address_components)
26976 for (var i = 0; i < address_components.length; i++) {
26977 var component = address_components[i];
26978 if (component.types.indexOf("postal_code") >= 0) {
26979 result.postalCode = component.short_name;
26980 } else if (component.types.indexOf("street_number") >= 0) {
26981 result.streetNumber = component.short_name;
26982 } else if (component.types.indexOf("route") >= 0) {
26983 result.streetName = component.short_name;
26984 } else if (component.types.indexOf("neighborhood") >= 0) {
26985 result.city = component.short_name;
26986 } else if (component.types.indexOf("locality") >= 0) {
26987 result.city = component.short_name;
26988 } else if (component.types.indexOf("sublocality") >= 0) {
26989 result.district = component.short_name;
26990 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26991 result.stateOrProvince = component.short_name;
26992 } else if (component.types.indexOf("country") >= 0) {
26993 result.country = component.short_name;
26997 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26998 result.addressLine2 = "";
27002 setZoomLevel: function(zoom)
27004 this.gMapContext.map.setZoom(zoom);
27017 this.fireEvent('show', this);
27028 this.fireEvent('hide', this);
27033 Roo.apply(Roo.bootstrap.LocationPicker, {
27035 OverlayView : function(map, options)
27037 options = options || {};
27051 * @class Roo.bootstrap.Alert
27052 * @extends Roo.bootstrap.Component
27053 * Bootstrap Alert class
27054 * @cfg {String} title The title of alert
27055 * @cfg {String} html The content of alert
27056 * @cfg {String} weight ( success | info | warning | danger )
27057 * @cfg {String} faicon font-awesomeicon
27060 * Create a new alert
27061 * @param {Object} config The config object
27065 Roo.bootstrap.Alert = function(config){
27066 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27070 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27077 getAutoCreate : function()
27086 cls : 'roo-alert-icon'
27091 cls : 'roo-alert-title',
27096 cls : 'roo-alert-text',
27103 cfg.cn[0].cls += ' fa ' + this.faicon;
27107 cfg.cls += ' alert-' + this.weight;
27113 initEvents: function()
27115 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27118 setTitle : function(str)
27120 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27123 setText : function(str)
27125 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27128 setWeight : function(weight)
27131 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27134 this.weight = weight;
27136 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27139 setIcon : function(icon)
27142 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27145 this.faicon = icon;
27147 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27168 * @class Roo.bootstrap.UploadCropbox
27169 * @extends Roo.bootstrap.Component
27170 * Bootstrap UploadCropbox class
27171 * @cfg {String} emptyText show when image has been loaded
27172 * @cfg {String} rotateNotify show when image too small to rotate
27173 * @cfg {Number} errorTimeout default 3000
27174 * @cfg {Number} minWidth default 300
27175 * @cfg {Number} minHeight default 300
27176 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27177 * @cfg {Boolean} isDocument (true|false) default false
27178 * @cfg {String} url action url
27179 * @cfg {String} paramName default 'imageUpload'
27180 * @cfg {String} method default POST
27181 * @cfg {Boolean} loadMask (true|false) default true
27182 * @cfg {Boolean} loadingText default 'Loading...'
27185 * Create a new UploadCropbox
27186 * @param {Object} config The config object
27189 Roo.bootstrap.UploadCropbox = function(config){
27190 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27194 * @event beforeselectfile
27195 * Fire before select file
27196 * @param {Roo.bootstrap.UploadCropbox} this
27198 "beforeselectfile" : true,
27201 * Fire after initEvent
27202 * @param {Roo.bootstrap.UploadCropbox} this
27207 * Fire after initEvent
27208 * @param {Roo.bootstrap.UploadCropbox} this
27209 * @param {String} data
27214 * Fire when preparing the file data
27215 * @param {Roo.bootstrap.UploadCropbox} this
27216 * @param {Object} file
27221 * Fire when get exception
27222 * @param {Roo.bootstrap.UploadCropbox} this
27223 * @param {XMLHttpRequest} xhr
27225 "exception" : true,
27227 * @event beforeloadcanvas
27228 * Fire before load the canvas
27229 * @param {Roo.bootstrap.UploadCropbox} this
27230 * @param {String} src
27232 "beforeloadcanvas" : true,
27235 * Fire when trash image
27236 * @param {Roo.bootstrap.UploadCropbox} this
27241 * Fire when download the image
27242 * @param {Roo.bootstrap.UploadCropbox} this
27246 * @event footerbuttonclick
27247 * Fire when footerbuttonclick
27248 * @param {Roo.bootstrap.UploadCropbox} this
27249 * @param {String} type
27251 "footerbuttonclick" : true,
27255 * @param {Roo.bootstrap.UploadCropbox} this
27260 * Fire when rotate the image
27261 * @param {Roo.bootstrap.UploadCropbox} this
27262 * @param {String} pos
27267 * Fire when inspect the file
27268 * @param {Roo.bootstrap.UploadCropbox} this
27269 * @param {Object} file
27274 * Fire when xhr upload the file
27275 * @param {Roo.bootstrap.UploadCropbox} this
27276 * @param {Object} data
27281 * Fire when arrange the file data
27282 * @param {Roo.bootstrap.UploadCropbox} this
27283 * @param {Object} formData
27288 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27291 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27293 emptyText : 'Click to upload image',
27294 rotateNotify : 'Image is too small to rotate',
27295 errorTimeout : 3000,
27309 cropType : 'image/jpeg',
27311 canvasLoaded : false,
27312 isDocument : false,
27314 paramName : 'imageUpload',
27316 loadingText : 'Loading...',
27319 getAutoCreate : function()
27323 cls : 'roo-upload-cropbox',
27327 cls : 'roo-upload-cropbox-selector',
27332 cls : 'roo-upload-cropbox-body',
27333 style : 'cursor:pointer',
27337 cls : 'roo-upload-cropbox-preview'
27341 cls : 'roo-upload-cropbox-thumb'
27345 cls : 'roo-upload-cropbox-empty-notify',
27346 html : this.emptyText
27350 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27351 html : this.rotateNotify
27357 cls : 'roo-upload-cropbox-footer',
27360 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27370 onRender : function(ct, position)
27372 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27374 if (this.buttons.length) {
27376 Roo.each(this.buttons, function(bb) {
27378 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27380 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27386 this.maskEl = this.el;
27390 initEvents : function()
27392 this.urlAPI = (window.createObjectURL && window) ||
27393 (window.URL && URL.revokeObjectURL && URL) ||
27394 (window.webkitURL && webkitURL);
27396 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27397 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27399 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27400 this.selectorEl.hide();
27402 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27403 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27405 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27406 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27407 this.thumbEl.hide();
27409 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27410 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27412 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27413 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27414 this.errorEl.hide();
27416 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27417 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27418 this.footerEl.hide();
27420 this.setThumbBoxSize();
27426 this.fireEvent('initial', this);
27433 window.addEventListener("resize", function() { _this.resize(); } );
27435 this.bodyEl.on('click', this.beforeSelectFile, this);
27438 this.bodyEl.on('touchstart', this.onTouchStart, this);
27439 this.bodyEl.on('touchmove', this.onTouchMove, this);
27440 this.bodyEl.on('touchend', this.onTouchEnd, this);
27444 this.bodyEl.on('mousedown', this.onMouseDown, this);
27445 this.bodyEl.on('mousemove', this.onMouseMove, this);
27446 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27447 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27448 Roo.get(document).on('mouseup', this.onMouseUp, this);
27451 this.selectorEl.on('change', this.onFileSelected, this);
27457 this.baseScale = 1;
27459 this.baseRotate = 1;
27460 this.dragable = false;
27461 this.pinching = false;
27464 this.cropData = false;
27465 this.notifyEl.dom.innerHTML = this.emptyText;
27467 this.selectorEl.dom.value = '';
27471 resize : function()
27473 if(this.fireEvent('resize', this) != false){
27474 this.setThumbBoxPosition();
27475 this.setCanvasPosition();
27479 onFooterButtonClick : function(e, el, o, type)
27482 case 'rotate-left' :
27483 this.onRotateLeft(e);
27485 case 'rotate-right' :
27486 this.onRotateRight(e);
27489 this.beforeSelectFile(e);
27504 this.fireEvent('footerbuttonclick', this, type);
27507 beforeSelectFile : function(e)
27509 e.preventDefault();
27511 if(this.fireEvent('beforeselectfile', this) != false){
27512 this.selectorEl.dom.click();
27516 onFileSelected : function(e)
27518 e.preventDefault();
27520 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27524 var file = this.selectorEl.dom.files[0];
27526 if(this.fireEvent('inspect', this, file) != false){
27527 this.prepare(file);
27532 trash : function(e)
27534 this.fireEvent('trash', this);
27537 download : function(e)
27539 this.fireEvent('download', this);
27542 loadCanvas : function(src)
27544 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27548 this.imageEl = document.createElement('img');
27552 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27554 this.imageEl.src = src;
27558 onLoadCanvas : function()
27560 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27561 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27563 this.bodyEl.un('click', this.beforeSelectFile, this);
27565 this.notifyEl.hide();
27566 this.thumbEl.show();
27567 this.footerEl.show();
27569 this.baseRotateLevel();
27571 if(this.isDocument){
27572 this.setThumbBoxSize();
27575 this.setThumbBoxPosition();
27577 this.baseScaleLevel();
27583 this.canvasLoaded = true;
27586 this.maskEl.unmask();
27591 setCanvasPosition : function()
27593 if(!this.canvasEl){
27597 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27598 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27600 this.previewEl.setLeft(pw);
27601 this.previewEl.setTop(ph);
27605 onMouseDown : function(e)
27609 this.dragable = true;
27610 this.pinching = false;
27612 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27613 this.dragable = false;
27617 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27618 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27622 onMouseMove : function(e)
27626 if(!this.canvasLoaded){
27630 if (!this.dragable){
27634 var minX = Math.ceil(this.thumbEl.getLeft(true));
27635 var minY = Math.ceil(this.thumbEl.getTop(true));
27637 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27638 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27640 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27641 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27643 x = x - this.mouseX;
27644 y = y - this.mouseY;
27646 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27647 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27649 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27650 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27652 this.previewEl.setLeft(bgX);
27653 this.previewEl.setTop(bgY);
27655 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27656 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27659 onMouseUp : function(e)
27663 this.dragable = false;
27666 onMouseWheel : function(e)
27670 this.startScale = this.scale;
27672 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27674 if(!this.zoomable()){
27675 this.scale = this.startScale;
27684 zoomable : function()
27686 var minScale = this.thumbEl.getWidth() / this.minWidth;
27688 if(this.minWidth < this.minHeight){
27689 minScale = this.thumbEl.getHeight() / this.minHeight;
27692 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27693 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27697 (this.rotate == 0 || this.rotate == 180) &&
27699 width > this.imageEl.OriginWidth ||
27700 height > this.imageEl.OriginHeight ||
27701 (width < this.minWidth && height < this.minHeight)
27709 (this.rotate == 90 || this.rotate == 270) &&
27711 width > this.imageEl.OriginWidth ||
27712 height > this.imageEl.OriginHeight ||
27713 (width < this.minHeight && height < this.minWidth)
27720 !this.isDocument &&
27721 (this.rotate == 0 || this.rotate == 180) &&
27723 width < this.minWidth ||
27724 width > this.imageEl.OriginWidth ||
27725 height < this.minHeight ||
27726 height > this.imageEl.OriginHeight
27733 !this.isDocument &&
27734 (this.rotate == 90 || this.rotate == 270) &&
27736 width < this.minHeight ||
27737 width > this.imageEl.OriginWidth ||
27738 height < this.minWidth ||
27739 height > this.imageEl.OriginHeight
27749 onRotateLeft : function(e)
27751 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27753 var minScale = this.thumbEl.getWidth() / this.minWidth;
27755 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27756 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27758 this.startScale = this.scale;
27760 while (this.getScaleLevel() < minScale){
27762 this.scale = this.scale + 1;
27764 if(!this.zoomable()){
27769 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27770 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27775 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27782 this.scale = this.startScale;
27784 this.onRotateFail();
27789 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27791 if(this.isDocument){
27792 this.setThumbBoxSize();
27793 this.setThumbBoxPosition();
27794 this.setCanvasPosition();
27799 this.fireEvent('rotate', this, 'left');
27803 onRotateRight : function(e)
27805 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27807 var minScale = this.thumbEl.getWidth() / this.minWidth;
27809 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27810 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27812 this.startScale = this.scale;
27814 while (this.getScaleLevel() < minScale){
27816 this.scale = this.scale + 1;
27818 if(!this.zoomable()){
27823 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27824 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27829 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27836 this.scale = this.startScale;
27838 this.onRotateFail();
27843 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27845 if(this.isDocument){
27846 this.setThumbBoxSize();
27847 this.setThumbBoxPosition();
27848 this.setCanvasPosition();
27853 this.fireEvent('rotate', this, 'right');
27856 onRotateFail : function()
27858 this.errorEl.show(true);
27862 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27867 this.previewEl.dom.innerHTML = '';
27869 var canvasEl = document.createElement("canvas");
27871 var contextEl = canvasEl.getContext("2d");
27873 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27874 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27875 var center = this.imageEl.OriginWidth / 2;
27877 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27878 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27879 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27880 center = this.imageEl.OriginHeight / 2;
27883 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27885 contextEl.translate(center, center);
27886 contextEl.rotate(this.rotate * Math.PI / 180);
27888 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27890 this.canvasEl = document.createElement("canvas");
27892 this.contextEl = this.canvasEl.getContext("2d");
27894 switch (this.rotate) {
27897 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27898 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27900 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27905 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27906 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27908 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27909 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);
27913 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27918 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27919 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27921 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27922 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);
27926 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);
27931 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27932 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27934 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27935 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27939 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);
27946 this.previewEl.appendChild(this.canvasEl);
27948 this.setCanvasPosition();
27953 if(!this.canvasLoaded){
27957 var imageCanvas = document.createElement("canvas");
27959 var imageContext = imageCanvas.getContext("2d");
27961 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27962 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27964 var center = imageCanvas.width / 2;
27966 imageContext.translate(center, center);
27968 imageContext.rotate(this.rotate * Math.PI / 180);
27970 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27972 var canvas = document.createElement("canvas");
27974 var context = canvas.getContext("2d");
27976 canvas.width = this.minWidth;
27977 canvas.height = this.minHeight;
27979 switch (this.rotate) {
27982 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27983 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27985 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27986 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27988 var targetWidth = this.minWidth - 2 * x;
27989 var targetHeight = this.minHeight - 2 * y;
27993 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27994 scale = targetWidth / width;
27997 if(x > 0 && y == 0){
27998 scale = targetHeight / height;
28001 if(x > 0 && y > 0){
28002 scale = targetWidth / width;
28004 if(width < height){
28005 scale = targetHeight / height;
28009 context.scale(scale, scale);
28011 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28012 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28014 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28015 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28017 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28022 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28023 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28025 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28026 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28028 var targetWidth = this.minWidth - 2 * x;
28029 var targetHeight = this.minHeight - 2 * y;
28033 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28034 scale = targetWidth / width;
28037 if(x > 0 && y == 0){
28038 scale = targetHeight / height;
28041 if(x > 0 && y > 0){
28042 scale = targetWidth / width;
28044 if(width < height){
28045 scale = targetHeight / height;
28049 context.scale(scale, scale);
28051 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28052 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28054 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28055 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28057 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28059 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28064 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28065 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28067 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28068 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28070 var targetWidth = this.minWidth - 2 * x;
28071 var targetHeight = this.minHeight - 2 * y;
28075 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28076 scale = targetWidth / width;
28079 if(x > 0 && y == 0){
28080 scale = targetHeight / height;
28083 if(x > 0 && y > 0){
28084 scale = targetWidth / width;
28086 if(width < height){
28087 scale = targetHeight / height;
28091 context.scale(scale, scale);
28093 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28094 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28096 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28097 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28099 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28100 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28102 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28107 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28108 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28110 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28111 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28113 var targetWidth = this.minWidth - 2 * x;
28114 var targetHeight = this.minHeight - 2 * y;
28118 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28119 scale = targetWidth / width;
28122 if(x > 0 && y == 0){
28123 scale = targetHeight / height;
28126 if(x > 0 && y > 0){
28127 scale = targetWidth / width;
28129 if(width < height){
28130 scale = targetHeight / height;
28134 context.scale(scale, scale);
28136 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28137 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28139 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28140 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28142 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28144 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28151 this.cropData = canvas.toDataURL(this.cropType);
28153 if(this.fireEvent('crop', this, this.cropData) !== false){
28154 this.process(this.file, this.cropData);
28161 setThumbBoxSize : function()
28165 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28166 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28167 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28169 this.minWidth = width;
28170 this.minHeight = height;
28172 if(this.rotate == 90 || this.rotate == 270){
28173 this.minWidth = height;
28174 this.minHeight = width;
28179 width = Math.ceil(this.minWidth * height / this.minHeight);
28181 if(this.minWidth > this.minHeight){
28183 height = Math.ceil(this.minHeight * width / this.minWidth);
28186 this.thumbEl.setStyle({
28187 width : width + 'px',
28188 height : height + 'px'
28195 setThumbBoxPosition : function()
28197 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28198 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28200 this.thumbEl.setLeft(x);
28201 this.thumbEl.setTop(y);
28205 baseRotateLevel : function()
28207 this.baseRotate = 1;
28210 typeof(this.exif) != 'undefined' &&
28211 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28212 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28214 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28217 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28221 baseScaleLevel : function()
28225 if(this.isDocument){
28227 if(this.baseRotate == 6 || this.baseRotate == 8){
28229 height = this.thumbEl.getHeight();
28230 this.baseScale = height / this.imageEl.OriginWidth;
28232 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28233 width = this.thumbEl.getWidth();
28234 this.baseScale = width / this.imageEl.OriginHeight;
28240 height = this.thumbEl.getHeight();
28241 this.baseScale = height / this.imageEl.OriginHeight;
28243 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28244 width = this.thumbEl.getWidth();
28245 this.baseScale = width / this.imageEl.OriginWidth;
28251 if(this.baseRotate == 6 || this.baseRotate == 8){
28253 width = this.thumbEl.getHeight();
28254 this.baseScale = width / this.imageEl.OriginHeight;
28256 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28257 height = this.thumbEl.getWidth();
28258 this.baseScale = height / this.imageEl.OriginHeight;
28261 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28262 height = this.thumbEl.getWidth();
28263 this.baseScale = height / this.imageEl.OriginHeight;
28265 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28266 width = this.thumbEl.getHeight();
28267 this.baseScale = width / this.imageEl.OriginWidth;
28274 width = this.thumbEl.getWidth();
28275 this.baseScale = width / this.imageEl.OriginWidth;
28277 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28278 height = this.thumbEl.getHeight();
28279 this.baseScale = height / this.imageEl.OriginHeight;
28282 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28284 height = this.thumbEl.getHeight();
28285 this.baseScale = height / this.imageEl.OriginHeight;
28287 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28288 width = this.thumbEl.getWidth();
28289 this.baseScale = width / this.imageEl.OriginWidth;
28297 getScaleLevel : function()
28299 return this.baseScale * Math.pow(1.1, this.scale);
28302 onTouchStart : function(e)
28304 if(!this.canvasLoaded){
28305 this.beforeSelectFile(e);
28309 var touches = e.browserEvent.touches;
28315 if(touches.length == 1){
28316 this.onMouseDown(e);
28320 if(touches.length != 2){
28326 for(var i = 0, finger; finger = touches[i]; i++){
28327 coords.push(finger.pageX, finger.pageY);
28330 var x = Math.pow(coords[0] - coords[2], 2);
28331 var y = Math.pow(coords[1] - coords[3], 2);
28333 this.startDistance = Math.sqrt(x + y);
28335 this.startScale = this.scale;
28337 this.pinching = true;
28338 this.dragable = false;
28342 onTouchMove : function(e)
28344 if(!this.pinching && !this.dragable){
28348 var touches = e.browserEvent.touches;
28355 this.onMouseMove(e);
28361 for(var i = 0, finger; finger = touches[i]; i++){
28362 coords.push(finger.pageX, finger.pageY);
28365 var x = Math.pow(coords[0] - coords[2], 2);
28366 var y = Math.pow(coords[1] - coords[3], 2);
28368 this.endDistance = Math.sqrt(x + y);
28370 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28372 if(!this.zoomable()){
28373 this.scale = this.startScale;
28381 onTouchEnd : function(e)
28383 this.pinching = false;
28384 this.dragable = false;
28388 process : function(file, crop)
28391 this.maskEl.mask(this.loadingText);
28394 this.xhr = new XMLHttpRequest();
28396 file.xhr = this.xhr;
28398 this.xhr.open(this.method, this.url, true);
28401 "Accept": "application/json",
28402 "Cache-Control": "no-cache",
28403 "X-Requested-With": "XMLHttpRequest"
28406 for (var headerName in headers) {
28407 var headerValue = headers[headerName];
28409 this.xhr.setRequestHeader(headerName, headerValue);
28415 this.xhr.onload = function()
28417 _this.xhrOnLoad(_this.xhr);
28420 this.xhr.onerror = function()
28422 _this.xhrOnError(_this.xhr);
28425 var formData = new FormData();
28427 formData.append('returnHTML', 'NO');
28430 formData.append('crop', crop);
28433 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28434 formData.append(this.paramName, file, file.name);
28437 if(typeof(file.filename) != 'undefined'){
28438 formData.append('filename', file.filename);
28441 if(typeof(file.mimetype) != 'undefined'){
28442 formData.append('mimetype', file.mimetype);
28445 if(this.fireEvent('arrange', this, formData) != false){
28446 this.xhr.send(formData);
28450 xhrOnLoad : function(xhr)
28453 this.maskEl.unmask();
28456 if (xhr.readyState !== 4) {
28457 this.fireEvent('exception', this, xhr);
28461 var response = Roo.decode(xhr.responseText);
28463 if(!response.success){
28464 this.fireEvent('exception', this, xhr);
28468 var response = Roo.decode(xhr.responseText);
28470 this.fireEvent('upload', this, response);
28474 xhrOnError : function()
28477 this.maskEl.unmask();
28480 Roo.log('xhr on error');
28482 var response = Roo.decode(xhr.responseText);
28488 prepare : function(file)
28491 this.maskEl.mask(this.loadingText);
28497 if(typeof(file) === 'string'){
28498 this.loadCanvas(file);
28502 if(!file || !this.urlAPI){
28507 this.cropType = file.type;
28511 if(this.fireEvent('prepare', this, this.file) != false){
28513 var reader = new FileReader();
28515 reader.onload = function (e) {
28516 if (e.target.error) {
28517 Roo.log(e.target.error);
28521 var buffer = e.target.result,
28522 dataView = new DataView(buffer),
28524 maxOffset = dataView.byteLength - 4,
28528 if (dataView.getUint16(0) === 0xffd8) {
28529 while (offset < maxOffset) {
28530 markerBytes = dataView.getUint16(offset);
28532 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28533 markerLength = dataView.getUint16(offset + 2) + 2;
28534 if (offset + markerLength > dataView.byteLength) {
28535 Roo.log('Invalid meta data: Invalid segment size.');
28539 if(markerBytes == 0xffe1){
28540 _this.parseExifData(
28547 offset += markerLength;
28557 var url = _this.urlAPI.createObjectURL(_this.file);
28559 _this.loadCanvas(url);
28564 reader.readAsArrayBuffer(this.file);
28570 parseExifData : function(dataView, offset, length)
28572 var tiffOffset = offset + 10,
28576 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28577 // No Exif data, might be XMP data instead
28581 // Check for the ASCII code for "Exif" (0x45786966):
28582 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28583 // No Exif data, might be XMP data instead
28586 if (tiffOffset + 8 > dataView.byteLength) {
28587 Roo.log('Invalid Exif data: Invalid segment size.');
28590 // Check for the two null bytes:
28591 if (dataView.getUint16(offset + 8) !== 0x0000) {
28592 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28595 // Check the byte alignment:
28596 switch (dataView.getUint16(tiffOffset)) {
28598 littleEndian = true;
28601 littleEndian = false;
28604 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28607 // Check for the TIFF tag marker (0x002A):
28608 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28609 Roo.log('Invalid Exif data: Missing TIFF marker.');
28612 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28613 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28615 this.parseExifTags(
28618 tiffOffset + dirOffset,
28623 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28628 if (dirOffset + 6 > dataView.byteLength) {
28629 Roo.log('Invalid Exif data: Invalid directory offset.');
28632 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28633 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28634 if (dirEndOffset + 4 > dataView.byteLength) {
28635 Roo.log('Invalid Exif data: Invalid directory size.');
28638 for (i = 0; i < tagsNumber; i += 1) {
28642 dirOffset + 2 + 12 * i, // tag offset
28646 // Return the offset to the next directory:
28647 return dataView.getUint32(dirEndOffset, littleEndian);
28650 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28652 var tag = dataView.getUint16(offset, littleEndian);
28654 this.exif[tag] = this.getExifValue(
28658 dataView.getUint16(offset + 2, littleEndian), // tag type
28659 dataView.getUint32(offset + 4, littleEndian), // tag length
28664 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28666 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28675 Roo.log('Invalid Exif data: Invalid tag type.');
28679 tagSize = tagType.size * length;
28680 // Determine if the value is contained in the dataOffset bytes,
28681 // or if the value at the dataOffset is a pointer to the actual data:
28682 dataOffset = tagSize > 4 ?
28683 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28684 if (dataOffset + tagSize > dataView.byteLength) {
28685 Roo.log('Invalid Exif data: Invalid data offset.');
28688 if (length === 1) {
28689 return tagType.getValue(dataView, dataOffset, littleEndian);
28692 for (i = 0; i < length; i += 1) {
28693 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28696 if (tagType.ascii) {
28698 // Concatenate the chars:
28699 for (i = 0; i < values.length; i += 1) {
28701 // Ignore the terminating NULL byte(s):
28702 if (c === '\u0000') {
28714 Roo.apply(Roo.bootstrap.UploadCropbox, {
28716 'Orientation': 0x0112
28720 1: 0, //'top-left',
28722 3: 180, //'bottom-right',
28723 // 4: 'bottom-left',
28725 6: 90, //'right-top',
28726 // 7: 'right-bottom',
28727 8: 270 //'left-bottom'
28731 // byte, 8-bit unsigned int:
28733 getValue: function (dataView, dataOffset) {
28734 return dataView.getUint8(dataOffset);
28738 // ascii, 8-bit byte:
28740 getValue: function (dataView, dataOffset) {
28741 return String.fromCharCode(dataView.getUint8(dataOffset));
28746 // short, 16 bit int:
28748 getValue: function (dataView, dataOffset, littleEndian) {
28749 return dataView.getUint16(dataOffset, littleEndian);
28753 // long, 32 bit int:
28755 getValue: function (dataView, dataOffset, littleEndian) {
28756 return dataView.getUint32(dataOffset, littleEndian);
28760 // rational = two long values, first is numerator, second is denominator:
28762 getValue: function (dataView, dataOffset, littleEndian) {
28763 return dataView.getUint32(dataOffset, littleEndian) /
28764 dataView.getUint32(dataOffset + 4, littleEndian);
28768 // slong, 32 bit signed int:
28770 getValue: function (dataView, dataOffset, littleEndian) {
28771 return dataView.getInt32(dataOffset, littleEndian);
28775 // srational, two slongs, first is numerator, second is denominator:
28777 getValue: function (dataView, dataOffset, littleEndian) {
28778 return dataView.getInt32(dataOffset, littleEndian) /
28779 dataView.getInt32(dataOffset + 4, littleEndian);
28789 cls : 'btn-group roo-upload-cropbox-rotate-left',
28790 action : 'rotate-left',
28794 cls : 'btn btn-default',
28795 html : '<i class="fa fa-undo"></i>'
28801 cls : 'btn-group roo-upload-cropbox-picture',
28802 action : 'picture',
28806 cls : 'btn btn-default',
28807 html : '<i class="fa fa-picture-o"></i>'
28813 cls : 'btn-group roo-upload-cropbox-rotate-right',
28814 action : 'rotate-right',
28818 cls : 'btn btn-default',
28819 html : '<i class="fa fa-repeat"></i>'
28827 cls : 'btn-group roo-upload-cropbox-rotate-left',
28828 action : 'rotate-left',
28832 cls : 'btn btn-default',
28833 html : '<i class="fa fa-undo"></i>'
28839 cls : 'btn-group roo-upload-cropbox-download',
28840 action : 'download',
28844 cls : 'btn btn-default',
28845 html : '<i class="fa fa-download"></i>'
28851 cls : 'btn-group roo-upload-cropbox-crop',
28856 cls : 'btn btn-default',
28857 html : '<i class="fa fa-crop"></i>'
28863 cls : 'btn-group roo-upload-cropbox-trash',
28868 cls : 'btn btn-default',
28869 html : '<i class="fa fa-trash"></i>'
28875 cls : 'btn-group roo-upload-cropbox-rotate-right',
28876 action : 'rotate-right',
28880 cls : 'btn btn-default',
28881 html : '<i class="fa fa-repeat"></i>'
28889 cls : 'btn-group roo-upload-cropbox-rotate-left',
28890 action : 'rotate-left',
28894 cls : 'btn btn-default',
28895 html : '<i class="fa fa-undo"></i>'
28901 cls : 'btn-group roo-upload-cropbox-rotate-right',
28902 action : 'rotate-right',
28906 cls : 'btn btn-default',
28907 html : '<i class="fa fa-repeat"></i>'
28920 * @class Roo.bootstrap.DocumentManager
28921 * @extends Roo.bootstrap.Component
28922 * Bootstrap DocumentManager class
28923 * @cfg {String} paramName default 'imageUpload'
28924 * @cfg {String} toolTipName default 'filename'
28925 * @cfg {String} method default POST
28926 * @cfg {String} url action url
28927 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28928 * @cfg {Boolean} multiple multiple upload default true
28929 * @cfg {Number} thumbSize default 300
28930 * @cfg {String} fieldLabel
28931 * @cfg {Number} labelWidth default 4
28932 * @cfg {String} labelAlign (left|top) default left
28933 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28934 * @cfg {Number} labellg set the width of label (1-12)
28935 * @cfg {Number} labelmd set the width of label (1-12)
28936 * @cfg {Number} labelsm set the width of label (1-12)
28937 * @cfg {Number} labelxs set the width of label (1-12)
28940 * Create a new DocumentManager
28941 * @param {Object} config The config object
28944 Roo.bootstrap.DocumentManager = function(config){
28945 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28948 this.delegates = [];
28953 * Fire when initial the DocumentManager
28954 * @param {Roo.bootstrap.DocumentManager} this
28959 * inspect selected file
28960 * @param {Roo.bootstrap.DocumentManager} this
28961 * @param {File} file
28966 * Fire when xhr load exception
28967 * @param {Roo.bootstrap.DocumentManager} this
28968 * @param {XMLHttpRequest} xhr
28970 "exception" : true,
28972 * @event afterupload
28973 * Fire when xhr load exception
28974 * @param {Roo.bootstrap.DocumentManager} this
28975 * @param {XMLHttpRequest} xhr
28977 "afterupload" : true,
28980 * prepare the form data
28981 * @param {Roo.bootstrap.DocumentManager} this
28982 * @param {Object} formData
28987 * Fire when remove the file
28988 * @param {Roo.bootstrap.DocumentManager} this
28989 * @param {Object} file
28994 * Fire after refresh the file
28995 * @param {Roo.bootstrap.DocumentManager} this
29000 * Fire after click the image
29001 * @param {Roo.bootstrap.DocumentManager} this
29002 * @param {Object} file
29007 * Fire when upload a image and editable set to true
29008 * @param {Roo.bootstrap.DocumentManager} this
29009 * @param {Object} file
29013 * @event beforeselectfile
29014 * Fire before select file
29015 * @param {Roo.bootstrap.DocumentManager} this
29017 "beforeselectfile" : true,
29020 * Fire before process file
29021 * @param {Roo.bootstrap.DocumentManager} this
29022 * @param {Object} file
29026 * @event previewrendered
29027 * Fire when preview rendered
29028 * @param {Roo.bootstrap.DocumentManager} this
29029 * @param {Object} file
29031 "previewrendered" : true,
29034 "previewResize" : true
29039 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29048 paramName : 'imageUpload',
29049 toolTipName : 'filename',
29052 labelAlign : 'left',
29062 getAutoCreate : function()
29064 var managerWidget = {
29066 cls : 'roo-document-manager',
29070 cls : 'roo-document-manager-selector',
29075 cls : 'roo-document-manager-uploader',
29079 cls : 'roo-document-manager-upload-btn',
29080 html : '<i class="fa fa-plus"></i>'
29091 cls : 'column col-md-12',
29096 if(this.fieldLabel.length){
29101 cls : 'column col-md-12',
29102 html : this.fieldLabel
29106 cls : 'column col-md-12',
29111 if(this.labelAlign == 'left'){
29116 html : this.fieldLabel
29125 if(this.labelWidth > 12){
29126 content[0].style = "width: " + this.labelWidth + 'px';
29129 if(this.labelWidth < 13 && this.labelmd == 0){
29130 this.labelmd = this.labelWidth;
29133 if(this.labellg > 0){
29134 content[0].cls += ' col-lg-' + this.labellg;
29135 content[1].cls += ' col-lg-' + (12 - this.labellg);
29138 if(this.labelmd > 0){
29139 content[0].cls += ' col-md-' + this.labelmd;
29140 content[1].cls += ' col-md-' + (12 - this.labelmd);
29143 if(this.labelsm > 0){
29144 content[0].cls += ' col-sm-' + this.labelsm;
29145 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29148 if(this.labelxs > 0){
29149 content[0].cls += ' col-xs-' + this.labelxs;
29150 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29158 cls : 'row clearfix',
29166 initEvents : function()
29168 this.managerEl = this.el.select('.roo-document-manager', true).first();
29169 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29171 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29172 this.selectorEl.hide();
29175 this.selectorEl.attr('multiple', 'multiple');
29178 this.selectorEl.on('change', this.onFileSelected, this);
29180 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29181 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29183 this.uploader.on('click', this.onUploaderClick, this);
29185 this.renderProgressDialog();
29189 window.addEventListener("resize", function() { _this.refresh(); } );
29191 this.fireEvent('initial', this);
29194 renderProgressDialog : function()
29198 this.progressDialog = new Roo.bootstrap.Modal({
29199 cls : 'roo-document-manager-progress-dialog',
29200 allow_close : false,
29211 btnclick : function() {
29212 _this.uploadCancel();
29218 this.progressDialog.render(Roo.get(document.body));
29220 this.progress = new Roo.bootstrap.Progress({
29221 cls : 'roo-document-manager-progress',
29226 this.progress.render(this.progressDialog.getChildContainer());
29228 this.progressBar = new Roo.bootstrap.ProgressBar({
29229 cls : 'roo-document-manager-progress-bar',
29232 aria_valuemax : 12,
29236 this.progressBar.render(this.progress.getChildContainer());
29239 onUploaderClick : function(e)
29241 e.preventDefault();
29243 if(this.fireEvent('beforeselectfile', this) != false){
29244 this.selectorEl.dom.click();
29249 onFileSelected : function(e)
29251 e.preventDefault();
29253 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29257 Roo.each(this.selectorEl.dom.files, function(file){
29258 if(this.fireEvent('inspect', this, file) != false){
29259 this.files.push(file);
29269 this.selectorEl.dom.value = '';
29271 if(!this.files || !this.files.length){
29275 if(this.boxes > 0 && this.files.length > this.boxes){
29276 this.files = this.files.slice(0, this.boxes);
29279 this.uploader.show();
29281 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29282 this.uploader.hide();
29291 Roo.each(this.files, function(file){
29293 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29294 var f = this.renderPreview(file);
29299 if(file.type.indexOf('image') != -1){
29300 this.delegates.push(
29302 _this.process(file);
29303 }).createDelegate(this)
29311 _this.process(file);
29312 }).createDelegate(this)
29317 this.files = files;
29319 this.delegates = this.delegates.concat(docs);
29321 if(!this.delegates.length){
29326 this.progressBar.aria_valuemax = this.delegates.length;
29333 arrange : function()
29335 if(!this.delegates.length){
29336 this.progressDialog.hide();
29341 var delegate = this.delegates.shift();
29343 this.progressDialog.show();
29345 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29347 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29352 refresh : function()
29354 this.uploader.show();
29356 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29357 this.uploader.hide();
29360 Roo.isTouch ? this.closable(false) : this.closable(true);
29362 this.fireEvent('refresh', this);
29365 onRemove : function(e, el, o)
29367 e.preventDefault();
29369 this.fireEvent('remove', this, o);
29373 remove : function(o)
29377 Roo.each(this.files, function(file){
29378 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29387 this.files = files;
29394 Roo.each(this.files, function(file){
29399 file.target.remove();
29408 onClick : function(e, el, o)
29410 e.preventDefault();
29412 this.fireEvent('click', this, o);
29416 closable : function(closable)
29418 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29420 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29432 xhrOnLoad : function(xhr)
29434 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29438 if (xhr.readyState !== 4) {
29440 this.fireEvent('exception', this, xhr);
29444 var response = Roo.decode(xhr.responseText);
29446 if(!response.success){
29448 this.fireEvent('exception', this, xhr);
29452 var file = this.renderPreview(response.data);
29454 this.files.push(file);
29458 this.fireEvent('afterupload', this, xhr);
29462 xhrOnError : function(xhr)
29464 Roo.log('xhr on error');
29466 var response = Roo.decode(xhr.responseText);
29473 process : function(file)
29475 if(this.fireEvent('process', this, file) !== false){
29476 if(this.editable && file.type.indexOf('image') != -1){
29477 this.fireEvent('edit', this, file);
29481 this.uploadStart(file, false);
29488 uploadStart : function(file, crop)
29490 this.xhr = new XMLHttpRequest();
29492 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29497 file.xhr = this.xhr;
29499 this.managerEl.createChild({
29501 cls : 'roo-document-manager-loading',
29505 tooltip : file.name,
29506 cls : 'roo-document-manager-thumb',
29507 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29513 this.xhr.open(this.method, this.url, true);
29516 "Accept": "application/json",
29517 "Cache-Control": "no-cache",
29518 "X-Requested-With": "XMLHttpRequest"
29521 for (var headerName in headers) {
29522 var headerValue = headers[headerName];
29524 this.xhr.setRequestHeader(headerName, headerValue);
29530 this.xhr.onload = function()
29532 _this.xhrOnLoad(_this.xhr);
29535 this.xhr.onerror = function()
29537 _this.xhrOnError(_this.xhr);
29540 var formData = new FormData();
29542 formData.append('returnHTML', 'NO');
29545 formData.append('crop', crop);
29548 formData.append(this.paramName, file, file.name);
29555 if(this.fireEvent('prepare', this, formData, options) != false){
29557 if(options.manually){
29561 this.xhr.send(formData);
29565 this.uploadCancel();
29568 uploadCancel : function()
29574 this.delegates = [];
29576 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29583 renderPreview : function(file)
29585 if(typeof(file.target) != 'undefined' && file.target){
29589 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29591 var previewEl = this.managerEl.createChild({
29593 cls : 'roo-document-manager-preview',
29597 tooltip : file[this.toolTipName],
29598 cls : 'roo-document-manager-thumb',
29599 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29604 html : '<i class="fa fa-times-circle"></i>'
29609 var close = previewEl.select('button.close', true).first();
29611 close.on('click', this.onRemove, this, file);
29613 file.target = previewEl;
29615 var image = previewEl.select('img', true).first();
29619 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29621 image.on('click', this.onClick, this, file);
29623 this.fireEvent('previewrendered', this, file);
29629 onPreviewLoad : function(file, image)
29631 if(typeof(file.target) == 'undefined' || !file.target){
29635 var width = image.dom.naturalWidth || image.dom.width;
29636 var height = image.dom.naturalHeight || image.dom.height;
29638 if(!this.previewResize) {
29642 if(width > height){
29643 file.target.addClass('wide');
29647 file.target.addClass('tall');
29652 uploadFromSource : function(file, crop)
29654 this.xhr = new XMLHttpRequest();
29656 this.managerEl.createChild({
29658 cls : 'roo-document-manager-loading',
29662 tooltip : file.name,
29663 cls : 'roo-document-manager-thumb',
29664 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29670 this.xhr.open(this.method, this.url, true);
29673 "Accept": "application/json",
29674 "Cache-Control": "no-cache",
29675 "X-Requested-With": "XMLHttpRequest"
29678 for (var headerName in headers) {
29679 var headerValue = headers[headerName];
29681 this.xhr.setRequestHeader(headerName, headerValue);
29687 this.xhr.onload = function()
29689 _this.xhrOnLoad(_this.xhr);
29692 this.xhr.onerror = function()
29694 _this.xhrOnError(_this.xhr);
29697 var formData = new FormData();
29699 formData.append('returnHTML', 'NO');
29701 formData.append('crop', crop);
29703 if(typeof(file.filename) != 'undefined'){
29704 formData.append('filename', file.filename);
29707 if(typeof(file.mimetype) != 'undefined'){
29708 formData.append('mimetype', file.mimetype);
29713 if(this.fireEvent('prepare', this, formData) != false){
29714 this.xhr.send(formData);
29724 * @class Roo.bootstrap.DocumentViewer
29725 * @extends Roo.bootstrap.Component
29726 * Bootstrap DocumentViewer class
29727 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29728 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29731 * Create a new DocumentViewer
29732 * @param {Object} config The config object
29735 Roo.bootstrap.DocumentViewer = function(config){
29736 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29741 * Fire after initEvent
29742 * @param {Roo.bootstrap.DocumentViewer} this
29748 * @param {Roo.bootstrap.DocumentViewer} this
29753 * Fire after download button
29754 * @param {Roo.bootstrap.DocumentViewer} this
29759 * Fire after trash button
29760 * @param {Roo.bootstrap.DocumentViewer} this
29767 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29769 showDownload : true,
29773 getAutoCreate : function()
29777 cls : 'roo-document-viewer',
29781 cls : 'roo-document-viewer-body',
29785 cls : 'roo-document-viewer-thumb',
29789 cls : 'roo-document-viewer-image'
29797 cls : 'roo-document-viewer-footer',
29800 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29804 cls : 'btn-group roo-document-viewer-download',
29808 cls : 'btn btn-default',
29809 html : '<i class="fa fa-download"></i>'
29815 cls : 'btn-group roo-document-viewer-trash',
29819 cls : 'btn btn-default',
29820 html : '<i class="fa fa-trash"></i>'
29833 initEvents : function()
29835 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29836 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29838 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29839 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29841 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29842 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29844 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29845 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29847 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29848 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29850 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29851 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29853 this.bodyEl.on('click', this.onClick, this);
29854 this.downloadBtn.on('click', this.onDownload, this);
29855 this.trashBtn.on('click', this.onTrash, this);
29857 this.downloadBtn.hide();
29858 this.trashBtn.hide();
29860 if(this.showDownload){
29861 this.downloadBtn.show();
29864 if(this.showTrash){
29865 this.trashBtn.show();
29868 if(!this.showDownload && !this.showTrash) {
29869 this.footerEl.hide();
29874 initial : function()
29876 this.fireEvent('initial', this);
29880 onClick : function(e)
29882 e.preventDefault();
29884 this.fireEvent('click', this);
29887 onDownload : function(e)
29889 e.preventDefault();
29891 this.fireEvent('download', this);
29894 onTrash : function(e)
29896 e.preventDefault();
29898 this.fireEvent('trash', this);
29910 * @class Roo.bootstrap.NavProgressBar
29911 * @extends Roo.bootstrap.Component
29912 * Bootstrap NavProgressBar class
29915 * Create a new nav progress bar
29916 * @param {Object} config The config object
29919 Roo.bootstrap.NavProgressBar = function(config){
29920 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29922 this.bullets = this.bullets || [];
29924 // Roo.bootstrap.NavProgressBar.register(this);
29928 * Fires when the active item changes
29929 * @param {Roo.bootstrap.NavProgressBar} this
29930 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29931 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29938 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29943 getAutoCreate : function()
29945 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29949 cls : 'roo-navigation-bar-group',
29953 cls : 'roo-navigation-top-bar'
29957 cls : 'roo-navigation-bullets-bar',
29961 cls : 'roo-navigation-bar'
29968 cls : 'roo-navigation-bottom-bar'
29978 initEvents: function()
29983 onRender : function(ct, position)
29985 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29987 if(this.bullets.length){
29988 Roo.each(this.bullets, function(b){
29997 addItem : function(cfg)
29999 var item = new Roo.bootstrap.NavProgressItem(cfg);
30001 item.parentId = this.id;
30002 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30005 var top = new Roo.bootstrap.Element({
30007 cls : 'roo-navigation-bar-text'
30010 var bottom = new Roo.bootstrap.Element({
30012 cls : 'roo-navigation-bar-text'
30015 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30016 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30018 var topText = new Roo.bootstrap.Element({
30020 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30023 var bottomText = new Roo.bootstrap.Element({
30025 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30028 topText.onRender(top.el, null);
30029 bottomText.onRender(bottom.el, null);
30032 item.bottomEl = bottom;
30035 this.barItems.push(item);
30040 getActive : function()
30042 var active = false;
30044 Roo.each(this.barItems, function(v){
30046 if (!v.isActive()) {
30058 setActiveItem : function(item)
30062 Roo.each(this.barItems, function(v){
30063 if (v.rid == item.rid) {
30067 if (v.isActive()) {
30068 v.setActive(false);
30073 item.setActive(true);
30075 this.fireEvent('changed', this, item, prev);
30078 getBarItem: function(rid)
30082 Roo.each(this.barItems, function(e) {
30083 if (e.rid != rid) {
30094 indexOfItem : function(item)
30098 Roo.each(this.barItems, function(v, i){
30100 if (v.rid != item.rid) {
30111 setActiveNext : function()
30113 var i = this.indexOfItem(this.getActive());
30115 if (i > this.barItems.length) {
30119 this.setActiveItem(this.barItems[i+1]);
30122 setActivePrev : function()
30124 var i = this.indexOfItem(this.getActive());
30130 this.setActiveItem(this.barItems[i-1]);
30133 format : function()
30135 if(!this.barItems.length){
30139 var width = 100 / this.barItems.length;
30141 Roo.each(this.barItems, function(i){
30142 i.el.setStyle('width', width + '%');
30143 i.topEl.el.setStyle('width', width + '%');
30144 i.bottomEl.el.setStyle('width', width + '%');
30153 * Nav Progress Item
30158 * @class Roo.bootstrap.NavProgressItem
30159 * @extends Roo.bootstrap.Component
30160 * Bootstrap NavProgressItem class
30161 * @cfg {String} rid the reference id
30162 * @cfg {Boolean} active (true|false) Is item active default false
30163 * @cfg {Boolean} disabled (true|false) Is item active default false
30164 * @cfg {String} html
30165 * @cfg {String} position (top|bottom) text position default bottom
30166 * @cfg {String} icon show icon instead of number
30169 * Create a new NavProgressItem
30170 * @param {Object} config The config object
30172 Roo.bootstrap.NavProgressItem = function(config){
30173 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30178 * The raw click event for the entire grid.
30179 * @param {Roo.bootstrap.NavProgressItem} this
30180 * @param {Roo.EventObject} e
30187 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30193 position : 'bottom',
30196 getAutoCreate : function()
30198 var iconCls = 'roo-navigation-bar-item-icon';
30200 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30204 cls: 'roo-navigation-bar-item',
30214 cfg.cls += ' active';
30217 cfg.cls += ' disabled';
30223 disable : function()
30225 this.setDisabled(true);
30228 enable : function()
30230 this.setDisabled(false);
30233 initEvents: function()
30235 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30237 this.iconEl.on('click', this.onClick, this);
30240 onClick : function(e)
30242 e.preventDefault();
30248 if(this.fireEvent('click', this, e) === false){
30252 this.parent().setActiveItem(this);
30255 isActive: function ()
30257 return this.active;
30260 setActive : function(state)
30262 if(this.active == state){
30266 this.active = state;
30269 this.el.addClass('active');
30273 this.el.removeClass('active');
30278 setDisabled : function(state)
30280 if(this.disabled == state){
30284 this.disabled = state;
30287 this.el.addClass('disabled');
30291 this.el.removeClass('disabled');
30294 tooltipEl : function()
30296 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30309 * @class Roo.bootstrap.FieldLabel
30310 * @extends Roo.bootstrap.Component
30311 * Bootstrap FieldLabel class
30312 * @cfg {String} html contents of the element
30313 * @cfg {String} tag tag of the element default label
30314 * @cfg {String} cls class of the element
30315 * @cfg {String} target label target
30316 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30317 * @cfg {String} invalidClass default "text-warning"
30318 * @cfg {String} validClass default "text-success"
30319 * @cfg {String} iconTooltip default "This field is required"
30320 * @cfg {String} indicatorpos (left|right) default left
30323 * Create a new FieldLabel
30324 * @param {Object} config The config object
30327 Roo.bootstrap.FieldLabel = function(config){
30328 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30333 * Fires after the field has been marked as invalid.
30334 * @param {Roo.form.FieldLabel} this
30335 * @param {String} msg The validation message
30340 * Fires after the field has been validated with no errors.
30341 * @param {Roo.form.FieldLabel} this
30347 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30354 invalidClass : 'has-warning',
30355 validClass : 'has-success',
30356 iconTooltip : 'This field is required',
30357 indicatorpos : 'left',
30359 getAutoCreate : function(){
30362 if (!this.allowBlank) {
30368 cls : 'roo-bootstrap-field-label ' + this.cls,
30373 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30374 tooltip : this.iconTooltip
30383 if(this.indicatorpos == 'right'){
30386 cls : 'roo-bootstrap-field-label ' + this.cls,
30395 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30396 tooltip : this.iconTooltip
30405 initEvents: function()
30407 Roo.bootstrap.Element.superclass.initEvents.call(this);
30409 this.indicator = this.indicatorEl();
30411 if(this.indicator){
30412 this.indicator.removeClass('visible');
30413 this.indicator.addClass('invisible');
30416 Roo.bootstrap.FieldLabel.register(this);
30419 indicatorEl : function()
30421 var indicator = this.el.select('i.roo-required-indicator',true).first();
30432 * Mark this field as valid
30434 markValid : function()
30436 if(this.indicator){
30437 this.indicator.removeClass('visible');
30438 this.indicator.addClass('invisible');
30441 this.el.removeClass(this.invalidClass);
30443 this.el.addClass(this.validClass);
30445 this.fireEvent('valid', this);
30449 * Mark this field as invalid
30450 * @param {String} msg The validation message
30452 markInvalid : function(msg)
30454 if(this.indicator){
30455 this.indicator.removeClass('invisible');
30456 this.indicator.addClass('visible');
30459 this.el.removeClass(this.validClass);
30461 this.el.addClass(this.invalidClass);
30463 this.fireEvent('invalid', this, msg);
30469 Roo.apply(Roo.bootstrap.FieldLabel, {
30474 * register a FieldLabel Group
30475 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30477 register : function(label)
30479 if(this.groups.hasOwnProperty(label.target)){
30483 this.groups[label.target] = label;
30487 * fetch a FieldLabel Group based on the target
30488 * @param {string} target
30489 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30491 get: function(target) {
30492 if (typeof(this.groups[target]) == 'undefined') {
30496 return this.groups[target] ;
30505 * page DateSplitField.
30511 * @class Roo.bootstrap.DateSplitField
30512 * @extends Roo.bootstrap.Component
30513 * Bootstrap DateSplitField class
30514 * @cfg {string} fieldLabel - the label associated
30515 * @cfg {Number} labelWidth set the width of label (0-12)
30516 * @cfg {String} labelAlign (top|left)
30517 * @cfg {Boolean} dayAllowBlank (true|false) default false
30518 * @cfg {Boolean} monthAllowBlank (true|false) default false
30519 * @cfg {Boolean} yearAllowBlank (true|false) default false
30520 * @cfg {string} dayPlaceholder
30521 * @cfg {string} monthPlaceholder
30522 * @cfg {string} yearPlaceholder
30523 * @cfg {string} dayFormat default 'd'
30524 * @cfg {string} monthFormat default 'm'
30525 * @cfg {string} yearFormat default 'Y'
30526 * @cfg {Number} labellg set the width of label (1-12)
30527 * @cfg {Number} labelmd set the width of label (1-12)
30528 * @cfg {Number} labelsm set the width of label (1-12)
30529 * @cfg {Number} labelxs set the width of label (1-12)
30533 * Create a new DateSplitField
30534 * @param {Object} config The config object
30537 Roo.bootstrap.DateSplitField = function(config){
30538 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30544 * getting the data of years
30545 * @param {Roo.bootstrap.DateSplitField} this
30546 * @param {Object} years
30551 * getting the data of days
30552 * @param {Roo.bootstrap.DateSplitField} this
30553 * @param {Object} days
30558 * Fires after the field has been marked as invalid.
30559 * @param {Roo.form.Field} this
30560 * @param {String} msg The validation message
30565 * Fires after the field has been validated with no errors.
30566 * @param {Roo.form.Field} this
30572 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30575 labelAlign : 'top',
30577 dayAllowBlank : false,
30578 monthAllowBlank : false,
30579 yearAllowBlank : false,
30580 dayPlaceholder : '',
30581 monthPlaceholder : '',
30582 yearPlaceholder : '',
30586 isFormField : true,
30592 getAutoCreate : function()
30596 cls : 'row roo-date-split-field-group',
30601 cls : 'form-hidden-field roo-date-split-field-group-value',
30607 var labelCls = 'col-md-12';
30608 var contentCls = 'col-md-4';
30610 if(this.fieldLabel){
30614 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30618 html : this.fieldLabel
30623 if(this.labelAlign == 'left'){
30625 if(this.labelWidth > 12){
30626 label.style = "width: " + this.labelWidth + 'px';
30629 if(this.labelWidth < 13 && this.labelmd == 0){
30630 this.labelmd = this.labelWidth;
30633 if(this.labellg > 0){
30634 labelCls = ' col-lg-' + this.labellg;
30635 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30638 if(this.labelmd > 0){
30639 labelCls = ' col-md-' + this.labelmd;
30640 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30643 if(this.labelsm > 0){
30644 labelCls = ' col-sm-' + this.labelsm;
30645 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30648 if(this.labelxs > 0){
30649 labelCls = ' col-xs-' + this.labelxs;
30650 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30654 label.cls += ' ' + labelCls;
30656 cfg.cn.push(label);
30659 Roo.each(['day', 'month', 'year'], function(t){
30662 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30669 inputEl: function ()
30671 return this.el.select('.roo-date-split-field-group-value', true).first();
30674 onRender : function(ct, position)
30678 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30680 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30682 this.dayField = new Roo.bootstrap.ComboBox({
30683 allowBlank : this.dayAllowBlank,
30684 alwaysQuery : true,
30685 displayField : 'value',
30688 forceSelection : true,
30690 placeholder : this.dayPlaceholder,
30691 selectOnFocus : true,
30692 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30693 triggerAction : 'all',
30695 valueField : 'value',
30696 store : new Roo.data.SimpleStore({
30697 data : (function() {
30699 _this.fireEvent('days', _this, days);
30702 fields : [ 'value' ]
30705 select : function (_self, record, index)
30707 _this.setValue(_this.getValue());
30712 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30714 this.monthField = new Roo.bootstrap.MonthField({
30715 after : '<i class=\"fa fa-calendar\"></i>',
30716 allowBlank : this.monthAllowBlank,
30717 placeholder : this.monthPlaceholder,
30720 render : function (_self)
30722 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30723 e.preventDefault();
30727 select : function (_self, oldvalue, newvalue)
30729 _this.setValue(_this.getValue());
30734 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30736 this.yearField = new Roo.bootstrap.ComboBox({
30737 allowBlank : this.yearAllowBlank,
30738 alwaysQuery : true,
30739 displayField : 'value',
30742 forceSelection : true,
30744 placeholder : this.yearPlaceholder,
30745 selectOnFocus : true,
30746 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30747 triggerAction : 'all',
30749 valueField : 'value',
30750 store : new Roo.data.SimpleStore({
30751 data : (function() {
30753 _this.fireEvent('years', _this, years);
30756 fields : [ 'value' ]
30759 select : function (_self, record, index)
30761 _this.setValue(_this.getValue());
30766 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30769 setValue : function(v, format)
30771 this.inputEl.dom.value = v;
30773 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30775 var d = Date.parseDate(v, f);
30782 this.setDay(d.format(this.dayFormat));
30783 this.setMonth(d.format(this.monthFormat));
30784 this.setYear(d.format(this.yearFormat));
30791 setDay : function(v)
30793 this.dayField.setValue(v);
30794 this.inputEl.dom.value = this.getValue();
30799 setMonth : function(v)
30801 this.monthField.setValue(v, true);
30802 this.inputEl.dom.value = this.getValue();
30807 setYear : function(v)
30809 this.yearField.setValue(v);
30810 this.inputEl.dom.value = this.getValue();
30815 getDay : function()
30817 return this.dayField.getValue();
30820 getMonth : function()
30822 return this.monthField.getValue();
30825 getYear : function()
30827 return this.yearField.getValue();
30830 getValue : function()
30832 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30834 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30844 this.inputEl.dom.value = '';
30849 validate : function()
30851 var d = this.dayField.validate();
30852 var m = this.monthField.validate();
30853 var y = this.yearField.validate();
30858 (!this.dayAllowBlank && !d) ||
30859 (!this.monthAllowBlank && !m) ||
30860 (!this.yearAllowBlank && !y)
30865 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30874 this.markInvalid();
30879 markValid : function()
30882 var label = this.el.select('label', true).first();
30883 var icon = this.el.select('i.fa-star', true).first();
30889 this.fireEvent('valid', this);
30893 * Mark this field as invalid
30894 * @param {String} msg The validation message
30896 markInvalid : function(msg)
30899 var label = this.el.select('label', true).first();
30900 var icon = this.el.select('i.fa-star', true).first();
30902 if(label && !icon){
30903 this.el.select('.roo-date-split-field-label', true).createChild({
30905 cls : 'text-danger fa fa-lg fa-star',
30906 tooltip : 'This field is required',
30907 style : 'margin-right:5px;'
30911 this.fireEvent('invalid', this, msg);
30914 clearInvalid : function()
30916 var label = this.el.select('label', true).first();
30917 var icon = this.el.select('i.fa-star', true).first();
30923 this.fireEvent('valid', this);
30926 getName: function()
30936 * http://masonry.desandro.com
30938 * The idea is to render all the bricks based on vertical width...
30940 * The original code extends 'outlayer' - we might need to use that....
30946 * @class Roo.bootstrap.LayoutMasonry
30947 * @extends Roo.bootstrap.Component
30948 * Bootstrap Layout Masonry class
30951 * Create a new Element
30952 * @param {Object} config The config object
30955 Roo.bootstrap.LayoutMasonry = function(config){
30957 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30961 Roo.bootstrap.LayoutMasonry.register(this);
30967 * Fire after layout the items
30968 * @param {Roo.bootstrap.LayoutMasonry} this
30969 * @param {Roo.EventObject} e
30976 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30979 * @cfg {Boolean} isLayoutInstant = no animation?
30981 isLayoutInstant : false, // needed?
30984 * @cfg {Number} boxWidth width of the columns
30989 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30994 * @cfg {Number} padWidth padding below box..
30999 * @cfg {Number} gutter gutter width..
31004 * @cfg {Number} maxCols maximum number of columns
31010 * @cfg {Boolean} isAutoInitial defalut true
31012 isAutoInitial : true,
31017 * @cfg {Boolean} isHorizontal defalut false
31019 isHorizontal : false,
31021 currentSize : null,
31027 bricks: null, //CompositeElement
31031 _isLayoutInited : false,
31033 // isAlternative : false, // only use for vertical layout...
31036 * @cfg {Number} alternativePadWidth padding below box..
31038 alternativePadWidth : 50,
31040 selectedBrick : [],
31042 getAutoCreate : function(){
31044 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31048 cls: 'blog-masonary-wrapper ' + this.cls,
31050 cls : 'mas-boxes masonary'
31057 getChildContainer: function( )
31059 if (this.boxesEl) {
31060 return this.boxesEl;
31063 this.boxesEl = this.el.select('.mas-boxes').first();
31065 return this.boxesEl;
31069 initEvents : function()
31073 if(this.isAutoInitial){
31074 Roo.log('hook children rendered');
31075 this.on('childrenrendered', function() {
31076 Roo.log('children rendered');
31082 initial : function()
31084 this.selectedBrick = [];
31086 this.currentSize = this.el.getBox(true);
31088 Roo.EventManager.onWindowResize(this.resize, this);
31090 if(!this.isAutoInitial){
31098 //this.layout.defer(500,this);
31102 resize : function()
31104 var cs = this.el.getBox(true);
31107 this.currentSize.width == cs.width &&
31108 this.currentSize.x == cs.x &&
31109 this.currentSize.height == cs.height &&
31110 this.currentSize.y == cs.y
31112 Roo.log("no change in with or X or Y");
31116 this.currentSize = cs;
31122 layout : function()
31124 this._resetLayout();
31126 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31128 this.layoutItems( isInstant );
31130 this._isLayoutInited = true;
31132 this.fireEvent('layout', this);
31136 _resetLayout : function()
31138 if(this.isHorizontal){
31139 this.horizontalMeasureColumns();
31143 this.verticalMeasureColumns();
31147 verticalMeasureColumns : function()
31149 this.getContainerWidth();
31151 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31152 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31156 var boxWidth = this.boxWidth + this.padWidth;
31158 if(this.containerWidth < this.boxWidth){
31159 boxWidth = this.containerWidth
31162 var containerWidth = this.containerWidth;
31164 var cols = Math.floor(containerWidth / boxWidth);
31166 this.cols = Math.max( cols, 1 );
31168 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31170 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31172 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31174 this.colWidth = boxWidth + avail - this.padWidth;
31176 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31177 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31180 horizontalMeasureColumns : function()
31182 this.getContainerWidth();
31184 var boxWidth = this.boxWidth;
31186 if(this.containerWidth < boxWidth){
31187 boxWidth = this.containerWidth;
31190 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31192 this.el.setHeight(boxWidth);
31196 getContainerWidth : function()
31198 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31201 layoutItems : function( isInstant )
31203 Roo.log(this.bricks);
31205 var items = Roo.apply([], this.bricks);
31207 if(this.isHorizontal){
31208 this._horizontalLayoutItems( items , isInstant );
31212 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31213 // this._verticalAlternativeLayoutItems( items , isInstant );
31217 this._verticalLayoutItems( items , isInstant );
31221 _verticalLayoutItems : function ( items , isInstant)
31223 if ( !items || !items.length ) {
31228 ['xs', 'xs', 'xs', 'tall'],
31229 ['xs', 'xs', 'tall'],
31230 ['xs', 'xs', 'sm'],
31231 ['xs', 'xs', 'xs'],
31237 ['sm', 'xs', 'xs'],
31241 ['tall', 'xs', 'xs', 'xs'],
31242 ['tall', 'xs', 'xs'],
31254 Roo.each(items, function(item, k){
31256 switch (item.size) {
31257 // these layouts take up a full box,
31268 boxes.push([item]);
31291 var filterPattern = function(box, length)
31299 var pattern = box.slice(0, length);
31303 Roo.each(pattern, function(i){
31304 format.push(i.size);
31307 Roo.each(standard, function(s){
31309 if(String(s) != String(format)){
31318 if(!match && length == 1){
31323 filterPattern(box, length - 1);
31327 queue.push(pattern);
31329 box = box.slice(length, box.length);
31331 filterPattern(box, 4);
31337 Roo.each(boxes, function(box, k){
31343 if(box.length == 1){
31348 filterPattern(box, 4);
31352 this._processVerticalLayoutQueue( queue, isInstant );
31356 // _verticalAlternativeLayoutItems : function( items , isInstant )
31358 // if ( !items || !items.length ) {
31362 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31366 _horizontalLayoutItems : function ( items , isInstant)
31368 if ( !items || !items.length || items.length < 3) {
31374 var eItems = items.slice(0, 3);
31376 items = items.slice(3, items.length);
31379 ['xs', 'xs', 'xs', 'wide'],
31380 ['xs', 'xs', 'wide'],
31381 ['xs', 'xs', 'sm'],
31382 ['xs', 'xs', 'xs'],
31388 ['sm', 'xs', 'xs'],
31392 ['wide', 'xs', 'xs', 'xs'],
31393 ['wide', 'xs', 'xs'],
31406 Roo.each(items, function(item, k){
31408 switch (item.size) {
31419 boxes.push([item]);
31443 var filterPattern = function(box, length)
31451 var pattern = box.slice(0, length);
31455 Roo.each(pattern, function(i){
31456 format.push(i.size);
31459 Roo.each(standard, function(s){
31461 if(String(s) != String(format)){
31470 if(!match && length == 1){
31475 filterPattern(box, length - 1);
31479 queue.push(pattern);
31481 box = box.slice(length, box.length);
31483 filterPattern(box, 4);
31489 Roo.each(boxes, function(box, k){
31495 if(box.length == 1){
31500 filterPattern(box, 4);
31507 var pos = this.el.getBox(true);
31511 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31513 var hit_end = false;
31515 Roo.each(queue, function(box){
31519 Roo.each(box, function(b){
31521 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31531 Roo.each(box, function(b){
31533 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31536 mx = Math.max(mx, b.x);
31540 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31544 Roo.each(box, function(b){
31546 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31560 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31563 /** Sets position of item in DOM
31564 * @param {Element} item
31565 * @param {Number} x - horizontal position
31566 * @param {Number} y - vertical position
31567 * @param {Boolean} isInstant - disables transitions
31569 _processVerticalLayoutQueue : function( queue, isInstant )
31571 var pos = this.el.getBox(true);
31576 for (var i = 0; i < this.cols; i++){
31580 Roo.each(queue, function(box, k){
31582 var col = k % this.cols;
31584 Roo.each(box, function(b,kk){
31586 b.el.position('absolute');
31588 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31589 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31591 if(b.size == 'md-left' || b.size == 'md-right'){
31592 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31593 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31596 b.el.setWidth(width);
31597 b.el.setHeight(height);
31599 b.el.select('iframe',true).setSize(width,height);
31603 for (var i = 0; i < this.cols; i++){
31605 if(maxY[i] < maxY[col]){
31610 col = Math.min(col, i);
31614 x = pos.x + col * (this.colWidth + this.padWidth);
31618 var positions = [];
31620 switch (box.length){
31622 positions = this.getVerticalOneBoxColPositions(x, y, box);
31625 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31628 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31631 positions = this.getVerticalFourBoxColPositions(x, y, box);
31637 Roo.each(box, function(b,kk){
31639 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31641 var sz = b.el.getSize();
31643 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31651 for (var i = 0; i < this.cols; i++){
31652 mY = Math.max(mY, maxY[i]);
31655 this.el.setHeight(mY - pos.y);
31659 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31661 // var pos = this.el.getBox(true);
31664 // var maxX = pos.right;
31666 // var maxHeight = 0;
31668 // Roo.each(items, function(item, k){
31672 // item.el.position('absolute');
31674 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31676 // item.el.setWidth(width);
31678 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31680 // item.el.setHeight(height);
31683 // item.el.setXY([x, y], isInstant ? false : true);
31685 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31688 // y = y + height + this.alternativePadWidth;
31690 // maxHeight = maxHeight + height + this.alternativePadWidth;
31694 // this.el.setHeight(maxHeight);
31698 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31700 var pos = this.el.getBox(true);
31705 var maxX = pos.right;
31707 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31709 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31711 Roo.each(queue, function(box, k){
31713 Roo.each(box, function(b, kk){
31715 b.el.position('absolute');
31717 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31718 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31720 if(b.size == 'md-left' || b.size == 'md-right'){
31721 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31722 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31725 b.el.setWidth(width);
31726 b.el.setHeight(height);
31734 var positions = [];
31736 switch (box.length){
31738 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31741 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31744 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31747 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31753 Roo.each(box, function(b,kk){
31755 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31757 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31765 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31767 Roo.each(eItems, function(b,k){
31769 b.size = (k == 0) ? 'sm' : 'xs';
31770 b.x = (k == 0) ? 2 : 1;
31771 b.y = (k == 0) ? 2 : 1;
31773 b.el.position('absolute');
31775 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31777 b.el.setWidth(width);
31779 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31781 b.el.setHeight(height);
31785 var positions = [];
31788 x : maxX - this.unitWidth * 2 - this.gutter,
31793 x : maxX - this.unitWidth,
31794 y : minY + (this.unitWidth + this.gutter) * 2
31798 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31802 Roo.each(eItems, function(b,k){
31804 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31810 getVerticalOneBoxColPositions : function(x, y, box)
31814 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31816 if(box[0].size == 'md-left'){
31820 if(box[0].size == 'md-right'){
31825 x : x + (this.unitWidth + this.gutter) * rand,
31832 getVerticalTwoBoxColPositions : function(x, y, box)
31836 if(box[0].size == 'xs'){
31840 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31844 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31858 x : x + (this.unitWidth + this.gutter) * 2,
31859 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31866 getVerticalThreeBoxColPositions : function(x, y, box)
31870 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31878 x : x + (this.unitWidth + this.gutter) * 1,
31883 x : x + (this.unitWidth + this.gutter) * 2,
31891 if(box[0].size == 'xs' && box[1].size == 'xs'){
31900 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31904 x : x + (this.unitWidth + this.gutter) * 1,
31918 x : x + (this.unitWidth + this.gutter) * 2,
31923 x : x + (this.unitWidth + this.gutter) * 2,
31924 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31931 getVerticalFourBoxColPositions : function(x, y, box)
31935 if(box[0].size == 'xs'){
31944 y : y + (this.unitHeight + this.gutter) * 1
31949 y : y + (this.unitHeight + this.gutter) * 2
31953 x : x + (this.unitWidth + this.gutter) * 1,
31967 x : x + (this.unitWidth + this.gutter) * 2,
31972 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31973 y : y + (this.unitHeight + this.gutter) * 1
31977 x : x + (this.unitWidth + this.gutter) * 2,
31978 y : y + (this.unitWidth + this.gutter) * 2
31985 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31989 if(box[0].size == 'md-left'){
31991 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31998 if(box[0].size == 'md-right'){
32000 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32001 y : minY + (this.unitWidth + this.gutter) * 1
32007 var rand = Math.floor(Math.random() * (4 - box[0].y));
32010 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32011 y : minY + (this.unitWidth + this.gutter) * rand
32018 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32022 if(box[0].size == 'xs'){
32025 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32030 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32031 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32039 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32044 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32045 y : minY + (this.unitWidth + this.gutter) * 2
32052 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32056 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32059 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32064 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32065 y : minY + (this.unitWidth + this.gutter) * 1
32069 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32070 y : minY + (this.unitWidth + this.gutter) * 2
32077 if(box[0].size == 'xs' && box[1].size == 'xs'){
32080 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32085 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32090 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32091 y : minY + (this.unitWidth + this.gutter) * 1
32099 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32104 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32105 y : minY + (this.unitWidth + this.gutter) * 2
32109 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32110 y : minY + (this.unitWidth + this.gutter) * 2
32117 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32121 if(box[0].size == 'xs'){
32124 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32129 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32134 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),
32139 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32140 y : minY + (this.unitWidth + this.gutter) * 1
32148 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32153 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32154 y : minY + (this.unitWidth + this.gutter) * 2
32158 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32159 y : minY + (this.unitWidth + this.gutter) * 2
32163 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),
32164 y : minY + (this.unitWidth + this.gutter) * 2
32172 * remove a Masonry Brick
32173 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32175 removeBrick : function(brick_id)
32181 for (var i = 0; i<this.bricks.length; i++) {
32182 if (this.bricks[i].id == brick_id) {
32183 this.bricks.splice(i,1);
32184 this.el.dom.removeChild(Roo.get(brick_id).dom);
32191 * adds a Masonry Brick
32192 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32194 addBrick : function(cfg)
32196 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32197 //this.register(cn);
32198 cn.parentId = this.id;
32199 cn.render(this.el);
32204 * register a Masonry Brick
32205 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32208 register : function(brick)
32210 this.bricks.push(brick);
32211 brick.masonryId = this.id;
32215 * clear all the Masonry Brick
32217 clearAll : function()
32220 //this.getChildContainer().dom.innerHTML = "";
32221 this.el.dom.innerHTML = '';
32224 getSelected : function()
32226 if (!this.selectedBrick) {
32230 return this.selectedBrick;
32234 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32238 * register a Masonry Layout
32239 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32242 register : function(layout)
32244 this.groups[layout.id] = layout;
32247 * fetch a Masonry Layout based on the masonry layout ID
32248 * @param {string} the masonry layout to add
32249 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32252 get: function(layout_id) {
32253 if (typeof(this.groups[layout_id]) == 'undefined') {
32256 return this.groups[layout_id] ;
32268 * http://masonry.desandro.com
32270 * The idea is to render all the bricks based on vertical width...
32272 * The original code extends 'outlayer' - we might need to use that....
32278 * @class Roo.bootstrap.LayoutMasonryAuto
32279 * @extends Roo.bootstrap.Component
32280 * Bootstrap Layout Masonry class
32283 * Create a new Element
32284 * @param {Object} config The config object
32287 Roo.bootstrap.LayoutMasonryAuto = function(config){
32288 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32291 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32294 * @cfg {Boolean} isFitWidth - resize the width..
32296 isFitWidth : false, // options..
32298 * @cfg {Boolean} isOriginLeft = left align?
32300 isOriginLeft : true,
32302 * @cfg {Boolean} isOriginTop = top align?
32304 isOriginTop : false,
32306 * @cfg {Boolean} isLayoutInstant = no animation?
32308 isLayoutInstant : false, // needed?
32310 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32312 isResizingContainer : true,
32314 * @cfg {Number} columnWidth width of the columns
32320 * @cfg {Number} maxCols maximum number of columns
32325 * @cfg {Number} padHeight padding below box..
32331 * @cfg {Boolean} isAutoInitial defalut true
32334 isAutoInitial : true,
32340 initialColumnWidth : 0,
32341 currentSize : null,
32343 colYs : null, // array.
32350 bricks: null, //CompositeElement
32351 cols : 0, // array?
32352 // element : null, // wrapped now this.el
32353 _isLayoutInited : null,
32356 getAutoCreate : function(){
32360 cls: 'blog-masonary-wrapper ' + this.cls,
32362 cls : 'mas-boxes masonary'
32369 getChildContainer: function( )
32371 if (this.boxesEl) {
32372 return this.boxesEl;
32375 this.boxesEl = this.el.select('.mas-boxes').first();
32377 return this.boxesEl;
32381 initEvents : function()
32385 if(this.isAutoInitial){
32386 Roo.log('hook children rendered');
32387 this.on('childrenrendered', function() {
32388 Roo.log('children rendered');
32395 initial : function()
32397 this.reloadItems();
32399 this.currentSize = this.el.getBox(true);
32401 /// was window resize... - let's see if this works..
32402 Roo.EventManager.onWindowResize(this.resize, this);
32404 if(!this.isAutoInitial){
32409 this.layout.defer(500,this);
32412 reloadItems: function()
32414 this.bricks = this.el.select('.masonry-brick', true);
32416 this.bricks.each(function(b) {
32417 //Roo.log(b.getSize());
32418 if (!b.attr('originalwidth')) {
32419 b.attr('originalwidth', b.getSize().width);
32424 Roo.log(this.bricks.elements.length);
32427 resize : function()
32430 var cs = this.el.getBox(true);
32432 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32433 Roo.log("no change in with or X");
32436 this.currentSize = cs;
32440 layout : function()
32443 this._resetLayout();
32444 //this._manageStamps();
32446 // don't animate first layout
32447 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32448 this.layoutItems( isInstant );
32450 // flag for initalized
32451 this._isLayoutInited = true;
32454 layoutItems : function( isInstant )
32456 //var items = this._getItemsForLayout( this.items );
32457 // original code supports filtering layout items.. we just ignore it..
32459 this._layoutItems( this.bricks , isInstant );
32461 this._postLayout();
32463 _layoutItems : function ( items , isInstant)
32465 //this.fireEvent( 'layout', this, items );
32468 if ( !items || !items.elements.length ) {
32469 // no items, emit event with empty array
32474 items.each(function(item) {
32475 Roo.log("layout item");
32477 // get x/y object from method
32478 var position = this._getItemLayoutPosition( item );
32480 position.item = item;
32481 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32482 queue.push( position );
32485 this._processLayoutQueue( queue );
32487 /** Sets position of item in DOM
32488 * @param {Element} item
32489 * @param {Number} x - horizontal position
32490 * @param {Number} y - vertical position
32491 * @param {Boolean} isInstant - disables transitions
32493 _processLayoutQueue : function( queue )
32495 for ( var i=0, len = queue.length; i < len; i++ ) {
32496 var obj = queue[i];
32497 obj.item.position('absolute');
32498 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32504 * Any logic you want to do after each layout,
32505 * i.e. size the container
32507 _postLayout : function()
32509 this.resizeContainer();
32512 resizeContainer : function()
32514 if ( !this.isResizingContainer ) {
32517 var size = this._getContainerSize();
32519 this.el.setSize(size.width,size.height);
32520 this.boxesEl.setSize(size.width,size.height);
32526 _resetLayout : function()
32528 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32529 this.colWidth = this.el.getWidth();
32530 //this.gutter = this.el.getWidth();
32532 this.measureColumns();
32538 this.colYs.push( 0 );
32544 measureColumns : function()
32546 this.getContainerWidth();
32547 // if columnWidth is 0, default to outerWidth of first item
32548 if ( !this.columnWidth ) {
32549 var firstItem = this.bricks.first();
32550 Roo.log(firstItem);
32551 this.columnWidth = this.containerWidth;
32552 if (firstItem && firstItem.attr('originalwidth') ) {
32553 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32555 // columnWidth fall back to item of first element
32556 Roo.log("set column width?");
32557 this.initialColumnWidth = this.columnWidth ;
32559 // if first elem has no width, default to size of container
32564 if (this.initialColumnWidth) {
32565 this.columnWidth = this.initialColumnWidth;
32570 // column width is fixed at the top - however if container width get's smaller we should
32573 // this bit calcs how man columns..
32575 var columnWidth = this.columnWidth += this.gutter;
32577 // calculate columns
32578 var containerWidth = this.containerWidth + this.gutter;
32580 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32581 // fix rounding errors, typically with gutters
32582 var excess = columnWidth - containerWidth % columnWidth;
32585 // if overshoot is less than a pixel, round up, otherwise floor it
32586 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32587 cols = Math[ mathMethod ]( cols );
32588 this.cols = Math.max( cols, 1 );
32589 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32591 // padding positioning..
32592 var totalColWidth = this.cols * this.columnWidth;
32593 var padavail = this.containerWidth - totalColWidth;
32594 // so for 2 columns - we need 3 'pads'
32596 var padNeeded = (1+this.cols) * this.padWidth;
32598 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32600 this.columnWidth += padExtra
32601 //this.padWidth = Math.floor(padavail / ( this.cols));
32603 // adjust colum width so that padding is fixed??
32605 // we have 3 columns ... total = width * 3
32606 // we have X left over... that should be used by
32608 //if (this.expandC) {
32616 getContainerWidth : function()
32618 /* // container is parent if fit width
32619 var container = this.isFitWidth ? this.element.parentNode : this.element;
32620 // check that this.size and size are there
32621 // IE8 triggers resize on body size change, so they might not be
32623 var size = getSize( container ); //FIXME
32624 this.containerWidth = size && size.innerWidth; //FIXME
32627 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32631 _getItemLayoutPosition : function( item ) // what is item?
32633 // we resize the item to our columnWidth..
32635 item.setWidth(this.columnWidth);
32636 item.autoBoxAdjust = false;
32638 var sz = item.getSize();
32640 // how many columns does this brick span
32641 var remainder = this.containerWidth % this.columnWidth;
32643 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32644 // round if off by 1 pixel, otherwise use ceil
32645 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32646 colSpan = Math.min( colSpan, this.cols );
32648 // normally this should be '1' as we dont' currently allow multi width columns..
32650 var colGroup = this._getColGroup( colSpan );
32651 // get the minimum Y value from the columns
32652 var minimumY = Math.min.apply( Math, colGroup );
32653 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32655 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32657 // position the brick
32659 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32660 y: this.currentSize.y + minimumY + this.padHeight
32664 // apply setHeight to necessary columns
32665 var setHeight = minimumY + sz.height + this.padHeight;
32666 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32668 var setSpan = this.cols + 1 - colGroup.length;
32669 for ( var i = 0; i < setSpan; i++ ) {
32670 this.colYs[ shortColIndex + i ] = setHeight ;
32677 * @param {Number} colSpan - number of columns the element spans
32678 * @returns {Array} colGroup
32680 _getColGroup : function( colSpan )
32682 if ( colSpan < 2 ) {
32683 // if brick spans only one column, use all the column Ys
32688 // how many different places could this brick fit horizontally
32689 var groupCount = this.cols + 1 - colSpan;
32690 // for each group potential horizontal position
32691 for ( var i = 0; i < groupCount; i++ ) {
32692 // make an array of colY values for that one group
32693 var groupColYs = this.colYs.slice( i, i + colSpan );
32694 // and get the max value of the array
32695 colGroup[i] = Math.max.apply( Math, groupColYs );
32700 _manageStamp : function( stamp )
32702 var stampSize = stamp.getSize();
32703 var offset = stamp.getBox();
32704 // get the columns that this stamp affects
32705 var firstX = this.isOriginLeft ? offset.x : offset.right;
32706 var lastX = firstX + stampSize.width;
32707 var firstCol = Math.floor( firstX / this.columnWidth );
32708 firstCol = Math.max( 0, firstCol );
32710 var lastCol = Math.floor( lastX / this.columnWidth );
32711 // lastCol should not go over if multiple of columnWidth #425
32712 lastCol -= lastX % this.columnWidth ? 0 : 1;
32713 lastCol = Math.min( this.cols - 1, lastCol );
32715 // set colYs to bottom of the stamp
32716 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32719 for ( var i = firstCol; i <= lastCol; i++ ) {
32720 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32725 _getContainerSize : function()
32727 this.maxY = Math.max.apply( Math, this.colYs );
32732 if ( this.isFitWidth ) {
32733 size.width = this._getContainerFitWidth();
32739 _getContainerFitWidth : function()
32741 var unusedCols = 0;
32742 // count unused columns
32745 if ( this.colYs[i] !== 0 ) {
32750 // fit container to columns that have been used
32751 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32754 needsResizeLayout : function()
32756 var previousWidth = this.containerWidth;
32757 this.getContainerWidth();
32758 return previousWidth !== this.containerWidth;
32773 * @class Roo.bootstrap.MasonryBrick
32774 * @extends Roo.bootstrap.Component
32775 * Bootstrap MasonryBrick class
32778 * Create a new MasonryBrick
32779 * @param {Object} config The config object
32782 Roo.bootstrap.MasonryBrick = function(config){
32784 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32786 Roo.bootstrap.MasonryBrick.register(this);
32792 * When a MasonryBrick is clcik
32793 * @param {Roo.bootstrap.MasonryBrick} this
32794 * @param {Roo.EventObject} e
32800 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32803 * @cfg {String} title
32807 * @cfg {String} html
32811 * @cfg {String} bgimage
32815 * @cfg {String} videourl
32819 * @cfg {String} cls
32823 * @cfg {String} href
32827 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32832 * @cfg {String} placetitle (center|bottom)
32837 * @cfg {Boolean} isFitContainer defalut true
32839 isFitContainer : true,
32842 * @cfg {Boolean} preventDefault defalut false
32844 preventDefault : false,
32847 * @cfg {Boolean} inverse defalut false
32849 maskInverse : false,
32851 getAutoCreate : function()
32853 if(!this.isFitContainer){
32854 return this.getSplitAutoCreate();
32857 var cls = 'masonry-brick masonry-brick-full';
32859 if(this.href.length){
32860 cls += ' masonry-brick-link';
32863 if(this.bgimage.length){
32864 cls += ' masonry-brick-image';
32867 if(this.maskInverse){
32868 cls += ' mask-inverse';
32871 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32872 cls += ' enable-mask';
32876 cls += ' masonry-' + this.size + '-brick';
32879 if(this.placetitle.length){
32881 switch (this.placetitle) {
32883 cls += ' masonry-center-title';
32886 cls += ' masonry-bottom-title';
32893 if(!this.html.length && !this.bgimage.length){
32894 cls += ' masonry-center-title';
32897 if(!this.html.length && this.bgimage.length){
32898 cls += ' masonry-bottom-title';
32903 cls += ' ' + this.cls;
32907 tag: (this.href.length) ? 'a' : 'div',
32912 cls: 'masonry-brick-mask'
32916 cls: 'masonry-brick-paragraph',
32922 if(this.href.length){
32923 cfg.href = this.href;
32926 var cn = cfg.cn[1].cn;
32928 if(this.title.length){
32931 cls: 'masonry-brick-title',
32936 if(this.html.length){
32939 cls: 'masonry-brick-text',
32944 if (!this.title.length && !this.html.length) {
32945 cfg.cn[1].cls += ' hide';
32948 if(this.bgimage.length){
32951 cls: 'masonry-brick-image-view',
32956 if(this.videourl.length){
32957 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32958 // youtube support only?
32961 cls: 'masonry-brick-image-view',
32964 allowfullscreen : true
32972 getSplitAutoCreate : function()
32974 var cls = 'masonry-brick masonry-brick-split';
32976 if(this.href.length){
32977 cls += ' masonry-brick-link';
32980 if(this.bgimage.length){
32981 cls += ' masonry-brick-image';
32985 cls += ' masonry-' + this.size + '-brick';
32988 switch (this.placetitle) {
32990 cls += ' masonry-center-title';
32993 cls += ' masonry-bottom-title';
32996 if(!this.bgimage.length){
32997 cls += ' masonry-center-title';
33000 if(this.bgimage.length){
33001 cls += ' masonry-bottom-title';
33007 cls += ' ' + this.cls;
33011 tag: (this.href.length) ? 'a' : 'div',
33016 cls: 'masonry-brick-split-head',
33020 cls: 'masonry-brick-paragraph',
33027 cls: 'masonry-brick-split-body',
33033 if(this.href.length){
33034 cfg.href = this.href;
33037 if(this.title.length){
33038 cfg.cn[0].cn[0].cn.push({
33040 cls: 'masonry-brick-title',
33045 if(this.html.length){
33046 cfg.cn[1].cn.push({
33048 cls: 'masonry-brick-text',
33053 if(this.bgimage.length){
33054 cfg.cn[0].cn.push({
33056 cls: 'masonry-brick-image-view',
33061 if(this.videourl.length){
33062 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33063 // youtube support only?
33064 cfg.cn[0].cn.cn.push({
33066 cls: 'masonry-brick-image-view',
33069 allowfullscreen : true
33076 initEvents: function()
33078 switch (this.size) {
33111 this.el.on('touchstart', this.onTouchStart, this);
33112 this.el.on('touchmove', this.onTouchMove, this);
33113 this.el.on('touchend', this.onTouchEnd, this);
33114 this.el.on('contextmenu', this.onContextMenu, this);
33116 this.el.on('mouseenter' ,this.enter, this);
33117 this.el.on('mouseleave', this.leave, this);
33118 this.el.on('click', this.onClick, this);
33121 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33122 this.parent().bricks.push(this);
33127 onClick: function(e, el)
33129 var time = this.endTimer - this.startTimer;
33130 // Roo.log(e.preventDefault());
33133 e.preventDefault();
33138 if(!this.preventDefault){
33142 e.preventDefault();
33144 if (this.activeClass != '') {
33145 this.selectBrick();
33148 this.fireEvent('click', this, e);
33151 enter: function(e, el)
33153 e.preventDefault();
33155 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33159 if(this.bgimage.length && this.html.length){
33160 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33164 leave: function(e, el)
33166 e.preventDefault();
33168 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33172 if(this.bgimage.length && this.html.length){
33173 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33177 onTouchStart: function(e, el)
33179 // e.preventDefault();
33181 this.touchmoved = false;
33183 if(!this.isFitContainer){
33187 if(!this.bgimage.length || !this.html.length){
33191 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33193 this.timer = new Date().getTime();
33197 onTouchMove: function(e, el)
33199 this.touchmoved = true;
33202 onContextMenu : function(e,el)
33204 e.preventDefault();
33205 e.stopPropagation();
33209 onTouchEnd: function(e, el)
33211 // e.preventDefault();
33213 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33220 if(!this.bgimage.length || !this.html.length){
33222 if(this.href.length){
33223 window.location.href = this.href;
33229 if(!this.isFitContainer){
33233 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33235 window.location.href = this.href;
33238 //selection on single brick only
33239 selectBrick : function() {
33241 if (!this.parentId) {
33245 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33246 var index = m.selectedBrick.indexOf(this.id);
33249 m.selectedBrick.splice(index,1);
33250 this.el.removeClass(this.activeClass);
33254 for(var i = 0; i < m.selectedBrick.length; i++) {
33255 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33256 b.el.removeClass(b.activeClass);
33259 m.selectedBrick = [];
33261 m.selectedBrick.push(this.id);
33262 this.el.addClass(this.activeClass);
33266 isSelected : function(){
33267 return this.el.hasClass(this.activeClass);
33272 Roo.apply(Roo.bootstrap.MasonryBrick, {
33275 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33277 * register a Masonry Brick
33278 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33281 register : function(brick)
33283 //this.groups[brick.id] = brick;
33284 this.groups.add(brick.id, brick);
33287 * fetch a masonry brick based on the masonry brick ID
33288 * @param {string} the masonry brick to add
33289 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33292 get: function(brick_id)
33294 // if (typeof(this.groups[brick_id]) == 'undefined') {
33297 // return this.groups[brick_id] ;
33299 if(this.groups.key(brick_id)) {
33300 return this.groups.key(brick_id);
33318 * @class Roo.bootstrap.Brick
33319 * @extends Roo.bootstrap.Component
33320 * Bootstrap Brick class
33323 * Create a new Brick
33324 * @param {Object} config The config object
33327 Roo.bootstrap.Brick = function(config){
33328 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33334 * When a Brick is click
33335 * @param {Roo.bootstrap.Brick} this
33336 * @param {Roo.EventObject} e
33342 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33345 * @cfg {String} title
33349 * @cfg {String} html
33353 * @cfg {String} bgimage
33357 * @cfg {String} cls
33361 * @cfg {String} href
33365 * @cfg {String} video
33369 * @cfg {Boolean} square
33373 getAutoCreate : function()
33375 var cls = 'roo-brick';
33377 if(this.href.length){
33378 cls += ' roo-brick-link';
33381 if(this.bgimage.length){
33382 cls += ' roo-brick-image';
33385 if(!this.html.length && !this.bgimage.length){
33386 cls += ' roo-brick-center-title';
33389 if(!this.html.length && this.bgimage.length){
33390 cls += ' roo-brick-bottom-title';
33394 cls += ' ' + this.cls;
33398 tag: (this.href.length) ? 'a' : 'div',
33403 cls: 'roo-brick-paragraph',
33409 if(this.href.length){
33410 cfg.href = this.href;
33413 var cn = cfg.cn[0].cn;
33415 if(this.title.length){
33418 cls: 'roo-brick-title',
33423 if(this.html.length){
33426 cls: 'roo-brick-text',
33433 if(this.bgimage.length){
33436 cls: 'roo-brick-image-view',
33444 initEvents: function()
33446 if(this.title.length || this.html.length){
33447 this.el.on('mouseenter' ,this.enter, this);
33448 this.el.on('mouseleave', this.leave, this);
33451 Roo.EventManager.onWindowResize(this.resize, this);
33453 if(this.bgimage.length){
33454 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33455 this.imageEl.on('load', this.onImageLoad, this);
33462 onImageLoad : function()
33467 resize : function()
33469 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33471 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33473 if(this.bgimage.length){
33474 var image = this.el.select('.roo-brick-image-view', true).first();
33476 image.setWidth(paragraph.getWidth());
33479 image.setHeight(paragraph.getWidth());
33482 this.el.setHeight(image.getHeight());
33483 paragraph.setHeight(image.getHeight());
33489 enter: function(e, el)
33491 e.preventDefault();
33493 if(this.bgimage.length){
33494 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33495 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33499 leave: function(e, el)
33501 e.preventDefault();
33503 if(this.bgimage.length){
33504 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33505 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33520 * @class Roo.bootstrap.NumberField
33521 * @extends Roo.bootstrap.Input
33522 * Bootstrap NumberField class
33528 * Create a new NumberField
33529 * @param {Object} config The config object
33532 Roo.bootstrap.NumberField = function(config){
33533 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33536 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33539 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33541 allowDecimals : true,
33543 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33545 decimalSeparator : ".",
33547 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33549 decimalPrecision : 2,
33551 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33553 allowNegative : true,
33556 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33560 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33562 minValue : Number.NEGATIVE_INFINITY,
33564 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33566 maxValue : Number.MAX_VALUE,
33568 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33570 minText : "The minimum value for this field is {0}",
33572 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33574 maxText : "The maximum value for this field is {0}",
33576 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33577 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33579 nanText : "{0} is not a valid number",
33581 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33583 thousandsDelimiter : false,
33585 * @cfg {String} valueAlign alignment of value
33587 valueAlign : "left",
33589 getAutoCreate : function()
33591 var hiddenInput = {
33595 cls: 'hidden-number-input'
33599 hiddenInput.name = this.name;
33604 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33606 this.name = hiddenInput.name;
33608 if(cfg.cn.length > 0) {
33609 cfg.cn.push(hiddenInput);
33616 initEvents : function()
33618 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33620 var allowed = "0123456789";
33622 if(this.allowDecimals){
33623 allowed += this.decimalSeparator;
33626 if(this.allowNegative){
33630 if(this.thousandsDelimiter) {
33634 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33636 var keyPress = function(e){
33638 var k = e.getKey();
33640 var c = e.getCharCode();
33643 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33644 allowed.indexOf(String.fromCharCode(c)) === -1
33650 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33654 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33659 this.el.on("keypress", keyPress, this);
33662 validateValue : function(value)
33665 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33669 var num = this.parseValue(value);
33672 this.markInvalid(String.format(this.nanText, value));
33676 if(num < this.minValue){
33677 this.markInvalid(String.format(this.minText, this.minValue));
33681 if(num > this.maxValue){
33682 this.markInvalid(String.format(this.maxText, this.maxValue));
33689 getValue : function()
33691 var v = this.hiddenEl().getValue();
33693 return this.fixPrecision(this.parseValue(v));
33696 parseValue : function(value)
33698 if(this.thousandsDelimiter) {
33700 r = new RegExp(",", "g");
33701 value = value.replace(r, "");
33704 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33705 return isNaN(value) ? '' : value;
33708 fixPrecision : function(value)
33710 if(this.thousandsDelimiter) {
33712 r = new RegExp(",", "g");
33713 value = value.replace(r, "");
33716 var nan = isNaN(value);
33718 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33719 return nan ? '' : value;
33721 return parseFloat(value).toFixed(this.decimalPrecision);
33724 setValue : function(v)
33726 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33732 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33734 this.inputEl().dom.value = (v == '') ? '' :
33735 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33737 if(!this.allowZero && v === '0') {
33738 this.hiddenEl().dom.value = '';
33739 this.inputEl().dom.value = '';
33746 decimalPrecisionFcn : function(v)
33748 return Math.floor(v);
33751 beforeBlur : function()
33753 var v = this.parseValue(this.getRawValue());
33755 if(v || v === 0 || v === ''){
33760 hiddenEl : function()
33762 return this.el.select('input.hidden-number-input',true).first();
33774 * @class Roo.bootstrap.DocumentSlider
33775 * @extends Roo.bootstrap.Component
33776 * Bootstrap DocumentSlider class
33779 * Create a new DocumentViewer
33780 * @param {Object} config The config object
33783 Roo.bootstrap.DocumentSlider = function(config){
33784 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33791 * Fire after initEvent
33792 * @param {Roo.bootstrap.DocumentSlider} this
33797 * Fire after update
33798 * @param {Roo.bootstrap.DocumentSlider} this
33804 * @param {Roo.bootstrap.DocumentSlider} this
33810 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33816 getAutoCreate : function()
33820 cls : 'roo-document-slider',
33824 cls : 'roo-document-slider-header',
33828 cls : 'roo-document-slider-header-title'
33834 cls : 'roo-document-slider-body',
33838 cls : 'roo-document-slider-prev',
33842 cls : 'fa fa-chevron-left'
33848 cls : 'roo-document-slider-thumb',
33852 cls : 'roo-document-slider-image'
33858 cls : 'roo-document-slider-next',
33862 cls : 'fa fa-chevron-right'
33874 initEvents : function()
33876 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33877 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33879 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33880 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33882 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33883 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33885 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33886 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33888 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33889 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33891 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33892 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33894 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33895 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33897 this.thumbEl.on('click', this.onClick, this);
33899 this.prevIndicator.on('click', this.prev, this);
33901 this.nextIndicator.on('click', this.next, this);
33905 initial : function()
33907 if(this.files.length){
33908 this.indicator = 1;
33912 this.fireEvent('initial', this);
33915 update : function()
33917 this.imageEl.attr('src', this.files[this.indicator - 1]);
33919 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33921 this.prevIndicator.show();
33923 if(this.indicator == 1){
33924 this.prevIndicator.hide();
33927 this.nextIndicator.show();
33929 if(this.indicator == this.files.length){
33930 this.nextIndicator.hide();
33933 this.thumbEl.scrollTo('top');
33935 this.fireEvent('update', this);
33938 onClick : function(e)
33940 e.preventDefault();
33942 this.fireEvent('click', this);
33947 e.preventDefault();
33949 this.indicator = Math.max(1, this.indicator - 1);
33956 e.preventDefault();
33958 this.indicator = Math.min(this.files.length, this.indicator + 1);
33972 * @class Roo.bootstrap.RadioSet
33973 * @extends Roo.bootstrap.Input
33974 * Bootstrap RadioSet class
33975 * @cfg {String} indicatorpos (left|right) default left
33976 * @cfg {Boolean} inline (true|false) inline the element (default true)
33977 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33979 * Create a new RadioSet
33980 * @param {Object} config The config object
33983 Roo.bootstrap.RadioSet = function(config){
33985 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33989 Roo.bootstrap.RadioSet.register(this);
33994 * Fires when the element is checked or unchecked.
33995 * @param {Roo.bootstrap.RadioSet} this This radio
33996 * @param {Roo.bootstrap.Radio} item The checked item
34001 * Fires when the element is click.
34002 * @param {Roo.bootstrap.RadioSet} this This radio set
34003 * @param {Roo.bootstrap.Radio} item The checked item
34004 * @param {Roo.EventObject} e The event object
34011 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
34019 indicatorpos : 'left',
34021 getAutoCreate : function()
34025 cls : 'roo-radio-set-label',
34029 html : this.fieldLabel
34034 if(this.indicatorpos == 'left'){
34037 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34038 tooltip : 'This field is required'
34043 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34044 tooltip : 'This field is required'
34050 cls : 'roo-radio-set-items'
34053 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34055 if (align === 'left' && this.fieldLabel.length) {
34058 cls : "roo-radio-set-right",
34064 if(this.labelWidth > 12){
34065 label.style = "width: " + this.labelWidth + 'px';
34068 if(this.labelWidth < 13 && this.labelmd == 0){
34069 this.labelmd = this.labelWidth;
34072 if(this.labellg > 0){
34073 label.cls += ' col-lg-' + this.labellg;
34074 items.cls += ' col-lg-' + (12 - this.labellg);
34077 if(this.labelmd > 0){
34078 label.cls += ' col-md-' + this.labelmd;
34079 items.cls += ' col-md-' + (12 - this.labelmd);
34082 if(this.labelsm > 0){
34083 label.cls += ' col-sm-' + this.labelsm;
34084 items.cls += ' col-sm-' + (12 - this.labelsm);
34087 if(this.labelxs > 0){
34088 label.cls += ' col-xs-' + this.labelxs;
34089 items.cls += ' col-xs-' + (12 - this.labelxs);
34095 cls : 'roo-radio-set',
34099 cls : 'roo-radio-set-input',
34102 value : this.value ? this.value : ''
34109 if(this.weight.length){
34110 cfg.cls += ' roo-radio-' + this.weight;
34114 cfg.cls += ' roo-radio-set-inline';
34118 ['xs','sm','md','lg'].map(function(size){
34119 if (settings[size]) {
34120 cfg.cls += ' col-' + size + '-' + settings[size];
34128 initEvents : function()
34130 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34131 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34133 if(!this.fieldLabel.length){
34134 this.labelEl.hide();
34137 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34138 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34140 this.indicator = this.indicatorEl();
34142 if(this.indicator){
34143 this.indicator.addClass('invisible');
34146 this.originalValue = this.getValue();
34150 inputEl: function ()
34152 return this.el.select('.roo-radio-set-input', true).first();
34155 getChildContainer : function()
34157 return this.itemsEl;
34160 register : function(item)
34162 this.radioes.push(item);
34166 validate : function()
34168 if(this.getVisibilityEl().hasClass('hidden')){
34174 Roo.each(this.radioes, function(i){
34183 if(this.allowBlank) {
34187 if(this.disabled || valid){
34192 this.markInvalid();
34197 markValid : function()
34199 if(this.labelEl.isVisible(true)){
34200 this.indicatorEl().removeClass('visible');
34201 this.indicatorEl().addClass('invisible');
34204 this.el.removeClass([this.invalidClass, this.validClass]);
34205 this.el.addClass(this.validClass);
34207 this.fireEvent('valid', this);
34210 markInvalid : function(msg)
34212 if(this.allowBlank || this.disabled){
34216 if(this.labelEl.isVisible(true)){
34217 this.indicatorEl().removeClass('invisible');
34218 this.indicatorEl().addClass('visible');
34221 this.el.removeClass([this.invalidClass, this.validClass]);
34222 this.el.addClass(this.invalidClass);
34224 this.fireEvent('invalid', this, msg);
34228 setValue : function(v, suppressEvent)
34230 if(this.value === v){
34237 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34240 Roo.each(this.radioes, function(i){
34242 i.el.removeClass('checked');
34245 Roo.each(this.radioes, function(i){
34247 if(i.value === v || i.value.toString() === v.toString()){
34249 i.el.addClass('checked');
34251 if(suppressEvent !== true){
34252 this.fireEvent('check', this, i);
34263 clearInvalid : function(){
34265 if(!this.el || this.preventMark){
34269 this.el.removeClass([this.invalidClass]);
34271 this.fireEvent('valid', this);
34276 Roo.apply(Roo.bootstrap.RadioSet, {
34280 register : function(set)
34282 this.groups[set.name] = set;
34285 get: function(name)
34287 if (typeof(this.groups[name]) == 'undefined') {
34291 return this.groups[name] ;
34297 * Ext JS Library 1.1.1
34298 * Copyright(c) 2006-2007, Ext JS, LLC.
34300 * Originally Released Under LGPL - original licence link has changed is not relivant.
34303 * <script type="text/javascript">
34308 * @class Roo.bootstrap.SplitBar
34309 * @extends Roo.util.Observable
34310 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34314 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34315 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34316 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34317 split.minSize = 100;
34318 split.maxSize = 600;
34319 split.animate = true;
34320 split.on('moved', splitterMoved);
34323 * Create a new SplitBar
34324 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34325 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34326 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34327 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34328 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34329 position of the SplitBar).
34331 Roo.bootstrap.SplitBar = function(cfg){
34336 // dragElement : elm
34337 // resizingElement: el,
34339 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34340 // placement : Roo.bootstrap.SplitBar.LEFT ,
34341 // existingProxy ???
34344 this.el = Roo.get(cfg.dragElement, true);
34345 this.el.dom.unselectable = "on";
34347 this.resizingEl = Roo.get(cfg.resizingElement, true);
34351 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34352 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34355 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34358 * The minimum size of the resizing element. (Defaults to 0)
34364 * The maximum size of the resizing element. (Defaults to 2000)
34367 this.maxSize = 2000;
34370 * Whether to animate the transition to the new size
34373 this.animate = false;
34376 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34379 this.useShim = false;
34384 if(!cfg.existingProxy){
34386 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34388 this.proxy = Roo.get(cfg.existingProxy).dom;
34391 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34394 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34397 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34400 this.dragSpecs = {};
34403 * @private The adapter to use to positon and resize elements
34405 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34406 this.adapter.init(this);
34408 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34410 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34411 this.el.addClass("roo-splitbar-h");
34414 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34415 this.el.addClass("roo-splitbar-v");
34421 * Fires when the splitter is moved (alias for {@link #event-moved})
34422 * @param {Roo.bootstrap.SplitBar} this
34423 * @param {Number} newSize the new width or height
34428 * Fires when the splitter is moved
34429 * @param {Roo.bootstrap.SplitBar} this
34430 * @param {Number} newSize the new width or height
34434 * @event beforeresize
34435 * Fires before the splitter is dragged
34436 * @param {Roo.bootstrap.SplitBar} this
34438 "beforeresize" : true,
34440 "beforeapply" : true
34443 Roo.util.Observable.call(this);
34446 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34447 onStartProxyDrag : function(x, y){
34448 this.fireEvent("beforeresize", this);
34450 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34452 o.enableDisplayMode("block");
34453 // all splitbars share the same overlay
34454 Roo.bootstrap.SplitBar.prototype.overlay = o;
34456 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34457 this.overlay.show();
34458 Roo.get(this.proxy).setDisplayed("block");
34459 var size = this.adapter.getElementSize(this);
34460 this.activeMinSize = this.getMinimumSize();;
34461 this.activeMaxSize = this.getMaximumSize();;
34462 var c1 = size - this.activeMinSize;
34463 var c2 = Math.max(this.activeMaxSize - size, 0);
34464 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34465 this.dd.resetConstraints();
34466 this.dd.setXConstraint(
34467 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34468 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34470 this.dd.setYConstraint(0, 0);
34472 this.dd.resetConstraints();
34473 this.dd.setXConstraint(0, 0);
34474 this.dd.setYConstraint(
34475 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34476 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34479 this.dragSpecs.startSize = size;
34480 this.dragSpecs.startPoint = [x, y];
34481 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34485 * @private Called after the drag operation by the DDProxy
34487 onEndProxyDrag : function(e){
34488 Roo.get(this.proxy).setDisplayed(false);
34489 var endPoint = Roo.lib.Event.getXY(e);
34491 this.overlay.hide();
34494 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34495 newSize = this.dragSpecs.startSize +
34496 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34497 endPoint[0] - this.dragSpecs.startPoint[0] :
34498 this.dragSpecs.startPoint[0] - endPoint[0]
34501 newSize = this.dragSpecs.startSize +
34502 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34503 endPoint[1] - this.dragSpecs.startPoint[1] :
34504 this.dragSpecs.startPoint[1] - endPoint[1]
34507 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34508 if(newSize != this.dragSpecs.startSize){
34509 if(this.fireEvent('beforeapply', this, newSize) !== false){
34510 this.adapter.setElementSize(this, newSize);
34511 this.fireEvent("moved", this, newSize);
34512 this.fireEvent("resize", this, newSize);
34518 * Get the adapter this SplitBar uses
34519 * @return The adapter object
34521 getAdapter : function(){
34522 return this.adapter;
34526 * Set the adapter this SplitBar uses
34527 * @param {Object} adapter A SplitBar adapter object
34529 setAdapter : function(adapter){
34530 this.adapter = adapter;
34531 this.adapter.init(this);
34535 * Gets the minimum size for the resizing element
34536 * @return {Number} The minimum size
34538 getMinimumSize : function(){
34539 return this.minSize;
34543 * Sets the minimum size for the resizing element
34544 * @param {Number} minSize The minimum size
34546 setMinimumSize : function(minSize){
34547 this.minSize = minSize;
34551 * Gets the maximum size for the resizing element
34552 * @return {Number} The maximum size
34554 getMaximumSize : function(){
34555 return this.maxSize;
34559 * Sets the maximum size for the resizing element
34560 * @param {Number} maxSize The maximum size
34562 setMaximumSize : function(maxSize){
34563 this.maxSize = maxSize;
34567 * Sets the initialize size for the resizing element
34568 * @param {Number} size The initial size
34570 setCurrentSize : function(size){
34571 var oldAnimate = this.animate;
34572 this.animate = false;
34573 this.adapter.setElementSize(this, size);
34574 this.animate = oldAnimate;
34578 * Destroy this splitbar.
34579 * @param {Boolean} removeEl True to remove the element
34581 destroy : function(removeEl){
34583 this.shim.remove();
34586 this.proxy.parentNode.removeChild(this.proxy);
34594 * @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.
34596 Roo.bootstrap.SplitBar.createProxy = function(dir){
34597 var proxy = new Roo.Element(document.createElement("div"));
34598 proxy.unselectable();
34599 var cls = 'roo-splitbar-proxy';
34600 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34601 document.body.appendChild(proxy.dom);
34606 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34607 * Default Adapter. It assumes the splitter and resizing element are not positioned
34608 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34610 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34613 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34614 // do nothing for now
34615 init : function(s){
34619 * Called before drag operations to get the current size of the resizing element.
34620 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34622 getElementSize : function(s){
34623 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34624 return s.resizingEl.getWidth();
34626 return s.resizingEl.getHeight();
34631 * Called after drag operations to set the size of the resizing element.
34632 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34633 * @param {Number} newSize The new size to set
34634 * @param {Function} onComplete A function to be invoked when resizing is complete
34636 setElementSize : function(s, newSize, onComplete){
34637 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34639 s.resizingEl.setWidth(newSize);
34641 onComplete(s, newSize);
34644 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34649 s.resizingEl.setHeight(newSize);
34651 onComplete(s, newSize);
34654 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34661 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34662 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34663 * Adapter that moves the splitter element to align with the resized sizing element.
34664 * Used with an absolute positioned SplitBar.
34665 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34666 * document.body, make sure you assign an id to the body element.
34668 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34669 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34670 this.container = Roo.get(container);
34673 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34674 init : function(s){
34675 this.basic.init(s);
34678 getElementSize : function(s){
34679 return this.basic.getElementSize(s);
34682 setElementSize : function(s, newSize, onComplete){
34683 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34686 moveSplitter : function(s){
34687 var yes = Roo.bootstrap.SplitBar;
34688 switch(s.placement){
34690 s.el.setX(s.resizingEl.getRight());
34693 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34696 s.el.setY(s.resizingEl.getBottom());
34699 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34706 * Orientation constant - Create a vertical SplitBar
34710 Roo.bootstrap.SplitBar.VERTICAL = 1;
34713 * Orientation constant - Create a horizontal SplitBar
34717 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34720 * Placement constant - The resizing element is to the left of the splitter element
34724 Roo.bootstrap.SplitBar.LEFT = 1;
34727 * Placement constant - The resizing element is to the right of the splitter element
34731 Roo.bootstrap.SplitBar.RIGHT = 2;
34734 * Placement constant - The resizing element is positioned above the splitter element
34738 Roo.bootstrap.SplitBar.TOP = 3;
34741 * Placement constant - The resizing element is positioned under splitter element
34745 Roo.bootstrap.SplitBar.BOTTOM = 4;
34746 Roo.namespace("Roo.bootstrap.layout");/*
34748 * Ext JS Library 1.1.1
34749 * Copyright(c) 2006-2007, Ext JS, LLC.
34751 * Originally Released Under LGPL - original licence link has changed is not relivant.
34754 * <script type="text/javascript">
34758 * @class Roo.bootstrap.layout.Manager
34759 * @extends Roo.bootstrap.Component
34760 * Base class for layout managers.
34762 Roo.bootstrap.layout.Manager = function(config)
34764 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34770 /** false to disable window resize monitoring @type Boolean */
34771 this.monitorWindowResize = true;
34776 * Fires when a layout is performed.
34777 * @param {Roo.LayoutManager} this
34781 * @event regionresized
34782 * Fires when the user resizes a region.
34783 * @param {Roo.LayoutRegion} region The resized region
34784 * @param {Number} newSize The new size (width for east/west, height for north/south)
34786 "regionresized" : true,
34788 * @event regioncollapsed
34789 * Fires when a region is collapsed.
34790 * @param {Roo.LayoutRegion} region The collapsed region
34792 "regioncollapsed" : true,
34794 * @event regionexpanded
34795 * Fires when a region is expanded.
34796 * @param {Roo.LayoutRegion} region The expanded region
34798 "regionexpanded" : true
34800 this.updating = false;
34803 this.el = Roo.get(config.el);
34809 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34814 monitorWindowResize : true,
34820 onRender : function(ct, position)
34823 this.el = Roo.get(ct);
34826 //this.fireEvent('render',this);
34830 initEvents: function()
34834 // ie scrollbar fix
34835 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34836 document.body.scroll = "no";
34837 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34838 this.el.position('relative');
34840 this.id = this.el.id;
34841 this.el.addClass("roo-layout-container");
34842 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34843 if(this.el.dom != document.body ) {
34844 this.el.on('resize', this.layout,this);
34845 this.el.on('show', this.layout,this);
34851 * Returns true if this layout is currently being updated
34852 * @return {Boolean}
34854 isUpdating : function(){
34855 return this.updating;
34859 * Suspend the LayoutManager from doing auto-layouts while
34860 * making multiple add or remove calls
34862 beginUpdate : function(){
34863 this.updating = true;
34867 * Restore auto-layouts and optionally disable the manager from performing a layout
34868 * @param {Boolean} noLayout true to disable a layout update
34870 endUpdate : function(noLayout){
34871 this.updating = false;
34877 layout: function(){
34881 onRegionResized : function(region, newSize){
34882 this.fireEvent("regionresized", region, newSize);
34886 onRegionCollapsed : function(region){
34887 this.fireEvent("regioncollapsed", region);
34890 onRegionExpanded : function(region){
34891 this.fireEvent("regionexpanded", region);
34895 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34896 * performs box-model adjustments.
34897 * @return {Object} The size as an object {width: (the width), height: (the height)}
34899 getViewSize : function()
34902 if(this.el.dom != document.body){
34903 size = this.el.getSize();
34905 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34907 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34908 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34913 * Returns the Element this layout is bound to.
34914 * @return {Roo.Element}
34916 getEl : function(){
34921 * Returns the specified region.
34922 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34923 * @return {Roo.LayoutRegion}
34925 getRegion : function(target){
34926 return this.regions[target.toLowerCase()];
34929 onWindowResize : function(){
34930 if(this.monitorWindowResize){
34937 * Ext JS Library 1.1.1
34938 * Copyright(c) 2006-2007, Ext JS, LLC.
34940 * Originally Released Under LGPL - original licence link has changed is not relivant.
34943 * <script type="text/javascript">
34946 * @class Roo.bootstrap.layout.Border
34947 * @extends Roo.bootstrap.layout.Manager
34948 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34949 * please see: examples/bootstrap/nested.html<br><br>
34951 <b>The container the layout is rendered into can be either the body element or any other element.
34952 If it is not the body element, the container needs to either be an absolute positioned element,
34953 or you will need to add "position:relative" to the css of the container. You will also need to specify
34954 the container size if it is not the body element.</b>
34957 * Create a new Border
34958 * @param {Object} config Configuration options
34960 Roo.bootstrap.layout.Border = function(config){
34961 config = config || {};
34962 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34966 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34967 if(config[region]){
34968 config[region].region = region;
34969 this.addRegion(config[region]);
34975 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34977 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34979 * Creates and adds a new region if it doesn't already exist.
34980 * @param {String} target The target region key (north, south, east, west or center).
34981 * @param {Object} config The regions config object
34982 * @return {BorderLayoutRegion} The new region
34984 addRegion : function(config)
34986 if(!this.regions[config.region]){
34987 var r = this.factory(config);
34988 this.bindRegion(r);
34990 return this.regions[config.region];
34994 bindRegion : function(r){
34995 this.regions[r.config.region] = r;
34997 r.on("visibilitychange", this.layout, this);
34998 r.on("paneladded", this.layout, this);
34999 r.on("panelremoved", this.layout, this);
35000 r.on("invalidated", this.layout, this);
35001 r.on("resized", this.onRegionResized, this);
35002 r.on("collapsed", this.onRegionCollapsed, this);
35003 r.on("expanded", this.onRegionExpanded, this);
35007 * Performs a layout update.
35009 layout : function()
35011 if(this.updating) {
35015 // render all the rebions if they have not been done alreayd?
35016 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35017 if(this.regions[region] && !this.regions[region].bodyEl){
35018 this.regions[region].onRender(this.el)
35022 var size = this.getViewSize();
35023 var w = size.width;
35024 var h = size.height;
35029 //var x = 0, y = 0;
35031 var rs = this.regions;
35032 var north = rs["north"];
35033 var south = rs["south"];
35034 var west = rs["west"];
35035 var east = rs["east"];
35036 var center = rs["center"];
35037 //if(this.hideOnLayout){ // not supported anymore
35038 //c.el.setStyle("display", "none");
35040 if(north && north.isVisible()){
35041 var b = north.getBox();
35042 var m = north.getMargins();
35043 b.width = w - (m.left+m.right);
35046 centerY = b.height + b.y + m.bottom;
35047 centerH -= centerY;
35048 north.updateBox(this.safeBox(b));
35050 if(south && south.isVisible()){
35051 var b = south.getBox();
35052 var m = south.getMargins();
35053 b.width = w - (m.left+m.right);
35055 var totalHeight = (b.height + m.top + m.bottom);
35056 b.y = h - totalHeight + m.top;
35057 centerH -= totalHeight;
35058 south.updateBox(this.safeBox(b));
35060 if(west && west.isVisible()){
35061 var b = west.getBox();
35062 var m = west.getMargins();
35063 b.height = centerH - (m.top+m.bottom);
35065 b.y = centerY + m.top;
35066 var totalWidth = (b.width + m.left + m.right);
35067 centerX += totalWidth;
35068 centerW -= totalWidth;
35069 west.updateBox(this.safeBox(b));
35071 if(east && east.isVisible()){
35072 var b = east.getBox();
35073 var m = east.getMargins();
35074 b.height = centerH - (m.top+m.bottom);
35075 var totalWidth = (b.width + m.left + m.right);
35076 b.x = w - totalWidth + m.left;
35077 b.y = centerY + m.top;
35078 centerW -= totalWidth;
35079 east.updateBox(this.safeBox(b));
35082 var m = center.getMargins();
35084 x: centerX + m.left,
35085 y: centerY + m.top,
35086 width: centerW - (m.left+m.right),
35087 height: centerH - (m.top+m.bottom)
35089 //if(this.hideOnLayout){
35090 //center.el.setStyle("display", "block");
35092 center.updateBox(this.safeBox(centerBox));
35095 this.fireEvent("layout", this);
35099 safeBox : function(box){
35100 box.width = Math.max(0, box.width);
35101 box.height = Math.max(0, box.height);
35106 * Adds a ContentPanel (or subclass) to this layout.
35107 * @param {String} target The target region key (north, south, east, west or center).
35108 * @param {Roo.ContentPanel} panel The panel to add
35109 * @return {Roo.ContentPanel} The added panel
35111 add : function(target, panel){
35113 target = target.toLowerCase();
35114 return this.regions[target].add(panel);
35118 * Remove a ContentPanel (or subclass) to this layout.
35119 * @param {String} target The target region key (north, south, east, west or center).
35120 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35121 * @return {Roo.ContentPanel} The removed panel
35123 remove : function(target, panel){
35124 target = target.toLowerCase();
35125 return this.regions[target].remove(panel);
35129 * Searches all regions for a panel with the specified id
35130 * @param {String} panelId
35131 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35133 findPanel : function(panelId){
35134 var rs = this.regions;
35135 for(var target in rs){
35136 if(typeof rs[target] != "function"){
35137 var p = rs[target].getPanel(panelId);
35147 * Searches all regions for a panel with the specified id and activates (shows) it.
35148 * @param {String/ContentPanel} panelId The panels id or the panel itself
35149 * @return {Roo.ContentPanel} The shown panel or null
35151 showPanel : function(panelId) {
35152 var rs = this.regions;
35153 for(var target in rs){
35154 var r = rs[target];
35155 if(typeof r != "function"){
35156 if(r.hasPanel(panelId)){
35157 return r.showPanel(panelId);
35165 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35166 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35169 restoreState : function(provider){
35171 provider = Roo.state.Manager;
35173 var sm = new Roo.LayoutStateManager();
35174 sm.init(this, provider);
35180 * Adds a xtype elements to the layout.
35184 xtype : 'ContentPanel',
35191 xtype : 'NestedLayoutPanel',
35197 items : [ ... list of content panels or nested layout panels.. ]
35201 * @param {Object} cfg Xtype definition of item to add.
35203 addxtype : function(cfg)
35205 // basically accepts a pannel...
35206 // can accept a layout region..!?!?
35207 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35210 // theory? children can only be panels??
35212 //if (!cfg.xtype.match(/Panel$/)) {
35217 if (typeof(cfg.region) == 'undefined') {
35218 Roo.log("Failed to add Panel, region was not set");
35222 var region = cfg.region;
35228 xitems = cfg.items;
35235 case 'Content': // ContentPanel (el, cfg)
35236 case 'Scroll': // ContentPanel (el, cfg)
35238 cfg.autoCreate = true;
35239 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35241 // var el = this.el.createChild();
35242 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35245 this.add(region, ret);
35249 case 'TreePanel': // our new panel!
35250 cfg.el = this.el.createChild();
35251 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35252 this.add(region, ret);
35257 // create a new Layout (which is a Border Layout...
35259 var clayout = cfg.layout;
35260 clayout.el = this.el.createChild();
35261 clayout.items = clayout.items || [];
35265 // replace this exitems with the clayout ones..
35266 xitems = clayout.items;
35268 // force background off if it's in center...
35269 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35270 cfg.background = false;
35272 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35275 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35276 //console.log('adding nested layout panel ' + cfg.toSource());
35277 this.add(region, ret);
35278 nb = {}; /// find first...
35283 // needs grid and region
35285 //var el = this.getRegion(region).el.createChild();
35287 *var el = this.el.createChild();
35288 // create the grid first...
35289 cfg.grid.container = el;
35290 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35293 if (region == 'center' && this.active ) {
35294 cfg.background = false;
35297 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35299 this.add(region, ret);
35301 if (cfg.background) {
35302 // render grid on panel activation (if panel background)
35303 ret.on('activate', function(gp) {
35304 if (!gp.grid.rendered) {
35305 // gp.grid.render(el);
35309 // cfg.grid.render(el);
35315 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35316 // it was the old xcomponent building that caused this before.
35317 // espeically if border is the top element in the tree.
35327 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35329 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35330 this.add(region, ret);
35334 throw "Can not add '" + cfg.xtype + "' to Border";
35340 this.beginUpdate();
35344 Roo.each(xitems, function(i) {
35345 region = nb && i.region ? i.region : false;
35347 var add = ret.addxtype(i);
35350 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35351 if (!i.background) {
35352 abn[region] = nb[region] ;
35359 // make the last non-background panel active..
35360 //if (nb) { Roo.log(abn); }
35363 for(var r in abn) {
35364 region = this.getRegion(r);
35366 // tried using nb[r], but it does not work..
35368 region.showPanel(abn[r]);
35379 factory : function(cfg)
35382 var validRegions = Roo.bootstrap.layout.Border.regions;
35384 var target = cfg.region;
35387 var r = Roo.bootstrap.layout;
35391 return new r.North(cfg);
35393 return new r.South(cfg);
35395 return new r.East(cfg);
35397 return new r.West(cfg);
35399 return new r.Center(cfg);
35401 throw 'Layout region "'+target+'" not supported.';
35408 * Ext JS Library 1.1.1
35409 * Copyright(c) 2006-2007, Ext JS, LLC.
35411 * Originally Released Under LGPL - original licence link has changed is not relivant.
35414 * <script type="text/javascript">
35418 * @class Roo.bootstrap.layout.Basic
35419 * @extends Roo.util.Observable
35420 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35421 * and does not have a titlebar, tabs or any other features. All it does is size and position
35422 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35423 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35424 * @cfg {string} region the region that it inhabits..
35425 * @cfg {bool} skipConfig skip config?
35429 Roo.bootstrap.layout.Basic = function(config){
35431 this.mgr = config.mgr;
35433 this.position = config.region;
35435 var skipConfig = config.skipConfig;
35439 * @scope Roo.BasicLayoutRegion
35443 * @event beforeremove
35444 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35445 * @param {Roo.LayoutRegion} this
35446 * @param {Roo.ContentPanel} panel The panel
35447 * @param {Object} e The cancel event object
35449 "beforeremove" : true,
35451 * @event invalidated
35452 * Fires when the layout for this region is changed.
35453 * @param {Roo.LayoutRegion} this
35455 "invalidated" : true,
35457 * @event visibilitychange
35458 * Fires when this region is shown or hidden
35459 * @param {Roo.LayoutRegion} this
35460 * @param {Boolean} visibility true or false
35462 "visibilitychange" : true,
35464 * @event paneladded
35465 * Fires when a panel is added.
35466 * @param {Roo.LayoutRegion} this
35467 * @param {Roo.ContentPanel} panel The panel
35469 "paneladded" : true,
35471 * @event panelremoved
35472 * Fires when a panel is removed.
35473 * @param {Roo.LayoutRegion} this
35474 * @param {Roo.ContentPanel} panel The panel
35476 "panelremoved" : true,
35478 * @event beforecollapse
35479 * Fires when this region before collapse.
35480 * @param {Roo.LayoutRegion} this
35482 "beforecollapse" : true,
35485 * Fires when this region is collapsed.
35486 * @param {Roo.LayoutRegion} this
35488 "collapsed" : true,
35491 * Fires when this region is expanded.
35492 * @param {Roo.LayoutRegion} this
35497 * Fires when this region is slid into view.
35498 * @param {Roo.LayoutRegion} this
35500 "slideshow" : true,
35503 * Fires when this region slides out of view.
35504 * @param {Roo.LayoutRegion} this
35506 "slidehide" : true,
35508 * @event panelactivated
35509 * Fires when a panel is activated.
35510 * @param {Roo.LayoutRegion} this
35511 * @param {Roo.ContentPanel} panel The activated panel
35513 "panelactivated" : true,
35516 * Fires when the user resizes this region.
35517 * @param {Roo.LayoutRegion} this
35518 * @param {Number} newSize The new size (width for east/west, height for north/south)
35522 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35523 this.panels = new Roo.util.MixedCollection();
35524 this.panels.getKey = this.getPanelId.createDelegate(this);
35526 this.activePanel = null;
35527 // ensure listeners are added...
35529 if (config.listeners || config.events) {
35530 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35531 listeners : config.listeners || {},
35532 events : config.events || {}
35536 if(skipConfig !== true){
35537 this.applyConfig(config);
35541 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35543 getPanelId : function(p){
35547 applyConfig : function(config){
35548 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35549 this.config = config;
35554 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35555 * the width, for horizontal (north, south) the height.
35556 * @param {Number} newSize The new width or height
35558 resizeTo : function(newSize){
35559 var el = this.el ? this.el :
35560 (this.activePanel ? this.activePanel.getEl() : null);
35562 switch(this.position){
35565 el.setWidth(newSize);
35566 this.fireEvent("resized", this, newSize);
35570 el.setHeight(newSize);
35571 this.fireEvent("resized", this, newSize);
35577 getBox : function(){
35578 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35581 getMargins : function(){
35582 return this.margins;
35585 updateBox : function(box){
35587 var el = this.activePanel.getEl();
35588 el.dom.style.left = box.x + "px";
35589 el.dom.style.top = box.y + "px";
35590 this.activePanel.setSize(box.width, box.height);
35594 * Returns the container element for this region.
35595 * @return {Roo.Element}
35597 getEl : function(){
35598 return this.activePanel;
35602 * Returns true if this region is currently visible.
35603 * @return {Boolean}
35605 isVisible : function(){
35606 return this.activePanel ? true : false;
35609 setActivePanel : function(panel){
35610 panel = this.getPanel(panel);
35611 if(this.activePanel && this.activePanel != panel){
35612 this.activePanel.setActiveState(false);
35613 this.activePanel.getEl().setLeftTop(-10000,-10000);
35615 this.activePanel = panel;
35616 panel.setActiveState(true);
35618 panel.setSize(this.box.width, this.box.height);
35620 this.fireEvent("panelactivated", this, panel);
35621 this.fireEvent("invalidated");
35625 * Show the specified panel.
35626 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35627 * @return {Roo.ContentPanel} The shown panel or null
35629 showPanel : function(panel){
35630 panel = this.getPanel(panel);
35632 this.setActivePanel(panel);
35638 * Get the active panel for this region.
35639 * @return {Roo.ContentPanel} The active panel or null
35641 getActivePanel : function(){
35642 return this.activePanel;
35646 * Add the passed ContentPanel(s)
35647 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35648 * @return {Roo.ContentPanel} The panel added (if only one was added)
35650 add : function(panel){
35651 if(arguments.length > 1){
35652 for(var i = 0, len = arguments.length; i < len; i++) {
35653 this.add(arguments[i]);
35657 if(this.hasPanel(panel)){
35658 this.showPanel(panel);
35661 var el = panel.getEl();
35662 if(el.dom.parentNode != this.mgr.el.dom){
35663 this.mgr.el.dom.appendChild(el.dom);
35665 if(panel.setRegion){
35666 panel.setRegion(this);
35668 this.panels.add(panel);
35669 el.setStyle("position", "absolute");
35670 if(!panel.background){
35671 this.setActivePanel(panel);
35672 if(this.config.initialSize && this.panels.getCount()==1){
35673 this.resizeTo(this.config.initialSize);
35676 this.fireEvent("paneladded", this, panel);
35681 * Returns true if the panel is in this region.
35682 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35683 * @return {Boolean}
35685 hasPanel : function(panel){
35686 if(typeof panel == "object"){ // must be panel obj
35687 panel = panel.getId();
35689 return this.getPanel(panel) ? true : false;
35693 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35694 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35695 * @param {Boolean} preservePanel Overrides the config preservePanel option
35696 * @return {Roo.ContentPanel} The panel that was removed
35698 remove : function(panel, preservePanel){
35699 panel = this.getPanel(panel);
35704 this.fireEvent("beforeremove", this, panel, e);
35705 if(e.cancel === true){
35708 var panelId = panel.getId();
35709 this.panels.removeKey(panelId);
35714 * Returns the panel specified or null if it's not in this region.
35715 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35716 * @return {Roo.ContentPanel}
35718 getPanel : function(id){
35719 if(typeof id == "object"){ // must be panel obj
35722 return this.panels.get(id);
35726 * Returns this regions position (north/south/east/west/center).
35729 getPosition: function(){
35730 return this.position;
35734 * Ext JS Library 1.1.1
35735 * Copyright(c) 2006-2007, Ext JS, LLC.
35737 * Originally Released Under LGPL - original licence link has changed is not relivant.
35740 * <script type="text/javascript">
35744 * @class Roo.bootstrap.layout.Region
35745 * @extends Roo.bootstrap.layout.Basic
35746 * This class represents a region in a layout manager.
35748 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35749 * @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})
35750 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35751 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35752 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35753 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35754 * @cfg {String} title The title for the region (overrides panel titles)
35755 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35756 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35757 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35758 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35759 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35760 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35761 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35762 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35763 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35764 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35766 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35767 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35768 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35769 * @cfg {Number} width For East/West panels
35770 * @cfg {Number} height For North/South panels
35771 * @cfg {Boolean} split To show the splitter
35772 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35774 * @cfg {string} cls Extra CSS classes to add to region
35776 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35777 * @cfg {string} region the region that it inhabits..
35780 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35781 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35783 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35784 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35785 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35787 Roo.bootstrap.layout.Region = function(config)
35789 this.applyConfig(config);
35791 var mgr = config.mgr;
35792 var pos = config.region;
35793 config.skipConfig = true;
35794 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35797 this.onRender(mgr.el);
35800 this.visible = true;
35801 this.collapsed = false;
35802 this.unrendered_panels = [];
35805 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35807 position: '', // set by wrapper (eg. north/south etc..)
35808 unrendered_panels : null, // unrendered panels.
35809 createBody : function(){
35810 /** This region's body element
35811 * @type Roo.Element */
35812 this.bodyEl = this.el.createChild({
35814 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35818 onRender: function(ctr, pos)
35820 var dh = Roo.DomHelper;
35821 /** This region's container element
35822 * @type Roo.Element */
35823 this.el = dh.append(ctr.dom, {
35825 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35827 /** This region's title element
35828 * @type Roo.Element */
35830 this.titleEl = dh.append(this.el.dom,
35833 unselectable: "on",
35834 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35836 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35837 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35840 this.titleEl.enableDisplayMode();
35841 /** This region's title text element
35842 * @type HTMLElement */
35843 this.titleTextEl = this.titleEl.dom.firstChild;
35844 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35846 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35847 this.closeBtn.enableDisplayMode();
35848 this.closeBtn.on("click", this.closeClicked, this);
35849 this.closeBtn.hide();
35851 this.createBody(this.config);
35852 if(this.config.hideWhenEmpty){
35854 this.on("paneladded", this.validateVisibility, this);
35855 this.on("panelremoved", this.validateVisibility, this);
35857 if(this.autoScroll){
35858 this.bodyEl.setStyle("overflow", "auto");
35860 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35862 //if(c.titlebar !== false){
35863 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35864 this.titleEl.hide();
35866 this.titleEl.show();
35867 if(this.config.title){
35868 this.titleTextEl.innerHTML = this.config.title;
35872 if(this.config.collapsed){
35873 this.collapse(true);
35875 if(this.config.hidden){
35879 if (this.unrendered_panels && this.unrendered_panels.length) {
35880 for (var i =0;i< this.unrendered_panels.length; i++) {
35881 this.add(this.unrendered_panels[i]);
35883 this.unrendered_panels = null;
35889 applyConfig : function(c)
35892 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35893 var dh = Roo.DomHelper;
35894 if(c.titlebar !== false){
35895 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35896 this.collapseBtn.on("click", this.collapse, this);
35897 this.collapseBtn.enableDisplayMode();
35899 if(c.showPin === true || this.showPin){
35900 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35901 this.stickBtn.enableDisplayMode();
35902 this.stickBtn.on("click", this.expand, this);
35903 this.stickBtn.hide();
35908 /** This region's collapsed element
35909 * @type Roo.Element */
35912 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35913 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35916 if(c.floatable !== false){
35917 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35918 this.collapsedEl.on("click", this.collapseClick, this);
35921 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35922 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35923 id: "message", unselectable: "on", style:{"float":"left"}});
35924 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35926 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35927 this.expandBtn.on("click", this.expand, this);
35931 if(this.collapseBtn){
35932 this.collapseBtn.setVisible(c.collapsible == true);
35935 this.cmargins = c.cmargins || this.cmargins ||
35936 (this.position == "west" || this.position == "east" ?
35937 {top: 0, left: 2, right:2, bottom: 0} :
35938 {top: 2, left: 0, right:0, bottom: 2});
35940 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35943 this.bottomTabs = c.tabPosition != "top";
35945 this.autoScroll = c.autoScroll || false;
35950 this.duration = c.duration || .30;
35951 this.slideDuration = c.slideDuration || .45;
35956 * Returns true if this region is currently visible.
35957 * @return {Boolean}
35959 isVisible : function(){
35960 return this.visible;
35964 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35965 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35967 //setCollapsedTitle : function(title){
35968 // title = title || " ";
35969 // if(this.collapsedTitleTextEl){
35970 // this.collapsedTitleTextEl.innerHTML = title;
35974 getBox : function(){
35976 // if(!this.collapsed){
35977 b = this.el.getBox(false, true);
35979 // b = this.collapsedEl.getBox(false, true);
35984 getMargins : function(){
35985 return this.margins;
35986 //return this.collapsed ? this.cmargins : this.margins;
35989 highlight : function(){
35990 this.el.addClass("x-layout-panel-dragover");
35993 unhighlight : function(){
35994 this.el.removeClass("x-layout-panel-dragover");
35997 updateBox : function(box)
35999 if (!this.bodyEl) {
36000 return; // not rendered yet..
36004 if(!this.collapsed){
36005 this.el.dom.style.left = box.x + "px";
36006 this.el.dom.style.top = box.y + "px";
36007 this.updateBody(box.width, box.height);
36009 this.collapsedEl.dom.style.left = box.x + "px";
36010 this.collapsedEl.dom.style.top = box.y + "px";
36011 this.collapsedEl.setSize(box.width, box.height);
36014 this.tabs.autoSizeTabs();
36018 updateBody : function(w, h)
36021 this.el.setWidth(w);
36022 w -= this.el.getBorderWidth("rl");
36023 if(this.config.adjustments){
36024 w += this.config.adjustments[0];
36027 if(h !== null && h > 0){
36028 this.el.setHeight(h);
36029 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36030 h -= this.el.getBorderWidth("tb");
36031 if(this.config.adjustments){
36032 h += this.config.adjustments[1];
36034 this.bodyEl.setHeight(h);
36036 h = this.tabs.syncHeight(h);
36039 if(this.panelSize){
36040 w = w !== null ? w : this.panelSize.width;
36041 h = h !== null ? h : this.panelSize.height;
36043 if(this.activePanel){
36044 var el = this.activePanel.getEl();
36045 w = w !== null ? w : el.getWidth();
36046 h = h !== null ? h : el.getHeight();
36047 this.panelSize = {width: w, height: h};
36048 this.activePanel.setSize(w, h);
36050 if(Roo.isIE && this.tabs){
36051 this.tabs.el.repaint();
36056 * Returns the container element for this region.
36057 * @return {Roo.Element}
36059 getEl : function(){
36064 * Hides this region.
36067 //if(!this.collapsed){
36068 this.el.dom.style.left = "-2000px";
36071 // this.collapsedEl.dom.style.left = "-2000px";
36072 // this.collapsedEl.hide();
36074 this.visible = false;
36075 this.fireEvent("visibilitychange", this, false);
36079 * Shows this region if it was previously hidden.
36082 //if(!this.collapsed){
36085 // this.collapsedEl.show();
36087 this.visible = true;
36088 this.fireEvent("visibilitychange", this, true);
36091 closeClicked : function(){
36092 if(this.activePanel){
36093 this.remove(this.activePanel);
36097 collapseClick : function(e){
36099 e.stopPropagation();
36102 e.stopPropagation();
36108 * Collapses this region.
36109 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36112 collapse : function(skipAnim, skipCheck = false){
36113 if(this.collapsed) {
36117 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36119 this.collapsed = true;
36121 this.split.el.hide();
36123 if(this.config.animate && skipAnim !== true){
36124 this.fireEvent("invalidated", this);
36125 this.animateCollapse();
36127 this.el.setLocation(-20000,-20000);
36129 this.collapsedEl.show();
36130 this.fireEvent("collapsed", this);
36131 this.fireEvent("invalidated", this);
36137 animateCollapse : function(){
36142 * Expands this region if it was previously collapsed.
36143 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36144 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36147 expand : function(e, skipAnim){
36149 e.stopPropagation();
36151 if(!this.collapsed || this.el.hasActiveFx()) {
36155 this.afterSlideIn();
36158 this.collapsed = false;
36159 if(this.config.animate && skipAnim !== true){
36160 this.animateExpand();
36164 this.split.el.show();
36166 this.collapsedEl.setLocation(-2000,-2000);
36167 this.collapsedEl.hide();
36168 this.fireEvent("invalidated", this);
36169 this.fireEvent("expanded", this);
36173 animateExpand : function(){
36177 initTabs : function()
36179 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36181 var ts = new Roo.bootstrap.panel.Tabs({
36182 el: this.bodyEl.dom,
36183 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36184 disableTooltips: this.config.disableTabTips,
36185 toolbar : this.config.toolbar
36188 if(this.config.hideTabs){
36189 ts.stripWrap.setDisplayed(false);
36192 ts.resizeTabs = this.config.resizeTabs === true;
36193 ts.minTabWidth = this.config.minTabWidth || 40;
36194 ts.maxTabWidth = this.config.maxTabWidth || 250;
36195 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36196 ts.monitorResize = false;
36197 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36198 ts.bodyEl.addClass('roo-layout-tabs-body');
36199 this.panels.each(this.initPanelAsTab, this);
36202 initPanelAsTab : function(panel){
36203 var ti = this.tabs.addTab(
36207 this.config.closeOnTab && panel.isClosable(),
36210 if(panel.tabTip !== undefined){
36211 ti.setTooltip(panel.tabTip);
36213 ti.on("activate", function(){
36214 this.setActivePanel(panel);
36217 if(this.config.closeOnTab){
36218 ti.on("beforeclose", function(t, e){
36220 this.remove(panel);
36224 panel.tabItem = ti;
36229 updatePanelTitle : function(panel, title)
36231 if(this.activePanel == panel){
36232 this.updateTitle(title);
36235 var ti = this.tabs.getTab(panel.getEl().id);
36237 if(panel.tabTip !== undefined){
36238 ti.setTooltip(panel.tabTip);
36243 updateTitle : function(title){
36244 if(this.titleTextEl && !this.config.title){
36245 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36249 setActivePanel : function(panel)
36251 panel = this.getPanel(panel);
36252 if(this.activePanel && this.activePanel != panel){
36253 if(this.activePanel.setActiveState(false) === false){
36257 this.activePanel = panel;
36258 panel.setActiveState(true);
36259 if(this.panelSize){
36260 panel.setSize(this.panelSize.width, this.panelSize.height);
36263 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36265 this.updateTitle(panel.getTitle());
36267 this.fireEvent("invalidated", this);
36269 this.fireEvent("panelactivated", this, panel);
36273 * Shows the specified panel.
36274 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36275 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36277 showPanel : function(panel)
36279 panel = this.getPanel(panel);
36282 var tab = this.tabs.getTab(panel.getEl().id);
36283 if(tab.isHidden()){
36284 this.tabs.unhideTab(tab.id);
36288 this.setActivePanel(panel);
36295 * Get the active panel for this region.
36296 * @return {Roo.ContentPanel} The active panel or null
36298 getActivePanel : function(){
36299 return this.activePanel;
36302 validateVisibility : function(){
36303 if(this.panels.getCount() < 1){
36304 this.updateTitle(" ");
36305 this.closeBtn.hide();
36308 if(!this.isVisible()){
36315 * Adds the passed ContentPanel(s) to this region.
36316 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36317 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36319 add : function(panel)
36321 if(arguments.length > 1){
36322 for(var i = 0, len = arguments.length; i < len; i++) {
36323 this.add(arguments[i]);
36328 // if we have not been rendered yet, then we can not really do much of this..
36329 if (!this.bodyEl) {
36330 this.unrendered_panels.push(panel);
36337 if(this.hasPanel(panel)){
36338 this.showPanel(panel);
36341 panel.setRegion(this);
36342 this.panels.add(panel);
36343 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36344 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36345 // and hide them... ???
36346 this.bodyEl.dom.appendChild(panel.getEl().dom);
36347 if(panel.background !== true){
36348 this.setActivePanel(panel);
36350 this.fireEvent("paneladded", this, panel);
36357 this.initPanelAsTab(panel);
36361 if(panel.background !== true){
36362 this.tabs.activate(panel.getEl().id);
36364 this.fireEvent("paneladded", this, panel);
36369 * Hides the tab for the specified panel.
36370 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36372 hidePanel : function(panel){
36373 if(this.tabs && (panel = this.getPanel(panel))){
36374 this.tabs.hideTab(panel.getEl().id);
36379 * Unhides the tab for a previously hidden panel.
36380 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36382 unhidePanel : function(panel){
36383 if(this.tabs && (panel = this.getPanel(panel))){
36384 this.tabs.unhideTab(panel.getEl().id);
36388 clearPanels : function(){
36389 while(this.panels.getCount() > 0){
36390 this.remove(this.panels.first());
36395 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36396 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36397 * @param {Boolean} preservePanel Overrides the config preservePanel option
36398 * @return {Roo.ContentPanel} The panel that was removed
36400 remove : function(panel, preservePanel)
36402 panel = this.getPanel(panel);
36407 this.fireEvent("beforeremove", this, panel, e);
36408 if(e.cancel === true){
36411 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36412 var panelId = panel.getId();
36413 this.panels.removeKey(panelId);
36415 document.body.appendChild(panel.getEl().dom);
36418 this.tabs.removeTab(panel.getEl().id);
36419 }else if (!preservePanel){
36420 this.bodyEl.dom.removeChild(panel.getEl().dom);
36422 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36423 var p = this.panels.first();
36424 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36425 tempEl.appendChild(p.getEl().dom);
36426 this.bodyEl.update("");
36427 this.bodyEl.dom.appendChild(p.getEl().dom);
36429 this.updateTitle(p.getTitle());
36431 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36432 this.setActivePanel(p);
36434 panel.setRegion(null);
36435 if(this.activePanel == panel){
36436 this.activePanel = null;
36438 if(this.config.autoDestroy !== false && preservePanel !== true){
36439 try{panel.destroy();}catch(e){}
36441 this.fireEvent("panelremoved", this, panel);
36446 * Returns the TabPanel component used by this region
36447 * @return {Roo.TabPanel}
36449 getTabs : function(){
36453 createTool : function(parentEl, className){
36454 var btn = Roo.DomHelper.append(parentEl, {
36456 cls: "x-layout-tools-button",
36459 cls: "roo-layout-tools-button-inner " + className,
36463 btn.addClassOnOver("roo-layout-tools-button-over");
36468 * Ext JS Library 1.1.1
36469 * Copyright(c) 2006-2007, Ext JS, LLC.
36471 * Originally Released Under LGPL - original licence link has changed is not relivant.
36474 * <script type="text/javascript">
36480 * @class Roo.SplitLayoutRegion
36481 * @extends Roo.LayoutRegion
36482 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36484 Roo.bootstrap.layout.Split = function(config){
36485 this.cursor = config.cursor;
36486 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36489 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36491 splitTip : "Drag to resize.",
36492 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36493 useSplitTips : false,
36495 applyConfig : function(config){
36496 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36499 onRender : function(ctr,pos) {
36501 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36502 if(!this.config.split){
36507 var splitEl = Roo.DomHelper.append(ctr.dom, {
36509 id: this.el.id + "-split",
36510 cls: "roo-layout-split roo-layout-split-"+this.position,
36513 /** The SplitBar for this region
36514 * @type Roo.SplitBar */
36515 // does not exist yet...
36516 Roo.log([this.position, this.orientation]);
36518 this.split = new Roo.bootstrap.SplitBar({
36519 dragElement : splitEl,
36520 resizingElement: this.el,
36521 orientation : this.orientation
36524 this.split.on("moved", this.onSplitMove, this);
36525 this.split.useShim = this.config.useShim === true;
36526 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36527 if(this.useSplitTips){
36528 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36530 //if(config.collapsible){
36531 // this.split.el.on("dblclick", this.collapse, this);
36534 if(typeof this.config.minSize != "undefined"){
36535 this.split.minSize = this.config.minSize;
36537 if(typeof this.config.maxSize != "undefined"){
36538 this.split.maxSize = this.config.maxSize;
36540 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36541 this.hideSplitter();
36546 getHMaxSize : function(){
36547 var cmax = this.config.maxSize || 10000;
36548 var center = this.mgr.getRegion("center");
36549 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36552 getVMaxSize : function(){
36553 var cmax = this.config.maxSize || 10000;
36554 var center = this.mgr.getRegion("center");
36555 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36558 onSplitMove : function(split, newSize){
36559 this.fireEvent("resized", this, newSize);
36563 * Returns the {@link Roo.SplitBar} for this region.
36564 * @return {Roo.SplitBar}
36566 getSplitBar : function(){
36571 this.hideSplitter();
36572 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36575 hideSplitter : function(){
36577 this.split.el.setLocation(-2000,-2000);
36578 this.split.el.hide();
36584 this.split.el.show();
36586 Roo.bootstrap.layout.Split.superclass.show.call(this);
36589 beforeSlide: function(){
36590 if(Roo.isGecko){// firefox overflow auto bug workaround
36591 this.bodyEl.clip();
36593 this.tabs.bodyEl.clip();
36595 if(this.activePanel){
36596 this.activePanel.getEl().clip();
36598 if(this.activePanel.beforeSlide){
36599 this.activePanel.beforeSlide();
36605 afterSlide : function(){
36606 if(Roo.isGecko){// firefox overflow auto bug workaround
36607 this.bodyEl.unclip();
36609 this.tabs.bodyEl.unclip();
36611 if(this.activePanel){
36612 this.activePanel.getEl().unclip();
36613 if(this.activePanel.afterSlide){
36614 this.activePanel.afterSlide();
36620 initAutoHide : function(){
36621 if(this.autoHide !== false){
36622 if(!this.autoHideHd){
36623 var st = new Roo.util.DelayedTask(this.slideIn, this);
36624 this.autoHideHd = {
36625 "mouseout": function(e){
36626 if(!e.within(this.el, true)){
36630 "mouseover" : function(e){
36636 this.el.on(this.autoHideHd);
36640 clearAutoHide : function(){
36641 if(this.autoHide !== false){
36642 this.el.un("mouseout", this.autoHideHd.mouseout);
36643 this.el.un("mouseover", this.autoHideHd.mouseover);
36647 clearMonitor : function(){
36648 Roo.get(document).un("click", this.slideInIf, this);
36651 // these names are backwards but not changed for compat
36652 slideOut : function(){
36653 if(this.isSlid || this.el.hasActiveFx()){
36656 this.isSlid = true;
36657 if(this.collapseBtn){
36658 this.collapseBtn.hide();
36660 this.closeBtnState = this.closeBtn.getStyle('display');
36661 this.closeBtn.hide();
36663 this.stickBtn.show();
36666 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36667 this.beforeSlide();
36668 this.el.setStyle("z-index", 10001);
36669 this.el.slideIn(this.getSlideAnchor(), {
36670 callback: function(){
36672 this.initAutoHide();
36673 Roo.get(document).on("click", this.slideInIf, this);
36674 this.fireEvent("slideshow", this);
36681 afterSlideIn : function(){
36682 this.clearAutoHide();
36683 this.isSlid = false;
36684 this.clearMonitor();
36685 this.el.setStyle("z-index", "");
36686 if(this.collapseBtn){
36687 this.collapseBtn.show();
36689 this.closeBtn.setStyle('display', this.closeBtnState);
36691 this.stickBtn.hide();
36693 this.fireEvent("slidehide", this);
36696 slideIn : function(cb){
36697 if(!this.isSlid || this.el.hasActiveFx()){
36701 this.isSlid = false;
36702 this.beforeSlide();
36703 this.el.slideOut(this.getSlideAnchor(), {
36704 callback: function(){
36705 this.el.setLeftTop(-10000, -10000);
36707 this.afterSlideIn();
36715 slideInIf : function(e){
36716 if(!e.within(this.el)){
36721 animateCollapse : function(){
36722 this.beforeSlide();
36723 this.el.setStyle("z-index", 20000);
36724 var anchor = this.getSlideAnchor();
36725 this.el.slideOut(anchor, {
36726 callback : function(){
36727 this.el.setStyle("z-index", "");
36728 this.collapsedEl.slideIn(anchor, {duration:.3});
36730 this.el.setLocation(-10000,-10000);
36732 this.fireEvent("collapsed", this);
36739 animateExpand : function(){
36740 this.beforeSlide();
36741 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36742 this.el.setStyle("z-index", 20000);
36743 this.collapsedEl.hide({
36746 this.el.slideIn(this.getSlideAnchor(), {
36747 callback : function(){
36748 this.el.setStyle("z-index", "");
36751 this.split.el.show();
36753 this.fireEvent("invalidated", this);
36754 this.fireEvent("expanded", this);
36782 getAnchor : function(){
36783 return this.anchors[this.position];
36786 getCollapseAnchor : function(){
36787 return this.canchors[this.position];
36790 getSlideAnchor : function(){
36791 return this.sanchors[this.position];
36794 getAlignAdj : function(){
36795 var cm = this.cmargins;
36796 switch(this.position){
36812 getExpandAdj : function(){
36813 var c = this.collapsedEl, cm = this.cmargins;
36814 switch(this.position){
36816 return [-(cm.right+c.getWidth()+cm.left), 0];
36819 return [cm.right+c.getWidth()+cm.left, 0];
36822 return [0, -(cm.top+cm.bottom+c.getHeight())];
36825 return [0, cm.top+cm.bottom+c.getHeight()];
36831 * Ext JS Library 1.1.1
36832 * Copyright(c) 2006-2007, Ext JS, LLC.
36834 * Originally Released Under LGPL - original licence link has changed is not relivant.
36837 * <script type="text/javascript">
36840 * These classes are private internal classes
36842 Roo.bootstrap.layout.Center = function(config){
36843 config.region = "center";
36844 Roo.bootstrap.layout.Region.call(this, config);
36845 this.visible = true;
36846 this.minWidth = config.minWidth || 20;
36847 this.minHeight = config.minHeight || 20;
36850 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36852 // center panel can't be hidden
36856 // center panel can't be hidden
36859 getMinWidth: function(){
36860 return this.minWidth;
36863 getMinHeight: function(){
36864 return this.minHeight;
36877 Roo.bootstrap.layout.North = function(config)
36879 config.region = 'north';
36880 config.cursor = 'n-resize';
36882 Roo.bootstrap.layout.Split.call(this, config);
36886 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36887 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36888 this.split.el.addClass("roo-layout-split-v");
36890 var size = config.initialSize || config.height;
36891 if(typeof size != "undefined"){
36892 this.el.setHeight(size);
36895 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36897 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36901 getBox : function(){
36902 if(this.collapsed){
36903 return this.collapsedEl.getBox();
36905 var box = this.el.getBox();
36907 box.height += this.split.el.getHeight();
36912 updateBox : function(box){
36913 if(this.split && !this.collapsed){
36914 box.height -= this.split.el.getHeight();
36915 this.split.el.setLeft(box.x);
36916 this.split.el.setTop(box.y+box.height);
36917 this.split.el.setWidth(box.width);
36919 if(this.collapsed){
36920 this.updateBody(box.width, null);
36922 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36930 Roo.bootstrap.layout.South = function(config){
36931 config.region = 'south';
36932 config.cursor = 's-resize';
36933 Roo.bootstrap.layout.Split.call(this, config);
36935 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36936 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36937 this.split.el.addClass("roo-layout-split-v");
36939 var size = config.initialSize || config.height;
36940 if(typeof size != "undefined"){
36941 this.el.setHeight(size);
36945 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36946 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36947 getBox : function(){
36948 if(this.collapsed){
36949 return this.collapsedEl.getBox();
36951 var box = this.el.getBox();
36953 var sh = this.split.el.getHeight();
36960 updateBox : function(box){
36961 if(this.split && !this.collapsed){
36962 var sh = this.split.el.getHeight();
36965 this.split.el.setLeft(box.x);
36966 this.split.el.setTop(box.y-sh);
36967 this.split.el.setWidth(box.width);
36969 if(this.collapsed){
36970 this.updateBody(box.width, null);
36972 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36976 Roo.bootstrap.layout.East = function(config){
36977 config.region = "east";
36978 config.cursor = "e-resize";
36979 Roo.bootstrap.layout.Split.call(this, config);
36981 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36982 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36983 this.split.el.addClass("roo-layout-split-h");
36985 var size = config.initialSize || config.width;
36986 if(typeof size != "undefined"){
36987 this.el.setWidth(size);
36990 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36991 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36992 getBox : function(){
36993 if(this.collapsed){
36994 return this.collapsedEl.getBox();
36996 var box = this.el.getBox();
36998 var sw = this.split.el.getWidth();
37005 updateBox : function(box){
37006 if(this.split && !this.collapsed){
37007 var sw = this.split.el.getWidth();
37009 this.split.el.setLeft(box.x);
37010 this.split.el.setTop(box.y);
37011 this.split.el.setHeight(box.height);
37014 if(this.collapsed){
37015 this.updateBody(null, box.height);
37017 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37021 Roo.bootstrap.layout.West = function(config){
37022 config.region = "west";
37023 config.cursor = "w-resize";
37025 Roo.bootstrap.layout.Split.call(this, config);
37027 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37028 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37029 this.split.el.addClass("roo-layout-split-h");
37033 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37034 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37036 onRender: function(ctr, pos)
37038 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37039 var size = this.config.initialSize || this.config.width;
37040 if(typeof size != "undefined"){
37041 this.el.setWidth(size);
37045 getBox : function(){
37046 if(this.collapsed){
37047 return this.collapsedEl.getBox();
37049 var box = this.el.getBox();
37051 box.width += this.split.el.getWidth();
37056 updateBox : function(box){
37057 if(this.split && !this.collapsed){
37058 var sw = this.split.el.getWidth();
37060 this.split.el.setLeft(box.x+box.width);
37061 this.split.el.setTop(box.y);
37062 this.split.el.setHeight(box.height);
37064 if(this.collapsed){
37065 this.updateBody(null, box.height);
37067 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37070 Roo.namespace("Roo.bootstrap.panel");/*
37072 * Ext JS Library 1.1.1
37073 * Copyright(c) 2006-2007, Ext JS, LLC.
37075 * Originally Released Under LGPL - original licence link has changed is not relivant.
37078 * <script type="text/javascript">
37081 * @class Roo.ContentPanel
37082 * @extends Roo.util.Observable
37083 * A basic ContentPanel element.
37084 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37085 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37086 * @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
37087 * @cfg {Boolean} closable True if the panel can be closed/removed
37088 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37089 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37090 * @cfg {Toolbar} toolbar A toolbar for this panel
37091 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37092 * @cfg {String} title The title for this panel
37093 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37094 * @cfg {String} url Calls {@link #setUrl} with this value
37095 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37096 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37097 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37098 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37099 * @cfg {Boolean} badges render the badges
37102 * Create a new ContentPanel.
37103 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37104 * @param {String/Object} config A string to set only the title or a config object
37105 * @param {String} content (optional) Set the HTML content for this panel
37106 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37108 Roo.bootstrap.panel.Content = function( config){
37110 this.tpl = config.tpl || false;
37112 var el = config.el;
37113 var content = config.content;
37115 if(config.autoCreate){ // xtype is available if this is called from factory
37118 this.el = Roo.get(el);
37119 if(!this.el && config && config.autoCreate){
37120 if(typeof config.autoCreate == "object"){
37121 if(!config.autoCreate.id){
37122 config.autoCreate.id = config.id||el;
37124 this.el = Roo.DomHelper.append(document.body,
37125 config.autoCreate, true);
37127 var elcfg = { tag: "div",
37128 cls: "roo-layout-inactive-content",
37132 elcfg.html = config.html;
37136 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37139 this.closable = false;
37140 this.loaded = false;
37141 this.active = false;
37144 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37146 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37148 this.wrapEl = this.el; //this.el.wrap();
37150 if (config.toolbar.items) {
37151 ti = config.toolbar.items ;
37152 delete config.toolbar.items ;
37156 this.toolbar.render(this.wrapEl, 'before');
37157 for(var i =0;i < ti.length;i++) {
37158 // Roo.log(['add child', items[i]]);
37159 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37161 this.toolbar.items = nitems;
37162 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37163 delete config.toolbar;
37167 // xtype created footer. - not sure if will work as we normally have to render first..
37168 if (this.footer && !this.footer.el && this.footer.xtype) {
37169 if (!this.wrapEl) {
37170 this.wrapEl = this.el.wrap();
37173 this.footer.container = this.wrapEl.createChild();
37175 this.footer = Roo.factory(this.footer, Roo);
37180 if(typeof config == "string"){
37181 this.title = config;
37183 Roo.apply(this, config);
37187 this.resizeEl = Roo.get(this.resizeEl, true);
37189 this.resizeEl = this.el;
37191 // handle view.xtype
37199 * Fires when this panel is activated.
37200 * @param {Roo.ContentPanel} this
37204 * @event deactivate
37205 * Fires when this panel is activated.
37206 * @param {Roo.ContentPanel} this
37208 "deactivate" : true,
37212 * Fires when this panel is resized if fitToFrame is true.
37213 * @param {Roo.ContentPanel} this
37214 * @param {Number} width The width after any component adjustments
37215 * @param {Number} height The height after any component adjustments
37221 * Fires when this tab is created
37222 * @param {Roo.ContentPanel} this
37233 if(this.autoScroll){
37234 this.resizeEl.setStyle("overflow", "auto");
37236 // fix randome scrolling
37237 //this.el.on('scroll', function() {
37238 // Roo.log('fix random scolling');
37239 // this.scrollTo('top',0);
37242 content = content || this.content;
37244 this.setContent(content);
37246 if(config && config.url){
37247 this.setUrl(this.url, this.params, this.loadOnce);
37252 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37254 if (this.view && typeof(this.view.xtype) != 'undefined') {
37255 this.view.el = this.el.appendChild(document.createElement("div"));
37256 this.view = Roo.factory(this.view);
37257 this.view.render && this.view.render(false, '');
37261 this.fireEvent('render', this);
37264 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37268 setRegion : function(region){
37269 this.region = region;
37270 this.setActiveClass(region && !this.background);
37274 setActiveClass: function(state)
37277 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37278 this.el.setStyle('position','relative');
37280 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37281 this.el.setStyle('position', 'absolute');
37286 * Returns the toolbar for this Panel if one was configured.
37287 * @return {Roo.Toolbar}
37289 getToolbar : function(){
37290 return this.toolbar;
37293 setActiveState : function(active)
37295 this.active = active;
37296 this.setActiveClass(active);
37298 if(this.fireEvent("deactivate", this) === false){
37303 this.fireEvent("activate", this);
37307 * Updates this panel's element
37308 * @param {String} content The new content
37309 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37311 setContent : function(content, loadScripts){
37312 this.el.update(content, loadScripts);
37315 ignoreResize : function(w, h){
37316 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37319 this.lastSize = {width: w, height: h};
37324 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37325 * @return {Roo.UpdateManager} The UpdateManager
37327 getUpdateManager : function(){
37328 return this.el.getUpdateManager();
37331 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37332 * @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:
37335 url: "your-url.php",
37336 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37337 callback: yourFunction,
37338 scope: yourObject, //(optional scope)
37341 text: "Loading...",
37346 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37347 * 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.
37348 * @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}
37349 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37350 * @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.
37351 * @return {Roo.ContentPanel} this
37354 var um = this.el.getUpdateManager();
37355 um.update.apply(um, arguments);
37361 * 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.
37362 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37363 * @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)
37364 * @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)
37365 * @return {Roo.UpdateManager} The UpdateManager
37367 setUrl : function(url, params, loadOnce){
37368 if(this.refreshDelegate){
37369 this.removeListener("activate", this.refreshDelegate);
37371 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37372 this.on("activate", this.refreshDelegate);
37373 return this.el.getUpdateManager();
37376 _handleRefresh : function(url, params, loadOnce){
37377 if(!loadOnce || !this.loaded){
37378 var updater = this.el.getUpdateManager();
37379 updater.update(url, params, this._setLoaded.createDelegate(this));
37383 _setLoaded : function(){
37384 this.loaded = true;
37388 * Returns this panel's id
37391 getId : function(){
37396 * Returns this panel's element - used by regiosn to add.
37397 * @return {Roo.Element}
37399 getEl : function(){
37400 return this.wrapEl || this.el;
37405 adjustForComponents : function(width, height)
37407 //Roo.log('adjustForComponents ');
37408 if(this.resizeEl != this.el){
37409 width -= this.el.getFrameWidth('lr');
37410 height -= this.el.getFrameWidth('tb');
37413 var te = this.toolbar.getEl();
37414 te.setWidth(width);
37415 height -= te.getHeight();
37418 var te = this.footer.getEl();
37419 te.setWidth(width);
37420 height -= te.getHeight();
37424 if(this.adjustments){
37425 width += this.adjustments[0];
37426 height += this.adjustments[1];
37428 return {"width": width, "height": height};
37431 setSize : function(width, height){
37432 if(this.fitToFrame && !this.ignoreResize(width, height)){
37433 if(this.fitContainer && this.resizeEl != this.el){
37434 this.el.setSize(width, height);
37436 var size = this.adjustForComponents(width, height);
37437 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37438 this.fireEvent('resize', this, size.width, size.height);
37443 * Returns this panel's title
37446 getTitle : function(){
37448 if (typeof(this.title) != 'object') {
37453 for (var k in this.title) {
37454 if (!this.title.hasOwnProperty(k)) {
37458 if (k.indexOf('-') >= 0) {
37459 var s = k.split('-');
37460 for (var i = 0; i<s.length; i++) {
37461 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37464 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37471 * Set this panel's title
37472 * @param {String} title
37474 setTitle : function(title){
37475 this.title = title;
37477 this.region.updatePanelTitle(this, title);
37482 * Returns true is this panel was configured to be closable
37483 * @return {Boolean}
37485 isClosable : function(){
37486 return this.closable;
37489 beforeSlide : function(){
37491 this.resizeEl.clip();
37494 afterSlide : function(){
37496 this.resizeEl.unclip();
37500 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37501 * Will fail silently if the {@link #setUrl} method has not been called.
37502 * This does not activate the panel, just updates its content.
37504 refresh : function(){
37505 if(this.refreshDelegate){
37506 this.loaded = false;
37507 this.refreshDelegate();
37512 * Destroys this panel
37514 destroy : function(){
37515 this.el.removeAllListeners();
37516 var tempEl = document.createElement("span");
37517 tempEl.appendChild(this.el.dom);
37518 tempEl.innerHTML = "";
37524 * form - if the content panel contains a form - this is a reference to it.
37525 * @type {Roo.form.Form}
37529 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37530 * This contains a reference to it.
37536 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37546 * @param {Object} cfg Xtype definition of item to add.
37550 getChildContainer: function () {
37551 return this.getEl();
37556 var ret = new Roo.factory(cfg);
37561 if (cfg.xtype.match(/^Form$/)) {
37564 //if (this.footer) {
37565 // el = this.footer.container.insertSibling(false, 'before');
37567 el = this.el.createChild();
37570 this.form = new Roo.form.Form(cfg);
37573 if ( this.form.allItems.length) {
37574 this.form.render(el.dom);
37578 // should only have one of theses..
37579 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37580 // views.. should not be just added - used named prop 'view''
37582 cfg.el = this.el.appendChild(document.createElement("div"));
37585 var ret = new Roo.factory(cfg);
37587 ret.render && ret.render(false, ''); // render blank..
37597 * @class Roo.bootstrap.panel.Grid
37598 * @extends Roo.bootstrap.panel.Content
37600 * Create a new GridPanel.
37601 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37602 * @param {Object} config A the config object
37608 Roo.bootstrap.panel.Grid = function(config)
37612 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37613 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37615 config.el = this.wrapper;
37616 //this.el = this.wrapper;
37618 if (config.container) {
37619 // ctor'ed from a Border/panel.grid
37622 this.wrapper.setStyle("overflow", "hidden");
37623 this.wrapper.addClass('roo-grid-container');
37628 if(config.toolbar){
37629 var tool_el = this.wrapper.createChild();
37630 this.toolbar = Roo.factory(config.toolbar);
37632 if (config.toolbar.items) {
37633 ti = config.toolbar.items ;
37634 delete config.toolbar.items ;
37638 this.toolbar.render(tool_el);
37639 for(var i =0;i < ti.length;i++) {
37640 // Roo.log(['add child', items[i]]);
37641 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37643 this.toolbar.items = nitems;
37645 delete config.toolbar;
37648 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37649 config.grid.scrollBody = true;;
37650 config.grid.monitorWindowResize = false; // turn off autosizing
37651 config.grid.autoHeight = false;
37652 config.grid.autoWidth = false;
37654 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37656 if (config.background) {
37657 // render grid on panel activation (if panel background)
37658 this.on('activate', function(gp) {
37659 if (!gp.grid.rendered) {
37660 gp.grid.render(this.wrapper);
37661 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37666 this.grid.render(this.wrapper);
37667 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37670 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37671 // ??? needed ??? config.el = this.wrapper;
37676 // xtype created footer. - not sure if will work as we normally have to render first..
37677 if (this.footer && !this.footer.el && this.footer.xtype) {
37679 var ctr = this.grid.getView().getFooterPanel(true);
37680 this.footer.dataSource = this.grid.dataSource;
37681 this.footer = Roo.factory(this.footer, Roo);
37682 this.footer.render(ctr);
37692 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37693 getId : function(){
37694 return this.grid.id;
37698 * Returns the grid for this panel
37699 * @return {Roo.bootstrap.Table}
37701 getGrid : function(){
37705 setSize : function(width, height){
37706 if(!this.ignoreResize(width, height)){
37707 var grid = this.grid;
37708 var size = this.adjustForComponents(width, height);
37709 var gridel = grid.getGridEl();
37710 gridel.setSize(size.width, size.height);
37712 var thd = grid.getGridEl().select('thead',true).first();
37713 var tbd = grid.getGridEl().select('tbody', true).first();
37715 tbd.setSize(width, height - thd.getHeight());
37724 beforeSlide : function(){
37725 this.grid.getView().scroller.clip();
37728 afterSlide : function(){
37729 this.grid.getView().scroller.unclip();
37732 destroy : function(){
37733 this.grid.destroy();
37735 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37740 * @class Roo.bootstrap.panel.Nest
37741 * @extends Roo.bootstrap.panel.Content
37743 * Create a new Panel, that can contain a layout.Border.
37746 * @param {Roo.BorderLayout} layout The layout for this panel
37747 * @param {String/Object} config A string to set only the title or a config object
37749 Roo.bootstrap.panel.Nest = function(config)
37751 // construct with only one argument..
37752 /* FIXME - implement nicer consturctors
37753 if (layout.layout) {
37755 layout = config.layout;
37756 delete config.layout;
37758 if (layout.xtype && !layout.getEl) {
37759 // then layout needs constructing..
37760 layout = Roo.factory(layout, Roo);
37764 config.el = config.layout.getEl();
37766 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37768 config.layout.monitorWindowResize = false; // turn off autosizing
37769 this.layout = config.layout;
37770 this.layout.getEl().addClass("roo-layout-nested-layout");
37777 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37779 setSize : function(width, height){
37780 if(!this.ignoreResize(width, height)){
37781 var size = this.adjustForComponents(width, height);
37782 var el = this.layout.getEl();
37783 if (size.height < 1) {
37784 el.setWidth(size.width);
37786 el.setSize(size.width, size.height);
37788 var touch = el.dom.offsetWidth;
37789 this.layout.layout();
37790 // ie requires a double layout on the first pass
37791 if(Roo.isIE && !this.initialized){
37792 this.initialized = true;
37793 this.layout.layout();
37798 // activate all subpanels if not currently active..
37800 setActiveState : function(active){
37801 this.active = active;
37802 this.setActiveClass(active);
37805 this.fireEvent("deactivate", this);
37809 this.fireEvent("activate", this);
37810 // not sure if this should happen before or after..
37811 if (!this.layout) {
37812 return; // should not happen..
37815 for (var r in this.layout.regions) {
37816 reg = this.layout.getRegion(r);
37817 if (reg.getActivePanel()) {
37818 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37819 reg.setActivePanel(reg.getActivePanel());
37822 if (!reg.panels.length) {
37825 reg.showPanel(reg.getPanel(0));
37834 * Returns the nested BorderLayout for this panel
37835 * @return {Roo.BorderLayout}
37837 getLayout : function(){
37838 return this.layout;
37842 * Adds a xtype elements to the layout of the nested panel
37846 xtype : 'ContentPanel',
37853 xtype : 'NestedLayoutPanel',
37859 items : [ ... list of content panels or nested layout panels.. ]
37863 * @param {Object} cfg Xtype definition of item to add.
37865 addxtype : function(cfg) {
37866 return this.layout.addxtype(cfg);
37871 * Ext JS Library 1.1.1
37872 * Copyright(c) 2006-2007, Ext JS, LLC.
37874 * Originally Released Under LGPL - original licence link has changed is not relivant.
37877 * <script type="text/javascript">
37880 * @class Roo.TabPanel
37881 * @extends Roo.util.Observable
37882 * A lightweight tab container.
37886 // basic tabs 1, built from existing content
37887 var tabs = new Roo.TabPanel("tabs1");
37888 tabs.addTab("script", "View Script");
37889 tabs.addTab("markup", "View Markup");
37890 tabs.activate("script");
37892 // more advanced tabs, built from javascript
37893 var jtabs = new Roo.TabPanel("jtabs");
37894 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37896 // set up the UpdateManager
37897 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37898 var updater = tab2.getUpdateManager();
37899 updater.setDefaultUrl("ajax1.htm");
37900 tab2.on('activate', updater.refresh, updater, true);
37902 // Use setUrl for Ajax loading
37903 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37904 tab3.setUrl("ajax2.htm", null, true);
37907 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37910 jtabs.activate("jtabs-1");
37913 * Create a new TabPanel.
37914 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37915 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37917 Roo.bootstrap.panel.Tabs = function(config){
37919 * The container element for this TabPanel.
37920 * @type Roo.Element
37922 this.el = Roo.get(config.el);
37925 if(typeof config == "boolean"){
37926 this.tabPosition = config ? "bottom" : "top";
37928 Roo.apply(this, config);
37932 if(this.tabPosition == "bottom"){
37933 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37934 this.el.addClass("roo-tabs-bottom");
37936 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37937 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37938 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37940 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37942 if(this.tabPosition != "bottom"){
37943 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37944 * @type Roo.Element
37946 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37947 this.el.addClass("roo-tabs-top");
37951 this.bodyEl.setStyle("position", "relative");
37953 this.active = null;
37954 this.activateDelegate = this.activate.createDelegate(this);
37959 * Fires when the active tab changes
37960 * @param {Roo.TabPanel} this
37961 * @param {Roo.TabPanelItem} activePanel The new active tab
37965 * @event beforetabchange
37966 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37967 * @param {Roo.TabPanel} this
37968 * @param {Object} e Set cancel to true on this object to cancel the tab change
37969 * @param {Roo.TabPanelItem} tab The tab being changed to
37971 "beforetabchange" : true
37974 Roo.EventManager.onWindowResize(this.onResize, this);
37975 this.cpad = this.el.getPadding("lr");
37976 this.hiddenCount = 0;
37979 // toolbar on the tabbar support...
37980 if (this.toolbar) {
37981 alert("no toolbar support yet");
37982 this.toolbar = false;
37984 var tcfg = this.toolbar;
37985 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37986 this.toolbar = new Roo.Toolbar(tcfg);
37987 if (Roo.isSafari) {
37988 var tbl = tcfg.container.child('table', true);
37989 tbl.setAttribute('width', '100%');
37997 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38000 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38002 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38004 tabPosition : "top",
38006 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38008 currentTabWidth : 0,
38010 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38014 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38018 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38020 preferredTabWidth : 175,
38022 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38024 resizeTabs : false,
38026 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38028 monitorResize : true,
38030 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
38035 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38036 * @param {String} id The id of the div to use <b>or create</b>
38037 * @param {String} text The text for the tab
38038 * @param {String} content (optional) Content to put in the TabPanelItem body
38039 * @param {Boolean} closable (optional) True to create a close icon on the tab
38040 * @return {Roo.TabPanelItem} The created TabPanelItem
38042 addTab : function(id, text, content, closable, tpl)
38044 var item = new Roo.bootstrap.panel.TabItem({
38048 closable : closable,
38051 this.addTabItem(item);
38053 item.setContent(content);
38059 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38060 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38061 * @return {Roo.TabPanelItem}
38063 getTab : function(id){
38064 return this.items[id];
38068 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38069 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38071 hideTab : function(id){
38072 var t = this.items[id];
38075 this.hiddenCount++;
38076 this.autoSizeTabs();
38081 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38082 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38084 unhideTab : function(id){
38085 var t = this.items[id];
38087 t.setHidden(false);
38088 this.hiddenCount--;
38089 this.autoSizeTabs();
38094 * Adds an existing {@link Roo.TabPanelItem}.
38095 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38097 addTabItem : function(item){
38098 this.items[item.id] = item;
38099 this.items.push(item);
38100 // if(this.resizeTabs){
38101 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38102 // this.autoSizeTabs();
38104 // item.autoSize();
38109 * Removes a {@link Roo.TabPanelItem}.
38110 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38112 removeTab : function(id){
38113 var items = this.items;
38114 var tab = items[id];
38115 if(!tab) { return; }
38116 var index = items.indexOf(tab);
38117 if(this.active == tab && items.length > 1){
38118 var newTab = this.getNextAvailable(index);
38123 this.stripEl.dom.removeChild(tab.pnode.dom);
38124 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38125 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38127 items.splice(index, 1);
38128 delete this.items[tab.id];
38129 tab.fireEvent("close", tab);
38130 tab.purgeListeners();
38131 this.autoSizeTabs();
38134 getNextAvailable : function(start){
38135 var items = this.items;
38137 // look for a next tab that will slide over to
38138 // replace the one being removed
38139 while(index < items.length){
38140 var item = items[++index];
38141 if(item && !item.isHidden()){
38145 // if one isn't found select the previous tab (on the left)
38148 var item = items[--index];
38149 if(item && !item.isHidden()){
38157 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38158 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38160 disableTab : function(id){
38161 var tab = this.items[id];
38162 if(tab && this.active != tab){
38168 * Enables a {@link Roo.TabPanelItem} that is disabled.
38169 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38171 enableTab : function(id){
38172 var tab = this.items[id];
38177 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38178 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38179 * @return {Roo.TabPanelItem} The TabPanelItem.
38181 activate : function(id){
38182 var tab = this.items[id];
38186 if(tab == this.active || tab.disabled){
38190 this.fireEvent("beforetabchange", this, e, tab);
38191 if(e.cancel !== true && !tab.disabled){
38193 this.active.hide();
38195 this.active = this.items[id];
38196 this.active.show();
38197 this.fireEvent("tabchange", this, this.active);
38203 * Gets the active {@link Roo.TabPanelItem}.
38204 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38206 getActiveTab : function(){
38207 return this.active;
38211 * Updates the tab body element to fit the height of the container element
38212 * for overflow scrolling
38213 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38215 syncHeight : function(targetHeight){
38216 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38217 var bm = this.bodyEl.getMargins();
38218 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38219 this.bodyEl.setHeight(newHeight);
38223 onResize : function(){
38224 if(this.monitorResize){
38225 this.autoSizeTabs();
38230 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38232 beginUpdate : function(){
38233 this.updating = true;
38237 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38239 endUpdate : function(){
38240 this.updating = false;
38241 this.autoSizeTabs();
38245 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38247 autoSizeTabs : function(){
38248 var count = this.items.length;
38249 var vcount = count - this.hiddenCount;
38250 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38253 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38254 var availWidth = Math.floor(w / vcount);
38255 var b = this.stripBody;
38256 if(b.getWidth() > w){
38257 var tabs = this.items;
38258 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38259 if(availWidth < this.minTabWidth){
38260 /*if(!this.sleft){ // incomplete scrolling code
38261 this.createScrollButtons();
38264 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38267 if(this.currentTabWidth < this.preferredTabWidth){
38268 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38274 * Returns the number of tabs in this TabPanel.
38277 getCount : function(){
38278 return this.items.length;
38282 * Resizes all the tabs to the passed width
38283 * @param {Number} The new width
38285 setTabWidth : function(width){
38286 this.currentTabWidth = width;
38287 for(var i = 0, len = this.items.length; i < len; i++) {
38288 if(!this.items[i].isHidden()) {
38289 this.items[i].setWidth(width);
38295 * Destroys this TabPanel
38296 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38298 destroy : function(removeEl){
38299 Roo.EventManager.removeResizeListener(this.onResize, this);
38300 for(var i = 0, len = this.items.length; i < len; i++){
38301 this.items[i].purgeListeners();
38303 if(removeEl === true){
38304 this.el.update("");
38309 createStrip : function(container)
38311 var strip = document.createElement("nav");
38312 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38313 container.appendChild(strip);
38317 createStripList : function(strip)
38319 // div wrapper for retard IE
38320 // returns the "tr" element.
38321 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38322 //'<div class="x-tabs-strip-wrap">'+
38323 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38324 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38325 return strip.firstChild; //.firstChild.firstChild.firstChild;
38327 createBody : function(container)
38329 var body = document.createElement("div");
38330 Roo.id(body, "tab-body");
38331 //Roo.fly(body).addClass("x-tabs-body");
38332 Roo.fly(body).addClass("tab-content");
38333 container.appendChild(body);
38336 createItemBody :function(bodyEl, id){
38337 var body = Roo.getDom(id);
38339 body = document.createElement("div");
38342 //Roo.fly(body).addClass("x-tabs-item-body");
38343 Roo.fly(body).addClass("tab-pane");
38344 bodyEl.insertBefore(body, bodyEl.firstChild);
38348 createStripElements : function(stripEl, text, closable, tpl)
38350 var td = document.createElement("li"); // was td..
38353 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38356 stripEl.appendChild(td);
38358 td.className = "x-tabs-closable";
38359 if(!this.closeTpl){
38360 this.closeTpl = new Roo.Template(
38361 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38362 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38363 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38366 var el = this.closeTpl.overwrite(td, {"text": text});
38367 var close = el.getElementsByTagName("div")[0];
38368 var inner = el.getElementsByTagName("em")[0];
38369 return {"el": el, "close": close, "inner": inner};
38372 // not sure what this is..
38373 // if(!this.tabTpl){
38374 //this.tabTpl = new Roo.Template(
38375 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38376 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38378 // this.tabTpl = new Roo.Template(
38379 // '<a href="#">' +
38380 // '<span unselectable="on"' +
38381 // (this.disableTooltips ? '' : ' title="{text}"') +
38382 // ' >{text}</span></a>'
38388 var template = tpl || this.tabTpl || false;
38392 template = new Roo.Template(
38394 '<span unselectable="on"' +
38395 (this.disableTooltips ? '' : ' title="{text}"') +
38396 ' >{text}</span></a>'
38400 switch (typeof(template)) {
38404 template = new Roo.Template(template);
38410 var el = template.overwrite(td, {"text": text});
38412 var inner = el.getElementsByTagName("span")[0];
38414 return {"el": el, "inner": inner};
38422 * @class Roo.TabPanelItem
38423 * @extends Roo.util.Observable
38424 * Represents an individual item (tab plus body) in a TabPanel.
38425 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38426 * @param {String} id The id of this TabPanelItem
38427 * @param {String} text The text for the tab of this TabPanelItem
38428 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38430 Roo.bootstrap.panel.TabItem = function(config){
38432 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38433 * @type Roo.TabPanel
38435 this.tabPanel = config.panel;
38437 * The id for this TabPanelItem
38440 this.id = config.id;
38442 this.disabled = false;
38444 this.text = config.text;
38446 this.loaded = false;
38447 this.closable = config.closable;
38450 * The body element for this TabPanelItem.
38451 * @type Roo.Element
38453 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38454 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38455 this.bodyEl.setStyle("display", "block");
38456 this.bodyEl.setStyle("zoom", "1");
38457 //this.hideAction();
38459 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38461 this.el = Roo.get(els.el);
38462 this.inner = Roo.get(els.inner, true);
38463 this.textEl = Roo.get(this.el.dom.firstChild, true);
38464 this.pnode = Roo.get(els.el.parentNode, true);
38465 // this.el.on("mousedown", this.onTabMouseDown, this);
38466 this.el.on("click", this.onTabClick, this);
38468 if(config.closable){
38469 var c = Roo.get(els.close, true);
38470 c.dom.title = this.closeText;
38471 c.addClassOnOver("close-over");
38472 c.on("click", this.closeClick, this);
38478 * Fires when this tab becomes the active tab.
38479 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38480 * @param {Roo.TabPanelItem} this
38484 * @event beforeclose
38485 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38486 * @param {Roo.TabPanelItem} this
38487 * @param {Object} e Set cancel to true on this object to cancel the close.
38489 "beforeclose": true,
38492 * Fires when this tab is closed.
38493 * @param {Roo.TabPanelItem} this
38497 * @event deactivate
38498 * Fires when this tab is no longer the active tab.
38499 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38500 * @param {Roo.TabPanelItem} this
38502 "deactivate" : true
38504 this.hidden = false;
38506 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38509 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38511 purgeListeners : function(){
38512 Roo.util.Observable.prototype.purgeListeners.call(this);
38513 this.el.removeAllListeners();
38516 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38519 this.pnode.addClass("active");
38522 this.tabPanel.stripWrap.repaint();
38524 this.fireEvent("activate", this.tabPanel, this);
38528 * Returns true if this tab is the active tab.
38529 * @return {Boolean}
38531 isActive : function(){
38532 return this.tabPanel.getActiveTab() == this;
38536 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38539 this.pnode.removeClass("active");
38541 this.fireEvent("deactivate", this.tabPanel, this);
38544 hideAction : function(){
38545 this.bodyEl.hide();
38546 this.bodyEl.setStyle("position", "absolute");
38547 this.bodyEl.setLeft("-20000px");
38548 this.bodyEl.setTop("-20000px");
38551 showAction : function(){
38552 this.bodyEl.setStyle("position", "relative");
38553 this.bodyEl.setTop("");
38554 this.bodyEl.setLeft("");
38555 this.bodyEl.show();
38559 * Set the tooltip for the tab.
38560 * @param {String} tooltip The tab's tooltip
38562 setTooltip : function(text){
38563 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38564 this.textEl.dom.qtip = text;
38565 this.textEl.dom.removeAttribute('title');
38567 this.textEl.dom.title = text;
38571 onTabClick : function(e){
38572 e.preventDefault();
38573 this.tabPanel.activate(this.id);
38576 onTabMouseDown : function(e){
38577 e.preventDefault();
38578 this.tabPanel.activate(this.id);
38581 getWidth : function(){
38582 return this.inner.getWidth();
38585 setWidth : function(width){
38586 var iwidth = width - this.pnode.getPadding("lr");
38587 this.inner.setWidth(iwidth);
38588 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38589 this.pnode.setWidth(width);
38593 * Show or hide the tab
38594 * @param {Boolean} hidden True to hide or false to show.
38596 setHidden : function(hidden){
38597 this.hidden = hidden;
38598 this.pnode.setStyle("display", hidden ? "none" : "");
38602 * Returns true if this tab is "hidden"
38603 * @return {Boolean}
38605 isHidden : function(){
38606 return this.hidden;
38610 * Returns the text for this tab
38613 getText : function(){
38617 autoSize : function(){
38618 //this.el.beginMeasure();
38619 this.textEl.setWidth(1);
38621 * #2804 [new] Tabs in Roojs
38622 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38624 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38625 //this.el.endMeasure();
38629 * Sets the text for the tab (Note: this also sets the tooltip text)
38630 * @param {String} text The tab's text and tooltip
38632 setText : function(text){
38634 this.textEl.update(text);
38635 this.setTooltip(text);
38636 //if(!this.tabPanel.resizeTabs){
38637 // this.autoSize();
38641 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38643 activate : function(){
38644 this.tabPanel.activate(this.id);
38648 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38650 disable : function(){
38651 if(this.tabPanel.active != this){
38652 this.disabled = true;
38653 this.pnode.addClass("disabled");
38658 * Enables this TabPanelItem if it was previously disabled.
38660 enable : function(){
38661 this.disabled = false;
38662 this.pnode.removeClass("disabled");
38666 * Sets the content for this TabPanelItem.
38667 * @param {String} content The content
38668 * @param {Boolean} loadScripts true to look for and load scripts
38670 setContent : function(content, loadScripts){
38671 this.bodyEl.update(content, loadScripts);
38675 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38676 * @return {Roo.UpdateManager} The UpdateManager
38678 getUpdateManager : function(){
38679 return this.bodyEl.getUpdateManager();
38683 * Set a URL to be used to load the content for this TabPanelItem.
38684 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38685 * @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)
38686 * @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)
38687 * @return {Roo.UpdateManager} The UpdateManager
38689 setUrl : function(url, params, loadOnce){
38690 if(this.refreshDelegate){
38691 this.un('activate', this.refreshDelegate);
38693 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38694 this.on("activate", this.refreshDelegate);
38695 return this.bodyEl.getUpdateManager();
38699 _handleRefresh : function(url, params, loadOnce){
38700 if(!loadOnce || !this.loaded){
38701 var updater = this.bodyEl.getUpdateManager();
38702 updater.update(url, params, this._setLoaded.createDelegate(this));
38707 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38708 * Will fail silently if the setUrl method has not been called.
38709 * This does not activate the panel, just updates its content.
38711 refresh : function(){
38712 if(this.refreshDelegate){
38713 this.loaded = false;
38714 this.refreshDelegate();
38719 _setLoaded : function(){
38720 this.loaded = true;
38724 closeClick : function(e){
38727 this.fireEvent("beforeclose", this, o);
38728 if(o.cancel !== true){
38729 this.tabPanel.removeTab(this.id);
38733 * The text displayed in the tooltip for the close icon.
38736 closeText : "Close this tab"
38739 * This script refer to:
38740 * Title: International Telephone Input
38741 * Author: Jack O'Connor
38742 * Code version: v12.1.12
38743 * Availability: https://github.com/jackocnr/intl-tel-input.git
38746 Roo.bootstrap.PhoneInputData = function() {
38749 "Afghanistan (افغانستان)",
38754 "Albania (Shqipëri)",
38759 "Algeria (الجزائر)",
38784 "Antigua and Barbuda",
38794 "Armenia (Հայաստան)",
38810 "Austria (Österreich)",
38815 "Azerbaijan (Azərbaycan)",
38825 "Bahrain (البحرين)",
38830 "Bangladesh (বাংলাদেশ)",
38840 "Belarus (Беларусь)",
38845 "Belgium (België)",
38875 "Bosnia and Herzegovina (Босна и Херцеговина)",
38890 "British Indian Ocean Territory",
38895 "British Virgin Islands",
38905 "Bulgaria (България)",
38915 "Burundi (Uburundi)",
38920 "Cambodia (កម្ពុជា)",
38925 "Cameroon (Cameroun)",
38934 ["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"]
38937 "Cape Verde (Kabu Verdi)",
38942 "Caribbean Netherlands",
38953 "Central African Republic (République centrafricaine)",
38973 "Christmas Island",
38979 "Cocos (Keeling) Islands",
38990 "Comoros (جزر القمر)",
38995 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39000 "Congo (Republic) (Congo-Brazzaville)",
39020 "Croatia (Hrvatska)",
39041 "Czech Republic (Česká republika)",
39046 "Denmark (Danmark)",
39061 "Dominican Republic (República Dominicana)",
39065 ["809", "829", "849"]
39083 "Equatorial Guinea (Guinea Ecuatorial)",
39103 "Falkland Islands (Islas Malvinas)",
39108 "Faroe Islands (Føroyar)",
39129 "French Guiana (Guyane française)",
39134 "French Polynesia (Polynésie française)",
39149 "Georgia (საქართველო)",
39154 "Germany (Deutschland)",
39174 "Greenland (Kalaallit Nunaat)",
39211 "Guinea-Bissau (Guiné Bissau)",
39236 "Hungary (Magyarország)",
39241 "Iceland (Ísland)",
39261 "Iraq (العراق)",
39277 "Israel (ישראל)",
39304 "Jordan (الأردن)",
39309 "Kazakhstan (Казахстан)",
39330 "Kuwait (الكويت)",
39335 "Kyrgyzstan (Кыргызстан)",
39345 "Latvia (Latvija)",
39350 "Lebanon (لبنان)",
39365 "Libya (ليبيا)",
39375 "Lithuania (Lietuva)",
39390 "Macedonia (FYROM) (Македонија)",
39395 "Madagascar (Madagasikara)",
39425 "Marshall Islands",
39435 "Mauritania (موريتانيا)",
39440 "Mauritius (Moris)",
39461 "Moldova (Republica Moldova)",
39471 "Mongolia (Монгол)",
39476 "Montenegro (Crna Gora)",
39486 "Morocco (المغرب)",
39492 "Mozambique (Moçambique)",
39497 "Myanmar (Burma) (မြန်မာ)",
39502 "Namibia (Namibië)",
39517 "Netherlands (Nederland)",
39522 "New Caledonia (Nouvelle-Calédonie)",
39557 "North Korea (조선 민주주의 인민 공화국)",
39562 "Northern Mariana Islands",
39578 "Pakistan (پاکستان)",
39588 "Palestine (فلسطين)",
39598 "Papua New Guinea",
39640 "Réunion (La Réunion)",
39646 "Romania (România)",
39662 "Saint Barthélemy",
39673 "Saint Kitts and Nevis",
39683 "Saint Martin (Saint-Martin (partie française))",
39689 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39694 "Saint Vincent and the Grenadines",
39709 "São Tomé and Príncipe (São Tomé e Príncipe)",
39714 "Saudi Arabia (المملكة العربية السعودية)",
39719 "Senegal (Sénégal)",
39749 "Slovakia (Slovensko)",
39754 "Slovenia (Slovenija)",
39764 "Somalia (Soomaaliya)",
39774 "South Korea (대한민국)",
39779 "South Sudan (جنوب السودان)",
39789 "Sri Lanka (ශ්රී ලංකාව)",
39794 "Sudan (السودان)",
39804 "Svalbard and Jan Mayen",
39815 "Sweden (Sverige)",
39820 "Switzerland (Schweiz)",
39825 "Syria (سوريا)",
39870 "Trinidad and Tobago",
39875 "Tunisia (تونس)",
39880 "Turkey (Türkiye)",
39890 "Turks and Caicos Islands",
39900 "U.S. Virgin Islands",
39910 "Ukraine (Україна)",
39915 "United Arab Emirates (الإمارات العربية المتحدة)",
39937 "Uzbekistan (Oʻzbekiston)",
39947 "Vatican City (Città del Vaticano)",
39958 "Vietnam (Việt Nam)",
39963 "Wallis and Futuna (Wallis-et-Futuna)",
39968 "Western Sahara (الصحراء الغربية)",
39974 "Yemen (اليمن)",
39998 * This script refer to:
39999 * Title: International Telephone Input
40000 * Author: Jack O'Connor
40001 * Code version: v12.1.12
40002 * Availability: https://github.com/jackocnr/intl-tel-input.git
40006 * @class Roo.bootstrap.PhoneInput
40007 * @extends Roo.bootstrap.TriggerField
40008 * An input with International dial-code selection
40010 * @cfg {String} defaultDialCode default '+852'
40011 * @cfg {Array} preferedCountries default []
40014 * Create a new PhoneInput.
40015 * @param {Object} config Configuration options
40018 Roo.bootstrap.PhoneInput = function(config) {
40019 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40022 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40024 listWidth: undefined,
40026 selectedClass: 'active',
40028 invalidClass : "has-warning",
40030 validClass: 'has-success',
40032 allowed: '0123456789',
40037 * @cfg {String} defaultDialCode The default dial code when initializing the input
40039 defaultDialCode: '+852',
40042 * @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
40044 preferedCountries: false,
40046 getAutoCreate : function()
40048 var data = Roo.bootstrap.PhoneInputData();
40049 var align = this.labelAlign || this.parentLabelAlign();
40052 this.allCountries = [];
40053 this.dialCodeMapping = [];
40055 for (var i = 0; i < data.length; i++) {
40057 this.allCountries[i] = {
40061 priority: c[3] || 0,
40062 areaCodes: c[4] || null
40064 this.dialCodeMapping[c[2]] = {
40067 priority: c[3] || 0,
40068 areaCodes: c[4] || null
40080 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40081 maxlength: this.max_length,
40082 cls : 'form-control tel-input',
40083 autocomplete: 'new-password'
40086 var hiddenInput = {
40089 cls: 'hidden-tel-input'
40093 hiddenInput.name = this.name;
40096 if (this.disabled) {
40097 input.disabled = true;
40100 var flag_container = {
40117 cls: this.hasFeedback ? 'has-feedback' : '',
40123 cls: 'dial-code-holder',
40130 cls: 'roo-select2-container input-group',
40137 if (this.fieldLabel.length) {
40140 tooltip: 'This field is required'
40146 cls: 'control-label',
40152 html: this.fieldLabel
40155 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40161 if(this.indicatorpos == 'right') {
40162 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40169 if(align == 'left') {
40177 if(this.labelWidth > 12){
40178 label.style = "width: " + this.labelWidth + 'px';
40180 if(this.labelWidth < 13 && this.labelmd == 0){
40181 this.labelmd = this.labelWidth;
40183 if(this.labellg > 0){
40184 label.cls += ' col-lg-' + this.labellg;
40185 input.cls += ' col-lg-' + (12 - this.labellg);
40187 if(this.labelmd > 0){
40188 label.cls += ' col-md-' + this.labelmd;
40189 container.cls += ' col-md-' + (12 - this.labelmd);
40191 if(this.labelsm > 0){
40192 label.cls += ' col-sm-' + this.labelsm;
40193 container.cls += ' col-sm-' + (12 - this.labelsm);
40195 if(this.labelxs > 0){
40196 label.cls += ' col-xs-' + this.labelxs;
40197 container.cls += ' col-xs-' + (12 - this.labelxs);
40207 var settings = this;
40209 ['xs','sm','md','lg'].map(function(size){
40210 if (settings[size]) {
40211 cfg.cls += ' col-' + size + '-' + settings[size];
40215 this.store = new Roo.data.Store({
40216 proxy : new Roo.data.MemoryProxy({}),
40217 reader : new Roo.data.JsonReader({
40228 'name' : 'dialCode',
40232 'name' : 'priority',
40236 'name' : 'areaCodes',
40243 if(!this.preferedCountries) {
40244 this.preferedCountries = [
40251 var p = this.preferedCountries.reverse();
40254 for (var i = 0; i < p.length; i++) {
40255 for (var j = 0; j < this.allCountries.length; j++) {
40256 if(this.allCountries[j].iso2 == p[i]) {
40257 var t = this.allCountries[j];
40258 this.allCountries.splice(j,1);
40259 this.allCountries.unshift(t);
40265 this.store.proxy.data = {
40267 data: this.allCountries
40273 initEvents : function()
40276 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40278 this.indicator = this.indicatorEl();
40279 this.flag = this.flagEl();
40280 this.dialCodeHolder = this.dialCodeHolderEl();
40282 this.trigger = this.el.select('div.flag-box',true).first();
40283 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40288 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40289 _this.list.setWidth(lw);
40292 this.list.on('mouseover', this.onViewOver, this);
40293 this.list.on('mousemove', this.onViewMove, this);
40294 this.inputEl().on("keyup", this.onKeyUp, this);
40295 this.inputEl().on("keypress", this.onKeyPress, this);
40297 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40299 this.view = new Roo.View(this.list, this.tpl, {
40300 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40303 this.view.on('click', this.onViewClick, this);
40304 this.setValue(this.defaultDialCode);
40307 onTriggerClick : function(e)
40309 Roo.log('trigger click');
40314 if(this.isExpanded()){
40316 this.hasFocus = false;
40318 this.store.load({});
40319 this.hasFocus = true;
40324 isExpanded : function()
40326 return this.list.isVisible();
40329 collapse : function()
40331 if(!this.isExpanded()){
40335 Roo.get(document).un('mousedown', this.collapseIf, this);
40336 Roo.get(document).un('mousewheel', this.collapseIf, this);
40337 this.fireEvent('collapse', this);
40341 expand : function()
40345 if(this.isExpanded() || !this.hasFocus){
40349 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40350 this.list.setWidth(lw);
40353 this.restrictHeight();
40355 Roo.get(document).on('mousedown', this.collapseIf, this);
40356 Roo.get(document).on('mousewheel', this.collapseIf, this);
40358 this.fireEvent('expand', this);
40361 restrictHeight : function()
40363 this.list.alignTo(this.inputEl(), this.listAlign);
40364 this.list.alignTo(this.inputEl(), this.listAlign);
40367 onViewOver : function(e, t)
40369 if(this.inKeyMode){
40372 var item = this.view.findItemFromChild(t);
40375 var index = this.view.indexOf(item);
40376 this.select(index, false);
40381 onViewClick : function(view, doFocus, el, e)
40383 var index = this.view.getSelectedIndexes()[0];
40385 var r = this.store.getAt(index);
40388 this.onSelect(r, index);
40390 if(doFocus !== false && !this.blockFocus){
40391 this.inputEl().focus();
40395 onViewMove : function(e, t)
40397 this.inKeyMode = false;
40400 select : function(index, scrollIntoView)
40402 this.selectedIndex = index;
40403 this.view.select(index);
40404 if(scrollIntoView !== false){
40405 var el = this.view.getNode(index);
40407 this.list.scrollChildIntoView(el, false);
40412 createList : function()
40414 this.list = Roo.get(document.body).createChild({
40416 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40417 style: 'display:none'
40420 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40423 collapseIf : function(e)
40425 var in_combo = e.within(this.el);
40426 var in_list = e.within(this.list);
40427 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40429 if (in_combo || in_list || is_list) {
40435 onSelect : function(record, index)
40437 if(this.fireEvent('beforeselect', this, record, index) !== false){
40439 this.setFlagClass(record.data.iso2);
40440 this.setDialCode(record.data.dialCode);
40441 this.hasFocus = false;
40443 this.fireEvent('select', this, record, index);
40447 flagEl : function()
40449 var flag = this.el.select('div.flag',true).first();
40456 dialCodeHolderEl : function()
40458 var d = this.el.select('input.dial-code-holder',true).first();
40465 setDialCode : function(v)
40467 this.dialCodeHolder.dom.value = '+'+v;
40470 setFlagClass : function(n)
40472 this.flag.dom.className = 'flag '+n;
40475 getValue : function()
40477 var v = this.inputEl().getValue();
40478 if(this.dialCodeHolder) {
40479 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40484 setValue : function(v)
40486 var d = this.getDialCode(v);
40488 //invalid dial code
40489 if(v.length == 0 || !d || d.length == 0) {
40491 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40492 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40498 this.setFlagClass(this.dialCodeMapping[d].iso2);
40499 this.setDialCode(d);
40500 this.inputEl().dom.value = v.replace('+'+d,'');
40501 this.hiddenEl().dom.value = this.getValue();
40506 getDialCode : function(v)
40510 if (v.length == 0) {
40511 return this.dialCodeHolder.dom.value;
40515 if (v.charAt(0) != "+") {
40518 var numericChars = "";
40519 for (var i = 1; i < v.length; i++) {
40520 var c = v.charAt(i);
40523 if (this.dialCodeMapping[numericChars]) {
40524 dialCode = v.substr(1, i);
40526 if (numericChars.length == 4) {
40536 this.setValue(this.defaultDialCode);
40540 hiddenEl : function()
40542 return this.el.select('input.hidden-tel-input',true).first();
40545 // after setting val
40546 onKeyUp : function(e){
40547 this.setValue(this.getValue());
40550 onKeyPress : function(e){
40551 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40558 * @class Roo.bootstrap.MoneyField
40559 * @extends Roo.bootstrap.ComboBox
40560 * Bootstrap MoneyField class
40563 * Create a new MoneyField.
40564 * @param {Object} config Configuration options
40567 Roo.bootstrap.MoneyField = function(config) {
40569 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40573 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40576 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40578 allowDecimals : true,
40580 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40582 decimalSeparator : ".",
40584 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40586 decimalPrecision : 0,
40588 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40590 allowNegative : true,
40592 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40596 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40598 minValue : Number.NEGATIVE_INFINITY,
40600 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40602 maxValue : Number.MAX_VALUE,
40604 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40606 minText : "The minimum value for this field is {0}",
40608 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40610 maxText : "The maximum value for this field is {0}",
40612 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40613 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40615 nanText : "{0} is not a valid number",
40617 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40621 * @cfg {String} defaults currency of the MoneyField
40622 * value should be in lkey
40624 defaultCurrency : false,
40626 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40628 thousandsDelimiter : false,
40630 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40641 getAutoCreate : function()
40643 var align = this.labelAlign || this.parentLabelAlign();
40655 cls : 'form-control roo-money-amount-input',
40656 autocomplete: 'new-password'
40659 var hiddenInput = {
40663 cls: 'hidden-number-input'
40666 if(this.max_length) {
40667 input.maxlength = this.max_length;
40671 hiddenInput.name = this.name;
40674 if (this.disabled) {
40675 input.disabled = true;
40678 var clg = 12 - this.inputlg;
40679 var cmd = 12 - this.inputmd;
40680 var csm = 12 - this.inputsm;
40681 var cxs = 12 - this.inputxs;
40685 cls : 'row roo-money-field',
40689 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40693 cls: 'roo-select2-container input-group',
40697 cls : 'form-control roo-money-currency-input',
40698 autocomplete: 'new-password',
40700 name : this.currencyName
40704 cls : 'input-group-addon',
40718 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40722 cls: this.hasFeedback ? 'has-feedback' : '',
40733 if (this.fieldLabel.length) {
40736 tooltip: 'This field is required'
40742 cls: 'control-label',
40748 html: this.fieldLabel
40751 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40757 if(this.indicatorpos == 'right') {
40758 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40765 if(align == 'left') {
40773 if(this.labelWidth > 12){
40774 label.style = "width: " + this.labelWidth + 'px';
40776 if(this.labelWidth < 13 && this.labelmd == 0){
40777 this.labelmd = this.labelWidth;
40779 if(this.labellg > 0){
40780 label.cls += ' col-lg-' + this.labellg;
40781 input.cls += ' col-lg-' + (12 - this.labellg);
40783 if(this.labelmd > 0){
40784 label.cls += ' col-md-' + this.labelmd;
40785 container.cls += ' col-md-' + (12 - this.labelmd);
40787 if(this.labelsm > 0){
40788 label.cls += ' col-sm-' + this.labelsm;
40789 container.cls += ' col-sm-' + (12 - this.labelsm);
40791 if(this.labelxs > 0){
40792 label.cls += ' col-xs-' + this.labelxs;
40793 container.cls += ' col-xs-' + (12 - this.labelxs);
40804 var settings = this;
40806 ['xs','sm','md','lg'].map(function(size){
40807 if (settings[size]) {
40808 cfg.cls += ' col-' + size + '-' + settings[size];
40815 initEvents : function()
40817 this.indicator = this.indicatorEl();
40819 this.initCurrencyEvent();
40821 this.initNumberEvent();
40824 initCurrencyEvent : function()
40827 throw "can not find store for combo";
40830 this.store = Roo.factory(this.store, Roo.data);
40831 this.store.parent = this;
40835 this.triggerEl = this.el.select('.input-group-addon', true).first();
40837 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40842 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40843 _this.list.setWidth(lw);
40846 this.list.on('mouseover', this.onViewOver, this);
40847 this.list.on('mousemove', this.onViewMove, this);
40848 this.list.on('scroll', this.onViewScroll, this);
40851 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40854 this.view = new Roo.View(this.list, this.tpl, {
40855 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40858 this.view.on('click', this.onViewClick, this);
40860 this.store.on('beforeload', this.onBeforeLoad, this);
40861 this.store.on('load', this.onLoad, this);
40862 this.store.on('loadexception', this.onLoadException, this);
40864 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40865 "up" : function(e){
40866 this.inKeyMode = true;
40870 "down" : function(e){
40871 if(!this.isExpanded()){
40872 this.onTriggerClick();
40874 this.inKeyMode = true;
40879 "enter" : function(e){
40882 if(this.fireEvent("specialkey", this, e)){
40883 this.onViewClick(false);
40889 "esc" : function(e){
40893 "tab" : function(e){
40896 if(this.fireEvent("specialkey", this, e)){
40897 this.onViewClick(false);
40905 doRelay : function(foo, bar, hname){
40906 if(hname == 'down' || this.scope.isExpanded()){
40907 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40915 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40919 initNumberEvent : function(e)
40921 this.inputEl().on("keydown" , this.fireKey, this);
40922 this.inputEl().on("focus", this.onFocus, this);
40923 this.inputEl().on("blur", this.onBlur, this);
40925 this.inputEl().relayEvent('keyup', this);
40927 if(this.indicator){
40928 this.indicator.addClass('invisible');
40931 this.originalValue = this.getValue();
40933 if(this.validationEvent == 'keyup'){
40934 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40935 this.inputEl().on('keyup', this.filterValidation, this);
40937 else if(this.validationEvent !== false){
40938 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40941 if(this.selectOnFocus){
40942 this.on("focus", this.preFocus, this);
40945 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40946 this.inputEl().on("keypress", this.filterKeys, this);
40948 this.inputEl().relayEvent('keypress', this);
40951 var allowed = "0123456789";
40953 if(this.allowDecimals){
40954 allowed += this.decimalSeparator;
40957 if(this.allowNegative){
40961 if(this.thousandsDelimiter) {
40965 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40967 var keyPress = function(e){
40969 var k = e.getKey();
40971 var c = e.getCharCode();
40974 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40975 allowed.indexOf(String.fromCharCode(c)) === -1
40981 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40985 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40990 this.inputEl().on("keypress", keyPress, this);
40994 onTriggerClick : function(e)
41001 this.loadNext = false;
41003 if(this.isExpanded()){
41008 this.hasFocus = true;
41010 if(this.triggerAction == 'all') {
41011 this.doQuery(this.allQuery, true);
41015 this.doQuery(this.getRawValue());
41018 getCurrency : function()
41020 var v = this.currencyEl().getValue();
41025 restrictHeight : function()
41027 this.list.alignTo(this.currencyEl(), this.listAlign);
41028 this.list.alignTo(this.currencyEl(), this.listAlign);
41031 onViewClick : function(view, doFocus, el, e)
41033 var index = this.view.getSelectedIndexes()[0];
41035 var r = this.store.getAt(index);
41038 this.onSelect(r, index);
41042 onSelect : function(record, index){
41044 if(this.fireEvent('beforeselect', this, record, index) !== false){
41046 this.setFromCurrencyData(index > -1 ? record.data : false);
41050 this.fireEvent('select', this, record, index);
41054 setFromCurrencyData : function(o)
41058 this.lastCurrency = o;
41060 if (this.currencyField) {
41061 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41063 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41066 this.lastSelectionText = currency;
41068 //setting default currency
41069 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41070 this.setCurrency(this.defaultCurrency);
41074 this.setCurrency(currency);
41077 setFromData : function(o)
41081 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41083 this.setFromCurrencyData(c);
41088 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41090 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41093 this.setValue(value);
41097 setCurrency : function(v)
41099 this.currencyValue = v;
41102 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41107 setValue : function(v)
41109 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41115 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41117 this.inputEl().dom.value = (v == '') ? '' :
41118 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41120 if(!this.allowZero && v === '0') {
41121 this.hiddenEl().dom.value = '';
41122 this.inputEl().dom.value = '';
41129 getRawValue : function()
41131 var v = this.inputEl().getValue();
41136 getValue : function()
41138 return this.fixPrecision(this.parseValue(this.getRawValue()));
41141 parseValue : function(value)
41143 if(this.thousandsDelimiter) {
41145 r = new RegExp(",", "g");
41146 value = value.replace(r, "");
41149 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41150 return isNaN(value) ? '' : value;
41154 fixPrecision : function(value)
41156 if(this.thousandsDelimiter) {
41158 r = new RegExp(",", "g");
41159 value = value.replace(r, "");
41162 var nan = isNaN(value);
41164 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41165 return nan ? '' : value;
41167 return parseFloat(value).toFixed(this.decimalPrecision);
41170 decimalPrecisionFcn : function(v)
41172 return Math.floor(v);
41175 validateValue : function(value)
41177 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41181 var num = this.parseValue(value);
41184 this.markInvalid(String.format(this.nanText, value));
41188 if(num < this.minValue){
41189 this.markInvalid(String.format(this.minText, this.minValue));
41193 if(num > this.maxValue){
41194 this.markInvalid(String.format(this.maxText, this.maxValue));
41201 validate : function()
41203 if(this.disabled || this.allowBlank){
41208 var currency = this.getCurrency();
41210 if(this.validateValue(this.getRawValue()) && currency.length){
41215 this.markInvalid();
41219 getName: function()
41224 beforeBlur : function()
41230 var v = this.parseValue(this.getRawValue());
41237 onBlur : function()
41241 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41242 //this.el.removeClass(this.focusClass);
41245 this.hasFocus = false;
41247 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41251 var v = this.getValue();
41253 if(String(v) !== String(this.startValue)){
41254 this.fireEvent('change', this, v, this.startValue);
41257 this.fireEvent("blur", this);
41260 inputEl : function()
41262 return this.el.select('.roo-money-amount-input', true).first();
41265 currencyEl : function()
41267 return this.el.select('.roo-money-currency-input', true).first();
41270 hiddenEl : function()
41272 return this.el.select('input.hidden-number-input',true).first();