4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
21 * Do not use directly - it does not do anything..
22 * @param {Object} config The config object
27 Roo.bootstrap.Component = function(config){
28 Roo.bootstrap.Component.superclass.constructor.call(this, config);
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
34 allowDomMove : false, // to stop relocations in parent onRender...
42 initEvents : function() { },
48 can_build_overlaid : true,
55 // returns the parent component..
56 return Roo.ComponentMgr.get(this.parentId)
62 onRender : function(ct, position)
64 // Roo.log("Call onRender: " + this.xtype);
66 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
69 if (this.el.attr('xtype')) {
70 this.el.attr('xtypex', this.el.attr('xtype'));
71 this.el.dom.removeAttribute('xtype');
81 var cfg = Roo.apply({}, this.getAutoCreate());
84 // fill in the extra attributes
85 if (this.xattr && typeof(this.xattr) =='object') {
86 for (var i in this.xattr) {
87 cfg[i] = this.xattr[i];
92 cfg.dataId = this.dataId;
96 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
99 if (this.style) { // fixme needs to support more complex style data.
100 cfg.style = this.style;
104 cfg.name = this.name;
107 this.el = ct.createChild(cfg, position);
109 if(this.tabIndex !== undefined){
110 this.el.dom.setAttribute('tabIndex', this.tabIndex);
117 getChildContainer : function()
123 addxtype : function(tree,cntr)
127 cn = Roo.factory(tree);
129 cn.parentType = this.xtype; //??
130 cn.parentId = this.id;
132 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
134 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
136 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
138 var build_from_html = Roo.XComponent.build_from_html;
140 var is_body = (tree.xtype == 'Body') ;
142 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
144 var self_cntr_el = Roo.get(this[cntr](false));
146 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148 return this.addxtypeChild(tree,cntr);
151 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
154 return this.addxtypeChild(Roo.apply({}, tree),cntr);
157 Roo.log('skipping render');
165 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
171 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
175 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
180 addxtypeChild : function (tree, cntr)
182 Roo.log('addxtypeChild:' + cntr);
184 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
187 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188 (typeof(tree['flexy:foreach']) != 'undefined');
192 skip_children = false;
193 // render the element if it's not BODY.
194 if (tree.xtype != 'Body') {
196 cn = Roo.factory(tree);
198 cn.parentType = this.xtype; //??
199 cn.parentId = this.id;
201 var build_from_html = Roo.XComponent.build_from_html;
204 // does the container contain child eleemnts with 'xtype' attributes.
205 // that match this xtype..
206 // note - when we render we create these as well..
207 // so we should check to see if body has xtype set.
208 if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
210 var self_cntr_el = Roo.get(this[cntr](false));
211 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
214 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
215 // and are not displayed -this causes this to use up the wrong element when matching.
216 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
219 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
220 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
226 //echild.dom.removeAttribute('xtype');
228 Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
229 Roo.log(self_cntr_el);
237 // if object has flexy:if - then it may or may not be rendered.
238 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
239 // skip a flexy if element.
240 Roo.log('skipping render');
243 Roo.log('skipping all children');
244 skip_children = true;
249 // actually if flexy:foreach is found, we really want to create
250 // multiple copies here...
252 //Roo.log(this[cntr]());
253 cn.render(this[cntr](true));
255 // then add the element..
263 if (typeof (tree.menu) != 'undefined') {
264 tree.menu.parentType = cn.xtype;
265 tree.menu.triggerEl = cn.el;
266 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
270 if (!tree.items || !tree.items.length) {
274 var items = tree.items;
277 //Roo.log(items.length);
279 if (!skip_children) {
280 for(var i =0;i < items.length;i++) {
281 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
303 * @class Roo.bootstrap.Body
304 * @extends Roo.bootstrap.Component
305 * Bootstrap Body class
309 * @param {Object} config The config object
312 Roo.bootstrap.Body = function(config){
313 Roo.bootstrap.Body.superclass.constructor.call(this, config);
314 this.el = Roo.get(document.body);
315 if (this.cls && this.cls.length) {
316 Roo.get(document.body).addClass(this.cls);
320 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
325 onRender : function(ct, position)
327 /* Roo.log("Roo.bootstrap.Body - onRender");
328 if (this.cls && this.cls.length) {
329 Roo.get(document.body).addClass(this.cls);
349 * @class Roo.bootstrap.ButtonGroup
350 * @extends Roo.bootstrap.Component
351 * Bootstrap ButtonGroup class
352 * @cfg {String} size lg | sm | xs (default empty normal)
353 * @cfg {String} align vertical | justified (default none)
354 * @cfg {String} direction up | down (default down)
355 * @cfg {Boolean} toolbar false | true
356 * @cfg {Boolean} btn true | false
361 * @param {Object} config The config object
364 Roo.bootstrap.ButtonGroup = function(config){
365 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
368 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
376 getAutoCreate : function(){
382 cfg.html = this.html || cfg.html;
393 if (['vertical','justified'].indexOf(this.align)!==-1) {
394 cfg.cls = 'btn-group-' + this.align;
396 if (this.align == 'justified') {
397 console.log(this.items);
401 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
402 cfg.cls += ' btn-group-' + this.size;
405 if (this.direction == 'up') {
406 cfg.cls += ' dropup' ;
422 * @class Roo.bootstrap.Button
423 * @extends Roo.bootstrap.Component
424 * Bootstrap Button class
425 * @cfg {String} html The button content
426 * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
427 * @cfg {String} size empty | lg | sm | xs
428 * @cfg {String} tag empty | a | input | submit
429 * @cfg {String} href empty or href
430 * @cfg {Boolean} disabled false | true
431 * @cfg {Boolean} isClose false | true
432 * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
433 * @cfg {String} badge text for badge
434 * @cfg {String} theme default (or empty) | glow
435 * @cfg {Boolean} inverse false | true
436 * @cfg {Boolean} toggle false | true
437 * @cfg {String} ontext text for on toggle state
438 * @cfg {String} offtext text for off toggle state
439 * @cfg {Boolean} defaulton true | false
440 * @cfg {Boolean} preventDefault (true | false) default true
441 * @cfg {Boolean} removeClass true | false remove the standard class..
442 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
445 * Create a new button
446 * @param {Object} config The config object
450 Roo.bootstrap.Button = function(config){
451 Roo.bootstrap.Button.superclass.constructor.call(this, config);
456 * When a butotn is pressed
457 * @param {Roo.EventObject} e
462 * After the button has been toggles
463 * @param {Roo.EventObject} e
464 * @param {boolean} pressed (also available as button.pressed)
470 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
488 preventDefault: true,
497 getAutoCreate : function(){
505 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
506 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
511 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
513 if (this.toggle == true) {
516 cls: 'slider-frame roo-button',
521 'data-off-text':'OFF',
522 cls: 'slider-button',
528 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
529 cfg.cls += ' '+this.weight;
538 cfg["aria-hidden"] = true;
540 cfg.html = "×";
546 if (this.theme==='default') {
547 cfg.cls = 'btn roo-button';
549 //if (this.parentType != 'Navbar') {
550 this.weight = this.weight.length ? this.weight : 'default';
552 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
554 cfg.cls += ' btn-' + this.weight;
556 } else if (this.theme==='glow') {
559 cfg.cls = 'btn-glow roo-button';
561 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
563 cfg.cls += ' ' + this.weight;
569 this.cls += ' inverse';
574 cfg.cls += ' active';
578 cfg.disabled = 'disabled';
582 Roo.log('changing to ul' );
584 this.glyphicon = 'caret';
587 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
589 //gsRoo.log(this.parentType);
590 if (this.parentType === 'Navbar' && !this.parent().bar) {
591 Roo.log('changing to li?');
600 href : this.href || '#'
603 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
604 cfg.cls += ' dropdown';
611 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
613 if (this.glyphicon) {
614 cfg.html = ' ' + cfg.html;
619 cls: 'glyphicon glyphicon-' + this.glyphicon
629 // cfg.cls='btn roo-button';
633 var value = cfg.html;
638 cls: 'glyphicon glyphicon-' + this.glyphicon,
657 cfg.cls += ' dropdown';
658 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
661 if (cfg.tag !== 'a' && this.href !== '') {
662 throw "Tag must be a to set href.";
663 } else if (this.href.length > 0) {
664 cfg.href = this.href;
667 if(this.removeClass){
672 cfg.target = this.target;
677 initEvents: function() {
678 // Roo.log('init events?');
679 // Roo.log(this.el.dom);
682 if (typeof (this.menu) != 'undefined') {
683 this.menu.parentType = this.xtype;
684 this.menu.triggerEl = this.el;
685 this.addxtype(Roo.apply({}, this.menu));
689 if (this.el.hasClass('roo-button')) {
690 this.el.on('click', this.onClick, this);
692 this.el.select('.roo-button').on('click', this.onClick, this);
695 if(this.removeClass){
696 this.el.on('click', this.onClick, this);
699 this.el.enableDisplayMode();
702 onClick : function(e)
708 Roo.log('button on click ');
709 if(this.preventDefault){
712 if (this.pressed === true || this.pressed === false) {
713 this.pressed = !this.pressed;
714 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
715 this.fireEvent('toggle', this, e, this.pressed);
719 this.fireEvent('click', this, e);
723 * Enables this button
727 this.disabled = false;
728 this.el.removeClass('disabled');
732 * Disable this button
736 this.disabled = true;
737 this.el.addClass('disabled');
740 * sets the active state on/off,
741 * @param {Boolean} state (optional) Force a particular state
743 setActive : function(v) {
745 this.el[v ? 'addClass' : 'removeClass']('active');
748 * toggles the current active state
750 toggleActive : function()
752 var active = this.el.hasClass('active');
753 this.setActive(!active);
757 setText : function(str)
759 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
763 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
786 * @class Roo.bootstrap.Column
787 * @extends Roo.bootstrap.Component
788 * Bootstrap Column class
789 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
790 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
791 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
792 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
793 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
794 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
795 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
796 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
799 * @cfg {Boolean} hidden (true|false) hide the element
800 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
801 * @cfg {String} fa (ban|check|...) font awesome icon
802 * @cfg {Number} fasize (1|2|....) font awsome size
804 * @cfg {String} icon (info-sign|check|...) glyphicon name
806 * @cfg {String} html content of column.
809 * Create a new Column
810 * @param {Object} config The config object
813 Roo.bootstrap.Column = function(config){
814 Roo.bootstrap.Column.superclass.constructor.call(this, config);
817 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
835 getAutoCreate : function(){
836 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
844 ['xs','sm','md','lg'].map(function(size){
845 //Roo.log( size + ':' + settings[size]);
847 if (settings[size+'off'] !== false) {
848 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
851 if (settings[size] === false) {
854 Roo.log(settings[size]);
855 if (!settings[size]) { // 0 = hidden
856 cfg.cls += ' hidden-' + size;
859 cfg.cls += ' col-' + size + '-' + settings[size];
864 cfg.cls += ' hidden';
867 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
868 cfg.cls +=' alert alert-' + this.alert;
872 if (this.html.length) {
873 cfg.html = this.html;
877 if (this.fasize > 1) {
878 fasize = ' fa-' + this.fasize + 'x';
880 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
885 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
904 * @class Roo.bootstrap.Container
905 * @extends Roo.bootstrap.Component
906 * Bootstrap Container class
907 * @cfg {Boolean} jumbotron is it a jumbotron element
908 * @cfg {String} html content of element
909 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
910 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
911 * @cfg {String} header content of header (for panel)
912 * @cfg {String} footer content of footer (for panel)
913 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
914 * @cfg {String} tag (header|aside|section) type of HTML tag.
915 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
916 * @cfg {String} fa (ban|check|...) font awesome icon
917 * @cfg {String} icon (info-sign|check|...) glyphicon name
918 * @cfg {Boolean} hidden (true|false) hide the element
922 * Create a new Container
923 * @param {Object} config The config object
926 Roo.bootstrap.Container = function(config){
927 Roo.bootstrap.Container.superclass.constructor.call(this, config);
930 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
944 getChildContainer : function() {
950 if (this.panel.length) {
951 return this.el.select('.panel-body',true).first();
958 getAutoCreate : function(){
961 tag : this.tag || 'div',
965 if (this.jumbotron) {
966 cfg.cls = 'jumbotron';
971 // - this is applied by the parent..
973 // cfg.cls = this.cls + '';
976 if (this.sticky.length) {
978 var bd = Roo.get(document.body);
979 if (!bd.hasClass('bootstrap-sticky')) {
980 bd.addClass('bootstrap-sticky');
981 Roo.select('html',true).setStyle('height', '100%');
984 cfg.cls += 'bootstrap-sticky-' + this.sticky;
988 if (this.well.length) {
992 cfg.cls +=' well well-' +this.well;
1001 cfg.cls += ' hidden';
1005 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1006 cfg.cls +=' alert alert-' + this.alert;
1011 if (this.panel.length) {
1012 cfg.cls += ' panel panel-' + this.panel;
1014 if (this.header.length) {
1017 cls : 'panel-heading',
1020 cls : 'panel-title',
1033 if (this.footer.length) {
1035 cls : 'panel-footer',
1044 body.html = this.html || cfg.html;
1045 // prefix with the icons..
1047 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1050 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1055 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1056 cfg.cls = 'container';
1062 titleEl : function()
1064 if(!this.el || !this.panel.length || !this.header.length){
1068 return this.el.select('.panel-title',true).first();
1071 setTitle : function(v)
1073 var titleEl = this.titleEl();
1079 titleEl.dom.innerHTML = v;
1082 getTitle : function()
1085 var titleEl = this.titleEl();
1091 return titleEl.dom.innerHTML;
1105 * @class Roo.bootstrap.Img
1106 * @extends Roo.bootstrap.Component
1107 * Bootstrap Img class
1108 * @cfg {Boolean} imgResponsive false | true
1109 * @cfg {String} border rounded | circle | thumbnail
1110 * @cfg {String} src image source
1111 * @cfg {String} alt image alternative text
1112 * @cfg {String} href a tag href
1113 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1116 * Create a new Input
1117 * @param {Object} config The config object
1120 Roo.bootstrap.Img = function(config){
1121 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1127 * The img click event for the img.
1128 * @param {Roo.EventObject} e
1134 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1136 imgResponsive: true,
1142 getAutoCreate : function(){
1146 cls: (this.imgResponsive) ? 'img-responsive' : '',
1150 cfg.html = this.html || cfg.html;
1152 cfg.src = this.src || cfg.src;
1154 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1155 cfg.cls += ' img-' + this.border;
1172 a.target = this.target;
1178 return (this.href) ? a : cfg;
1181 initEvents: function() {
1184 this.el.on('click', this.onClick, this);
1188 onClick : function(e)
1190 Roo.log('img onclick');
1191 this.fireEvent('click', this, e);
1205 * @class Roo.bootstrap.Link
1206 * @extends Roo.bootstrap.Component
1207 * Bootstrap Link Class
1208 * @cfg {String} alt image alternative text
1209 * @cfg {String} href a tag href
1210 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1211 * @cfg {String} html the content of the link.
1212 * @cfg {String} anchor name for the anchor link
1214 * @cfg {Boolean} preventDefault (true | false) default false
1218 * Create a new Input
1219 * @param {Object} config The config object
1222 Roo.bootstrap.Link = function(config){
1223 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1229 * The img click event for the img.
1230 * @param {Roo.EventObject} e
1236 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1240 preventDefault: false,
1244 getAutoCreate : function()
1250 // anchor's do not require html/href...
1251 if (this.anchor === false) {
1252 cfg.html = this.html || 'html-missing';
1253 cfg.href = this.href || '#';
1255 cfg.name = this.anchor;
1256 if (this.html !== false) {
1257 cfg.html = this.html;
1259 if (this.href !== false) {
1260 cfg.href = this.href;
1264 if(this.alt !== false){
1269 if(this.target !== false) {
1270 cfg.target = this.target;
1276 initEvents: function() {
1278 if(!this.href || this.preventDefault){
1279 this.el.on('click', this.onClick, this);
1283 onClick : function(e)
1285 if(this.preventDefault){
1288 //Roo.log('img onclick');
1289 this.fireEvent('click', this, e);
1302 * @class Roo.bootstrap.Header
1303 * @extends Roo.bootstrap.Component
1304 * Bootstrap Header class
1305 * @cfg {String} html content of header
1306 * @cfg {Number} level (1|2|3|4|5|6) default 1
1309 * Create a new Header
1310 * @param {Object} config The config object
1314 Roo.bootstrap.Header = function(config){
1315 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1318 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1326 getAutoCreate : function(){
1329 tag: 'h' + (1 *this.level),
1330 html: this.html || 'fill in html'
1342 * Ext JS Library 1.1.1
1343 * Copyright(c) 2006-2007, Ext JS, LLC.
1345 * Originally Released Under LGPL - original licence link has changed is not relivant.
1348 * <script type="text/javascript">
1352 * @class Roo.bootstrap.MenuMgr
1353 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1356 Roo.bootstrap.MenuMgr = function(){
1357 var menus, active, groups = {}, attached = false, lastShow = new Date();
1359 // private - called when first menu is created
1362 active = new Roo.util.MixedCollection();
1363 Roo.get(document).addKeyListener(27, function(){
1364 if(active.length > 0){
1372 if(active && active.length > 0){
1373 var c = active.clone();
1383 if(active.length < 1){
1384 Roo.get(document).un("mouseup", onMouseDown);
1392 var last = active.last();
1393 lastShow = new Date();
1396 Roo.get(document).on("mouseup", onMouseDown);
1401 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1402 m.parentMenu.activeChild = m;
1403 }else if(last && last.isVisible()){
1404 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1409 function onBeforeHide(m){
1411 m.activeChild.hide();
1413 if(m.autoHideTimer){
1414 clearTimeout(m.autoHideTimer);
1415 delete m.autoHideTimer;
1420 function onBeforeShow(m){
1421 var pm = m.parentMenu;
1422 if(!pm && !m.allowOtherMenus){
1424 }else if(pm && pm.activeChild && active != m){
1425 pm.activeChild.hide();
1430 function onMouseDown(e){
1431 Roo.log("on MouseDown");
1432 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1440 function onBeforeCheck(mi, state){
1442 var g = groups[mi.group];
1443 for(var i = 0, l = g.length; i < l; i++){
1445 g[i].setChecked(false);
1454 * Hides all menus that are currently visible
1456 hideAll : function(){
1461 register : function(menu){
1465 menus[menu.id] = menu;
1466 menu.on("beforehide", onBeforeHide);
1467 menu.on("hide", onHide);
1468 menu.on("beforeshow", onBeforeShow);
1469 menu.on("show", onShow);
1471 if(g && menu.events["checkchange"]){
1475 groups[g].push(menu);
1476 menu.on("checkchange", onCheck);
1481 * Returns a {@link Roo.menu.Menu} object
1482 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1483 * be used to generate and return a new Menu instance.
1485 get : function(menu){
1486 if(typeof menu == "string"){ // menu id
1488 }else if(menu.events){ // menu instance
1491 /*else if(typeof menu.length == 'number'){ // array of menu items?
1492 return new Roo.bootstrap.Menu({items:menu});
1493 }else{ // otherwise, must be a config
1494 return new Roo.bootstrap.Menu(menu);
1501 unregister : function(menu){
1502 delete menus[menu.id];
1503 menu.un("beforehide", onBeforeHide);
1504 menu.un("hide", onHide);
1505 menu.un("beforeshow", onBeforeShow);
1506 menu.un("show", onShow);
1508 if(g && menu.events["checkchange"]){
1509 groups[g].remove(menu);
1510 menu.un("checkchange", onCheck);
1515 registerCheckable : function(menuItem){
1516 var g = menuItem.group;
1521 groups[g].push(menuItem);
1522 menuItem.on("beforecheckchange", onBeforeCheck);
1527 unregisterCheckable : function(menuItem){
1528 var g = menuItem.group;
1530 groups[g].remove(menuItem);
1531 menuItem.un("beforecheckchange", onBeforeCheck);
1543 * @class Roo.bootstrap.Menu
1544 * @extends Roo.bootstrap.Component
1545 * Bootstrap Menu class - container for MenuItems
1546 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1550 * @param {Object} config The config object
1554 Roo.bootstrap.Menu = function(config){
1555 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1556 if (this.registerMenu) {
1557 Roo.bootstrap.MenuMgr.register(this);
1562 * Fires before this menu is displayed
1563 * @param {Roo.menu.Menu} this
1568 * Fires before this menu is hidden
1569 * @param {Roo.menu.Menu} this
1574 * Fires after this menu is displayed
1575 * @param {Roo.menu.Menu} this
1580 * Fires after this menu is hidden
1581 * @param {Roo.menu.Menu} this
1586 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1587 * @param {Roo.menu.Menu} this
1588 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1589 * @param {Roo.EventObject} e
1594 * Fires when the mouse is hovering over this menu
1595 * @param {Roo.menu.Menu} this
1596 * @param {Roo.EventObject} e
1597 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1602 * Fires when the mouse exits this menu
1603 * @param {Roo.menu.Menu} this
1604 * @param {Roo.EventObject} e
1605 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1610 * Fires when a menu item contained in this menu is clicked
1611 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1612 * @param {Roo.EventObject} e
1616 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1619 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1623 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1626 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1628 registerMenu : true,
1630 menuItems :false, // stores the menu items..
1636 getChildContainer : function() {
1640 getAutoCreate : function(){
1642 //if (['right'].indexOf(this.align)!==-1) {
1643 // cfg.cn[1].cls += ' pull-right'
1649 cls : 'dropdown-menu' ,
1650 style : 'z-index:1000'
1654 if (this.type === 'submenu') {
1655 cfg.cls = 'submenu active';
1657 if (this.type === 'treeview') {
1658 cfg.cls = 'treeview-menu';
1663 initEvents : function() {
1665 // Roo.log("ADD event");
1666 // Roo.log(this.triggerEl.dom);
1667 this.triggerEl.on('click', this.onTriggerPress, this);
1668 this.triggerEl.addClass('dropdown-toggle');
1669 this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1671 this.el.on("mouseover", this.onMouseOver, this);
1672 this.el.on("mouseout", this.onMouseOut, this);
1676 findTargetItem : function(e){
1677 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1681 //Roo.log(t); Roo.log(t.id);
1683 //Roo.log(this.menuitems);
1684 return this.menuitems.get(t.id);
1686 //return this.items.get(t.menuItemId);
1691 onClick : function(e){
1692 Roo.log("menu.onClick");
1693 var t = this.findTargetItem(e);
1699 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
1700 if(t == this.activeItem && t.shouldDeactivate(e)){
1701 this.activeItem.deactivate();
1702 delete this.activeItem;
1706 this.setActiveItem(t, true);
1713 Roo.log('pass click event');
1717 this.fireEvent("click", this, t, e);
1721 onMouseOver : function(e){
1722 var t = this.findTargetItem(e);
1725 // if(t.canActivate && !t.disabled){
1726 // this.setActiveItem(t, true);
1730 this.fireEvent("mouseover", this, e, t);
1732 isVisible : function(){
1733 return !this.hidden;
1735 onMouseOut : function(e){
1736 var t = this.findTargetItem(e);
1739 // if(t == this.activeItem && t.shouldDeactivate(e)){
1740 // this.activeItem.deactivate();
1741 // delete this.activeItem;
1744 this.fireEvent("mouseout", this, e, t);
1749 * Displays this menu relative to another element
1750 * @param {String/HTMLElement/Roo.Element} element The element to align to
1751 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1752 * the element (defaults to this.defaultAlign)
1753 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1755 show : function(el, pos, parentMenu){
1756 this.parentMenu = parentMenu;
1760 this.fireEvent("beforeshow", this);
1761 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1764 * Displays this menu at a specific xy position
1765 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1766 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1768 showAt : function(xy, parentMenu, /* private: */_e){
1769 this.parentMenu = parentMenu;
1774 this.fireEvent("beforeshow", this);
1776 //xy = this.el.adjustForConstraints(xy);
1778 //this.el.setXY(xy);
1780 this.hideMenuItems();
1781 this.hidden = false;
1782 this.triggerEl.addClass('open');
1784 this.fireEvent("show", this);
1790 this.doFocus.defer(50, this);
1794 doFocus : function(){
1796 this.focusEl.focus();
1801 * Hides this menu and optionally all parent menus
1802 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1804 hide : function(deep){
1806 this.hideMenuItems();
1807 if(this.el && this.isVisible()){
1808 this.fireEvent("beforehide", this);
1809 if(this.activeItem){
1810 this.activeItem.deactivate();
1811 this.activeItem = null;
1813 this.triggerEl.removeClass('open');;
1815 this.fireEvent("hide", this);
1817 if(deep === true && this.parentMenu){
1818 this.parentMenu.hide(true);
1822 onTriggerPress : function(e)
1825 Roo.log('trigger press');
1826 //Roo.log(e.getTarget());
1827 // Roo.log(this.triggerEl.dom);
1828 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1831 if (this.isVisible()) {
1835 this.show(this.triggerEl, false, false);
1844 hideMenuItems : function()
1846 //$(backdrop).remove()
1847 Roo.select('.open',true).each(function(aa) {
1849 aa.removeClass('open');
1850 //var parent = getParent($(this))
1851 //var relatedTarget = { relatedTarget: this }
1853 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1854 //if (e.isDefaultPrevented()) return
1855 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1858 addxtypeChild : function (tree, cntr) {
1859 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1861 this.menuitems.add(comp);
1882 * @class Roo.bootstrap.MenuItem
1883 * @extends Roo.bootstrap.Component
1884 * Bootstrap MenuItem class
1885 * @cfg {String} html the menu label
1886 * @cfg {String} href the link
1887 * @cfg {Boolean} preventDefault (true | false) default true
1891 * Create a new MenuItem
1892 * @param {Object} config The config object
1896 Roo.bootstrap.MenuItem = function(config){
1897 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1902 * The raw click event for the entire grid.
1903 * @param {Roo.EventObject} e
1909 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
1913 preventDefault: true,
1915 getAutoCreate : function(){
1918 cls: 'dropdown-menu-item',
1927 if (this.parent().type == 'treeview') {
1928 cfg.cls = 'treeview-menu';
1931 cfg.cn[0].href = this.href || cfg.cn[0].href ;
1932 cfg.cn[0].html = this.html || cfg.cn[0].html ;
1936 initEvents: function() {
1938 //this.el.select('a').on('click', this.onClick, this);
1941 onClick : function(e)
1943 Roo.log('item on click ');
1944 //if(this.preventDefault){
1945 // e.preventDefault();
1947 //this.parent().hideMenuItems();
1949 this.fireEvent('click', this, e);
1968 * @class Roo.bootstrap.MenuSeparator
1969 * @extends Roo.bootstrap.Component
1970 * Bootstrap MenuSeparator class
1973 * Create a new MenuItem
1974 * @param {Object} config The config object
1978 Roo.bootstrap.MenuSeparator = function(config){
1979 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1982 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
1984 getAutoCreate : function(){
1999 <div class="modal fade">
2000 <div class="modal-dialog">
2001 <div class="modal-content">
2002 <div class="modal-header">
2003 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
2004 <h4 class="modal-title">Modal title</h4>
2006 <div class="modal-body">
2007 <p>One fine body…</p>
2009 <div class="modal-footer">
2010 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2011 <button type="button" class="btn btn-primary">Save changes</button>
2013 </div><!-- /.modal-content -->
2014 </div><!-- /.modal-dialog -->
2015 </div><!-- /.modal -->
2025 * @class Roo.bootstrap.Modal
2026 * @extends Roo.bootstrap.Component
2027 * Bootstrap Modal class
2028 * @cfg {String} title Title of dialog
2029 * @cfg {Boolean} specificTitle (true|false) default false
2030 * @cfg {Array} buttons Array of buttons or standard button set..
2031 * @cfg {String} buttonPosition (left|right|center) default right
2034 * Create a new Modal Dialog
2035 * @param {Object} config The config object
2038 Roo.bootstrap.Modal = function(config){
2039 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2044 * The raw btnclick event for the button
2045 * @param {Roo.EventObject} e
2049 this.buttons = this.buttons || [];
2052 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2054 title : 'test dialog',
2061 specificTitle: false,
2063 buttonPosition: 'right',
2065 onRender : function(ct, position)
2067 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2070 var cfg = Roo.apply({}, this.getAutoCreate());
2073 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2075 //if (!cfg.name.length) {
2079 cfg.cls += ' ' + this.cls;
2082 cfg.style = this.style;
2084 this.el = Roo.get(document.body).createChild(cfg, position);
2086 //var type = this.el.dom.type;
2088 if(this.tabIndex !== undefined){
2089 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2094 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2095 this.maskEl.enableDisplayMode("block");
2097 //this.el.addClass("x-dlg-modal");
2099 if (this.buttons.length) {
2100 Roo.each(this.buttons, function(bb) {
2101 b = Roo.apply({}, bb);
2102 b.xns = b.xns || Roo.bootstrap;
2103 b.xtype = b.xtype || 'Button';
2104 if (typeof(b.listeners) == 'undefined') {
2105 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2108 var btn = Roo.factory(b);
2110 btn.onRender(this.el.select('.modal-footer div').first());
2114 // render the children.
2117 if(typeof(this.items) != 'undefined'){
2118 var items = this.items;
2121 for(var i =0;i < items.length;i++) {
2122 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2126 this.items = nitems;
2128 this.body = this.el.select('.modal-body',true).first();
2129 this.close = this.el.select('.modal-header .close', true).first();
2130 this.footer = this.el.select('.modal-footer',true).first();
2132 //this.el.addClass([this.fieldClass, this.cls]);
2135 getAutoCreate : function(){
2140 html : this.html || ''
2145 cls : 'modal-title',
2149 if(this.specificTitle){
2155 style : 'display: none',
2158 cls: "modal-dialog",
2161 cls : "modal-content",
2164 cls : 'modal-header',
2176 cls : 'modal-footer',
2180 cls: 'btn-' + this.buttonPosition
2199 getChildContainer : function() {
2201 return this.el.select('.modal-body',true).first();
2204 getButtonContainer : function() {
2205 return this.el.select('.modal-footer div',true).first();
2208 initEvents : function()
2210 this.el.select('.modal-header .close').on('click', this.hide, this);
2212 // this.addxtype(this);
2216 if (!this.rendered) {
2220 this.el.addClass('on');
2221 this.el.removeClass('fade');
2222 this.el.setStyle('display', 'block');
2223 Roo.get(document.body).addClass("x-body-masked");
2224 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2226 this.el.setStyle('zIndex', '10001');
2227 this.fireEvent('show', this);
2233 Roo.log('Modal hide?!');
2235 Roo.get(document.body).removeClass("x-body-masked");
2236 this.el.removeClass('on');
2237 this.el.addClass('fade');
2238 this.el.setStyle('display', 'none');
2239 this.fireEvent('hide', this);
2242 addButton : function(str, cb)
2246 var b = Roo.apply({}, { html : str } );
2247 b.xns = b.xns || Roo.bootstrap;
2248 b.xtype = b.xtype || 'Button';
2249 if (typeof(b.listeners) == 'undefined') {
2250 b.listeners = { click : cb.createDelegate(this) };
2253 var btn = Roo.factory(b);
2255 btn.onRender(this.el.select('.modal-footer div').first());
2261 setDefaultButton : function(btn)
2263 //this.el.select('.modal-footer').()
2265 resizeTo: function(w,h)
2269 setContentSize : function(w, h)
2273 onButtonClick: function(btn,e)
2276 this.fireEvent('btnclick', btn.name, e);
2278 setTitle: function(str) {
2279 this.el.select('.modal-title',true).first().dom.innerHTML = str;
2285 Roo.apply(Roo.bootstrap.Modal, {
2287 * Button config that displays a single OK button
2296 * Button config that displays Yes and No buttons
2312 * Button config that displays OK and Cancel buttons
2327 * Button config that displays Yes, No and Cancel buttons
2349 * messagebox - can be used as a replace
2353 * @class Roo.MessageBox
2354 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2358 Roo.Msg.alert('Status', 'Changes saved successfully.');
2360 // Prompt for user data:
2361 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2363 // process text value...
2367 // Show a dialog using config options:
2369 title:'Save Changes?',
2370 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2371 buttons: Roo.Msg.YESNOCANCEL,
2378 Roo.bootstrap.MessageBox = function(){
2379 var dlg, opt, mask, waitTimer;
2380 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2381 var buttons, activeTextEl, bwidth;
2385 var handleButton = function(button){
2387 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2391 var handleHide = function(){
2393 dlg.el.removeClass(opt.cls);
2396 // Roo.TaskMgr.stop(waitTimer);
2397 // waitTimer = null;
2402 var updateButtons = function(b){
2405 buttons["ok"].hide();
2406 buttons["cancel"].hide();
2407 buttons["yes"].hide();
2408 buttons["no"].hide();
2409 //dlg.footer.dom.style.display = 'none';
2412 dlg.footer.dom.style.display = '';
2413 for(var k in buttons){
2414 if(typeof buttons[k] != "function"){
2417 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2418 width += buttons[k].el.getWidth()+15;
2428 var handleEsc = function(d, k, e){
2429 if(opt && opt.closable !== false){
2439 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2440 * @return {Roo.BasicDialog} The BasicDialog element
2442 getDialog : function(){
2444 dlg = new Roo.bootstrap.Modal( {
2447 //constraintoviewport:false,
2449 //collapsible : false,
2454 //buttonAlign:"center",
2455 closeClick : function(){
2456 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2459 handleButton("cancel");
2464 dlg.on("hide", handleHide);
2466 //dlg.addKeyListener(27, handleEsc);
2468 this.buttons = buttons;
2469 var bt = this.buttonText;
2470 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2471 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2472 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2473 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2475 bodyEl = dlg.body.createChild({
2477 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2478 '<textarea class="roo-mb-textarea"></textarea>' +
2479 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2481 msgEl = bodyEl.dom.firstChild;
2482 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2483 textboxEl.enableDisplayMode();
2484 textboxEl.addKeyListener([10,13], function(){
2485 if(dlg.isVisible() && opt && opt.buttons){
2488 }else if(opt.buttons.yes){
2489 handleButton("yes");
2493 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2494 textareaEl.enableDisplayMode();
2495 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2496 progressEl.enableDisplayMode();
2497 var pf = progressEl.dom.firstChild;
2499 pp = Roo.get(pf.firstChild);
2500 pp.setHeight(pf.offsetHeight);
2508 * Updates the message box body text
2509 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2510 * the XHTML-compliant non-breaking space character '&#160;')
2511 * @return {Roo.MessageBox} This message box
2513 updateText : function(text){
2514 if(!dlg.isVisible() && !opt.width){
2515 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2517 msgEl.innerHTML = text || ' ';
2519 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2520 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2522 Math.min(opt.width || cw , this.maxWidth),
2523 Math.max(opt.minWidth || this.minWidth, bwidth)
2526 activeTextEl.setWidth(w);
2528 if(dlg.isVisible()){
2529 dlg.fixedcenter = false;
2531 // to big, make it scroll. = But as usual stupid IE does not support
2534 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2535 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2536 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2538 bodyEl.dom.style.height = '';
2539 bodyEl.dom.style.overflowY = '';
2542 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2544 bodyEl.dom.style.overflowX = '';
2547 dlg.setContentSize(w, bodyEl.getHeight());
2548 if(dlg.isVisible()){
2549 dlg.fixedcenter = true;
2555 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2556 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2557 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2558 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2559 * @return {Roo.MessageBox} This message box
2561 updateProgress : function(value, text){
2563 this.updateText(text);
2565 if (pp) { // weird bug on my firefox - for some reason this is not defined
2566 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2572 * Returns true if the message box is currently displayed
2573 * @return {Boolean} True if the message box is visible, else false
2575 isVisible : function(){
2576 return dlg && dlg.isVisible();
2580 * Hides the message box if it is displayed
2583 if(this.isVisible()){
2589 * Displays a new message box, or reinitializes an existing message box, based on the config options
2590 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2591 * The following config object properties are supported:
2593 Property Type Description
2594 ---------- --------------- ------------------------------------------------------------------------------------
2595 animEl String/Element An id or Element from which the message box should animate as it opens and
2596 closes (defaults to undefined)
2597 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2598 cancel:'Bar'}), or false to not show any buttons (defaults to false)
2599 closable Boolean False to hide the top-right close button (defaults to true). Note that
2600 progress and wait dialogs will ignore this property and always hide the
2601 close button as they can only be closed programmatically.
2602 cls String A custom CSS class to apply to the message box element
2603 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
2604 displayed (defaults to 75)
2605 fn Function A callback function to execute after closing the dialog. The arguments to the
2606 function will be btn (the name of the button that was clicked, if applicable,
2607 e.g. "ok"), and text (the value of the active text field, if applicable).
2608 Progress and wait dialogs will ignore this option since they do not respond to
2609 user actions and can only be closed programmatically, so any required function
2610 should be called by the same code after it closes the dialog.
2611 icon String A CSS class that provides a background image to be used as an icon for
2612 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2613 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
2614 minWidth Number The minimum width in pixels of the message box (defaults to 100)
2615 modal Boolean False to allow user interaction with the page while the message box is
2616 displayed (defaults to true)
2617 msg String A string that will replace the existing message box body text (defaults
2618 to the XHTML-compliant non-breaking space character ' ')
2619 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
2620 progress Boolean True to display a progress bar (defaults to false)
2621 progressText String The text to display inside the progress bar if progress = true (defaults to '')
2622 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
2623 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
2624 title String The title text
2625 value String The string value to set into the active textbox element if displayed
2626 wait Boolean True to display a progress bar (defaults to false)
2627 width Number The width of the dialog in pixels
2634 msg: 'Please enter your address:',
2636 buttons: Roo.MessageBox.OKCANCEL,
2639 animEl: 'addAddressBtn'
2642 * @param {Object} config Configuration options
2643 * @return {Roo.MessageBox} This message box
2645 show : function(options)
2648 // this causes nightmares if you show one dialog after another
2649 // especially on callbacks..
2651 if(this.isVisible()){
2654 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2655 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
2656 Roo.log("New Dialog Message:" + options.msg )
2657 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2658 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2661 var d = this.getDialog();
2663 d.setTitle(opt.title || " ");
2664 d.close.setDisplayed(opt.closable !== false);
2665 activeTextEl = textboxEl;
2666 opt.prompt = opt.prompt || (opt.multiline ? true : false);
2671 textareaEl.setHeight(typeof opt.multiline == "number" ?
2672 opt.multiline : this.defaultTextHeight);
2673 activeTextEl = textareaEl;
2682 progressEl.setDisplayed(opt.progress === true);
2683 this.updateProgress(0);
2684 activeTextEl.dom.value = opt.value || "";
2686 dlg.setDefaultButton(activeTextEl);
2688 var bs = opt.buttons;
2692 }else if(bs && bs.yes){
2693 db = buttons["yes"];
2695 dlg.setDefaultButton(db);
2697 bwidth = updateButtons(opt.buttons);
2698 this.updateText(opt.msg);
2700 d.el.addClass(opt.cls);
2702 d.proxyDrag = opt.proxyDrag === true;
2703 d.modal = opt.modal !== false;
2704 d.mask = opt.modal !== false ? mask : false;
2706 // force it to the end of the z-index stack so it gets a cursor in FF
2707 document.body.appendChild(dlg.el.dom);
2708 d.animateTarget = null;
2709 d.show(options.animEl);
2715 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
2716 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2717 * and closing the message box when the process is complete.
2718 * @param {String} title The title bar text
2719 * @param {String} msg The message box body text
2720 * @return {Roo.MessageBox} This message box
2722 progress : function(title, msg){
2729 minWidth: this.minProgressWidth,
2736 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2737 * If a callback function is passed it will be called after the user clicks the button, and the
2738 * id of the button that was clicked will be passed as the only parameter to the callback
2739 * (could also be the top-right close button).
2740 * @param {String} title The title bar text
2741 * @param {String} msg The message box body text
2742 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2743 * @param {Object} scope (optional) The scope of the callback function
2744 * @return {Roo.MessageBox} This message box
2746 alert : function(title, msg, fn, scope){
2759 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
2760 * interaction while waiting for a long-running process to complete that does not have defined intervals.
2761 * You are responsible for closing the message box when the process is complete.
2762 * @param {String} msg The message box body text
2763 * @param {String} title (optional) The title bar text
2764 * @return {Roo.MessageBox} This message box
2766 wait : function(msg, title){
2777 waitTimer = Roo.TaskMgr.start({
2779 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2787 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2788 * If a callback function is passed it will be called after the user clicks either button, and the id of the
2789 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2790 * @param {String} title The title bar text
2791 * @param {String} msg The message box body text
2792 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2793 * @param {Object} scope (optional) The scope of the callback function
2794 * @return {Roo.MessageBox} This message box
2796 confirm : function(title, msg, fn, scope){
2800 buttons: this.YESNO,
2809 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2810 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
2811 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2812 * (could also be the top-right close button) and the text that was entered will be passed as the two
2813 * parameters to the callback.
2814 * @param {String} title The title bar text
2815 * @param {String} msg The message box body text
2816 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2817 * @param {Object} scope (optional) The scope of the callback function
2818 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2819 * property, or the height in pixels to create the textbox (defaults to false / single-line)
2820 * @return {Roo.MessageBox} This message box
2822 prompt : function(title, msg, fn, scope, multiline){
2826 buttons: this.OKCANCEL,
2831 multiline: multiline,
2838 * Button config that displays a single OK button
2843 * Button config that displays Yes and No buttons
2846 YESNO : {yes:true, no:true},
2848 * Button config that displays OK and Cancel buttons
2851 OKCANCEL : {ok:true, cancel:true},
2853 * Button config that displays Yes, No and Cancel buttons
2856 YESNOCANCEL : {yes:true, no:true, cancel:true},
2859 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2862 defaultTextHeight : 75,
2864 * The maximum width in pixels of the message box (defaults to 600)
2869 * The minimum width in pixels of the message box (defaults to 100)
2874 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
2875 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2878 minProgressWidth : 250,
2880 * An object containing the default button text strings that can be overriden for localized language support.
2881 * Supported properties are: ok, cancel, yes and no.
2882 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2895 * Shorthand for {@link Roo.MessageBox}
2897 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox
2898 Roo.Msg = Roo.Msg || Roo.MessageBox;
2907 * @class Roo.bootstrap.Navbar
2908 * @extends Roo.bootstrap.Component
2909 * Bootstrap Navbar class
2912 * Create a new Navbar
2913 * @param {Object} config The config object
2917 Roo.bootstrap.Navbar = function(config){
2918 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2922 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
2931 getAutoCreate : function(){
2934 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2938 initEvents :function ()
2940 //Roo.log(this.el.select('.navbar-toggle',true));
2941 this.el.select('.navbar-toggle',true).on('click', function() {
2942 // Roo.log('click');
2943 this.el.select('.navbar-collapse',true).toggleClass('in');
2951 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2953 var size = this.el.getSize();
2954 this.maskEl.setSize(size.width, size.height);
2955 this.maskEl.enableDisplayMode("block");
2964 getChildContainer : function()
2966 if (this.el.select('.collapse').getCount()) {
2967 return this.el.select('.collapse',true).first();
3000 * @class Roo.bootstrap.NavSimplebar
3001 * @extends Roo.bootstrap.Navbar
3002 * Bootstrap Sidebar class
3004 * @cfg {Boolean} inverse is inverted color
3006 * @cfg {String} type (nav | pills | tabs)
3007 * @cfg {Boolean} arrangement stacked | justified
3008 * @cfg {String} align (left | right) alignment
3010 * @cfg {Boolean} main (true|false) main nav bar? default false
3011 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3013 * @cfg {String} tag (header|footer|nav|div) default is nav
3019 * Create a new Sidebar
3020 * @param {Object} config The config object
3024 Roo.bootstrap.NavSimplebar = function(config){
3025 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3028 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3044 getAutoCreate : function(){
3048 tag : this.tag || 'div',
3061 this.type = this.type || 'nav';
3062 if (['tabs','pills'].indexOf(this.type)!==-1) {
3063 cfg.cn[0].cls += ' nav-' + this.type
3067 if (this.type!=='nav') {
3068 Roo.log('nav type must be nav/tabs/pills')
3070 cfg.cn[0].cls += ' navbar-nav'
3076 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3077 cfg.cn[0].cls += ' nav-' + this.arrangement;
3081 if (this.align === 'right') {
3082 cfg.cn[0].cls += ' navbar-right';
3086 cfg.cls += ' navbar-inverse';
3113 * @class Roo.bootstrap.NavHeaderbar
3114 * @extends Roo.bootstrap.NavSimplebar
3115 * Bootstrap Sidebar class
3117 * @cfg {String} brand what is brand
3118 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3119 * @cfg {String} brand_href href of the brand
3120 * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3121 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3124 * Create a new Sidebar
3125 * @param {Object} config The config object
3129 Roo.bootstrap.NavHeaderbar = function(config){
3130 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3133 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3141 getAutoCreate : function(){
3144 tag: this.nav || 'nav',
3153 cls: 'navbar-header',
3158 cls: 'navbar-toggle',
3159 'data-toggle': 'collapse',
3164 html: 'Toggle navigation'
3186 cls: 'collapse navbar-collapse',
3190 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3192 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3193 cfg.cls += ' navbar-' + this.position;
3195 // tag can override this..
3197 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3200 if (this.brand !== '') {
3203 href: this.brand_href ? this.brand_href : '#',
3204 cls: 'navbar-brand',
3212 cfg.cls += ' main-nav';
3220 initEvents : function()
3222 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3224 if (this.autohide) {
3229 Roo.get(document).on('scroll',function(e) {
3230 var ns = Roo.get(document).getScroll().top;
3231 var os = prevScroll;
3235 ft.removeClass('slideDown');
3236 ft.addClass('slideUp');
3239 ft.removeClass('slideUp');
3240 ft.addClass('slideDown');
3264 * @class Roo.bootstrap.NavSidebar
3265 * @extends Roo.bootstrap.Navbar
3266 * Bootstrap Sidebar class
3269 * Create a new Sidebar
3270 * @param {Object} config The config object
3274 Roo.bootstrap.NavSidebar = function(config){
3275 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3278 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3280 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3282 getAutoCreate : function(){
3287 cls: 'sidebar sidebar-nav'
3309 * @class Roo.bootstrap.NavGroup
3310 * @extends Roo.bootstrap.Component
3311 * Bootstrap NavGroup class
3312 * @cfg {String} align left | right
3313 * @cfg {Boolean} inverse false | true
3314 * @cfg {String} type (nav|pills|tab) default nav
3315 * @cfg {String} navId - reference Id for navbar.
3319 * Create a new nav group
3320 * @param {Object} config The config object
3323 Roo.bootstrap.NavGroup = function(config){
3324 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3327 Roo.bootstrap.NavGroup.register(this);
3331 * Fires when the active item changes
3332 * @param {Roo.bootstrap.NavGroup} this
3333 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3334 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3341 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3352 getAutoCreate : function()
3354 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3361 if (['tabs','pills'].indexOf(this.type)!==-1) {
3362 cfg.cls += ' nav-' + this.type
3364 if (this.type!=='nav') {
3365 Roo.log('nav type must be nav/tabs/pills')
3367 cfg.cls += ' navbar-nav'
3370 if (this.parent().sidebar) {
3373 cls: 'dashboard-menu sidebar-menu'
3379 if (this.form === true) {
3385 if (this.align === 'right') {
3386 cfg.cls += ' navbar-right';
3388 cfg.cls += ' navbar-left';
3392 if (this.align === 'right') {
3393 cfg.cls += ' navbar-right';
3397 cfg.cls += ' navbar-inverse';
3405 * sets the active Navigation item
3406 * @param {Roo.bootstrap.NavItem} the new current navitem
3408 setActiveItem : function(item)
3411 Roo.each(this.navItems, function(v){
3416 v.setActive(false, true);
3423 item.setActive(true, true);
3424 this.fireEvent('changed', this, item, prev);
3429 * gets the active Navigation item
3430 * @return {Roo.bootstrap.NavItem} the current navitem
3432 getActive : function()
3436 Roo.each(this.navItems, function(v){
3447 indexOfNav : function()
3451 Roo.each(this.navItems, function(v,i){
3462 * adds a Navigation item
3463 * @param {Roo.bootstrap.NavItem} the navitem to add
3465 addItem : function(cfg)
3467 var cn = new Roo.bootstrap.NavItem(cfg);
3469 cn.parentId = this.id;
3470 cn.onRender(this.el, null);
3474 * register a Navigation item
3475 * @param {Roo.bootstrap.NavItem} the navitem to add
3477 register : function(item)
3479 this.navItems.push( item);
3480 item.navId = this.navId;
3485 * clear all the Navigation item
3488 clearAll : function()
3491 this.el.dom.innerHTML = '';
3494 getNavItem: function(tabId)
3497 Roo.each(this.navItems, function(e) {
3498 if (e.tabId == tabId) {
3508 setActiveNext : function()
3510 var i = this.indexOfNav(this.getActive());
3511 if (i > this.navItems.length) {
3514 this.setActiveItem(this.navItems[i+1]);
3516 setActivePrev : function()
3518 var i = this.indexOfNav(this.getActive());
3522 this.setActiveItem(this.navItems[i-1]);
3524 clearWasActive : function(except) {
3525 Roo.each(this.navItems, function(e) {
3526 if (e.tabId != except.tabId && e.was_active) {
3527 e.was_active = false;
3534 getWasActive : function ()
3537 Roo.each(this.navItems, function(e) {
3552 Roo.apply(Roo.bootstrap.NavGroup, {
3556 * register a Navigation Group
3557 * @param {Roo.bootstrap.NavGroup} the navgroup to add
3559 register : function(navgrp)
3561 this.groups[navgrp.navId] = navgrp;
3565 * fetch a Navigation Group based on the navigation ID
3566 * @param {string} the navgroup to add
3567 * @returns {Roo.bootstrap.NavGroup} the navgroup
3569 get: function(navId) {
3570 if (typeof(this.groups[navId]) == 'undefined') {
3572 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3574 return this.groups[navId] ;
3589 * @class Roo.bootstrap.NavItem
3590 * @extends Roo.bootstrap.Component
3591 * Bootstrap Navbar.NavItem class
3592 * @cfg {String} href link to
3593 * @cfg {String} html content of button
3594 * @cfg {String} badge text inside badge
3595 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3596 * @cfg {String} glyphicon name of glyphicon
3597 * @cfg {String} icon name of font awesome icon
3598 * @cfg {Boolean} active Is item active
3599 * @cfg {Boolean} disabled Is item disabled
3601 * @cfg {Boolean} preventDefault (true | false) default false
3602 * @cfg {String} tabId the tab that this item activates.
3603 * @cfg {String} tagtype (a|span) render as a href or span?
3606 * Create a new Navbar Item
3607 * @param {Object} config The config object
3609 Roo.bootstrap.NavItem = function(config){
3610 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3615 * The raw click event for the entire grid.
3616 * @param {Roo.EventObject} e
3621 * Fires when the active item active state changes
3622 * @param {Roo.bootstrap.NavItem} this
3623 * @param {boolean} state the new state
3631 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
3639 preventDefault : false,
3646 getAutoCreate : function(){
3654 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3656 if (this.disabled) {
3657 cfg.cls += ' disabled';
3660 if (this.href || this.html || this.glyphicon || this.icon) {
3664 href : this.href || "#",
3665 html: this.html || ''
3670 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3673 if(this.glyphicon) {
3674 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
3679 cfg.cn[0].html += " <span class='caret'></span>";
3683 if (this.badge !== '') {
3685 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3693 initEvents: function() {
3694 // Roo.log('init events?');
3695 // Roo.log(this.el.dom);
3696 if (typeof (this.menu) != 'undefined') {
3697 this.menu.parentType = this.xtype;
3698 this.menu.triggerEl = this.el;
3699 this.addxtype(Roo.apply({}, this.menu));
3703 this.el.select('a',true).on('click', this.onClick, this);
3704 // at this point parent should be available..
3705 this.parent().register(this);
3708 onClick : function(e)
3711 if(this.preventDefault){
3714 if (this.disabled) {
3718 var tg = Roo.bootstrap.TabGroup.get(this.navId);
3719 if (tg && tg.transition) {
3720 Roo.log("waiting for the transitionend");
3724 Roo.log("fire event clicked");
3725 if(this.fireEvent('click', this, e) === false){
3728 var p = this.parent();
3729 if (['tabs','pills'].indexOf(p.type)!==-1) {
3730 if (typeof(p.setActiveItem) !== 'undefined') {
3731 p.setActiveItem(this);
3734 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3735 if (p.parentType == 'NavHeaderbar' && !this.menu) {
3736 // remove the collapsed menu expand...
3737 p.parent().el.select('.navbar-collapse',true).removeClass('in');
3742 isActive: function () {
3745 setActive : function(state, fire, is_was_active)
3747 if (this.active && !state & this.navId) {
3748 this.was_active = true;
3749 var nv = Roo.bootstrap.NavGroup.get(this.navId);
3751 nv.clearWasActive(this);
3755 this.active = state;
3758 this.el.removeClass('active');
3759 } else if (!this.el.hasClass('active')) {
3760 this.el.addClass('active');
3763 this.fireEvent('changed', this, state);
3766 // show a panel if it's registered and related..
3768 if (!this.navId || !this.tabId || !state || is_was_active) {
3772 var tg = Roo.bootstrap.TabGroup.get(this.navId);
3776 var pan = tg.getPanelByName(this.tabId);
3780 // if we can not flip to new panel - go back to old nav highlight..
3781 if (false == tg.showPanel(pan)) {
3782 var nv = Roo.bootstrap.NavGroup.get(this.navId);
3784 var onav = nv.getWasActive();
3786 onav.setActive(true, false, true);
3795 // this should not be here...
3796 setDisabled : function(state)
3798 this.disabled = state;
3800 this.el.removeClass('disabled');
3801 } else if (!this.el.hasClass('disabled')) {
3802 this.el.addClass('disabled');
3815 * <span> icon </span>
3816 * <span> text </span>
3817 * <span>badge </span>
3821 * @class Roo.bootstrap.NavSidebarItem
3822 * @extends Roo.bootstrap.NavItem
3823 * Bootstrap Navbar.NavSidebarItem class
3825 * Create a new Navbar Button
3826 * @param {Object} config The config object
3828 Roo.bootstrap.NavSidebarItem = function(config){
3829 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3834 * The raw click event for the entire grid.
3835 * @param {Roo.EventObject} e
3840 * Fires when the active item active state changes
3841 * @param {Roo.bootstrap.NavSidebarItem} this
3842 * @param {boolean} state the new state
3850 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
3853 getAutoCreate : function(){
3858 href : this.href || '#',
3870 html : this.html || ''
3875 cfg.cls += ' active';
3879 if (this.glyphicon || this.icon) {
3880 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
3881 a.cn.push({ tag : 'i', cls : c }) ;
3886 if (this.badge !== '') {
3887 a.cn.push({ tag: 'span', cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge });
3891 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3892 a.cls += 'dropdown-toggle treeview' ;
3916 * @class Roo.bootstrap.Row
3917 * @extends Roo.bootstrap.Component
3918 * Bootstrap Row class (contains columns...)
3922 * @param {Object} config The config object
3925 Roo.bootstrap.Row = function(config){
3926 Roo.bootstrap.Row.superclass.constructor.call(this, config);
3929 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
3931 getAutoCreate : function(){
3950 * @class Roo.bootstrap.Element
3951 * @extends Roo.bootstrap.Component
3952 * Bootstrap Element class
3953 * @cfg {String} html contents of the element
3954 * @cfg {String} tag tag of the element
3955 * @cfg {String} cls class of the element
3958 * Create a new Element
3959 * @param {Object} config The config object
3962 Roo.bootstrap.Element = function(config){
3963 Roo.bootstrap.Element.superclass.constructor.call(this, config);
3966 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
3973 getAutoCreate : function(){
3998 * @class Roo.bootstrap.Pagination
3999 * @extends Roo.bootstrap.Component
4000 * Bootstrap Pagination class
4001 * @cfg {String} size xs | sm | md | lg
4002 * @cfg {Boolean} inverse false | true
4005 * Create a new Pagination
4006 * @param {Object} config The config object
4009 Roo.bootstrap.Pagination = function(config){
4010 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4013 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4019 getAutoCreate : function(){
4025 cfg.cls += ' inverse';
4031 cfg.cls += " " + this.cls;
4049 * @class Roo.bootstrap.PaginationItem
4050 * @extends Roo.bootstrap.Component
4051 * Bootstrap PaginationItem class
4052 * @cfg {String} html text
4053 * @cfg {String} href the link
4054 * @cfg {Boolean} preventDefault (true | false) default true
4055 * @cfg {Boolean} active (true | false) default false
4059 * Create a new PaginationItem
4060 * @param {Object} config The config object
4064 Roo.bootstrap.PaginationItem = function(config){
4065 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4070 * The raw click event for the entire grid.
4071 * @param {Roo.EventObject} e
4077 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4081 preventDefault: true,
4085 getAutoCreate : function(){
4091 href : this.href ? this.href : '#',
4092 html : this.html ? this.html : ''
4102 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4108 initEvents: function() {
4110 this.el.on('click', this.onClick, this);
4113 onClick : function(e)
4115 Roo.log('PaginationItem on click ');
4116 if(this.preventDefault){
4120 this.fireEvent('click', this, e);
4136 * @class Roo.bootstrap.Slider
4137 * @extends Roo.bootstrap.Component
4138 * Bootstrap Slider class
4141 * Create a new Slider
4142 * @param {Object} config The config object
4145 Roo.bootstrap.Slider = function(config){
4146 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4149 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4151 getAutoCreate : function(){
4155 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4159 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4171 * Ext JS Library 1.1.1
4172 * Copyright(c) 2006-2007, Ext JS, LLC.
4174 * Originally Released Under LGPL - original licence link has changed is not relivant.
4177 * <script type="text/javascript">
4182 * @class Roo.grid.ColumnModel
4183 * @extends Roo.util.Observable
4184 * This is the default implementation of a ColumnModel used by the Grid. It defines
4185 * the columns in the grid.
4188 var colModel = new Roo.grid.ColumnModel([
4189 {header: "Ticker", width: 60, sortable: true, locked: true},
4190 {header: "Company Name", width: 150, sortable: true},
4191 {header: "Market Cap.", width: 100, sortable: true},
4192 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4193 {header: "Employees", width: 100, sortable: true, resizable: false}
4198 * The config options listed for this class are options which may appear in each
4199 * individual column definition.
4200 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4202 * @param {Object} config An Array of column config objects. See this class's
4203 * config objects for details.
4205 Roo.grid.ColumnModel = function(config){
4207 * The config passed into the constructor
4209 this.config = config;
4212 // if no id, create one
4213 // if the column does not have a dataIndex mapping,
4214 // map it to the order it is in the config
4215 for(var i = 0, len = config.length; i < len; i++){
4217 if(typeof c.dataIndex == "undefined"){
4220 if(typeof c.renderer == "string"){
4221 c.renderer = Roo.util.Format[c.renderer];
4223 if(typeof c.id == "undefined"){
4226 if(c.editor && c.editor.xtype){
4227 c.editor = Roo.factory(c.editor, Roo.grid);
4229 if(c.editor && c.editor.isFormField){
4230 c.editor = new Roo.grid.GridEditor(c.editor);
4232 this.lookup[c.id] = c;
4236 * The width of columns which have no width specified (defaults to 100)
4239 this.defaultWidth = 100;
4242 * Default sortable of columns which have no sortable specified (defaults to false)
4245 this.defaultSortable = false;
4249 * @event widthchange
4250 * Fires when the width of a column changes.
4251 * @param {ColumnModel} this
4252 * @param {Number} columnIndex The column index
4253 * @param {Number} newWidth The new width
4255 "widthchange": true,
4257 * @event headerchange
4258 * Fires when the text of a header changes.
4259 * @param {ColumnModel} this
4260 * @param {Number} columnIndex The column index
4261 * @param {Number} newText The new header text
4263 "headerchange": true,
4265 * @event hiddenchange
4266 * Fires when a column is hidden or "unhidden".
4267 * @param {ColumnModel} this
4268 * @param {Number} columnIndex The column index
4269 * @param {Boolean} hidden true if hidden, false otherwise
4271 "hiddenchange": true,
4273 * @event columnmoved
4274 * Fires when a column is moved.
4275 * @param {ColumnModel} this
4276 * @param {Number} oldIndex
4277 * @param {Number} newIndex
4279 "columnmoved" : true,
4281 * @event columlockchange
4282 * Fires when a column's locked state is changed
4283 * @param {ColumnModel} this
4284 * @param {Number} colIndex
4285 * @param {Boolean} locked true if locked
4287 "columnlockchange" : true
4289 Roo.grid.ColumnModel.superclass.constructor.call(this);
4291 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4293 * @cfg {String} header The header text to display in the Grid view.
4296 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4297 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4298 * specified, the column's index is used as an index into the Record's data Array.
4301 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4302 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4305 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4306 * Defaults to the value of the {@link #defaultSortable} property.
4307 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4310 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4313 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4316 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4319 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4322 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4323 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4324 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4325 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4328 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4331 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4335 * Returns the id of the column at the specified index.
4336 * @param {Number} index The column index
4337 * @return {String} the id
4339 getColumnId : function(index){
4340 return this.config[index].id;
4344 * Returns the column for a specified id.
4345 * @param {String} id The column id
4346 * @return {Object} the column
4348 getColumnById : function(id){
4349 return this.lookup[id];
4354 * Returns the column for a specified dataIndex.
4355 * @param {String} dataIndex The column dataIndex
4356 * @return {Object|Boolean} the column or false if not found
4358 getColumnByDataIndex: function(dataIndex){
4359 var index = this.findColumnIndex(dataIndex);
4360 return index > -1 ? this.config[index] : false;
4364 * Returns the index for a specified column id.
4365 * @param {String} id The column id
4366 * @return {Number} the index, or -1 if not found
4368 getIndexById : function(id){
4369 for(var i = 0, len = this.config.length; i < len; i++){
4370 if(this.config[i].id == id){
4378 * Returns the index for a specified column dataIndex.
4379 * @param {String} dataIndex The column dataIndex
4380 * @return {Number} the index, or -1 if not found
4383 findColumnIndex : function(dataIndex){
4384 for(var i = 0, len = this.config.length; i < len; i++){
4385 if(this.config[i].dataIndex == dataIndex){
4393 moveColumn : function(oldIndex, newIndex){
4394 var c = this.config[oldIndex];
4395 this.config.splice(oldIndex, 1);
4396 this.config.splice(newIndex, 0, c);
4397 this.dataMap = null;
4398 this.fireEvent("columnmoved", this, oldIndex, newIndex);
4401 isLocked : function(colIndex){
4402 return this.config[colIndex].locked === true;
4405 setLocked : function(colIndex, value, suppressEvent){
4406 if(this.isLocked(colIndex) == value){
4409 this.config[colIndex].locked = value;
4411 this.fireEvent("columnlockchange", this, colIndex, value);
4415 getTotalLockedWidth : function(){
4417 for(var i = 0; i < this.config.length; i++){
4418 if(this.isLocked(i) && !this.isHidden(i)){
4419 this.totalWidth += this.getColumnWidth(i);
4425 getLockedCount : function(){
4426 for(var i = 0, len = this.config.length; i < len; i++){
4427 if(!this.isLocked(i)){
4434 * Returns the number of columns.
4437 getColumnCount : function(visibleOnly){
4438 if(visibleOnly === true){
4440 for(var i = 0, len = this.config.length; i < len; i++){
4441 if(!this.isHidden(i)){
4447 return this.config.length;
4451 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4452 * @param {Function} fn
4453 * @param {Object} scope (optional)
4454 * @return {Array} result
4456 getColumnsBy : function(fn, scope){
4458 for(var i = 0, len = this.config.length; i < len; i++){
4459 var c = this.config[i];
4460 if(fn.call(scope||this, c, i) === true){
4468 * Returns true if the specified column is sortable.
4469 * @param {Number} col The column index
4472 isSortable : function(col){
4473 if(typeof this.config[col].sortable == "undefined"){
4474 return this.defaultSortable;
4476 return this.config[col].sortable;
4480 * Returns the rendering (formatting) function defined for the column.
4481 * @param {Number} col The column index.
4482 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4484 getRenderer : function(col){
4485 if(!this.config[col].renderer){
4486 return Roo.grid.ColumnModel.defaultRenderer;
4488 return this.config[col].renderer;
4492 * Sets the rendering (formatting) function for a column.
4493 * @param {Number} col The column index
4494 * @param {Function} fn The function to use to process the cell's raw data
4495 * to return HTML markup for the grid view. The render function is called with
4496 * the following parameters:<ul>
4497 * <li>Data value.</li>
4498 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4499 * <li>css A CSS style string to apply to the table cell.</li>
4500 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4501 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4502 * <li>Row index</li>
4503 * <li>Column index</li>
4504 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4506 setRenderer : function(col, fn){
4507 this.config[col].renderer = fn;
4511 * Returns the width for the specified column.
4512 * @param {Number} col The column index
4515 getColumnWidth : function(col){
4516 return this.config[col].width * 1 || this.defaultWidth;
4520 * Sets the width for a column.
4521 * @param {Number} col The column index
4522 * @param {Number} width The new width
4524 setColumnWidth : function(col, width, suppressEvent){
4525 this.config[col].width = width;
4526 this.totalWidth = null;
4528 this.fireEvent("widthchange", this, col, width);
4533 * Returns the total width of all columns.
4534 * @param {Boolean} includeHidden True to include hidden column widths
4537 getTotalWidth : function(includeHidden){
4538 if(!this.totalWidth){
4539 this.totalWidth = 0;
4540 for(var i = 0, len = this.config.length; i < len; i++){
4541 if(includeHidden || !this.isHidden(i)){
4542 this.totalWidth += this.getColumnWidth(i);
4546 return this.totalWidth;
4550 * Returns the header for the specified column.
4551 * @param {Number} col The column index
4554 getColumnHeader : function(col){
4555 return this.config[col].header;
4559 * Sets the header for a column.
4560 * @param {Number} col The column index
4561 * @param {String} header The new header
4563 setColumnHeader : function(col, header){
4564 this.config[col].header = header;
4565 this.fireEvent("headerchange", this, col, header);
4569 * Returns the tooltip for the specified column.
4570 * @param {Number} col The column index
4573 getColumnTooltip : function(col){
4574 return this.config[col].tooltip;
4577 * Sets the tooltip for a column.
4578 * @param {Number} col The column index
4579 * @param {String} tooltip The new tooltip
4581 setColumnTooltip : function(col, tooltip){
4582 this.config[col].tooltip = tooltip;
4586 * Returns the dataIndex for the specified column.
4587 * @param {Number} col The column index
4590 getDataIndex : function(col){
4591 return this.config[col].dataIndex;
4595 * Sets the dataIndex for a column.
4596 * @param {Number} col The column index
4597 * @param {Number} dataIndex The new dataIndex
4599 setDataIndex : function(col, dataIndex){
4600 this.config[col].dataIndex = dataIndex;
4606 * Returns true if the cell is editable.
4607 * @param {Number} colIndex The column index
4608 * @param {Number} rowIndex The row index
4611 isCellEditable : function(colIndex, rowIndex){
4612 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4616 * Returns the editor defined for the cell/column.
4617 * return false or null to disable editing.
4618 * @param {Number} colIndex The column index
4619 * @param {Number} rowIndex The row index
4622 getCellEditor : function(colIndex, rowIndex){
4623 return this.config[colIndex].editor;
4627 * Sets if a column is editable.
4628 * @param {Number} col The column index
4629 * @param {Boolean} editable True if the column is editable
4631 setEditable : function(col, editable){
4632 this.config[col].editable = editable;
4637 * Returns true if the column is hidden.
4638 * @param {Number} colIndex The column index
4641 isHidden : function(colIndex){
4642 return this.config[colIndex].hidden;
4647 * Returns true if the column width cannot be changed
4649 isFixed : function(colIndex){
4650 return this.config[colIndex].fixed;
4654 * Returns true if the column can be resized
4657 isResizable : function(colIndex){
4658 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4661 * Sets if a column is hidden.
4662 * @param {Number} colIndex The column index
4663 * @param {Boolean} hidden True if the column is hidden
4665 setHidden : function(colIndex, hidden){
4666 this.config[colIndex].hidden = hidden;
4667 this.totalWidth = null;
4668 this.fireEvent("hiddenchange", this, colIndex, hidden);
4672 * Sets the editor for a column.
4673 * @param {Number} col The column index
4674 * @param {Object} editor The editor object
4676 setEditor : function(col, editor){
4677 this.config[col].editor = editor;
4681 Roo.grid.ColumnModel.defaultRenderer = function(value){
4682 if(typeof value == "string" && value.length < 1){
4688 // Alias for backwards compatibility
4689 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4692 * Ext JS Library 1.1.1
4693 * Copyright(c) 2006-2007, Ext JS, LLC.
4695 * Originally Released Under LGPL - original licence link has changed is not relivant.
4698 * <script type="text/javascript">
4702 * @class Roo.LoadMask
4703 * A simple utility class for generically masking elements while loading data. If the element being masked has
4704 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4705 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
4706 * element's UpdateManager load indicator and will be destroyed after the initial load.
4708 * Create a new LoadMask
4709 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4710 * @param {Object} config The config object
4712 Roo.LoadMask = function(el, config){
4713 this.el = Roo.get(el);
4714 Roo.apply(this, config);
4716 this.store.on('beforeload', this.onBeforeLoad, this);
4717 this.store.on('load', this.onLoad, this);
4718 this.store.on('loadexception', this.onLoadException, this);
4719 this.removeMask = false;
4721 var um = this.el.getUpdateManager();
4722 um.showLoadIndicator = false; // disable the default indicator
4723 um.on('beforeupdate', this.onBeforeLoad, this);
4724 um.on('update', this.onLoad, this);
4725 um.on('failure', this.onLoad, this);
4726 this.removeMask = true;
4730 Roo.LoadMask.prototype = {
4732 * @cfg {Boolean} removeMask
4733 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4734 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
4738 * The text to display in a centered loading message box (defaults to 'Loading...')
4742 * @cfg {String} msgCls
4743 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4745 msgCls : 'x-mask-loading',
4748 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4754 * Disables the mask to prevent it from being displayed
4756 disable : function(){
4757 this.disabled = true;
4761 * Enables the mask so that it can be displayed
4763 enable : function(){
4764 this.disabled = false;
4767 onLoadException : function()
4771 if (typeof(arguments[3]) != 'undefined') {
4772 Roo.MessageBox.alert("Error loading",arguments[3]);
4776 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4777 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4786 this.el.unmask(this.removeMask);
4791 this.el.unmask(this.removeMask);
4795 onBeforeLoad : function(){
4797 this.el.mask(this.msg, this.msgCls);
4802 destroy : function(){
4804 this.store.un('beforeload', this.onBeforeLoad, this);
4805 this.store.un('load', this.onLoad, this);
4806 this.store.un('loadexception', this.onLoadException, this);
4808 var um = this.el.getUpdateManager();
4809 um.un('beforeupdate', this.onBeforeLoad, this);
4810 um.un('update', this.onLoad, this);
4811 um.un('failure', this.onLoad, this);
4822 * @class Roo.bootstrap.Table
4823 * @extends Roo.bootstrap.Component
4824 * Bootstrap Table class
4825 * @cfg {String} cls table class
4826 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4827 * @cfg {String} bgcolor Specifies the background color for a table
4828 * @cfg {Number} border Specifies whether the table cells should have borders or not
4829 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4830 * @cfg {Number} cellspacing Specifies the space between cells
4831 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4832 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4833 * @cfg {String} sortable Specifies that the table should be sortable
4834 * @cfg {String} summary Specifies a summary of the content of a table
4835 * @cfg {Number} width Specifies the width of a table
4836 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4838 * @cfg {boolean} striped Should the rows be alternative striped
4839 * @cfg {boolean} bordered Add borders to the table
4840 * @cfg {boolean} hover Add hover highlighting
4841 * @cfg {boolean} condensed Format condensed
4842 * @cfg {boolean} responsive Format condensed
4843 * @cfg {Boolean} loadMask (true|false) default false
4844 * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4845 * @cfg {Boolean} thead (true|false) generate thead, default true
4846 * @cfg {Boolean} RowSelection (true|false) default false
4847 * @cfg {Boolean} CellSelection (true|false) default false
4849 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
4853 * Create a new Table
4854 * @param {Object} config The config object
4857 Roo.bootstrap.Table = function(config){
4858 Roo.bootstrap.Table.superclass.constructor.call(this, config);
4861 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4862 this.sm = this.selModel;
4863 this.sm.xmodule = this.xmodule || false;
4865 if (this.cm && typeof(this.cm.config) == 'undefined') {
4866 this.colModel = new Roo.grid.ColumnModel(this.cm);
4867 this.cm = this.colModel;
4868 this.cm.xmodule = this.xmodule || false;
4871 this.store= Roo.factory(this.store, Roo.data);
4872 this.ds = this.store;
4873 this.ds.xmodule = this.xmodule || false;
4876 if (this.footer && this.store) {
4877 this.footer.dataSource = this.ds;
4878 this.footer = Roo.factory(this.footer);
4885 * Fires when a cell is clicked
4886 * @param {Roo.bootstrap.Table} this
4887 * @param {Roo.Element} el
4888 * @param {Number} rowIndex
4889 * @param {Number} columnIndex
4890 * @param {Roo.EventObject} e
4894 * @event celldblclick
4895 * Fires when a cell is double clicked
4896 * @param {Roo.bootstrap.Table} this
4897 * @param {Roo.Element} el
4898 * @param {Number} rowIndex
4899 * @param {Number} columnIndex
4900 * @param {Roo.EventObject} e
4902 "celldblclick" : true,
4905 * Fires when a row is clicked
4906 * @param {Roo.bootstrap.Table} this
4907 * @param {Roo.Element} el
4908 * @param {Number} rowIndex
4909 * @param {Roo.EventObject} e
4913 * @event rowdblclick
4914 * Fires when a row is double clicked
4915 * @param {Roo.bootstrap.Table} this
4916 * @param {Roo.Element} el
4917 * @param {Number} rowIndex
4918 * @param {Roo.EventObject} e
4920 "rowdblclick" : true,
4923 * Fires when a mouseover occur
4924 * @param {Roo.bootstrap.Table} this
4925 * @param {Roo.Element} el
4926 * @param {Number} rowIndex
4927 * @param {Number} columnIndex
4928 * @param {Roo.EventObject} e
4933 * Fires when a mouseout occur
4934 * @param {Roo.bootstrap.Table} this
4935 * @param {Roo.Element} el
4936 * @param {Number} rowIndex
4937 * @param {Number} columnIndex
4938 * @param {Roo.EventObject} e
4943 * Fires when a row is rendered, so you can change add a style to it.
4944 * @param {Roo.bootstrap.Table} this
4945 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
4952 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
4976 RowSelection : false,
4977 CellSelection : false,
4980 // Roo.Element - the tbody
4983 getAutoCreate : function(){
4984 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4993 cfg.cls += ' table-striped';
4997 cfg.cls += ' table-hover';
4999 if (this.bordered) {
5000 cfg.cls += ' table-bordered';
5002 if (this.condensed) {
5003 cfg.cls += ' table-condensed';
5005 if (this.responsive) {
5006 cfg.cls += ' table-responsive';
5010 cfg.cls+= ' ' +this.cls;
5013 // this lot should be simplifed...
5016 cfg.align=this.align;
5019 cfg.bgcolor=this.bgcolor;
5022 cfg.border=this.border;
5024 if (this.cellpadding) {
5025 cfg.cellpadding=this.cellpadding;
5027 if (this.cellspacing) {
5028 cfg.cellspacing=this.cellspacing;
5031 cfg.frame=this.frame;
5034 cfg.rules=this.rules;
5036 if (this.sortable) {
5037 cfg.sortable=this.sortable;
5040 cfg.summary=this.summary;
5043 cfg.width=this.width;
5046 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5049 if(this.store || this.cm){
5051 cfg.cn.push(this.renderHeader());
5054 cfg.cn.push(this.renderBody());
5057 cfg.cn.push(this.renderFooter());
5060 cfg.cls+= ' TableGrid';
5063 return { cn : [ cfg ] };
5066 initEvents : function()
5068 if(!this.store || !this.cm){
5072 //Roo.log('initEvents with ds!!!!');
5074 this.mainBody = this.el.select('tbody', true).first();
5079 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5080 e.on('click', _this.sort, _this);
5083 this.el.on("click", this.onClick, this);
5084 this.el.on("dblclick", this.onDblClick, this);
5086 this.parent().el.setStyle('position', 'relative');
5088 this.footer.parentId = this.id;
5089 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5092 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5094 this.store.on('load', this.onLoad, this);
5095 this.store.on('beforeload', this.onBeforeLoad, this);
5096 this.store.on('update', this.onUpdate, this);
5100 onMouseover : function(e, el)
5102 var cell = Roo.get(el);
5108 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5109 cell = cell.findParent('td', false, true);
5112 var row = cell.findParent('tr', false, true);
5113 var cellIndex = cell.dom.cellIndex;
5114 var rowIndex = row.dom.rowIndex - 1; // start from 0
5116 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5120 onMouseout : function(e, el)
5122 var cell = Roo.get(el);
5128 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5129 cell = cell.findParent('td', false, true);
5132 var row = cell.findParent('tr', false, true);
5133 var cellIndex = cell.dom.cellIndex;
5134 var rowIndex = row.dom.rowIndex - 1; // start from 0
5136 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5140 onClick : function(e, el)
5142 var cell = Roo.get(el);
5144 if(!cell || (!this.CellSelection && !this.RowSelection)){
5149 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5150 cell = cell.findParent('td', false, true);
5153 var row = cell.findParent('tr', false, true);
5154 var cellIndex = cell.dom.cellIndex;
5155 var rowIndex = row.dom.rowIndex - 1;
5157 if(this.CellSelection){
5158 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5161 if(this.RowSelection){
5162 this.fireEvent('rowclick', this, row, rowIndex, e);
5168 onDblClick : function(e,el)
5170 var cell = Roo.get(el);
5172 if(!cell || (!this.CellSelection && !this.RowSelection)){
5176 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5177 cell = cell.findParent('td', false, true);
5180 var row = cell.findParent('tr', false, true);
5181 var cellIndex = cell.dom.cellIndex;
5182 var rowIndex = row.dom.rowIndex - 1;
5184 if(this.CellSelection){
5185 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5188 if(this.RowSelection){
5189 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5193 sort : function(e,el)
5195 var col = Roo.get(el)
5197 if(!col.hasClass('sortable')){
5201 var sort = col.attr('sort');
5204 if(col.hasClass('glyphicon-arrow-up')){
5208 this.store.sortInfo = {field : sort, direction : dir};
5211 Roo.log("calling footer first");
5212 this.footer.onClick('first');
5215 this.store.load({ params : { start : 0 } });
5219 renderHeader : function()
5228 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5230 var config = cm.config[i];
5235 html: cm.getColumnHeader(i)
5238 if(typeof(config.hidden) != 'undefined' && config.hidden){
5239 c.style += ' display:none;';
5242 if(typeof(config.dataIndex) != 'undefined'){
5243 c.sort = config.dataIndex;
5246 if(typeof(config.sortable) != 'undefined' && config.sortable){
5250 if(typeof(config.align) != 'undefined' && config.align.length){
5251 c.style += ' text-align:' + config.align + ';';
5254 if(typeof(config.width) != 'undefined'){
5255 c.style += ' width:' + config.width + 'px;';
5264 renderBody : function()
5274 colspan : this.cm.getColumnCount()
5284 renderFooter : function()
5294 colspan : this.cm.getColumnCount()
5308 Roo.log('ds onload');
5313 var ds = this.store;
5315 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5316 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5318 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5319 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5322 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5323 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5327 var tbody = this.mainBody;
5329 if(ds.getCount() > 0){
5330 ds.data.each(function(d,rowIndex){
5331 var row = this.renderRow(cm, ds, rowIndex);
5333 tbody.createChild(row);
5337 if(row.cellObjects.length){
5338 Roo.each(row.cellObjects, function(r){
5339 _this.renderCellObject(r);
5346 Roo.each(this.el.select('tbody td', true).elements, function(e){
5347 e.on('mouseover', _this.onMouseover, _this);
5350 Roo.each(this.el.select('tbody td', true).elements, function(e){
5351 e.on('mouseout', _this.onMouseout, _this);
5354 //if(this.loadMask){
5355 // this.maskEl.hide();
5360 onUpdate : function(ds,record)
5362 this.refreshRow(record);
5364 onRemove : function(ds, record, index, isUpdate){
5365 if(isUpdate !== true){
5366 this.fireEvent("beforerowremoved", this, index, record);
5368 var bt = this.mainBody.dom;
5370 bt.removeChild(bt.rows[index]);
5373 if(isUpdate !== true){
5374 //this.stripeRows(index);
5375 //this.syncRowHeights(index, index);
5377 this.fireEvent("rowremoved", this, index, record);
5382 refreshRow : function(record){
5383 var ds = this.store, index;
5384 if(typeof record == 'number'){
5386 record = ds.getAt(index);
5388 index = ds.indexOf(record);
5390 this.insertRow(ds, index, true);
5391 this.onRemove(ds, record, index+1, true);
5392 //this.syncRowHeights(index, index);
5394 this.fireEvent("rowupdated", this, index, record);
5397 insertRow : function(dm, rowIndex, isUpdate){
5400 this.fireEvent("beforerowsinserted", this, rowIndex);
5402 //var s = this.getScrollState();
5403 var row = this.renderRow(this.cm, this.store, rowIndex);
5404 // insert before rowIndex..
5405 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5409 if(row.cellObjects.length){
5410 Roo.each(row.cellObjects, function(r){
5411 _this.renderCellObject(r);
5416 this.fireEvent("rowsinserted", this, rowIndex);
5417 //this.syncRowHeights(firstRow, lastRow);
5418 //this.stripeRows(firstRow);
5425 getRowDom : function(rowIndex)
5427 // not sure if I need to check this.. but let's do it anyway..
5428 return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5429 this.mainBody.dom.rows[rowIndex] : false
5431 // returns the object tree for a tr..
5434 renderRow : function(cm, ds, rowIndex) {
5436 var d = ds.getAt(rowIndex);
5443 var cellObjects = [];
5445 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5446 var config = cm.config[i];
5448 var renderer = cm.getRenderer(i);
5452 if(typeof(renderer) !== 'undefined'){
5453 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5455 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5456 // and are rendered into the cells after the row is rendered - using the id for the element.
5458 if(typeof(value) === 'object'){
5468 rowIndex : rowIndex,
5473 this.fireEvent('rowclass', this, rowcfg);
5477 cls : rowcfg.rowClass,
5479 html: (typeof(value) === 'object') ? '' : value
5486 if(typeof(config.hidden) != 'undefined' && config.hidden){
5487 td.style += ' display:none;';
5490 if(typeof(config.align) != 'undefined' && config.align.length){
5491 td.style += ' text-align:' + config.align + ';';
5494 if(typeof(config.width) != 'undefined'){
5495 td.style += ' width:' + config.width + 'px;';
5502 row.cellObjects = cellObjects;
5510 onBeforeLoad : function()
5512 //Roo.log('ds onBeforeLoad');
5516 //if(this.loadMask){
5517 // this.maskEl.show();
5523 this.el.select('tbody', true).first().dom.innerHTML = '';
5526 getSelectionModel : function(){
5528 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5530 return this.selModel;
5533 * Render the Roo.bootstrap object from renderder
5535 renderCellObject : function(r)
5539 var t = r.cfg.render(r.container);
5542 Roo.each(r.cfg.cn, function(c){
5544 container: t.getChildContainer(),
5547 _this.renderCellObject(child);
5564 * @class Roo.bootstrap.TableCell
5565 * @extends Roo.bootstrap.Component
5566 * Bootstrap TableCell class
5567 * @cfg {String} html cell contain text
5568 * @cfg {String} cls cell class
5569 * @cfg {String} tag cell tag (td|th) default td
5570 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5571 * @cfg {String} align Aligns the content in a cell
5572 * @cfg {String} axis Categorizes cells
5573 * @cfg {String} bgcolor Specifies the background color of a cell
5574 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5575 * @cfg {Number} colspan Specifies the number of columns a cell should span
5576 * @cfg {String} headers Specifies one or more header cells a cell is related to
5577 * @cfg {Number} height Sets the height of a cell
5578 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5579 * @cfg {Number} rowspan Sets the number of rows a cell should span
5580 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5581 * @cfg {String} valign Vertical aligns the content in a cell
5582 * @cfg {Number} width Specifies the width of a cell
5585 * Create a new TableCell
5586 * @param {Object} config The config object
5589 Roo.bootstrap.TableCell = function(config){
5590 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5593 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
5613 getAutoCreate : function(){
5614 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5634 cfg.align=this.align
5640 cfg.bgcolor=this.bgcolor
5643 cfg.charoff=this.charoff
5646 cfg.colspan=this.colspan
5649 cfg.headers=this.headers
5652 cfg.height=this.height
5655 cfg.nowrap=this.nowrap
5658 cfg.rowspan=this.rowspan
5661 cfg.scope=this.scope
5664 cfg.valign=this.valign
5667 cfg.width=this.width
5686 * @class Roo.bootstrap.TableRow
5687 * @extends Roo.bootstrap.Component
5688 * Bootstrap TableRow class
5689 * @cfg {String} cls row class
5690 * @cfg {String} align Aligns the content in a table row
5691 * @cfg {String} bgcolor Specifies a background color for a table row
5692 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5693 * @cfg {String} valign Vertical aligns the content in a table row
5696 * Create a new TableRow
5697 * @param {Object} config The config object
5700 Roo.bootstrap.TableRow = function(config){
5701 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5704 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
5712 getAutoCreate : function(){
5713 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5723 cfg.align = this.align;
5726 cfg.bgcolor = this.bgcolor;
5729 cfg.charoff = this.charoff;
5732 cfg.valign = this.valign;
5750 * @class Roo.bootstrap.TableBody
5751 * @extends Roo.bootstrap.Component
5752 * Bootstrap TableBody class
5753 * @cfg {String} cls element class
5754 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5755 * @cfg {String} align Aligns the content inside the element
5756 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5757 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5760 * Create a new TableBody
5761 * @param {Object} config The config object
5764 Roo.bootstrap.TableBody = function(config){
5765 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5768 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
5776 getAutoCreate : function(){
5777 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5791 cfg.align = this.align;
5794 cfg.charoff = this.charoff;
5797 cfg.valign = this.valign;
5804 // initEvents : function()
5811 // this.store = Roo.factory(this.store, Roo.data);
5812 // this.store.on('load', this.onLoad, this);
5814 // this.store.load();
5818 // onLoad: function ()
5820 // this.fireEvent('load', this);
5830 * Ext JS Library 1.1.1
5831 * Copyright(c) 2006-2007, Ext JS, LLC.
5833 * Originally Released Under LGPL - original licence link has changed is not relivant.
5836 * <script type="text/javascript">
5839 // as we use this in bootstrap.
5840 Roo.namespace('Roo.form');
5842 * @class Roo.form.Action
5843 * Internal Class used to handle form actions
5845 * @param {Roo.form.BasicForm} el The form element or its id
5846 * @param {Object} config Configuration options
5851 // define the action interface
5852 Roo.form.Action = function(form, options){
5854 this.options = options || {};
5857 * Client Validation Failed
5860 Roo.form.Action.CLIENT_INVALID = 'client';
5862 * Server Validation Failed
5865 Roo.form.Action.SERVER_INVALID = 'server';
5867 * Connect to Server Failed
5870 Roo.form.Action.CONNECT_FAILURE = 'connect';
5872 * Reading Data from Server Failed
5875 Roo.form.Action.LOAD_FAILURE = 'load';
5877 Roo.form.Action.prototype = {
5879 failureType : undefined,
5880 response : undefined,
5884 run : function(options){
5889 success : function(response){
5894 handleResponse : function(response){
5898 // default connection failure
5899 failure : function(response){
5901 this.response = response;
5902 this.failureType = Roo.form.Action.CONNECT_FAILURE;
5903 this.form.afterAction(this, false);
5906 processResponse : function(response){
5907 this.response = response;
5908 if(!response.responseText){
5911 this.result = this.handleResponse(response);
5915 // utility functions used internally
5916 getUrl : function(appendParams){
5917 var url = this.options.url || this.form.url || this.form.el.dom.action;
5919 var p = this.getParams();
5921 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5927 getMethod : function(){
5928 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5931 getParams : function(){
5932 var bp = this.form.baseParams;
5933 var p = this.options.params;
5935 if(typeof p == "object"){
5936 p = Roo.urlEncode(Roo.applyIf(p, bp));
5937 }else if(typeof p == 'string' && bp){
5938 p += '&' + Roo.urlEncode(bp);
5941 p = Roo.urlEncode(bp);
5946 createCallback : function(){
5948 success: this.success,
5949 failure: this.failure,
5951 timeout: (this.form.timeout*1000),
5952 upload: this.form.fileUpload ? this.success : undefined
5957 Roo.form.Action.Submit = function(form, options){
5958 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5961 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5964 haveProgress : false,
5965 uploadComplete : false,
5967 // uploadProgress indicator.
5968 uploadProgress : function()
5970 if (!this.form.progressUrl) {
5974 if (!this.haveProgress) {
5975 Roo.MessageBox.progress("Uploading", "Uploading");
5977 if (this.uploadComplete) {
5978 Roo.MessageBox.hide();
5982 this.haveProgress = true;
5984 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5986 var c = new Roo.data.Connection();
5988 url : this.form.progressUrl,
5993 success : function(req){
5994 //console.log(data);
5998 rdata = Roo.decode(req.responseText)
6000 Roo.log("Invalid data from server..");
6004 if (!rdata || !rdata.success) {
6006 Roo.MessageBox.alert(Roo.encode(rdata));
6009 var data = rdata.data;
6011 if (this.uploadComplete) {
6012 Roo.MessageBox.hide();
6017 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6018 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6021 this.uploadProgress.defer(2000,this);
6024 failure: function(data) {
6025 Roo.log('progress url failed ');
6036 // run get Values on the form, so it syncs any secondary forms.
6037 this.form.getValues();
6039 var o = this.options;
6040 var method = this.getMethod();
6041 var isPost = method == 'POST';
6042 if(o.clientValidation === false || this.form.isValid()){
6044 if (this.form.progressUrl) {
6045 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6046 (new Date() * 1) + '' + Math.random());
6051 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6052 form:this.form.el.dom,
6053 url:this.getUrl(!isPost),
6055 params:isPost ? this.getParams() : null,
6056 isUpload: this.form.fileUpload
6059 this.uploadProgress();
6061 }else if (o.clientValidation !== false){ // client validation failed
6062 this.failureType = Roo.form.Action.CLIENT_INVALID;
6063 this.form.afterAction(this, false);
6067 success : function(response)
6069 this.uploadComplete= true;
6070 if (this.haveProgress) {
6071 Roo.MessageBox.hide();
6075 var result = this.processResponse(response);
6076 if(result === true || result.success){
6077 this.form.afterAction(this, true);
6081 this.form.markInvalid(result.errors);
6082 this.failureType = Roo.form.Action.SERVER_INVALID;
6084 this.form.afterAction(this, false);
6086 failure : function(response)
6088 this.uploadComplete= true;
6089 if (this.haveProgress) {
6090 Roo.MessageBox.hide();
6093 this.response = response;
6094 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6095 this.form.afterAction(this, false);
6098 handleResponse : function(response){
6099 if(this.form.errorReader){
6100 var rs = this.form.errorReader.read(response);
6103 for(var i = 0, len = rs.records.length; i < len; i++) {
6104 var r = rs.records[i];
6108 if(errors.length < 1){
6112 success : rs.success,
6118 ret = Roo.decode(response.responseText);
6122 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6132 Roo.form.Action.Load = function(form, options){
6133 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6134 this.reader = this.form.reader;
6137 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6142 Roo.Ajax.request(Roo.apply(
6143 this.createCallback(), {
6144 method:this.getMethod(),
6145 url:this.getUrl(false),
6146 params:this.getParams()
6150 success : function(response){
6152 var result = this.processResponse(response);
6153 if(result === true || !result.success || !result.data){
6154 this.failureType = Roo.form.Action.LOAD_FAILURE;
6155 this.form.afterAction(this, false);
6158 this.form.clearInvalid();
6159 this.form.setValues(result.data);
6160 this.form.afterAction(this, true);
6163 handleResponse : function(response){
6164 if(this.form.reader){
6165 var rs = this.form.reader.read(response);
6166 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6168 success : rs.success,
6172 return Roo.decode(response.responseText);
6176 Roo.form.Action.ACTION_TYPES = {
6177 'load' : Roo.form.Action.Load,
6178 'submit' : Roo.form.Action.Submit
6187 * @class Roo.bootstrap.Form
6188 * @extends Roo.bootstrap.Component
6189 * Bootstrap Form class
6190 * @cfg {String} method GET | POST (default POST)
6191 * @cfg {String} labelAlign top | left (default top)
6192 * @cfg {String} align left | right - for navbars
6193 * @cfg {Boolean} loadMask load mask when submit (default true)
6198 * @param {Object} config The config object
6202 Roo.bootstrap.Form = function(config){
6203 Roo.bootstrap.Form.superclass.constructor.call(this, config);
6206 * @event clientvalidation
6207 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6208 * @param {Form} this
6209 * @param {Boolean} valid true if the form has passed client-side validation
6211 clientvalidation: true,
6213 * @event beforeaction
6214 * Fires before any action is performed. Return false to cancel the action.
6215 * @param {Form} this
6216 * @param {Action} action The action to be performed
6220 * @event actionfailed
6221 * Fires when an action fails.
6222 * @param {Form} this
6223 * @param {Action} action The action that failed
6225 actionfailed : true,
6227 * @event actioncomplete
6228 * Fires when an action is completed.
6229 * @param {Form} this
6230 * @param {Action} action The action that completed
6232 actioncomplete : true
6237 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
6240 * @cfg {String} method
6241 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6246 * The URL to use for form actions if one isn't supplied in the action options.
6249 * @cfg {Boolean} fileUpload
6250 * Set to true if this form is a file upload.
6254 * @cfg {Object} baseParams
6255 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6259 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6263 * @cfg {Sting} align (left|right) for navbar forms
6268 activeAction : null,
6271 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6272 * element by passing it or its id or mask the form itself by passing in true.
6275 waitMsgTarget : false,
6279 getAutoCreate : function(){
6283 method : this.method || 'POST',
6284 id : this.id || Roo.id(),
6287 if (this.parent().xtype.match(/^Nav/)) {
6288 cfg.cls = 'navbar-form navbar-' + this.align;
6292 if (this.labelAlign == 'left' ) {
6293 cfg.cls += ' form-horizontal';
6299 initEvents : function()
6301 this.el.on('submit', this.onSubmit, this);
6302 // this was added as random key presses on the form where triggering form submit.
6303 this.el.on('keypress', function(e) {
6304 if (e.getCharCode() != 13) {
6307 // we might need to allow it for textareas.. and some other items.
6308 // check e.getTarget().
6310 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6314 Roo.log("keypress blocked");
6322 onSubmit : function(e){
6327 * Returns true if client-side validation on the form is successful.
6330 isValid : function(){
6331 var items = this.getItems();
6333 items.each(function(f){
6342 * Returns true if any fields in this form have changed since their original load.
6345 isDirty : function(){
6347 var items = this.getItems();
6348 items.each(function(f){
6358 * Performs a predefined action (submit or load) or custom actions you define on this form.
6359 * @param {String} actionName The name of the action type
6360 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
6361 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6362 * accept other config options):
6364 Property Type Description
6365 ---------------- --------------- ----------------------------------------------------------------------------------
6366 url String The url for the action (defaults to the form's url)
6367 method String The form method to use (defaults to the form's method, or POST if not defined)
6368 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
6369 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
6370 validate the form on the client (defaults to false)
6372 * @return {BasicForm} this
6374 doAction : function(action, options){
6375 if(typeof action == 'string'){
6376 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6378 if(this.fireEvent('beforeaction', this, action) !== false){
6379 this.beforeAction(action);
6380 action.run.defer(100, action);
6386 beforeAction : function(action){
6387 var o = action.options;
6390 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6392 // not really supported yet.. ??
6394 //if(this.waitMsgTarget === true){
6395 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6396 //}else if(this.waitMsgTarget){
6397 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6398 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6400 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6406 afterAction : function(action, success){
6407 this.activeAction = null;
6408 var o = action.options;
6410 //if(this.waitMsgTarget === true){
6412 //}else if(this.waitMsgTarget){
6413 // this.waitMsgTarget.unmask();
6415 // Roo.MessageBox.updateProgress(1);
6416 // Roo.MessageBox.hide();
6423 Roo.callback(o.success, o.scope, [this, action]);
6424 this.fireEvent('actioncomplete', this, action);
6428 // failure condition..
6429 // we have a scenario where updates need confirming.
6430 // eg. if a locking scenario exists..
6431 // we look for { errors : { needs_confirm : true }} in the response.
6433 (typeof(action.result) != 'undefined') &&
6434 (typeof(action.result.errors) != 'undefined') &&
6435 (typeof(action.result.errors.needs_confirm) != 'undefined')
6438 Roo.log("not supported yet");
6441 Roo.MessageBox.confirm(
6442 "Change requires confirmation",
6443 action.result.errorMsg,
6448 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
6458 Roo.callback(o.failure, o.scope, [this, action]);
6459 // show an error message if no failed handler is set..
6460 if (!this.hasListener('actionfailed')) {
6461 Roo.log("need to add dialog support");
6463 Roo.MessageBox.alert("Error",
6464 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6465 action.result.errorMsg :
6466 "Saving Failed, please check your entries or try again"
6471 this.fireEvent('actionfailed', this, action);
6476 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6477 * @param {String} id The value to search for
6480 findField : function(id){
6481 var items = this.getItems();
6482 var field = items.get(id);
6484 items.each(function(f){
6485 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6492 return field || null;
6495 * Mark fields in this form invalid in bulk.
6496 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6497 * @return {BasicForm} this
6499 markInvalid : function(errors){
6500 if(errors instanceof Array){
6501 for(var i = 0, len = errors.length; i < len; i++){
6502 var fieldError = errors[i];
6503 var f = this.findField(fieldError.id);
6505 f.markInvalid(fieldError.msg);
6511 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6512 field.markInvalid(errors[id]);
6516 //Roo.each(this.childForms || [], function (f) {
6517 // f.markInvalid(errors);
6524 * Set values for fields in this form in bulk.
6525 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6526 * @return {BasicForm} this
6528 setValues : function(values){
6529 if(values instanceof Array){ // array of objects
6530 for(var i = 0, len = values.length; i < len; i++){
6532 var f = this.findField(v.id);
6534 f.setValue(v.value);
6535 if(this.trackResetOnLoad){
6536 f.originalValue = f.getValue();
6540 }else{ // object hash
6543 if(typeof values[id] != 'function' && (field = this.findField(id))){
6545 if (field.setFromData &&
6547 field.displayField &&
6548 // combos' with local stores can
6549 // be queried via setValue()
6550 // to set their value..
6551 (field.store && !field.store.isLocal)
6555 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6556 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6557 field.setFromData(sd);
6560 field.setValue(values[id]);
6564 if(this.trackResetOnLoad){
6565 field.originalValue = field.getValue();
6571 //Roo.each(this.childForms || [], function (f) {
6572 // f.setValues(values);
6579 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6580 * they are returned as an array.
6581 * @param {Boolean} asString
6584 getValues : function(asString){
6585 //if (this.childForms) {
6586 // copy values from the child forms
6587 // Roo.each(this.childForms, function (f) {
6588 // this.setValues(f.getValues());
6594 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6595 if(asString === true){
6598 return Roo.urlDecode(fs);
6602 * Returns the fields in this form as an object with key/value pairs.
6603 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6606 getFieldValues : function(with_hidden)
6608 var items = this.getItems();
6610 items.each(function(f){
6614 var v = f.getValue();
6615 if (f.inputType =='radio') {
6616 if (typeof(ret[f.getName()]) == 'undefined') {
6617 ret[f.getName()] = ''; // empty..
6620 if (!f.el.dom.checked) {
6628 // not sure if this supported any more..
6629 if ((typeof(v) == 'object') && f.getRawValue) {
6630 v = f.getRawValue() ; // dates..
6632 // combo boxes where name != hiddenName...
6633 if (f.name != f.getName()) {
6634 ret[f.name] = f.getRawValue();
6636 ret[f.getName()] = v;
6643 * Clears all invalid messages in this form.
6644 * @return {BasicForm} this
6646 clearInvalid : function(){
6647 var items = this.getItems();
6649 items.each(function(f){
6660 * @return {BasicForm} this
6663 var items = this.getItems();
6664 items.each(function(f){
6668 Roo.each(this.childForms || [], function (f) {
6675 getItems : function()
6677 var r=new Roo.util.MixedCollection(false, function(o){
6678 return o.id || (o.id = Roo.id());
6680 var iter = function(el) {
6687 Roo.each(el.items,function(e) {
6706 * Ext JS Library 1.1.1
6707 * Copyright(c) 2006-2007, Ext JS, LLC.
6709 * Originally Released Under LGPL - original licence link has changed is not relivant.
6712 * <script type="text/javascript">
6715 * @class Roo.form.VTypes
6716 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6719 Roo.form.VTypes = function(){
6720 // closure these in so they are only created once.
6721 var alpha = /^[a-zA-Z_]+$/;
6722 var alphanum = /^[a-zA-Z0-9_]+$/;
6723 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6724 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6726 // All these messages and functions are configurable
6729 * The function used to validate email addresses
6730 * @param {String} value The email address
6732 'email' : function(v){
6733 return email.test(v);
6736 * The error text to display when the email validation function returns false
6739 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6741 * The keystroke filter mask to be applied on email input
6744 'emailMask' : /[a-z0-9_\.\-@]/i,
6747 * The function used to validate URLs
6748 * @param {String} value The URL
6750 'url' : function(v){
6754 * The error text to display when the url validation function returns false
6757 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6760 * The function used to validate alpha values
6761 * @param {String} value The value
6763 'alpha' : function(v){
6764 return alpha.test(v);
6767 * The error text to display when the alpha validation function returns false
6770 'alphaText' : 'This field should only contain letters and _',
6772 * The keystroke filter mask to be applied on alpha input
6775 'alphaMask' : /[a-z_]/i,
6778 * The function used to validate alphanumeric values
6779 * @param {String} value The value
6781 'alphanum' : function(v){
6782 return alphanum.test(v);
6785 * The error text to display when the alphanumeric validation function returns false
6788 'alphanumText' : 'This field should only contain letters, numbers and _',
6790 * The keystroke filter mask to be applied on alphanumeric input
6793 'alphanumMask' : /[a-z0-9_]/i
6803 * @class Roo.bootstrap.Input
6804 * @extends Roo.bootstrap.Component
6805 * Bootstrap Input class
6806 * @cfg {Boolean} disabled is it disabled
6807 * @cfg {String} fieldLabel - the label associated
6808 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6809 * @cfg {String} name name of the input
6810 * @cfg {string} fieldLabel - the label associated
6811 * @cfg {string} inputType - input / file submit ...
6812 * @cfg {string} placeholder - placeholder to put in text.
6813 * @cfg {string} before - input group add on before
6814 * @cfg {string} after - input group add on after
6815 * @cfg {string} size - (lg|sm) or leave empty..
6816 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6817 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6818 * @cfg {Number} md colspan out of 12 for computer-sized screens
6819 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6820 * @cfg {string} value default value of the input
6821 * @cfg {Number} labelWidth set the width of label (0-12)
6822 * @cfg {String} labelAlign (top|left)
6823 * @cfg {Boolean} readOnly Specifies that the field should be read-only
6824 * @cfg {String} align (left|center|right) Default left
6828 * Create a new Input
6829 * @param {Object} config The config object
6832 Roo.bootstrap.Input = function(config){
6833 Roo.bootstrap.Input.superclass.constructor.call(this, config);
6838 * Fires when this field receives input focus.
6839 * @param {Roo.form.Field} this
6844 * Fires when this field loses input focus.
6845 * @param {Roo.form.Field} this
6850 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
6851 * {@link Roo.EventObject#getKey} to determine which key was pressed.
6852 * @param {Roo.form.Field} this
6853 * @param {Roo.EventObject} e The event object
6858 * Fires just before the field blurs if the field value has changed.
6859 * @param {Roo.form.Field} this
6860 * @param {Mixed} newValue The new value
6861 * @param {Mixed} oldValue The original value
6866 * Fires after the field has been marked as invalid.
6867 * @param {Roo.form.Field} this
6868 * @param {String} msg The validation message
6873 * Fires after the field has been validated with no errors.
6874 * @param {Roo.form.Field} this
6879 * Fires after the key up
6880 * @param {Roo.form.Field} this
6881 * @param {Roo.EventObject} e The event Object
6887 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
6889 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6890 automatic validation (defaults to "keyup").
6892 validationEvent : "keyup",
6894 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6896 validateOnBlur : true,
6898 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6900 validationDelay : 250,
6902 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6904 focusClass : "x-form-focus", // not needed???
6908 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6910 invalidClass : "has-error",
6913 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6915 selectOnFocus : false,
6918 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6922 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6927 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6929 disableKeyFilter : false,
6932 * @cfg {Boolean} disabled True to disable the field (defaults to false).
6936 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6940 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6942 blankText : "This field is required",
6945 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6949 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6951 maxLength : Number.MAX_VALUE,
6953 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6955 minLengthText : "The minimum length for this field is {0}",
6957 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6959 maxLengthText : "The maximum length for this field is {0}",
6963 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6964 * If available, this function will be called only after the basic validators all return true, and will be passed the
6965 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6969 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6970 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6971 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
6975 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6998 formatedValue : false,
7000 parentLabelAlign : function()
7003 while (parent.parent()) {
7004 parent = parent.parent();
7005 if (typeof(parent.labelAlign) !='undefined') {
7006 return parent.labelAlign;
7013 getAutoCreate : function(){
7015 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7021 if(this.inputType != 'hidden'){
7022 cfg.cls = 'form-group' //input-group
7028 type : this.inputType,
7030 cls : 'form-control',
7031 placeholder : this.placeholder || ''
7036 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7039 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7040 input.maxLength = this.maxLength;
7043 if (this.disabled) {
7044 input.disabled=true;
7047 if (this.readOnly) {
7048 input.readonly=true;
7052 input.name = this.name;
7055 input.cls += ' input-' + this.size;
7058 ['xs','sm','md','lg'].map(function(size){
7059 if (settings[size]) {
7060 cfg.cls += ' col-' + size + '-' + settings[size];
7064 var inputblock = input;
7066 if (this.before || this.after) {
7069 cls : 'input-group',
7072 if (this.before && typeof(this.before) == 'string') {
7074 inputblock.cn.push({
7076 cls : 'roo-input-before input-group-addon',
7080 if (this.before && typeof(this.before) == 'object') {
7081 this.before = Roo.factory(this.before);
7082 Roo.log(this.before);
7083 inputblock.cn.push({
7085 cls : 'roo-input-before input-group-' +
7086 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7090 inputblock.cn.push(input);
7092 if (this.after && typeof(this.after) == 'string') {
7093 inputblock.cn.push({
7095 cls : 'roo-input-after input-group-addon',
7099 if (this.after && typeof(this.after) == 'object') {
7100 this.after = Roo.factory(this.after);
7101 Roo.log(this.after);
7102 inputblock.cn.push({
7104 cls : 'roo-input-after input-group-' +
7105 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7110 if (align ==='left' && this.fieldLabel.length) {
7111 Roo.log("left and has label");
7117 cls : 'control-label col-sm-' + this.labelWidth,
7118 html : this.fieldLabel
7122 cls : "col-sm-" + (12 - this.labelWidth),
7129 } else if ( this.fieldLabel.length) {
7135 //cls : 'input-group-addon',
7136 html : this.fieldLabel
7146 Roo.log(" no label && no align");
7155 Roo.log('input-parentType: ' + this.parentType);
7157 if (this.parentType === 'Navbar' && this.parent().bar) {
7158 cfg.cls += ' navbar-form';
7166 * return the real input element.
7168 inputEl: function ()
7170 return this.el.select('input.form-control',true).first();
7172 setDisabled : function(v)
7174 var i = this.inputEl().dom;
7176 i.removeAttribute('disabled');
7180 i.setAttribute('disabled','true');
7182 initEvents : function()
7185 this.inputEl().on("keydown" , this.fireKey, this);
7186 this.inputEl().on("focus", this.onFocus, this);
7187 this.inputEl().on("blur", this.onBlur, this);
7189 this.inputEl().relayEvent('keyup', this);
7191 // reference to original value for reset
7192 this.originalValue = this.getValue();
7193 //Roo.form.TextField.superclass.initEvents.call(this);
7194 if(this.validationEvent == 'keyup'){
7195 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7196 this.inputEl().on('keyup', this.filterValidation, this);
7198 else if(this.validationEvent !== false){
7199 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7202 if(this.selectOnFocus){
7203 this.on("focus", this.preFocus, this);
7206 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7207 this.inputEl().on("keypress", this.filterKeys, this);
7210 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
7211 this.el.on("click", this.autoSize, this);
7214 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7215 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7218 if (typeof(this.before) == 'object') {
7219 this.before.render(this.el.select('.roo-input-before',true).first());
7221 if (typeof(this.after) == 'object') {
7222 this.after.render(this.el.select('.roo-input-after',true).first());
7227 filterValidation : function(e){
7228 if(!e.isNavKeyPress()){
7229 this.validationTask.delay(this.validationDelay);
7233 * Validates the field value
7234 * @return {Boolean} True if the value is valid, else false
7236 validate : function(){
7237 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7238 if(this.disabled || this.validateValue(this.getRawValue())){
7239 this.clearInvalid();
7247 * Validates a value according to the field's validation rules and marks the field as invalid
7248 * if the validation fails
7249 * @param {Mixed} value The value to validate
7250 * @return {Boolean} True if the value is valid, else false
7252 validateValue : function(value){
7253 if(value.length < 1) { // if it's blank
7254 if(this.allowBlank){
7255 this.clearInvalid();
7258 this.markInvalid(this.blankText);
7262 if(value.length < this.minLength){
7263 this.markInvalid(String.format(this.minLengthText, this.minLength));
7266 if(value.length > this.maxLength){
7267 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7271 var vt = Roo.form.VTypes;
7272 if(!vt[this.vtype](value, this)){
7273 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7277 if(typeof this.validator == "function"){
7278 var msg = this.validator(value);
7280 this.markInvalid(msg);
7284 if(this.regex && !this.regex.test(value)){
7285 this.markInvalid(this.regexText);
7294 fireKey : function(e){
7295 //Roo.log('field ' + e.getKey());
7296 if(e.isNavKeyPress()){
7297 this.fireEvent("specialkey", this, e);
7300 focus : function (selectText){
7302 this.inputEl().focus();
7303 if(selectText === true){
7304 this.inputEl().dom.select();
7310 onFocus : function(){
7311 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7312 // this.el.addClass(this.focusClass);
7315 this.hasFocus = true;
7316 this.startValue = this.getValue();
7317 this.fireEvent("focus", this);
7321 beforeBlur : Roo.emptyFn,
7325 onBlur : function(){
7327 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7328 //this.el.removeClass(this.focusClass);
7330 this.hasFocus = false;
7331 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7334 var v = this.getValue();
7335 if(String(v) !== String(this.startValue)){
7336 this.fireEvent('change', this, v, this.startValue);
7338 this.fireEvent("blur", this);
7342 * Resets the current field value to the originally loaded value and clears any validation messages
7345 this.setValue(this.originalValue);
7346 this.clearInvalid();
7349 * Returns the name of the field
7350 * @return {Mixed} name The name field
7352 getName: function(){
7356 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
7357 * @return {Mixed} value The field value
7359 getValue : function(){
7361 var v = this.inputEl().getValue();
7366 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
7367 * @return {Mixed} value The field value
7369 getRawValue : function(){
7370 var v = this.inputEl().getValue();
7376 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
7377 * @param {Mixed} value The value to set
7379 setRawValue : function(v){
7380 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7383 selectText : function(start, end){
7384 var v = this.getRawValue();
7386 start = start === undefined ? 0 : start;
7387 end = end === undefined ? v.length : end;
7388 var d = this.inputEl().dom;
7389 if(d.setSelectionRange){
7390 d.setSelectionRange(start, end);
7391 }else if(d.createTextRange){
7392 var range = d.createTextRange();
7393 range.moveStart("character", start);
7394 range.moveEnd("character", v.length-end);
7401 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
7402 * @param {Mixed} value The value to set
7404 setValue : function(v){
7407 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7413 processValue : function(value){
7414 if(this.stripCharsRe){
7415 var newValue = value.replace(this.stripCharsRe, '');
7416 if(newValue !== value){
7417 this.setRawValue(newValue);
7424 preFocus : function(){
7426 if(this.selectOnFocus){
7427 this.inputEl().dom.select();
7430 filterKeys : function(e){
7432 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7435 var c = e.getCharCode(), cc = String.fromCharCode(c);
7436 if(Roo.isIE && (e.isSpecialKey() || !cc)){
7439 if(!this.maskRe.test(cc)){
7444 * Clear any invalid styles/messages for this field
7446 clearInvalid : function(){
7448 if(!this.el || this.preventMark){ // not rendered
7451 this.el.removeClass(this.invalidClass);
7453 switch(this.msgTarget){
7455 this.el.dom.qtip = '';
7458 this.el.dom.title = '';
7462 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7467 this.errorIcon.dom.qtip = '';
7468 this.errorIcon.hide();
7469 this.un('resize', this.alignErrorIcon, this);
7473 var t = Roo.getDom(this.msgTarget);
7475 t.style.display = 'none';
7479 this.fireEvent('valid', this);
7482 * Mark this field as invalid
7483 * @param {String} msg The validation message
7485 markInvalid : function(msg){
7486 if(!this.el || this.preventMark){ // not rendered
7489 this.el.addClass(this.invalidClass);
7491 msg = msg || this.invalidText;
7492 switch(this.msgTarget){
7494 this.el.dom.qtip = msg;
7495 this.el.dom.qclass = 'x-form-invalid-tip';
7496 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7497 Roo.QuickTips.enable();
7501 this.el.dom.title = msg;
7505 var elp = this.el.findParent('.x-form-element', 5, true);
7506 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7507 this.errorEl.setWidth(elp.getWidth(true)-20);
7509 this.errorEl.update(msg);
7510 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7513 if(!this.errorIcon){
7514 var elp = this.el.findParent('.x-form-element', 5, true);
7515 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7517 this.alignErrorIcon();
7518 this.errorIcon.dom.qtip = msg;
7519 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7520 this.errorIcon.show();
7521 this.on('resize', this.alignErrorIcon, this);
7524 var t = Roo.getDom(this.msgTarget);
7526 t.style.display = this.msgDisplay;
7530 this.fireEvent('invalid', this, msg);
7533 SafariOnKeyDown : function(event)
7535 // this is a workaround for a password hang bug on chrome/ webkit.
7537 var isSelectAll = false;
7539 if(this.inputEl().dom.selectionEnd > 0){
7540 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7542 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7543 event.preventDefault();
7548 if(isSelectAll){ // backspace and delete key
7550 event.preventDefault();
7551 // this is very hacky as keydown always get's upper case.
7553 var cc = String.fromCharCode(event.getCharCode());
7554 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
7558 adjustWidth : function(tag, w){
7559 tag = tag.toLowerCase();
7560 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7561 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7565 if(tag == 'textarea'){
7568 }else if(Roo.isOpera){
7572 if(tag == 'textarea'){
7591 * @class Roo.bootstrap.TextArea
7592 * @extends Roo.bootstrap.Input
7593 * Bootstrap TextArea class
7594 * @cfg {Number} cols Specifies the visible width of a text area
7595 * @cfg {Number} rows Specifies the visible number of lines in a text area
7596 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7597 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7598 * @cfg {string} html text
7601 * Create a new TextArea
7602 * @param {Object} config The config object
7605 Roo.bootstrap.TextArea = function(config){
7606 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7610 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
7620 getAutoCreate : function(){
7622 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7633 value : this.value || '',
7634 html: this.html || '',
7635 cls : 'form-control',
7636 placeholder : this.placeholder || ''
7640 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7641 input.maxLength = this.maxLength;
7645 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7649 input.cols = this.cols;
7652 if (this.readOnly) {
7653 input.readonly = true;
7657 input.name = this.name;
7661 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7665 ['xs','sm','md','lg'].map(function(size){
7666 if (settings[size]) {
7667 cfg.cls += ' col-' + size + '-' + settings[size];
7671 var inputblock = input;
7673 if (this.before || this.after) {
7676 cls : 'input-group',
7680 inputblock.cn.push({
7682 cls : 'input-group-addon',
7686 inputblock.cn.push(input);
7688 inputblock.cn.push({
7690 cls : 'input-group-addon',
7697 if (align ==='left' && this.fieldLabel.length) {
7698 Roo.log("left and has label");
7704 cls : 'control-label col-sm-' + this.labelWidth,
7705 html : this.fieldLabel
7709 cls : "col-sm-" + (12 - this.labelWidth),
7716 } else if ( this.fieldLabel.length) {
7722 //cls : 'input-group-addon',
7723 html : this.fieldLabel
7733 Roo.log(" no label && no align");
7743 if (this.disabled) {
7744 input.disabled=true;
7751 * return the real textarea element.
7753 inputEl: function ()
7755 return this.el.select('textarea.form-control',true).first();
7763 * trigger field - base class for combo..
7768 * @class Roo.bootstrap.TriggerField
7769 * @extends Roo.bootstrap.Input
7770 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7771 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7772 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7773 * for which you can provide a custom implementation. For example:
7775 var trigger = new Roo.bootstrap.TriggerField();
7776 trigger.onTriggerClick = myTriggerFn;
7777 trigger.applyTo('my-field');
7780 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7781 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7782 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
7783 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7785 * Create a new TriggerField.
7786 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7787 * to the base TextField)
7789 Roo.bootstrap.TriggerField = function(config){
7790 this.mimicing = false;
7791 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7794 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
7796 * @cfg {String} triggerClass A CSS class to apply to the trigger
7799 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7803 /** @cfg {Boolean} grow @hide */
7804 /** @cfg {Number} growMin @hide */
7805 /** @cfg {Number} growMax @hide */
7811 autoSize: Roo.emptyFn,
7818 actionMode : 'wrap',
7822 getAutoCreate : function(){
7824 var align = this.labelAlign || this.parentLabelAlign();
7829 cls: 'form-group' //input-group
7836 type : this.inputType,
7837 cls : 'form-control',
7838 autocomplete: 'off',
7839 placeholder : this.placeholder || ''
7843 input.name = this.name;
7846 input.cls += ' input-' + this.size;
7849 if (this.disabled) {
7850 input.disabled=true;
7853 var inputblock = input;
7855 if (this.before || this.after) {
7858 cls : 'input-group',
7862 inputblock.cn.push({
7864 cls : 'input-group-addon',
7868 inputblock.cn.push(input);
7870 inputblock.cn.push({
7872 cls : 'input-group-addon',
7885 cls: 'form-hidden-field'
7893 Roo.log('multiple');
7901 cls: 'form-hidden-field'
7905 cls: 'select2-choices',
7909 cls: 'select2-search-field',
7922 cls: 'select2-container input-group',
7927 // cls: 'typeahead typeahead-long dropdown-menu',
7928 // style: 'display:none'
7933 if(!this.multiple && this.showToggleBtn){
7936 cls : 'input-group-addon btn dropdown-toggle',
7944 cls: 'combobox-clear',
7958 combobox.cls += ' select2-container-multi';
7961 if (align ==='left' && this.fieldLabel.length) {
7963 Roo.log("left and has label");
7969 cls : 'control-label col-sm-' + this.labelWidth,
7970 html : this.fieldLabel
7974 cls : "col-sm-" + (12 - this.labelWidth),
7981 } else if ( this.fieldLabel.length) {
7987 //cls : 'input-group-addon',
7988 html : this.fieldLabel
7998 Roo.log(" no label && no align");
8005 ['xs','sm','md','lg'].map(function(size){
8006 if (settings[size]) {
8007 cfg.cls += ' col-' + size + '-' + settings[size];
8018 onResize : function(w, h){
8019 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8020 // if(typeof w == 'number'){
8021 // var x = w - this.trigger.getWidth();
8022 // this.inputEl().setWidth(this.adjustWidth('input', x));
8023 // this.trigger.setStyle('left', x+'px');
8028 adjustSize : Roo.BoxComponent.prototype.adjustSize,
8031 getResizeEl : function(){
8032 return this.inputEl();
8036 getPositionEl : function(){
8037 return this.inputEl();
8041 alignErrorIcon : function(){
8042 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8046 initEvents : function(){
8050 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8051 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8052 if(!this.multiple && this.showToggleBtn){
8053 this.trigger = this.el.select('span.dropdown-toggle',true).first();
8054 if(this.hideTrigger){
8055 this.trigger.setDisplayed(false);
8057 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8061 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8064 //this.trigger.addClassOnOver('x-form-trigger-over');
8065 //this.trigger.addClassOnClick('x-form-trigger-click');
8068 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8072 createList : function()
8074 this.list = Roo.get(document.body).createChild({
8076 cls: 'typeahead typeahead-long dropdown-menu',
8077 style: 'display:none'
8080 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8085 initTrigger : function(){
8090 onDestroy : function(){
8092 this.trigger.removeAllListeners();
8093 // this.trigger.remove();
8096 // this.wrap.remove();
8098 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8102 onFocus : function(){
8103 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8106 this.wrap.addClass('x-trigger-wrap-focus');
8107 this.mimicing = true;
8108 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8109 if(this.monitorTab){
8110 this.el.on("keydown", this.checkTab, this);
8117 checkTab : function(e){
8118 if(e.getKey() == e.TAB){
8124 onBlur : function(){
8129 mimicBlur : function(e, t){
8131 if(!this.wrap.contains(t) && this.validateBlur()){
8138 triggerBlur : function(){
8139 this.mimicing = false;
8140 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8141 if(this.monitorTab){
8142 this.el.un("keydown", this.checkTab, this);
8144 //this.wrap.removeClass('x-trigger-wrap-focus');
8145 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8149 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8150 validateBlur : function(e, t){
8155 onDisable : function(){
8156 this.inputEl().dom.disabled = true;
8157 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8159 // this.wrap.addClass('x-item-disabled');
8164 onEnable : function(){
8165 this.inputEl().dom.disabled = false;
8166 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8168 // this.el.removeClass('x-item-disabled');
8173 onShow : function(){
8174 var ae = this.getActionEl();
8177 ae.dom.style.display = '';
8178 ae.dom.style.visibility = 'visible';
8184 onHide : function(){
8185 var ae = this.getActionEl();
8186 ae.dom.style.display = 'none';
8190 * The function that should handle the trigger's click event. This method does nothing by default until overridden
8191 * by an implementing function.
8193 * @param {EventObject} e
8195 onTriggerClick : Roo.emptyFn
8199 * Ext JS Library 1.1.1
8200 * Copyright(c) 2006-2007, Ext JS, LLC.
8202 * Originally Released Under LGPL - original licence link has changed is not relivant.
8205 * <script type="text/javascript">
8210 * @class Roo.data.SortTypes
8212 * Defines the default sorting (casting?) comparison functions used when sorting data.
8214 Roo.data.SortTypes = {
8216 * Default sort that does nothing
8217 * @param {Mixed} s The value being converted
8218 * @return {Mixed} The comparison value
8225 * The regular expression used to strip tags
8229 stripTagsRE : /<\/?[^>]+>/gi,
8232 * Strips all HTML tags to sort on text only
8233 * @param {Mixed} s The value being converted
8234 * @return {String} The comparison value
8236 asText : function(s){
8237 return String(s).replace(this.stripTagsRE, "");
8241 * Strips all HTML tags to sort on text only - Case insensitive
8242 * @param {Mixed} s The value being converted
8243 * @return {String} The comparison value
8245 asUCText : function(s){
8246 return String(s).toUpperCase().replace(this.stripTagsRE, "");
8250 * Case insensitive string
8251 * @param {Mixed} s The value being converted
8252 * @return {String} The comparison value
8254 asUCString : function(s) {
8255 return String(s).toUpperCase();
8260 * @param {Mixed} s The value being converted
8261 * @return {Number} The comparison value
8263 asDate : function(s) {
8267 if(s instanceof Date){
8270 return Date.parse(String(s));
8275 * @param {Mixed} s The value being converted
8276 * @return {Float} The comparison value
8278 asFloat : function(s) {
8279 var val = parseFloat(String(s).replace(/,/g, ""));
8280 if(isNaN(val)) val = 0;
8286 * @param {Mixed} s The value being converted
8287 * @return {Number} The comparison value
8289 asInt : function(s) {
8290 var val = parseInt(String(s).replace(/,/g, ""));
8291 if(isNaN(val)) val = 0;
8296 * Ext JS Library 1.1.1
8297 * Copyright(c) 2006-2007, Ext JS, LLC.
8299 * Originally Released Under LGPL - original licence link has changed is not relivant.
8302 * <script type="text/javascript">
8306 * @class Roo.data.Record
8307 * Instances of this class encapsulate both record <em>definition</em> information, and record
8308 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8309 * to access Records cached in an {@link Roo.data.Store} object.<br>
8311 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8312 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8315 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8317 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8318 * {@link #create}. The parameters are the same.
8319 * @param {Array} data An associative Array of data values keyed by the field name.
8320 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8321 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8322 * not specified an integer id is generated.
8324 Roo.data.Record = function(data, id){
8325 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8330 * Generate a constructor for a specific record layout.
8331 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8332 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8333 * Each field definition object may contain the following properties: <ul>
8334 * <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,
8335 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8336 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8337 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8338 * is being used, then this is a string containing the javascript expression to reference the data relative to
8339 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8340 * to the data item relative to the record element. If the mapping expression is the same as the field name,
8341 * this may be omitted.</p></li>
8342 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8343 * <ul><li>auto (Default, implies no conversion)</li>
8348 * <li>date</li></ul></p></li>
8349 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8350 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8351 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8352 * by the Reader into an object that will be stored in the Record. It is passed the
8353 * following parameters:<ul>
8354 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8356 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8358 * <br>usage:<br><pre><code>
8359 var TopicRecord = Roo.data.Record.create(
8360 {name: 'title', mapping: 'topic_title'},
8361 {name: 'author', mapping: 'username'},
8362 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8363 {name: 'lastPost', mapping: 'post_time', type: 'date'},
8364 {name: 'lastPoster', mapping: 'user2'},
8365 {name: 'excerpt', mapping: 'post_text'}
8368 var myNewRecord = new TopicRecord({
8369 title: 'Do my job please',
8372 lastPost: new Date(),
8373 lastPoster: 'Animal',
8374 excerpt: 'No way dude!'
8376 myStore.add(myNewRecord);
8381 Roo.data.Record.create = function(o){
8383 f.superclass.constructor.apply(this, arguments);
8385 Roo.extend(f, Roo.data.Record);
8386 var p = f.prototype;
8387 p.fields = new Roo.util.MixedCollection(false, function(field){
8390 for(var i = 0, len = o.length; i < len; i++){
8391 p.fields.add(new Roo.data.Field(o[i]));
8393 f.getField = function(name){
8394 return p.fields.get(name);
8399 Roo.data.Record.AUTO_ID = 1000;
8400 Roo.data.Record.EDIT = 'edit';
8401 Roo.data.Record.REJECT = 'reject';
8402 Roo.data.Record.COMMIT = 'commit';
8404 Roo.data.Record.prototype = {
8406 * Readonly flag - true if this record has been modified.
8415 join : function(store){
8420 * Set the named field to the specified value.
8421 * @param {String} name The name of the field to set.
8422 * @param {Object} value The value to set the field to.
8424 set : function(name, value){
8425 if(this.data[name] == value){
8432 if(typeof this.modified[name] == 'undefined'){
8433 this.modified[name] = this.data[name];
8435 this.data[name] = value;
8436 if(!this.editing && this.store){
8437 this.store.afterEdit(this);
8442 * Get the value of the named field.
8443 * @param {String} name The name of the field to get the value of.
8444 * @return {Object} The value of the field.
8446 get : function(name){
8447 return this.data[name];
8451 beginEdit : function(){
8452 this.editing = true;
8457 cancelEdit : function(){
8458 this.editing = false;
8459 delete this.modified;
8463 endEdit : function(){
8464 this.editing = false;
8465 if(this.dirty && this.store){
8466 this.store.afterEdit(this);
8471 * Usually called by the {@link Roo.data.Store} which owns the Record.
8472 * Rejects all changes made to the Record since either creation, or the last commit operation.
8473 * Modified fields are reverted to their original values.
8475 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8476 * of reject operations.
8478 reject : function(){
8479 var m = this.modified;
8481 if(typeof m[n] != "function"){
8482 this.data[n] = m[n];
8486 delete this.modified;
8487 this.editing = false;
8489 this.store.afterReject(this);
8494 * Usually called by the {@link Roo.data.Store} which owns the Record.
8495 * Commits all changes made to the Record since either creation, or the last commit operation.
8497 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8498 * of commit operations.
8500 commit : function(){
8502 delete this.modified;
8503 this.editing = false;
8505 this.store.afterCommit(this);
8510 hasError : function(){
8511 return this.error != null;
8515 clearError : function(){
8520 * Creates a copy of this record.
8521 * @param {String} id (optional) A new record id if you don't want to use this record's id
8524 copy : function(newId) {
8525 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8529 * Ext JS Library 1.1.1
8530 * Copyright(c) 2006-2007, Ext JS, LLC.
8532 * Originally Released Under LGPL - original licence link has changed is not relivant.
8535 * <script type="text/javascript">
8541 * @class Roo.data.Store
8542 * @extends Roo.util.Observable
8543 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8544 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8546 * 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
8547 * has no knowledge of the format of the data returned by the Proxy.<br>
8549 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8550 * instances from the data object. These records are cached and made available through accessor functions.
8552 * Creates a new Store.
8553 * @param {Object} config A config object containing the objects needed for the Store to access data,
8554 * and read the data into Records.
8556 Roo.data.Store = function(config){
8557 this.data = new Roo.util.MixedCollection(false);
8558 this.data.getKey = function(o){
8561 this.baseParams = {};
8568 "multisort" : "_multisort"
8571 if(config && config.data){
8572 this.inlineData = config.data;
8576 Roo.apply(this, config);
8578 if(this.reader){ // reader passed
8579 this.reader = Roo.factory(this.reader, Roo.data);
8580 this.reader.xmodule = this.xmodule || false;
8581 if(!this.recordType){
8582 this.recordType = this.reader.recordType;
8584 if(this.reader.onMetaChange){
8585 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8589 if(this.recordType){
8590 this.fields = this.recordType.prototype.fields;
8596 * @event datachanged
8597 * Fires when the data cache has changed, and a widget which is using this Store
8598 * as a Record cache should refresh its view.
8599 * @param {Store} this
8604 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8605 * @param {Store} this
8606 * @param {Object} meta The JSON metadata
8611 * Fires when Records have been added to the Store
8612 * @param {Store} this
8613 * @param {Roo.data.Record[]} records The array of Records added
8614 * @param {Number} index The index at which the record(s) were added
8619 * Fires when a Record has been removed from the Store
8620 * @param {Store} this
8621 * @param {Roo.data.Record} record The Record that was removed
8622 * @param {Number} index The index at which the record was removed
8627 * Fires when a Record has been updated
8628 * @param {Store} this
8629 * @param {Roo.data.Record} record The Record that was updated
8630 * @param {String} operation The update operation being performed. Value may be one of:
8632 Roo.data.Record.EDIT
8633 Roo.data.Record.REJECT
8634 Roo.data.Record.COMMIT
8640 * Fires when the data cache has been cleared.
8641 * @param {Store} this
8646 * Fires before a request is made for a new data object. If the beforeload handler returns false
8647 * the load action will be canceled.
8648 * @param {Store} this
8649 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8653 * @event beforeloadadd
8654 * Fires after a new set of Records has been loaded.
8655 * @param {Store} this
8656 * @param {Roo.data.Record[]} records The Records that were loaded
8657 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8659 beforeloadadd : true,
8662 * Fires after a new set of Records has been loaded, before they are added to the store.
8663 * @param {Store} this
8664 * @param {Roo.data.Record[]} records The Records that were loaded
8665 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8666 * @params {Object} return from reader
8670 * @event loadexception
8671 * Fires if an exception occurs in the Proxy during loading.
8672 * Called with the signature of the Proxy's "loadexception" event.
8673 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8676 * @param {Object} return from JsonData.reader() - success, totalRecords, records
8677 * @param {Object} load options
8678 * @param {Object} jsonData from your request (normally this contains the Exception)
8680 loadexception : true
8684 this.proxy = Roo.factory(this.proxy, Roo.data);
8685 this.proxy.xmodule = this.xmodule || false;
8686 this.relayEvents(this.proxy, ["loadexception"]);
8688 this.sortToggle = {};
8689 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8691 Roo.data.Store.superclass.constructor.call(this);
8693 if(this.inlineData){
8694 this.loadData(this.inlineData);
8695 delete this.inlineData;
8699 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8701 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
8702 * without a remote query - used by combo/forms at present.
8706 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8709 * @cfg {Array} data Inline data to be loaded when the store is initialized.
8712 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8713 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8716 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8717 * on any HTTP request
8720 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8723 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8727 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8728 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8733 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8734 * loaded or when a record is removed. (defaults to false).
8736 pruneModifiedRecords : false,
8742 * Add Records to the Store and fires the add event.
8743 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8745 add : function(records){
8746 records = [].concat(records);
8747 for(var i = 0, len = records.length; i < len; i++){
8748 records[i].join(this);
8750 var index = this.data.length;
8751 this.data.addAll(records);
8752 this.fireEvent("add", this, records, index);
8756 * Remove a Record from the Store and fires the remove event.
8757 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8759 remove : function(record){
8760 var index = this.data.indexOf(record);
8761 this.data.removeAt(index);
8762 if(this.pruneModifiedRecords){
8763 this.modified.remove(record);
8765 this.fireEvent("remove", this, record, index);
8769 * Remove all Records from the Store and fires the clear event.
8771 removeAll : function(){
8773 if(this.pruneModifiedRecords){
8776 this.fireEvent("clear", this);
8780 * Inserts Records to the Store at the given index and fires the add event.
8781 * @param {Number} index The start index at which to insert the passed Records.
8782 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8784 insert : function(index, records){
8785 records = [].concat(records);
8786 for(var i = 0, len = records.length; i < len; i++){
8787 this.data.insert(index, records[i]);
8788 records[i].join(this);
8790 this.fireEvent("add", this, records, index);
8794 * Get the index within the cache of the passed Record.
8795 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8796 * @return {Number} The index of the passed Record. Returns -1 if not found.
8798 indexOf : function(record){
8799 return this.data.indexOf(record);
8803 * Get the index within the cache of the Record with the passed id.
8804 * @param {String} id The id of the Record to find.
8805 * @return {Number} The index of the Record. Returns -1 if not found.
8807 indexOfId : function(id){
8808 return this.data.indexOfKey(id);
8812 * Get the Record with the specified id.
8813 * @param {String} id The id of the Record to find.
8814 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8816 getById : function(id){
8817 return this.data.key(id);
8821 * Get the Record at the specified index.
8822 * @param {Number} index The index of the Record to find.
8823 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8825 getAt : function(index){
8826 return this.data.itemAt(index);
8830 * Returns a range of Records between specified indices.
8831 * @param {Number} startIndex (optional) The starting index (defaults to 0)
8832 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8833 * @return {Roo.data.Record[]} An array of Records
8835 getRange : function(start, end){
8836 return this.data.getRange(start, end);
8840 storeOptions : function(o){
8841 o = Roo.apply({}, o);
8844 this.lastOptions = o;
8848 * Loads the Record cache from the configured Proxy using the configured Reader.
8850 * If using remote paging, then the first load call must specify the <em>start</em>
8851 * and <em>limit</em> properties in the options.params property to establish the initial
8852 * position within the dataset, and the number of Records to cache on each read from the Proxy.
8854 * <strong>It is important to note that for remote data sources, loading is asynchronous,
8855 * and this call will return before the new data has been loaded. Perform any post-processing
8856 * in a callback function, or in a "load" event handler.</strong>
8858 * @param {Object} options An object containing properties which control loading options:<ul>
8859 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8860 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8861 * passed the following arguments:<ul>
8862 * <li>r : Roo.data.Record[]</li>
8863 * <li>options: Options object from the load call</li>
8864 * <li>success: Boolean success indicator</li></ul></li>
8865 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8866 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8869 load : function(options){
8870 options = options || {};
8871 if(this.fireEvent("beforeload", this, options) !== false){
8872 this.storeOptions(options);
8873 var p = Roo.apply(options.params || {}, this.baseParams);
8874 // if meta was not loaded from remote source.. try requesting it.
8875 if (!this.reader.metaFromRemote) {
8878 if(this.sortInfo && this.remoteSort){
8879 var pn = this.paramNames;
8880 p[pn["sort"]] = this.sortInfo.field;
8881 p[pn["dir"]] = this.sortInfo.direction;
8883 if (this.multiSort) {
8884 var pn = this.paramNames;
8885 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8888 this.proxy.load(p, this.reader, this.loadRecords, this, options);
8893 * Reloads the Record cache from the configured Proxy using the configured Reader and
8894 * the options from the last load operation performed.
8895 * @param {Object} options (optional) An object containing properties which may override the options
8896 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8897 * the most recently used options are reused).
8899 reload : function(options){
8900 this.load(Roo.applyIf(options||{}, this.lastOptions));
8904 // Called as a callback by the Reader during a load operation.
8905 loadRecords : function(o, options, success){
8906 if(!o || success === false){
8907 if(success !== false){
8908 this.fireEvent("load", this, [], options, o);
8910 if(options.callback){
8911 options.callback.call(options.scope || this, [], options, false);
8915 // if data returned failure - throw an exception.
8916 if (o.success === false) {
8917 // show a message if no listener is registered.
8918 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8919 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8921 // loadmask wil be hooked into this..
8922 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8925 var r = o.records, t = o.totalRecords || r.length;
8927 this.fireEvent("beforeloadadd", this, r, options, o);
8929 if(!options || options.add !== true){
8930 if(this.pruneModifiedRecords){
8933 for(var i = 0, len = r.length; i < len; i++){
8937 this.data = this.snapshot;
8938 delete this.snapshot;
8941 this.data.addAll(r);
8942 this.totalLength = t;
8944 this.fireEvent("datachanged", this);
8946 this.totalLength = Math.max(t, this.data.length+r.length);
8949 this.fireEvent("load", this, r, options, o);
8950 if(options.callback){
8951 options.callback.call(options.scope || this, r, options, true);
8957 * Loads data from a passed data block. A Reader which understands the format of the data
8958 * must have been configured in the constructor.
8959 * @param {Object} data The data block from which to read the Records. The format of the data expected
8960 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8961 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8963 loadData : function(o, append){
8964 var r = this.reader.readRecords(o);
8965 this.loadRecords(r, {add: append}, true);
8969 * Gets the number of cached records.
8971 * <em>If using paging, this may not be the total size of the dataset. If the data object
8972 * used by the Reader contains the dataset size, then the getTotalCount() function returns
8973 * the data set size</em>
8975 getCount : function(){
8976 return this.data.length || 0;
8980 * Gets the total number of records in the dataset as returned by the server.
8982 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8983 * the dataset size</em>
8985 getTotalCount : function(){
8986 return this.totalLength || 0;
8990 * Returns the sort state of the Store as an object with two properties:
8992 field {String} The name of the field by which the Records are sorted
8993 direction {String} The sort order, "ASC" or "DESC"
8996 getSortState : function(){
8997 return this.sortInfo;
9001 applySort : function(){
9002 if(this.sortInfo && !this.remoteSort){
9003 var s = this.sortInfo, f = s.field;
9004 var st = this.fields.get(f).sortType;
9005 var fn = function(r1, r2){
9006 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9007 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9009 this.data.sort(s.direction, fn);
9010 if(this.snapshot && this.snapshot != this.data){
9011 this.snapshot.sort(s.direction, fn);
9017 * Sets the default sort column and order to be used by the next load operation.
9018 * @param {String} fieldName The name of the field to sort by.
9019 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9021 setDefaultSort : function(field, dir){
9022 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9027 * If remote sorting is used, the sort is performed on the server, and the cache is
9028 * reloaded. If local sorting is used, the cache is sorted internally.
9029 * @param {String} fieldName The name of the field to sort by.
9030 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9032 sort : function(fieldName, dir){
9033 var f = this.fields.get(fieldName);
9035 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9037 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9038 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9043 this.sortToggle[f.name] = dir;
9044 this.sortInfo = {field: f.name, direction: dir};
9045 if(!this.remoteSort){
9047 this.fireEvent("datachanged", this);
9049 this.load(this.lastOptions);
9054 * Calls the specified function for each of the Records in the cache.
9055 * @param {Function} fn The function to call. The Record is passed as the first parameter.
9056 * Returning <em>false</em> aborts and exits the iteration.
9057 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9059 each : function(fn, scope){
9060 this.data.each(fn, scope);
9064 * Gets all records modified since the last commit. Modified records are persisted across load operations
9065 * (e.g., during paging).
9066 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9068 getModifiedRecords : function(){
9069 return this.modified;
9073 createFilterFn : function(property, value, anyMatch){
9074 if(!value.exec){ // not a regex
9075 value = String(value);
9076 if(value.length == 0){
9079 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9082 return value.test(r.data[property]);
9087 * Sums the value of <i>property</i> for each record between start and end and returns the result.
9088 * @param {String} property A field on your records
9089 * @param {Number} start The record index to start at (defaults to 0)
9090 * @param {Number} end The last record index to include (defaults to length - 1)
9091 * @return {Number} The sum
9093 sum : function(property, start, end){
9094 var rs = this.data.items, v = 0;
9096 end = (end || end === 0) ? end : rs.length-1;
9098 for(var i = start; i <= end; i++){
9099 v += (rs[i].data[property] || 0);
9105 * Filter the records by a specified property.
9106 * @param {String} field A field on your records
9107 * @param {String/RegExp} value Either a string that the field
9108 * should start with or a RegExp to test against the field
9109 * @param {Boolean} anyMatch True to match any part not just the beginning
9111 filter : function(property, value, anyMatch){
9112 var fn = this.createFilterFn(property, value, anyMatch);
9113 return fn ? this.filterBy(fn) : this.clearFilter();
9117 * Filter by a function. The specified function will be called with each
9118 * record in this data source. If the function returns true the record is included,
9119 * otherwise it is filtered.
9120 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9121 * @param {Object} scope (optional) The scope of the function (defaults to this)
9123 filterBy : function(fn, scope){
9124 this.snapshot = this.snapshot || this.data;
9125 this.data = this.queryBy(fn, scope||this);
9126 this.fireEvent("datachanged", this);
9130 * Query the records by a specified property.
9131 * @param {String} field A field on your records
9132 * @param {String/RegExp} value Either a string that the field
9133 * should start with or a RegExp to test against the field
9134 * @param {Boolean} anyMatch True to match any part not just the beginning
9135 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9137 query : function(property, value, anyMatch){
9138 var fn = this.createFilterFn(property, value, anyMatch);
9139 return fn ? this.queryBy(fn) : this.data.clone();
9143 * Query by a function. The specified function will be called with each
9144 * record in this data source. If the function returns true the record is included
9146 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9147 * @param {Object} scope (optional) The scope of the function (defaults to this)
9148 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9150 queryBy : function(fn, scope){
9151 var data = this.snapshot || this.data;
9152 return data.filterBy(fn, scope||this);
9156 * Collects unique values for a particular dataIndex from this store.
9157 * @param {String} dataIndex The property to collect
9158 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9159 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9160 * @return {Array} An array of the unique values
9162 collect : function(dataIndex, allowNull, bypassFilter){
9163 var d = (bypassFilter === true && this.snapshot) ?
9164 this.snapshot.items : this.data.items;
9165 var v, sv, r = [], l = {};
9166 for(var i = 0, len = d.length; i < len; i++){
9167 v = d[i].data[dataIndex];
9169 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9178 * Revert to a view of the Record cache with no filtering applied.
9179 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9181 clearFilter : function(suppressEvent){
9182 if(this.snapshot && this.snapshot != this.data){
9183 this.data = this.snapshot;
9184 delete this.snapshot;
9185 if(suppressEvent !== true){
9186 this.fireEvent("datachanged", this);
9192 afterEdit : function(record){
9193 if(this.modified.indexOf(record) == -1){
9194 this.modified.push(record);
9196 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9200 afterReject : function(record){
9201 this.modified.remove(record);
9202 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9206 afterCommit : function(record){
9207 this.modified.remove(record);
9208 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9212 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9213 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9215 commitChanges : function(){
9216 var m = this.modified.slice(0);
9218 for(var i = 0, len = m.length; i < len; i++){
9224 * Cancel outstanding changes on all changed records.
9226 rejectChanges : function(){
9227 var m = this.modified.slice(0);
9229 for(var i = 0, len = m.length; i < len; i++){
9234 onMetaChange : function(meta, rtype, o){
9235 this.recordType = rtype;
9236 this.fields = rtype.prototype.fields;
9237 delete this.snapshot;
9238 this.sortInfo = meta.sortInfo || this.sortInfo;
9240 this.fireEvent('metachange', this, this.reader.meta);
9243 moveIndex : function(data, type)
9245 var index = this.indexOf(data);
9247 var newIndex = index + type;
9251 this.insert(newIndex, data);
9256 * Ext JS Library 1.1.1
9257 * Copyright(c) 2006-2007, Ext JS, LLC.
9259 * Originally Released Under LGPL - original licence link has changed is not relivant.
9262 * <script type="text/javascript">
9266 * @class Roo.data.SimpleStore
9267 * @extends Roo.data.Store
9268 * Small helper class to make creating Stores from Array data easier.
9269 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9270 * @cfg {Array} fields An array of field definition objects, or field name strings.
9271 * @cfg {Array} data The multi-dimensional array of data
9273 * @param {Object} config
9275 Roo.data.SimpleStore = function(config){
9276 Roo.data.SimpleStore.superclass.constructor.call(this, {
9278 reader: new Roo.data.ArrayReader({
9281 Roo.data.Record.create(config.fields)
9283 proxy : new Roo.data.MemoryProxy(config.data)
9287 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9289 * Ext JS Library 1.1.1
9290 * Copyright(c) 2006-2007, Ext JS, LLC.
9292 * Originally Released Under LGPL - original licence link has changed is not relivant.
9295 * <script type="text/javascript">
9300 * @extends Roo.data.Store
9301 * @class Roo.data.JsonStore
9302 * Small helper class to make creating Stores for JSON data easier. <br/>
9304 var store = new Roo.data.JsonStore({
9305 url: 'get-images.php',
9307 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9310 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9311 * JsonReader and HttpProxy (unless inline data is provided).</b>
9312 * @cfg {Array} fields An array of field definition objects, or field name strings.
9314 * @param {Object} config
9316 Roo.data.JsonStore = function(c){
9317 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9318 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9319 reader: new Roo.data.JsonReader(c, c.fields)
9322 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9324 * Ext JS Library 1.1.1
9325 * Copyright(c) 2006-2007, Ext JS, LLC.
9327 * Originally Released Under LGPL - original licence link has changed is not relivant.
9330 * <script type="text/javascript">
9334 Roo.data.Field = function(config){
9335 if(typeof config == "string"){
9336 config = {name: config};
9338 Roo.apply(this, config);
9344 var st = Roo.data.SortTypes;
9345 // named sortTypes are supported, here we look them up
9346 if(typeof this.sortType == "string"){
9347 this.sortType = st[this.sortType];
9350 // set default sortType for strings and dates
9354 this.sortType = st.asUCString;
9357 this.sortType = st.asDate;
9360 this.sortType = st.none;
9365 var stripRe = /[\$,%]/g;
9367 // prebuilt conversion function for this field, instead of
9368 // switching every time we're reading a value
9370 var cv, dateFormat = this.dateFormat;
9375 cv = function(v){ return v; };
9378 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9382 return v !== undefined && v !== null && v !== '' ?
9383 parseInt(String(v).replace(stripRe, ""), 10) : '';
9388 return v !== undefined && v !== null && v !== '' ?
9389 parseFloat(String(v).replace(stripRe, ""), 10) : '';
9394 cv = function(v){ return v === true || v === "true" || v == 1; };
9401 if(v instanceof Date){
9405 if(dateFormat == "timestamp"){
9406 return new Date(v*1000);
9408 return Date.parseDate(v, dateFormat);
9410 var parsed = Date.parse(v);
9411 return parsed ? new Date(parsed) : null;
9420 Roo.data.Field.prototype = {
9428 * Ext JS Library 1.1.1
9429 * Copyright(c) 2006-2007, Ext JS, LLC.
9431 * Originally Released Under LGPL - original licence link has changed is not relivant.
9434 * <script type="text/javascript">
9437 // Base class for reading structured data from a data source. This class is intended to be
9438 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9441 * @class Roo.data.DataReader
9442 * Base class for reading structured data from a data source. This class is intended to be
9443 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9446 Roo.data.DataReader = function(meta, recordType){
9450 this.recordType = recordType instanceof Array ?
9451 Roo.data.Record.create(recordType) : recordType;
9454 Roo.data.DataReader.prototype = {
9456 * Create an empty record
9457 * @param {Object} data (optional) - overlay some values
9458 * @return {Roo.data.Record} record created.
9460 newRow : function(d) {
9462 this.recordType.prototype.fields.each(function(c) {
9464 case 'int' : da[c.name] = 0; break;
9465 case 'date' : da[c.name] = new Date(); break;
9466 case 'float' : da[c.name] = 0.0; break;
9467 case 'boolean' : da[c.name] = false; break;
9468 default : da[c.name] = ""; break;
9472 return new this.recordType(Roo.apply(da, d));
9477 * Ext JS Library 1.1.1
9478 * Copyright(c) 2006-2007, Ext JS, LLC.
9480 * Originally Released Under LGPL - original licence link has changed is not relivant.
9483 * <script type="text/javascript">
9487 * @class Roo.data.DataProxy
9488 * @extends Roo.data.Observable
9489 * This class is an abstract base class for implementations which provide retrieval of
9490 * unformatted data objects.<br>
9492 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9493 * (of the appropriate type which knows how to parse the data object) to provide a block of
9494 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9496 * Custom implementations must implement the load method as described in
9497 * {@link Roo.data.HttpProxy#load}.
9499 Roo.data.DataProxy = function(){
9503 * Fires before a network request is made to retrieve a data object.
9504 * @param {Object} This DataProxy object.
9505 * @param {Object} params The params parameter to the load function.
9510 * Fires before the load method's callback is called.
9511 * @param {Object} This DataProxy object.
9512 * @param {Object} o The data object.
9513 * @param {Object} arg The callback argument object passed to the load function.
9517 * @event loadexception
9518 * Fires if an Exception occurs during data retrieval.
9519 * @param {Object} This DataProxy object.
9520 * @param {Object} o The data object.
9521 * @param {Object} arg The callback argument object passed to the load function.
9522 * @param {Object} e The Exception.
9524 loadexception : true
9526 Roo.data.DataProxy.superclass.constructor.call(this);
9529 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9532 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9536 * Ext JS Library 1.1.1
9537 * Copyright(c) 2006-2007, Ext JS, LLC.
9539 * Originally Released Under LGPL - original licence link has changed is not relivant.
9542 * <script type="text/javascript">
9545 * @class Roo.data.MemoryProxy
9546 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9547 * to the Reader when its load method is called.
9549 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9551 Roo.data.MemoryProxy = function(data){
9555 Roo.data.MemoryProxy.superclass.constructor.call(this);
9559 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9561 * Load data from the requested source (in this case an in-memory
9562 * data object passed to the constructor), read the data object into
9563 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9564 * process that block using the passed callback.
9565 * @param {Object} params This parameter is not used by the MemoryProxy class.
9566 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9567 * object into a block of Roo.data.Records.
9568 * @param {Function} callback The function into which to pass the block of Roo.data.records.
9569 * The function must be passed <ul>
9570 * <li>The Record block object</li>
9571 * <li>The "arg" argument from the load function</li>
9572 * <li>A boolean success indicator</li>
9574 * @param {Object} scope The scope in which to call the callback
9575 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9577 load : function(params, reader, callback, scope, arg){
9578 params = params || {};
9581 result = reader.readRecords(this.data);
9583 this.fireEvent("loadexception", this, arg, null, e);
9584 callback.call(scope, null, arg, false);
9587 callback.call(scope, result, arg, true);
9591 update : function(params, records){
9596 * Ext JS Library 1.1.1
9597 * Copyright(c) 2006-2007, Ext JS, LLC.
9599 * Originally Released Under LGPL - original licence link has changed is not relivant.
9602 * <script type="text/javascript">
9605 * @class Roo.data.HttpProxy
9606 * @extends Roo.data.DataProxy
9607 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9608 * configured to reference a certain URL.<br><br>
9610 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9611 * from which the running page was served.<br><br>
9613 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9615 * Be aware that to enable the browser to parse an XML document, the server must set
9616 * the Content-Type header in the HTTP response to "text/xml".
9618 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9619 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
9620 * will be used to make the request.
9622 Roo.data.HttpProxy = function(conn){
9623 Roo.data.HttpProxy.superclass.constructor.call(this);
9624 // is conn a conn config or a real conn?
9626 this.useAjax = !conn || !conn.events;
9630 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9631 // thse are take from connection...
9634 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9637 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9638 * extra parameters to each request made by this object. (defaults to undefined)
9641 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9642 * to each request made by this object. (defaults to undefined)
9645 * @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)
9648 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9651 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9657 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9661 * Return the {@link Roo.data.Connection} object being used by this Proxy.
9662 * @return {Connection} The Connection object. This object may be used to subscribe to events on
9663 * a finer-grained basis than the DataProxy events.
9665 getConnection : function(){
9666 return this.useAjax ? Roo.Ajax : this.conn;
9670 * Load data from the configured {@link Roo.data.Connection}, read the data object into
9671 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9672 * process that block using the passed callback.
9673 * @param {Object} params An object containing properties which are to be used as HTTP parameters
9674 * for the request to the remote server.
9675 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9676 * object into a block of Roo.data.Records.
9677 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9678 * The function must be passed <ul>
9679 * <li>The Record block object</li>
9680 * <li>The "arg" argument from the load function</li>
9681 * <li>A boolean success indicator</li>
9683 * @param {Object} scope The scope in which to call the callback
9684 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9686 load : function(params, reader, callback, scope, arg){
9687 if(this.fireEvent("beforeload", this, params) !== false){
9689 params : params || {},
9691 callback : callback,
9696 callback : this.loadResponse,
9700 Roo.applyIf(o, this.conn);
9701 if(this.activeRequest){
9702 Roo.Ajax.abort(this.activeRequest);
9704 this.activeRequest = Roo.Ajax.request(o);
9706 this.conn.request(o);
9709 callback.call(scope||this, null, arg, false);
9714 loadResponse : function(o, success, response){
9715 delete this.activeRequest;
9717 this.fireEvent("loadexception", this, o, response);
9718 o.request.callback.call(o.request.scope, null, o.request.arg, false);
9723 result = o.reader.read(response);
9725 this.fireEvent("loadexception", this, o, response, e);
9726 o.request.callback.call(o.request.scope, null, o.request.arg, false);
9730 this.fireEvent("load", this, o, o.request.arg);
9731 o.request.callback.call(o.request.scope, result, o.request.arg, true);
9735 update : function(dataSet){
9740 updateResponse : function(dataSet){
9745 * Ext JS Library 1.1.1
9746 * Copyright(c) 2006-2007, Ext JS, LLC.
9748 * Originally Released Under LGPL - original licence link has changed is not relivant.
9751 * <script type="text/javascript">
9755 * @class Roo.data.ScriptTagProxy
9756 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9757 * other than the originating domain of the running page.<br><br>
9759 * <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
9760 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9762 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9763 * source code that is used as the source inside a <script> tag.<br><br>
9765 * In order for the browser to process the returned data, the server must wrap the data object
9766 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9767 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9768 * depending on whether the callback name was passed:
9771 boolean scriptTag = false;
9772 String cb = request.getParameter("callback");
9775 response.setContentType("text/javascript");
9777 response.setContentType("application/x-json");
9779 Writer out = response.getWriter();
9781 out.write(cb + "(");
9783 out.print(dataBlock.toJsonString());
9790 * @param {Object} config A configuration object.
9792 Roo.data.ScriptTagProxy = function(config){
9793 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9794 Roo.apply(this, config);
9795 this.head = document.getElementsByTagName("head")[0];
9798 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9800 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9802 * @cfg {String} url The URL from which to request the data object.
9805 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9809 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9810 * the server the name of the callback function set up by the load call to process the returned data object.
9811 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9812 * javascript output which calls this named function passing the data object as its only parameter.
9814 callbackParam : "callback",
9816 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9817 * name to the request.
9822 * Load data from the configured URL, read the data object into
9823 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9824 * process that block using the passed callback.
9825 * @param {Object} params An object containing properties which are to be used as HTTP parameters
9826 * for the request to the remote server.
9827 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9828 * object into a block of Roo.data.Records.
9829 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9830 * The function must be passed <ul>
9831 * <li>The Record block object</li>
9832 * <li>The "arg" argument from the load function</li>
9833 * <li>A boolean success indicator</li>
9835 * @param {Object} scope The scope in which to call the callback
9836 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9838 load : function(params, reader, callback, scope, arg){
9839 if(this.fireEvent("beforeload", this, params) !== false){
9841 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9844 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9846 url += "&_dc=" + (new Date().getTime());
9848 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9851 cb : "stcCallback"+transId,
9852 scriptId : "stcScript"+transId,
9856 callback : callback,
9862 window[trans.cb] = function(o){
9863 conn.handleResponse(o, trans);
9866 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9868 if(this.autoAbort !== false){
9872 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9874 var script = document.createElement("script");
9875 script.setAttribute("src", url);
9876 script.setAttribute("type", "text/javascript");
9877 script.setAttribute("id", trans.scriptId);
9878 this.head.appendChild(script);
9882 callback.call(scope||this, null, arg, false);
9887 isLoading : function(){
9888 return this.trans ? true : false;
9892 * Abort the current server request.
9895 if(this.isLoading()){
9896 this.destroyTrans(this.trans);
9901 destroyTrans : function(trans, isLoaded){
9902 this.head.removeChild(document.getElementById(trans.scriptId));
9903 clearTimeout(trans.timeoutId);
9905 window[trans.cb] = undefined;
9907 delete window[trans.cb];
9910 // if hasn't been loaded, wait for load to remove it to prevent script error
9911 window[trans.cb] = function(){
9912 window[trans.cb] = undefined;
9914 delete window[trans.cb];
9921 handleResponse : function(o, trans){
9923 this.destroyTrans(trans, true);
9926 result = trans.reader.readRecords(o);
9928 this.fireEvent("loadexception", this, o, trans.arg, e);
9929 trans.callback.call(trans.scope||window, null, trans.arg, false);
9932 this.fireEvent("load", this, o, trans.arg);
9933 trans.callback.call(trans.scope||window, result, trans.arg, true);
9937 handleFailure : function(trans){
9939 this.destroyTrans(trans, false);
9940 this.fireEvent("loadexception", this, null, trans.arg);
9941 trans.callback.call(trans.scope||window, null, trans.arg, false);
9945 * Ext JS Library 1.1.1
9946 * Copyright(c) 2006-2007, Ext JS, LLC.
9948 * Originally Released Under LGPL - original licence link has changed is not relivant.
9951 * <script type="text/javascript">
9955 * @class Roo.data.JsonReader
9956 * @extends Roo.data.DataReader
9957 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9958 * based on mappings in a provided Roo.data.Record constructor.
9960 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9961 * in the reply previously.
9966 var RecordDef = Roo.data.Record.create([
9967 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
9968 {name: 'occupation'} // This field will use "occupation" as the mapping.
9970 var myReader = new Roo.data.JsonReader({
9971 totalProperty: "results", // The property which contains the total dataset size (optional)
9972 root: "rows", // The property which contains an Array of row objects
9973 id: "id" // The property within each row object that provides an ID for the record (optional)
9977 * This would consume a JSON file like this:
9979 { 'results': 2, 'rows': [
9980 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9981 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9984 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9985 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9986 * paged from the remote server.
9987 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9988 * @cfg {String} root name of the property which contains the Array of row objects.
9989 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9991 * Create a new JsonReader
9992 * @param {Object} meta Metadata configuration options
9993 * @param {Object} recordType Either an Array of field definition objects,
9994 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9996 Roo.data.JsonReader = function(meta, recordType){
9999 // set some defaults:
10000 Roo.applyIf(meta, {
10001 totalProperty: 'total',
10002 successProperty : 'success',
10007 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10009 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10012 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
10013 * Used by Store query builder to append _requestMeta to params.
10016 metaFromRemote : false,
10018 * This method is only used by a DataProxy which has retrieved data from a remote server.
10019 * @param {Object} response The XHR object which contains the JSON data in its responseText.
10020 * @return {Object} data A data block which is used by an Roo.data.Store object as
10021 * a cache of Roo.data.Records.
10023 read : function(response){
10024 var json = response.responseText;
10026 var o = /* eval:var:o */ eval("("+json+")");
10028 throw {message: "JsonReader.read: Json object not found"};
10034 this.metaFromRemote = true;
10035 this.meta = o.metaData;
10036 this.recordType = Roo.data.Record.create(o.metaData.fields);
10037 this.onMetaChange(this.meta, this.recordType, o);
10039 return this.readRecords(o);
10042 // private function a store will implement
10043 onMetaChange : function(meta, recordType, o){
10050 simpleAccess: function(obj, subsc) {
10057 getJsonAccessor: function(){
10059 return function(expr) {
10061 return(re.test(expr))
10062 ? new Function("obj", "return obj." + expr)
10067 return Roo.emptyFn;
10072 * Create a data block containing Roo.data.Records from an XML document.
10073 * @param {Object} o An object which contains an Array of row objects in the property specified
10074 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10075 * which contains the total size of the dataset.
10076 * @return {Object} data A data block which is used by an Roo.data.Store object as
10077 * a cache of Roo.data.Records.
10079 readRecords : function(o){
10081 * After any data loads, the raw JSON data is available for further custom processing.
10085 var s = this.meta, Record = this.recordType,
10086 f = Record.prototype.fields, fi = f.items, fl = f.length;
10088 // Generate extraction functions for the totalProperty, the root, the id, and for each field
10090 if(s.totalProperty) {
10091 this.getTotal = this.getJsonAccessor(s.totalProperty);
10093 if(s.successProperty) {
10094 this.getSuccess = this.getJsonAccessor(s.successProperty);
10096 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10098 var g = this.getJsonAccessor(s.id);
10099 this.getId = function(rec) {
10101 return (r === undefined || r === "") ? null : r;
10104 this.getId = function(){return null;};
10107 for(var jj = 0; jj < fl; jj++){
10109 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10110 this.ef[jj] = this.getJsonAccessor(map);
10114 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10115 if(s.totalProperty){
10116 var vt = parseInt(this.getTotal(o), 10);
10121 if(s.successProperty){
10122 var vs = this.getSuccess(o);
10123 if(vs === false || vs === 'false'){
10128 for(var i = 0; i < c; i++){
10131 var id = this.getId(n);
10132 for(var j = 0; j < fl; j++){
10134 var v = this.ef[j](n);
10136 Roo.log('missing convert for ' + f.name);
10140 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10142 var record = new Record(values, id);
10144 records[i] = record;
10150 totalRecords : totalRecords
10155 * Ext JS Library 1.1.1
10156 * Copyright(c) 2006-2007, Ext JS, LLC.
10158 * Originally Released Under LGPL - original licence link has changed is not relivant.
10161 * <script type="text/javascript">
10165 * @class Roo.data.ArrayReader
10166 * @extends Roo.data.DataReader
10167 * Data reader class to create an Array of Roo.data.Record objects from an Array.
10168 * Each element of that Array represents a row of data fields. The
10169 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10170 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10174 var RecordDef = Roo.data.Record.create([
10175 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
10176 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
10178 var myReader = new Roo.data.ArrayReader({
10179 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
10183 * This would consume an Array like this:
10185 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10187 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10189 * Create a new JsonReader
10190 * @param {Object} meta Metadata configuration options.
10191 * @param {Object} recordType Either an Array of field definition objects
10192 * as specified to {@link Roo.data.Record#create},
10193 * or an {@link Roo.data.Record} object
10194 * created using {@link Roo.data.Record#create}.
10196 Roo.data.ArrayReader = function(meta, recordType){
10197 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10200 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10202 * Create a data block containing Roo.data.Records from an XML document.
10203 * @param {Object} o An Array of row objects which represents the dataset.
10204 * @return {Object} data A data block which is used by an Roo.data.Store object as
10205 * a cache of Roo.data.Records.
10207 readRecords : function(o){
10208 var sid = this.meta ? this.meta.id : null;
10209 var recordType = this.recordType, fields = recordType.prototype.fields;
10212 for(var i = 0; i < root.length; i++){
10215 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10216 for(var j = 0, jlen = fields.length; j < jlen; j++){
10217 var f = fields.items[j];
10218 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10219 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10221 values[f.name] = v;
10223 var record = new recordType(values, id);
10225 records[records.length] = record;
10229 totalRecords : records.length
10238 * @class Roo.bootstrap.ComboBox
10239 * @extends Roo.bootstrap.TriggerField
10240 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10241 * @cfg {Boolean} append (true|false) default false
10242 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10243 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10244 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10245 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10246 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10248 * Create a new ComboBox.
10249 * @param {Object} config Configuration options
10251 Roo.bootstrap.ComboBox = function(config){
10252 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10256 * Fires when the dropdown list is expanded
10257 * @param {Roo.bootstrap.ComboBox} combo This combo box
10262 * Fires when the dropdown list is collapsed
10263 * @param {Roo.bootstrap.ComboBox} combo This combo box
10267 * @event beforeselect
10268 * Fires before a list item is selected. Return false to cancel the selection.
10269 * @param {Roo.bootstrap.ComboBox} combo This combo box
10270 * @param {Roo.data.Record} record The data record returned from the underlying store
10271 * @param {Number} index The index of the selected item in the dropdown list
10273 'beforeselect' : true,
10276 * Fires when a list item is selected
10277 * @param {Roo.bootstrap.ComboBox} combo This combo box
10278 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10279 * @param {Number} index The index of the selected item in the dropdown list
10283 * @event beforequery
10284 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10285 * The event object passed has these properties:
10286 * @param {Roo.bootstrap.ComboBox} combo This combo box
10287 * @param {String} query The query
10288 * @param {Boolean} forceAll true to force "all" query
10289 * @param {Boolean} cancel true to cancel the query
10290 * @param {Object} e The query event object
10292 'beforequery': true,
10295 * Fires when the 'add' icon is pressed (add a listener to enable add button)
10296 * @param {Roo.bootstrap.ComboBox} combo This combo box
10301 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10302 * @param {Roo.bootstrap.ComboBox} combo This combo box
10303 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10308 * Fires when the remove value from the combobox array
10309 * @param {Roo.bootstrap.ComboBox} combo This combo box
10316 this.tickItems = [];
10318 this.selectedIndex = -1;
10319 if(this.mode == 'local'){
10320 if(config.queryDelay === undefined){
10321 this.queryDelay = 10;
10323 if(config.minChars === undefined){
10329 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10332 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10333 * rendering into an Roo.Editor, defaults to false)
10336 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10337 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10340 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10343 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10344 * the dropdown list (defaults to undefined, with no header element)
10348 * @cfg {String/Roo.Template} tpl The template to use to render the output
10352 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10354 listWidth: undefined,
10356 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10357 * mode = 'remote' or 'text' if mode = 'local')
10359 displayField: undefined,
10361 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10362 * mode = 'remote' or 'value' if mode = 'local').
10363 * Note: use of a valueField requires the user make a selection
10364 * in order for a value to be mapped.
10366 valueField: undefined,
10370 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10371 * field's data value (defaults to the underlying DOM element's name)
10373 hiddenName: undefined,
10375 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10379 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10381 selectedClass: 'active',
10384 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10388 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10389 * anchor positions (defaults to 'tl-bl')
10391 listAlign: 'tl-bl?',
10393 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10397 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
10398 * query specified by the allQuery config option (defaults to 'query')
10400 triggerAction: 'query',
10402 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10403 * (defaults to 4, does not apply if editable = false)
10407 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10408 * delay (typeAheadDelay) if it matches a known value (defaults to false)
10412 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10413 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10417 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10418 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
10422 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
10423 * when editable = true (defaults to false)
10425 selectOnFocus:false,
10427 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10429 queryParam: 'query',
10431 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
10432 * when mode = 'remote' (defaults to 'Loading...')
10434 loadingText: 'Loading...',
10436 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10440 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10444 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10445 * traditional select (defaults to true)
10449 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10453 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10457 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10458 * listWidth has a higher value)
10462 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10463 * allow the user to set arbitrary text into the field (defaults to false)
10465 forceSelection:false,
10467 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10468 * if typeAhead = true (defaults to 250)
10470 typeAheadDelay : 250,
10472 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10473 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10475 valueNotFoundText : undefined,
10477 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10479 blockFocus : false,
10482 * @cfg {Boolean} disableClear Disable showing of clear button.
10484 disableClear : false,
10486 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
10488 alwaysQuery : false,
10491 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
10505 btnPosition : 'right',
10506 triggerList : true,
10507 showToggleBtn : true,
10508 // element that contains real text value.. (when hidden is used..)
10510 getAutoCreate : function()
10517 if(!this.tickable){
10518 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10523 * ComboBox with tickable selections
10526 var align = this.labelAlign || this.parentLabelAlign();
10529 cls : 'form-group roo-combobox-tickable' //input-group
10535 cls : 'tickable-buttons',
10540 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10547 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10554 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10561 Roo.each(buttons.cn, function(c){
10563 c.cls += ' btn-' + _this.size;
10566 if (_this.disabled) {
10577 cls: 'form-hidden-field'
10581 cls: 'select2-choices',
10585 cls: 'select2-search-field',
10597 cls: 'select2-container input-group select2-container-multi',
10602 // cls: 'typeahead typeahead-long dropdown-menu',
10603 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
10608 if (align ==='left' && this.fieldLabel.length) {
10610 Roo.log("left and has label");
10616 cls : 'control-label col-sm-' + this.labelWidth,
10617 html : this.fieldLabel
10621 cls : "col-sm-" + (12 - this.labelWidth),
10628 } else if ( this.fieldLabel.length) {
10634 //cls : 'input-group-addon',
10635 html : this.fieldLabel
10645 Roo.log(" no label && no align");
10652 ['xs','sm','md','lg'].map(function(size){
10653 if (settings[size]) {
10654 cfg.cls += ' col-' + size + '-' + settings[size];
10663 initEvents: function()
10667 throw "can not find store for combo";
10669 this.store = Roo.factory(this.store, Roo.data);
10672 this.initTickableEvents();
10676 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10678 if(this.hiddenName){
10680 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10682 this.hiddenField.dom.value =
10683 this.hiddenValue !== undefined ? this.hiddenValue :
10684 this.value !== undefined ? this.value : '';
10686 // prevent input submission
10687 this.el.dom.removeAttribute('name');
10688 this.hiddenField.dom.setAttribute('name', this.hiddenName);
10693 // this.el.dom.setAttribute('autocomplete', 'off');
10696 var cls = 'x-combo-list';
10698 //this.list = new Roo.Layer({
10699 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10705 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10706 _this.list.setWidth(lw);
10709 this.list.on('mouseover', this.onViewOver, this);
10710 this.list.on('mousemove', this.onViewMove, this);
10712 this.list.on('scroll', this.onViewScroll, this);
10715 this.list.swallowEvent('mousewheel');
10716 this.assetHeight = 0;
10719 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10720 this.assetHeight += this.header.getHeight();
10723 this.innerList = this.list.createChild({cls:cls+'-inner'});
10724 this.innerList.on('mouseover', this.onViewOver, this);
10725 this.innerList.on('mousemove', this.onViewMove, this);
10726 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10728 if(this.allowBlank && !this.pageSize && !this.disableClear){
10729 this.footer = this.list.createChild({cls:cls+'-ft'});
10730 this.pageTb = new Roo.Toolbar(this.footer);
10734 this.footer = this.list.createChild({cls:cls+'-ft'});
10735 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10736 {pageSize: this.pageSize});
10740 if (this.pageTb && this.allowBlank && !this.disableClear) {
10742 this.pageTb.add(new Roo.Toolbar.Fill(), {
10743 cls: 'x-btn-icon x-btn-clear',
10745 handler: function()
10748 _this.clearValue();
10749 _this.onSelect(false, -1);
10754 this.assetHeight += this.footer.getHeight();
10759 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10762 this.view = new Roo.View(this.list, this.tpl, {
10763 singleSelect:true, store: this.store, selectedClass: this.selectedClass
10765 //this.view.wrapEl.setDisplayed(false);
10766 this.view.on('click', this.onViewClick, this);
10770 this.store.on('beforeload', this.onBeforeLoad, this);
10771 this.store.on('load', this.onLoad, this);
10772 this.store.on('loadexception', this.onLoadException, this);
10774 if(this.resizable){
10775 this.resizer = new Roo.Resizable(this.list, {
10776 pinned:true, handles:'se'
10778 this.resizer.on('resize', function(r, w, h){
10779 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10780 this.listWidth = w;
10781 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10782 this.restrictHeight();
10784 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10787 if(!this.editable){
10788 this.editable = true;
10789 this.setEditable(false);
10794 if (typeof(this.events.add.listeners) != 'undefined') {
10796 this.addicon = this.wrap.createChild(
10797 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
10799 this.addicon.on('click', function(e) {
10800 this.fireEvent('add', this);
10803 if (typeof(this.events.edit.listeners) != 'undefined') {
10805 this.editicon = this.wrap.createChild(
10806 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
10807 if (this.addicon) {
10808 this.editicon.setStyle('margin-left', '40px');
10810 this.editicon.on('click', function(e) {
10812 // we fire even if inothing is selected..
10813 this.fireEvent('edit', this, this.lastData );
10819 this.keyNav = new Roo.KeyNav(this.inputEl(), {
10820 "up" : function(e){
10821 this.inKeyMode = true;
10825 "down" : function(e){
10826 if(!this.isExpanded()){
10827 this.onTriggerClick();
10829 this.inKeyMode = true;
10834 "enter" : function(e){
10835 // this.onViewClick();
10839 if(this.fireEvent("specialkey", this, e)){
10840 this.onViewClick(false);
10846 "esc" : function(e){
10850 "tab" : function(e){
10853 if(this.fireEvent("specialkey", this, e)){
10854 this.onViewClick(false);
10862 doRelay : function(foo, bar, hname){
10863 if(hname == 'down' || this.scope.isExpanded()){
10864 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10873 this.queryDelay = Math.max(this.queryDelay || 10,
10874 this.mode == 'local' ? 10 : 250);
10877 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10879 if(this.typeAhead){
10880 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10882 if(this.editable !== false){
10883 this.inputEl().on("keyup", this.onKeyUp, this);
10885 if(this.forceSelection){
10886 this.inputEl().on('blur', this.doForce, this);
10890 this.choices = this.el.select('ul.select2-choices', true).first();
10891 this.searchField = this.el.select('ul li.select2-search-field', true).first();
10895 initTickableEvents: function()
10899 if(this.hiddenName){
10901 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10903 this.hiddenField.dom.value =
10904 this.hiddenValue !== undefined ? this.hiddenValue :
10905 this.value !== undefined ? this.value : '';
10907 // prevent input submission
10908 this.el.dom.removeAttribute('name');
10909 this.hiddenField.dom.setAttribute('name', this.hiddenName);
10914 // this.list = this.el.select('ul.dropdown-menu',true).first();
10916 this.choices = this.el.select('ul.select2-choices', true).first();
10917 this.searchField = this.el.select('ul li.select2-search-field', true).first();
10918 if(this.triggerList){
10919 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10922 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10923 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10925 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10926 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10928 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10929 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10931 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10932 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10933 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10936 this.cancelBtn.hide();
10941 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10942 _this.list.setWidth(lw);
10945 this.list.on('mouseover', this.onViewOver, this);
10946 this.list.on('mousemove', this.onViewMove, this);
10948 this.list.on('scroll', this.onViewScroll, this);
10951 this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
10954 this.view = new Roo.View(this.list, this.tpl, {
10955 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10958 //this.view.wrapEl.setDisplayed(false);
10959 this.view.on('click', this.onViewClick, this);
10963 this.store.on('beforeload', this.onBeforeLoad, this);
10964 this.store.on('load', this.onLoad, this);
10965 this.store.on('loadexception', this.onLoadException, this);
10967 // this.keyNav = new Roo.KeyNav(this.inputEl(), {
10968 // "up" : function(e){
10969 // this.inKeyMode = true;
10970 // this.selectPrev();
10973 // "down" : function(e){
10974 // if(!this.isExpanded()){
10975 // this.onTriggerClick();
10977 // this.inKeyMode = true;
10978 // this.selectNext();
10982 // "enter" : function(e){
10983 //// this.onViewClick();
10985 // this.collapse();
10987 // if(this.fireEvent("specialkey", this, e)){
10988 // this.onViewClick(false);
10994 // "esc" : function(e){
10995 // this.collapse();
10998 // "tab" : function(e){
10999 // this.collapse();
11001 // if(this.fireEvent("specialkey", this, e)){
11002 // this.onViewClick(false);
11010 // doRelay : function(foo, bar, hname){
11011 // if(hname == 'down' || this.scope.isExpanded()){
11012 // return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11017 // forceKeyDown: true
11021 this.queryDelay = Math.max(this.queryDelay || 10,
11022 this.mode == 'local' ? 10 : 250);
11025 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11027 if(this.typeAhead){
11028 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11032 onDestroy : function(){
11034 this.view.setStore(null);
11035 this.view.el.removeAllListeners();
11036 this.view.el.remove();
11037 this.view.purgeListeners();
11040 this.list.dom.innerHTML = '';
11044 this.store.un('beforeload', this.onBeforeLoad, this);
11045 this.store.un('load', this.onLoad, this);
11046 this.store.un('loadexception', this.onLoadException, this);
11048 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11052 fireKey : function(e){
11053 if(e.isNavKeyPress() && !this.list.isVisible()){
11054 this.fireEvent("specialkey", this, e);
11059 onResize: function(w, h){
11060 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11062 // if(typeof w != 'number'){
11063 // // we do not handle it!?!?
11066 // var tw = this.trigger.getWidth();
11067 // // tw += this.addicon ? this.addicon.getWidth() : 0;
11068 // // tw += this.editicon ? this.editicon.getWidth() : 0;
11070 // this.inputEl().setWidth( this.adjustWidth('input', x));
11072 // //this.trigger.setStyle('left', x+'px');
11074 // if(this.list && this.listWidth === undefined){
11075 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11076 // this.list.setWidth(lw);
11077 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11085 * Allow or prevent the user from directly editing the field text. If false is passed,
11086 * the user will only be able to select from the items defined in the dropdown list. This method
11087 * is the runtime equivalent of setting the 'editable' config option at config time.
11088 * @param {Boolean} value True to allow the user to directly edit the field text
11090 setEditable : function(value){
11091 if(value == this.editable){
11094 this.editable = value;
11096 this.inputEl().dom.setAttribute('readOnly', true);
11097 this.inputEl().on('mousedown', this.onTriggerClick, this);
11098 this.inputEl().addClass('x-combo-noedit');
11100 this.inputEl().dom.setAttribute('readOnly', false);
11101 this.inputEl().un('mousedown', this.onTriggerClick, this);
11102 this.inputEl().removeClass('x-combo-noedit');
11108 onBeforeLoad : function(combo,opts){
11109 if(!this.hasFocus){
11113 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11115 this.restrictHeight();
11116 this.selectedIndex = -1;
11120 onLoad : function(){
11122 this.hasQuery = false;
11124 if(!this.hasFocus){
11128 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11129 this.loading.hide();
11132 if(this.store.getCount() > 0){
11134 // this.restrictHeight();
11135 if(this.lastQuery == this.allQuery){
11136 if(this.editable && !this.tickable){
11137 this.inputEl().dom.select();
11141 !this.selectByValue(this.value, true) &&
11142 this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' ||
11143 this.store.lastOptions.add != true)
11145 this.select(0, true);
11148 if(this.autoFocus){
11151 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11152 this.taTask.delay(this.typeAheadDelay);
11156 this.onEmptyResults();
11162 onLoadException : function()
11164 this.hasQuery = false;
11166 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11167 this.loading.hide();
11171 Roo.log(this.store.reader.jsonData);
11172 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11174 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11180 onTypeAhead : function(){
11181 if(this.store.getCount() > 0){
11182 var r = this.store.getAt(0);
11183 var newValue = r.data[this.displayField];
11184 var len = newValue.length;
11185 var selStart = this.getRawValue().length;
11187 if(selStart != len){
11188 this.setRawValue(newValue);
11189 this.selectText(selStart, newValue.length);
11195 onSelect : function(record, index){
11197 if(this.fireEvent('beforeselect', this, record, index) !== false){
11199 this.setFromData(index > -1 ? record.data : false);
11202 this.fireEvent('select', this, record, index);
11207 * Returns the currently selected field value or empty string if no value is set.
11208 * @return {String} value The selected value
11210 getValue : function(){
11213 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11216 if(this.valueField){
11217 return typeof this.value != 'undefined' ? this.value : '';
11219 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11224 * Clears any text/value currently set in the field
11226 clearValue : function(){
11227 if(this.hiddenField){
11228 this.hiddenField.dom.value = '';
11231 this.setRawValue('');
11232 this.lastSelectionText = '';
11237 * Sets the specified value into the field. If the value finds a match, the corresponding record text
11238 * will be displayed in the field. If the value does not match the data value of an existing item,
11239 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11240 * Otherwise the field will be blank (although the value will still be set).
11241 * @param {String} value The value to match
11243 setValue : function(v){
11250 if(this.valueField){
11251 var r = this.findRecord(this.valueField, v);
11253 text = r.data[this.displayField];
11254 }else if(this.valueNotFoundText !== undefined){
11255 text = this.valueNotFoundText;
11258 this.lastSelectionText = text;
11259 if(this.hiddenField){
11260 this.hiddenField.dom.value = v;
11262 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11266 * @property {Object} the last set data for the element
11271 * Sets the value of the field based on a object which is related to the record format for the store.
11272 * @param {Object} value the value to set as. or false on reset?
11274 setFromData : function(o){
11277 if(typeof o.display_name !== 'string'){
11278 for(var i=0;i<o.display_name.length;i++){
11279 this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
11287 var dv = ''; // display value
11288 var vv = ''; // value value..
11290 if (this.displayField) {
11291 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11293 // this is an error condition!!!
11294 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
11297 if(this.valueField){
11298 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11301 if(this.hiddenField){
11302 this.hiddenField.dom.value = vv;
11304 this.lastSelectionText = dv;
11305 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11309 // no hidden field.. - we store the value in 'value', but still display
11310 // display field!!!!
11311 this.lastSelectionText = dv;
11312 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11318 reset : function(){
11319 // overridden so that last data is reset..
11320 this.setValue(this.originalValue);
11321 this.clearInvalid();
11322 this.lastData = false;
11324 this.view.clearSelections();
11328 findRecord : function(prop, value){
11330 if(this.store.getCount() > 0){
11331 this.store.each(function(r){
11332 if(r.data[prop] == value){
11342 getName: function()
11344 // returns hidden if it's set..
11345 if (!this.rendered) {return ''};
11346 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
11350 onViewMove : function(e, t){
11351 this.inKeyMode = false;
11355 onViewOver : function(e, t){
11356 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11359 var item = this.view.findItemFromChild(t);
11362 var index = this.view.indexOf(item);
11363 this.select(index, false);
11368 onViewClick : function(view, doFocus, el, e)
11370 var index = this.view.getSelectedIndexes()[0];
11372 var r = this.store.getAt(index);
11376 if(e.getTarget().nodeName.toLowerCase() != 'input'){
11383 Roo.each(this.tickItems, function(v,k){
11385 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11386 _this.tickItems.splice(k, 1);
11396 this.tickItems.push(r.data);
11401 this.onSelect(r, index);
11403 if(doFocus !== false && !this.blockFocus){
11404 this.inputEl().focus();
11409 restrictHeight : function(){
11410 //this.innerList.dom.style.height = '';
11411 //var inner = this.innerList.dom;
11412 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11413 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11414 //this.list.beginUpdate();
11415 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11416 this.list.alignTo(this.inputEl(), this.listAlign);
11417 this.list.alignTo(this.inputEl(), this.listAlign);
11418 //this.list.endUpdate();
11422 onEmptyResults : function(){
11427 * Returns true if the dropdown list is expanded, else false.
11429 isExpanded : function(){
11430 return this.list.isVisible();
11434 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11435 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11436 * @param {String} value The data value of the item to select
11437 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11438 * selected item if it is not currently in view (defaults to true)
11439 * @return {Boolean} True if the value matched an item in the list, else false
11441 selectByValue : function(v, scrollIntoView){
11442 if(v !== undefined && v !== null){
11443 var r = this.findRecord(this.valueField || this.displayField, v);
11445 this.select(this.store.indexOf(r), scrollIntoView);
11453 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11454 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11455 * @param {Number} index The zero-based index of the list item to select
11456 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11457 * selected item if it is not currently in view (defaults to true)
11459 select : function(index, scrollIntoView){
11460 this.selectedIndex = index;
11461 this.view.select(index);
11462 if(scrollIntoView !== false){
11463 var el = this.view.getNode(index);
11464 if(el && !this.multiple && !this.tickable){
11465 this.list.scrollChildIntoView(el, false);
11471 selectNext : function(){
11472 var ct = this.store.getCount();
11474 if(this.selectedIndex == -1){
11476 }else if(this.selectedIndex < ct-1){
11477 this.select(this.selectedIndex+1);
11483 selectPrev : function(){
11484 var ct = this.store.getCount();
11486 if(this.selectedIndex == -1){
11488 }else if(this.selectedIndex != 0){
11489 this.select(this.selectedIndex-1);
11495 onKeyUp : function(e){
11496 if(this.editable !== false && !e.isSpecialKey()){
11497 this.lastKey = e.getKey();
11498 this.dqTask.delay(this.queryDelay);
11503 validateBlur : function(){
11504 return !this.list || !this.list.isVisible();
11508 initQuery : function(){
11509 this.doQuery(this.getRawValue());
11513 doForce : function(){
11514 if(this.inputEl().dom.value.length > 0){
11515 this.inputEl().dom.value =
11516 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11522 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
11523 * query allowing the query action to be canceled if needed.
11524 * @param {String} query The SQL query to execute
11525 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11526 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
11527 * saved in the current store (defaults to false)
11529 doQuery : function(q, forceAll){
11531 if(q === undefined || q === null){
11536 forceAll: forceAll,
11540 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11545 forceAll = qe.forceAll;
11546 if(forceAll === true || (q.length >= this.minChars)){
11548 this.hasQuery = true;
11550 if(this.lastQuery != q || this.alwaysQuery){
11551 this.lastQuery = q;
11552 if(this.mode == 'local'){
11553 this.selectedIndex = -1;
11555 this.store.clearFilter();
11557 this.store.filter(this.displayField, q);
11561 this.store.baseParams[this.queryParam] = q;
11563 var options = {params : this.getParams(q)};
11566 options.add = true;
11567 options.params.start = this.page * this.pageSize;
11570 this.store.load(options);
11572 * this code will make the page width larger, at the beginning, the list not align correctly,
11573 * we should expand the list on onLoad
11574 * so command out it
11579 this.selectedIndex = -1;
11584 this.loadNext = false;
11588 getParams : function(q){
11590 //p[this.queryParam] = q;
11594 p.limit = this.pageSize;
11600 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11602 collapse : function(){
11603 if(!this.isExpanded()){
11611 this.cancelBtn.hide();
11612 this.trigger.show();
11615 Roo.get(document).un('mousedown', this.collapseIf, this);
11616 Roo.get(document).un('mousewheel', this.collapseIf, this);
11617 if (!this.editable) {
11618 Roo.get(document).un('keydown', this.listKeyPress, this);
11620 this.fireEvent('collapse', this);
11624 collapseIf : function(e){
11625 var in_combo = e.within(this.el);
11626 var in_list = e.within(this.list);
11627 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11629 if (in_combo || in_list || is_list) {
11630 //e.stopPropagation();
11635 this.onTickableFooterButtonClick(e, false, false);
11643 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11645 expand : function(){
11647 if(this.isExpanded() || !this.hasFocus){
11651 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11652 this.list.setWidth(lw);
11659 this.restrictHeight();
11663 this.tickItems = Roo.apply([], this.item);
11666 this.cancelBtn.show();
11667 this.trigger.hide();
11671 Roo.get(document).on('mousedown', this.collapseIf, this);
11672 Roo.get(document).on('mousewheel', this.collapseIf, this);
11673 if (!this.editable) {
11674 Roo.get(document).on('keydown', this.listKeyPress, this);
11677 this.fireEvent('expand', this);
11681 // Implements the default empty TriggerField.onTriggerClick function
11682 onTriggerClick : function(e)
11684 Roo.log('trigger click');
11686 if(this.disabled || !this.triggerList){
11691 this.loadNext = false;
11693 if(this.isExpanded()){
11695 if (!this.blockFocus) {
11696 this.inputEl().focus();
11700 this.hasFocus = true;
11701 if(this.triggerAction == 'all') {
11702 this.doQuery(this.allQuery, true);
11704 this.doQuery(this.getRawValue());
11706 if (!this.blockFocus) {
11707 this.inputEl().focus();
11712 onTickableTriggerClick : function(e)
11719 this.loadNext = false;
11720 this.hasFocus = true;
11722 if(this.triggerAction == 'all') {
11723 this.doQuery(this.allQuery, true);
11725 this.doQuery(this.getRawValue());
11729 onSearchFieldClick : function(e)
11731 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11736 this.loadNext = false;
11737 this.hasFocus = true;
11739 if(this.triggerAction == 'all') {
11740 this.doQuery(this.allQuery, true);
11742 this.doQuery(this.getRawValue());
11746 listKeyPress : function(e)
11748 //Roo.log('listkeypress');
11749 // scroll to first matching element based on key pres..
11750 if (e.isSpecialKey()) {
11753 var k = String.fromCharCode(e.getKey()).toUpperCase();
11756 var csel = this.view.getSelectedNodes();
11757 var cselitem = false;
11759 var ix = this.view.indexOf(csel[0]);
11760 cselitem = this.store.getAt(ix);
11761 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11767 this.store.each(function(v) {
11769 // start at existing selection.
11770 if (cselitem.id == v.id) {
11776 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11777 match = this.store.indexOf(v);
11783 if (match === false) {
11784 return true; // no more action?
11787 this.view.select(match);
11788 var sn = Roo.get(this.view.getSelectedNodes()[0])
11789 //sn.scrollIntoView(sn.dom.parentNode, false);
11792 onViewScroll : function(e, t){
11794 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){
11798 this.hasQuery = true;
11800 this.loading = this.list.select('.loading', true).first();
11802 if(this.loading === null){
11803 this.list.createChild({
11805 cls: 'loading select2-more-results select2-active',
11806 html: 'Loading more results...'
11809 this.loading = this.list.select('.loading', true).first();
11811 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11813 this.loading.hide();
11816 this.loading.show();
11821 this.loadNext = true;
11823 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11828 addItem : function(o)
11830 var dv = ''; // display value
11832 if (this.displayField) {
11833 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11835 // this is an error condition!!!
11836 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
11843 var choice = this.choices.createChild({
11845 cls: 'select2-search-choice',
11854 cls: 'select2-search-choice-close',
11859 }, this.searchField);
11861 var close = choice.select('a.select2-search-choice-close', true).first()
11863 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11871 this.inputEl().dom.value = '';
11875 onRemoveItem : function(e, _self, o)
11877 e.preventDefault();
11878 var index = this.item.indexOf(o.data) * 1;
11881 Roo.log('not this item?!');
11885 this.item.splice(index, 1);
11890 this.fireEvent('remove', this, e);
11894 syncValue : function()
11896 if(!this.item.length){
11903 Roo.each(this.item, function(i){
11904 if(_this.valueField){
11905 value.push(i[_this.valueField]);
11912 this.value = value.join(',');
11914 if(this.hiddenField){
11915 this.hiddenField.dom.value = this.value;
11919 clearItem : function()
11921 if(!this.multiple){
11927 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11934 inputEl: function ()
11937 return this.searchField;
11939 return this.el.select('input.form-control',true).first();
11943 onTickableFooterButtonClick : function(e, btn, el)
11945 e.preventDefault();
11947 if(btn && btn.name == 'cancel'){
11948 this.tickItems = Roo.apply([], this.item);
11957 Roo.each(this.tickItems, function(o){
11968 * @cfg {Boolean} grow
11972 * @cfg {Number} growMin
11976 * @cfg {Number} growMax
11986 * Ext JS Library 1.1.1
11987 * Copyright(c) 2006-2007, Ext JS, LLC.
11989 * Originally Released Under LGPL - original licence link has changed is not relivant.
11992 * <script type="text/javascript">
11997 * @extends Roo.util.Observable
11998 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
11999 * This class also supports single and multi selection modes. <br>
12000 * Create a data model bound view:
12002 var store = new Roo.data.Store(...);
12004 var view = new Roo.View({
12006 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
12008 singleSelect: true,
12009 selectedClass: "ydataview-selected",
12013 // listen for node click?
12014 view.on("click", function(vw, index, node, e){
12015 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12019 dataModel.load("foobar.xml");
12021 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12023 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12024 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12026 * Note: old style constructor is still suported (container, template, config)
12029 * Create a new View
12030 * @param {Object} config The config object
12033 Roo.View = function(config, depreciated_tpl, depreciated_config){
12035 this.parent = false;
12037 if (typeof(depreciated_tpl) == 'undefined') {
12038 // new way.. - universal constructor.
12039 Roo.apply(this, config);
12040 this.el = Roo.get(this.el);
12043 this.el = Roo.get(config);
12044 this.tpl = depreciated_tpl;
12045 Roo.apply(this, depreciated_config);
12047 this.wrapEl = this.el.wrap().wrap();
12048 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12051 if(typeof(this.tpl) == "string"){
12052 this.tpl = new Roo.Template(this.tpl);
12054 // support xtype ctors..
12055 this.tpl = new Roo.factory(this.tpl, Roo);
12059 this.tpl.compile();
12064 * @event beforeclick
12065 * Fires before a click is processed. Returns false to cancel the default action.
12066 * @param {Roo.View} this
12067 * @param {Number} index The index of the target node
12068 * @param {HTMLElement} node The target node
12069 * @param {Roo.EventObject} e The raw event object
12071 "beforeclick" : true,
12074 * Fires when a template node is clicked.
12075 * @param {Roo.View} this
12076 * @param {Number} index The index of the target node
12077 * @param {HTMLElement} node The target node
12078 * @param {Roo.EventObject} e The raw event object
12083 * Fires when a template node is double clicked.
12084 * @param {Roo.View} this
12085 * @param {Number} index The index of the target node
12086 * @param {HTMLElement} node The target node
12087 * @param {Roo.EventObject} e The raw event object
12091 * @event contextmenu
12092 * Fires when a template node is right clicked.
12093 * @param {Roo.View} this
12094 * @param {Number} index The index of the target node
12095 * @param {HTMLElement} node The target node
12096 * @param {Roo.EventObject} e The raw event object
12098 "contextmenu" : true,
12100 * @event selectionchange
12101 * Fires when the selected nodes change.
12102 * @param {Roo.View} this
12103 * @param {Array} selections Array of the selected nodes
12105 "selectionchange" : true,
12108 * @event beforeselect
12109 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12110 * @param {Roo.View} this
12111 * @param {HTMLElement} node The node to be selected
12112 * @param {Array} selections Array of currently selected nodes
12114 "beforeselect" : true,
12116 * @event preparedata
12117 * Fires on every row to render, to allow you to change the data.
12118 * @param {Roo.View} this
12119 * @param {Object} data to be rendered (change this)
12121 "preparedata" : true
12129 "click": this.onClick,
12130 "dblclick": this.onDblClick,
12131 "contextmenu": this.onContextMenu,
12135 this.selections = [];
12137 this.cmp = new Roo.CompositeElementLite([]);
12139 this.store = Roo.factory(this.store, Roo.data);
12140 this.setStore(this.store, true);
12143 if ( this.footer && this.footer.xtype) {
12145 var fctr = this.wrapEl.appendChild(document.createElement("div"));
12147 this.footer.dataSource = this.store
12148 this.footer.container = fctr;
12149 this.footer = Roo.factory(this.footer, Roo);
12150 fctr.insertFirst(this.el);
12152 // this is a bit insane - as the paging toolbar seems to detach the el..
12153 // dom.parentNode.parentNode.parentNode
12154 // they get detached?
12158 Roo.View.superclass.constructor.call(this);
12163 Roo.extend(Roo.View, Roo.util.Observable, {
12166 * @cfg {Roo.data.Store} store Data store to load data from.
12171 * @cfg {String|Roo.Element} el The container element.
12176 * @cfg {String|Roo.Template} tpl The template used by this View
12180 * @cfg {String} dataName the named area of the template to use as the data area
12181 * Works with domtemplates roo-name="name"
12185 * @cfg {String} selectedClass The css class to add to selected nodes
12187 selectedClass : "x-view-selected",
12189 * @cfg {String} emptyText The empty text to show when nothing is loaded.
12194 * @cfg {String} text to display on mask (default Loading)
12198 * @cfg {Boolean} multiSelect Allow multiple selection
12200 multiSelect : false,
12202 * @cfg {Boolean} singleSelect Allow single selection
12204 singleSelect: false,
12207 * @cfg {Boolean} toggleSelect - selecting
12209 toggleSelect : false,
12212 * @cfg {Boolean} tickable - selecting
12217 * Returns the element this view is bound to.
12218 * @return {Roo.Element}
12220 getEl : function(){
12221 return this.wrapEl;
12227 * Refreshes the view. - called by datachanged on the store. - do not call directly.
12229 refresh : function(){
12230 Roo.log('refresh');
12233 // if we are using something like 'domtemplate', then
12234 // the what gets used is:
12235 // t.applySubtemplate(NAME, data, wrapping data..)
12236 // the outer template then get' applied with
12237 // the store 'extra data'
12238 // and the body get's added to the
12239 // roo-name="data" node?
12240 // <span class='roo-tpl-{name}'></span> ?????
12244 this.clearSelections();
12245 this.el.update("");
12247 var records = this.store.getRange();
12248 if(records.length < 1) {
12250 // is this valid?? = should it render a template??
12252 this.el.update(this.emptyText);
12256 if (this.dataName) {
12257 this.el.update(t.apply(this.store.meta)); //????
12258 el = this.el.child('.roo-tpl-' + this.dataName);
12261 for(var i = 0, len = records.length; i < len; i++){
12262 var data = this.prepareData(records[i].data, i, records[i]);
12263 this.fireEvent("preparedata", this, data, i, records[i]);
12265 var d = Roo.apply({}, data);
12268 Roo.apply(d, {'roo-id' : Roo.id()});
12272 Roo.each(this.parent.item, function(item){
12273 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12276 Roo.apply(d, {'roo-data-checked' : 'checked'});
12280 html[html.length] = Roo.util.Format.trim(
12282 t.applySubtemplate(this.dataName, d, this.store.meta) :
12289 el.update(html.join(""));
12290 this.nodes = el.dom.childNodes;
12291 this.updateIndexes(0);
12296 * Function to override to reformat the data that is sent to
12297 * the template for each node.
12298 * DEPRICATED - use the preparedata event handler.
12299 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12300 * a JSON object for an UpdateManager bound view).
12302 prepareData : function(data, index, record)
12304 this.fireEvent("preparedata", this, data, index, record);
12308 onUpdate : function(ds, record){
12309 Roo.log('on update');
12310 this.clearSelections();
12311 var index = this.store.indexOf(record);
12312 var n = this.nodes[index];
12313 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12314 n.parentNode.removeChild(n);
12315 this.updateIndexes(index, index);
12321 onAdd : function(ds, records, index)
12323 Roo.log(['on Add', ds, records, index] );
12324 this.clearSelections();
12325 if(this.nodes.length == 0){
12329 var n = this.nodes[index];
12330 for(var i = 0, len = records.length; i < len; i++){
12331 var d = this.prepareData(records[i].data, i, records[i]);
12333 this.tpl.insertBefore(n, d);
12336 this.tpl.append(this.el, d);
12339 this.updateIndexes(index);
12342 onRemove : function(ds, record, index){
12343 Roo.log('onRemove');
12344 this.clearSelections();
12345 var el = this.dataName ?
12346 this.el.child('.roo-tpl-' + this.dataName) :
12349 el.dom.removeChild(this.nodes[index]);
12350 this.updateIndexes(index);
12354 * Refresh an individual node.
12355 * @param {Number} index
12357 refreshNode : function(index){
12358 this.onUpdate(this.store, this.store.getAt(index));
12361 updateIndexes : function(startIndex, endIndex){
12362 var ns = this.nodes;
12363 startIndex = startIndex || 0;
12364 endIndex = endIndex || ns.length - 1;
12365 for(var i = startIndex; i <= endIndex; i++){
12366 ns[i].nodeIndex = i;
12371 * Changes the data store this view uses and refresh the view.
12372 * @param {Store} store
12374 setStore : function(store, initial){
12375 if(!initial && this.store){
12376 this.store.un("datachanged", this.refresh);
12377 this.store.un("add", this.onAdd);
12378 this.store.un("remove", this.onRemove);
12379 this.store.un("update", this.onUpdate);
12380 this.store.un("clear", this.refresh);
12381 this.store.un("beforeload", this.onBeforeLoad);
12382 this.store.un("load", this.onLoad);
12383 this.store.un("loadexception", this.onLoad);
12387 store.on("datachanged", this.refresh, this);
12388 store.on("add", this.onAdd, this);
12389 store.on("remove", this.onRemove, this);
12390 store.on("update", this.onUpdate, this);
12391 store.on("clear", this.refresh, this);
12392 store.on("beforeload", this.onBeforeLoad, this);
12393 store.on("load", this.onLoad, this);
12394 store.on("loadexception", this.onLoad, this);
12402 * onbeforeLoad - masks the loading area.
12405 onBeforeLoad : function(store,opts)
12407 Roo.log('onBeforeLoad');
12409 this.el.update("");
12411 this.el.mask(this.mask ? this.mask : "Loading" );
12413 onLoad : function ()
12420 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12421 * @param {HTMLElement} node
12422 * @return {HTMLElement} The template node
12424 findItemFromChild : function(node){
12425 var el = this.dataName ?
12426 this.el.child('.roo-tpl-' + this.dataName,true) :
12429 if(!node || node.parentNode == el){
12432 var p = node.parentNode;
12433 while(p && p != el){
12434 if(p.parentNode == el){
12443 onClick : function(e){
12444 var item = this.findItemFromChild(e.getTarget());
12446 var index = this.indexOf(item);
12447 if(this.onItemClick(item, index, e) !== false){
12448 this.fireEvent("click", this, index, item, e);
12451 this.clearSelections();
12456 onContextMenu : function(e){
12457 var item = this.findItemFromChild(e.getTarget());
12459 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12464 onDblClick : function(e){
12465 var item = this.findItemFromChild(e.getTarget());
12467 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12471 onItemClick : function(item, index, e)
12473 if(this.fireEvent("beforeclick", this, index, item, e) === false){
12476 if (this.toggleSelect) {
12477 var m = this.isSelected(item) ? 'unselect' : 'select';
12480 _t[m](item, true, false);
12483 if(this.multiSelect || this.singleSelect){
12484 if(this.multiSelect && e.shiftKey && this.lastSelection){
12485 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12487 this.select(item, this.multiSelect && e.ctrlKey);
12488 this.lastSelection = item;
12491 if(!this.tickable){
12492 e.preventDefault();
12500 * Get the number of selected nodes.
12503 getSelectionCount : function(){
12504 return this.selections.length;
12508 * Get the currently selected nodes.
12509 * @return {Array} An array of HTMLElements
12511 getSelectedNodes : function(){
12512 return this.selections;
12516 * Get the indexes of the selected nodes.
12519 getSelectedIndexes : function(){
12520 var indexes = [], s = this.selections;
12521 for(var i = 0, len = s.length; i < len; i++){
12522 indexes.push(s[i].nodeIndex);
12528 * Clear all selections
12529 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12531 clearSelections : function(suppressEvent){
12532 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12533 this.cmp.elements = this.selections;
12534 this.cmp.removeClass(this.selectedClass);
12535 this.selections = [];
12536 if(!suppressEvent){
12537 this.fireEvent("selectionchange", this, this.selections);
12543 * Returns true if the passed node is selected
12544 * @param {HTMLElement/Number} node The node or node index
12545 * @return {Boolean}
12547 isSelected : function(node){
12548 var s = this.selections;
12552 node = this.getNode(node);
12553 return s.indexOf(node) !== -1;
12558 * @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
12559 * @param {Boolean} keepExisting (optional) true to keep existing selections
12560 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12562 select : function(nodeInfo, keepExisting, suppressEvent){
12563 if(nodeInfo instanceof Array){
12565 this.clearSelections(true);
12567 for(var i = 0, len = nodeInfo.length; i < len; i++){
12568 this.select(nodeInfo[i], true, true);
12572 var node = this.getNode(nodeInfo);
12573 if(!node || this.isSelected(node)){
12574 return; // already selected.
12577 this.clearSelections(true);
12579 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12580 Roo.fly(node).addClass(this.selectedClass);
12581 this.selections.push(node);
12582 if(!suppressEvent){
12583 this.fireEvent("selectionchange", this, this.selections);
12591 * @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
12592 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12593 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12595 unselect : function(nodeInfo, keepExisting, suppressEvent)
12597 if(nodeInfo instanceof Array){
12598 Roo.each(this.selections, function(s) {
12599 this.unselect(s, nodeInfo);
12603 var node = this.getNode(nodeInfo);
12604 if(!node || !this.isSelected(node)){
12605 Roo.log("not selected");
12606 return; // not selected.
12610 Roo.each(this.selections, function(s) {
12612 Roo.fly(node).removeClass(this.selectedClass);
12619 this.selections= ns;
12620 this.fireEvent("selectionchange", this, this.selections);
12624 * Gets a template node.
12625 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12626 * @return {HTMLElement} The node or null if it wasn't found
12628 getNode : function(nodeInfo){
12629 if(typeof nodeInfo == "string"){
12630 return document.getElementById(nodeInfo);
12631 }else if(typeof nodeInfo == "number"){
12632 return this.nodes[nodeInfo];
12638 * Gets a range template nodes.
12639 * @param {Number} startIndex
12640 * @param {Number} endIndex
12641 * @return {Array} An array of nodes
12643 getNodes : function(start, end){
12644 var ns = this.nodes;
12645 start = start || 0;
12646 end = typeof end == "undefined" ? ns.length - 1 : end;
12649 for(var i = start; i <= end; i++){
12653 for(var i = start; i >= end; i--){
12661 * Finds the index of the passed node
12662 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12663 * @return {Number} The index of the node or -1
12665 indexOf : function(node){
12666 node = this.getNode(node);
12667 if(typeof node.nodeIndex == "number"){
12668 return node.nodeIndex;
12670 var ns = this.nodes;
12671 for(var i = 0, len = ns.length; i < len; i++){
12682 * based on jquery fullcalendar
12686 Roo.bootstrap = Roo.bootstrap || {};
12688 * @class Roo.bootstrap.Calendar
12689 * @extends Roo.bootstrap.Component
12690 * Bootstrap Calendar class
12691 * @cfg {Boolean} loadMask (true|false) default false
12692 * @cfg {Object} header generate the user specific header of the calendar, default false
12695 * Create a new Container
12696 * @param {Object} config The config object
12701 Roo.bootstrap.Calendar = function(config){
12702 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12706 * Fires when a date is selected
12707 * @param {DatePicker} this
12708 * @param {Date} date The selected date
12712 * @event monthchange
12713 * Fires when the displayed month changes
12714 * @param {DatePicker} this
12715 * @param {Date} date The selected month
12717 'monthchange': true,
12719 * @event evententer
12720 * Fires when mouse over an event
12721 * @param {Calendar} this
12722 * @param {event} Event
12724 'evententer': true,
12726 * @event eventleave
12727 * Fires when the mouse leaves an
12728 * @param {Calendar} this
12731 'eventleave': true,
12733 * @event eventclick
12734 * Fires when the mouse click an
12735 * @param {Calendar} this
12744 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
12747 * @cfg {Number} startDay
12748 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12756 getAutoCreate : function(){
12759 var fc_button = function(name, corner, style, content ) {
12760 return Roo.apply({},{
12762 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
12764 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12767 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12778 style : 'width:100%',
12785 cls : 'fc-header-left',
12787 fc_button('prev', 'left', 'arrow', '‹' ),
12788 fc_button('next', 'right', 'arrow', '›' ),
12789 { tag: 'span', cls: 'fc-header-space' },
12790 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
12798 cls : 'fc-header-center',
12802 cls: 'fc-header-title',
12805 html : 'month / year'
12813 cls : 'fc-header-right',
12815 /* fc_button('month', 'left', '', 'month' ),
12816 fc_button('week', '', '', 'week' ),
12817 fc_button('day', 'right', '', 'day' )
12829 header = this.header;
12832 var cal_heads = function() {
12834 // fixme - handle this.
12836 for (var i =0; i < Date.dayNames.length; i++) {
12837 var d = Date.dayNames[i];
12840 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12841 html : d.substring(0,3)
12845 ret[0].cls += ' fc-first';
12846 ret[6].cls += ' fc-last';
12849 var cal_cell = function(n) {
12852 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12857 cls: 'fc-day-number',
12861 cls: 'fc-day-content',
12865 style: 'position: relative;' // height: 17px;
12877 var cal_rows = function() {
12880 for (var r = 0; r < 6; r++) {
12887 for (var i =0; i < Date.dayNames.length; i++) {
12888 var d = Date.dayNames[i];
12889 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12892 row.cn[0].cls+=' fc-first';
12893 row.cn[0].cn[0].style = 'min-height:90px';
12894 row.cn[6].cls+=' fc-last';
12898 ret[0].cls += ' fc-first';
12899 ret[4].cls += ' fc-prev-last';
12900 ret[5].cls += ' fc-last';
12907 cls: 'fc-border-separate',
12908 style : 'width:100%',
12916 cls : 'fc-first fc-last',
12934 cls : 'fc-content',
12935 style : "position: relative;",
12938 cls : 'fc-view fc-view-month fc-grid',
12939 style : 'position: relative',
12940 unselectable : 'on',
12943 cls : 'fc-event-container',
12944 style : 'position:absolute;z-index:8;top:0;left:0;'
12962 initEvents : function()
12965 throw "can not find store for calendar";
12971 style: "text-align:center",
12975 style: "background-color:white;width:50%;margin:250 auto",
12979 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
12990 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12992 var size = this.el.select('.fc-content', true).first().getSize();
12993 this.maskEl.setSize(size.width, size.height);
12994 this.maskEl.enableDisplayMode("block");
12995 if(!this.loadMask){
12996 this.maskEl.hide();
12999 this.store = Roo.factory(this.store, Roo.data);
13000 this.store.on('load', this.onLoad, this);
13001 this.store.on('beforeload', this.onBeforeLoad, this);
13005 this.cells = this.el.select('.fc-day',true);
13006 //Roo.log(this.cells);
13007 this.textNodes = this.el.query('.fc-day-number');
13008 this.cells.addClassOnOver('fc-state-hover');
13010 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13011 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13012 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13013 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13015 this.on('monthchange', this.onMonthChange, this);
13017 this.update(new Date().clearTime());
13020 resize : function() {
13021 var sz = this.el.getSize();
13023 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13024 this.el.select('.fc-day-content div',true).setHeight(34);
13029 showPrevMonth : function(e){
13030 this.update(this.activeDate.add("mo", -1));
13032 showToday : function(e){
13033 this.update(new Date().clearTime());
13036 showNextMonth : function(e){
13037 this.update(this.activeDate.add("mo", 1));
13041 showPrevYear : function(){
13042 this.update(this.activeDate.add("y", -1));
13046 showNextYear : function(){
13047 this.update(this.activeDate.add("y", 1));
13052 update : function(date)
13054 var vd = this.activeDate;
13055 this.activeDate = date;
13056 // if(vd && this.el){
13057 // var t = date.getTime();
13058 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13059 // Roo.log('using add remove');
13061 // this.fireEvent('monthchange', this, date);
13063 // this.cells.removeClass("fc-state-highlight");
13064 // this.cells.each(function(c){
13065 // if(c.dateValue == t){
13066 // c.addClass("fc-state-highlight");
13067 // setTimeout(function(){
13068 // try{c.dom.firstChild.focus();}catch(e){}
13078 var days = date.getDaysInMonth();
13080 var firstOfMonth = date.getFirstDateOfMonth();
13081 var startingPos = firstOfMonth.getDay()-this.startDay;
13083 if(startingPos < this.startDay){
13087 var pm = date.add(Date.MONTH, -1);
13088 var prevStart = pm.getDaysInMonth()-startingPos;
13090 this.cells = this.el.select('.fc-day',true);
13091 this.textNodes = this.el.query('.fc-day-number');
13092 this.cells.addClassOnOver('fc-state-hover');
13094 var cells = this.cells.elements;
13095 var textEls = this.textNodes;
13097 Roo.each(cells, function(cell){
13098 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13101 days += startingPos;
13103 // convert everything to numbers so it's fast
13104 var day = 86400000;
13105 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13108 //Roo.log(prevStart);
13110 var today = new Date().clearTime().getTime();
13111 var sel = date.clearTime().getTime();
13112 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13113 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13114 var ddMatch = this.disabledDatesRE;
13115 var ddText = this.disabledDatesText;
13116 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13117 var ddaysText = this.disabledDaysText;
13118 var format = this.format;
13120 var setCellClass = function(cal, cell){
13124 //Roo.log('set Cell Class');
13126 var t = d.getTime();
13130 cell.dateValue = t;
13132 cell.className += " fc-today";
13133 cell.className += " fc-state-highlight";
13134 cell.title = cal.todayText;
13137 // disable highlight in other month..
13138 //cell.className += " fc-state-highlight";
13143 cell.className = " fc-state-disabled";
13144 cell.title = cal.minText;
13148 cell.className = " fc-state-disabled";
13149 cell.title = cal.maxText;
13153 if(ddays.indexOf(d.getDay()) != -1){
13154 cell.title = ddaysText;
13155 cell.className = " fc-state-disabled";
13158 if(ddMatch && format){
13159 var fvalue = d.dateFormat(format);
13160 if(ddMatch.test(fvalue)){
13161 cell.title = ddText.replace("%0", fvalue);
13162 cell.className = " fc-state-disabled";
13166 if (!cell.initialClassName) {
13167 cell.initialClassName = cell.dom.className;
13170 cell.dom.className = cell.initialClassName + ' ' + cell.className;
13175 for(; i < startingPos; i++) {
13176 textEls[i].innerHTML = (++prevStart);
13177 d.setDate(d.getDate()+1);
13179 cells[i].className = "fc-past fc-other-month";
13180 setCellClass(this, cells[i]);
13185 for(; i < days; i++){
13186 intDay = i - startingPos + 1;
13187 textEls[i].innerHTML = (intDay);
13188 d.setDate(d.getDate()+1);
13190 cells[i].className = ''; // "x-date-active";
13191 setCellClass(this, cells[i]);
13195 for(; i < 42; i++) {
13196 textEls[i].innerHTML = (++extraDays);
13197 d.setDate(d.getDate()+1);
13199 cells[i].className = "fc-future fc-other-month";
13200 setCellClass(this, cells[i]);
13203 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13205 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13207 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13208 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13210 if(totalRows != 6){
13211 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13212 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13215 this.fireEvent('monthchange', this, date);
13219 if(!this.internalRender){
13220 var main = this.el.dom.firstChild;
13221 var w = main.offsetWidth;
13222 this.el.setWidth(w + this.el.getBorderWidth("lr"));
13223 Roo.fly(main).setWidth(w);
13224 this.internalRender = true;
13225 // opera does not respect the auto grow header center column
13226 // then, after it gets a width opera refuses to recalculate
13227 // without a second pass
13228 if(Roo.isOpera && !this.secondPass){
13229 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13230 this.secondPass = true;
13231 this.update.defer(10, this, [date]);
13238 findCell : function(dt) {
13239 dt = dt.clearTime().getTime();
13241 this.cells.each(function(c){
13242 //Roo.log("check " +c.dateValue + '?=' + dt);
13243 if(c.dateValue == dt){
13253 findCells : function(ev) {
13254 var s = ev.start.clone().clearTime().getTime();
13256 var e= ev.end.clone().clearTime().getTime();
13259 this.cells.each(function(c){
13260 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13262 if(c.dateValue > e){
13265 if(c.dateValue < s){
13274 // findBestRow: function(cells)
13278 // for (var i =0 ; i < cells.length;i++) {
13279 // ret = Math.max(cells[i].rows || 0,ret);
13286 addItem : function(ev)
13288 // look for vertical location slot in
13289 var cells = this.findCells(ev);
13291 // ev.row = this.findBestRow(cells);
13293 // work out the location.
13297 for(var i =0; i < cells.length; i++) {
13299 cells[i].row = cells[0].row;
13302 cells[i].row = cells[i].row + 1;
13312 if (crow.start.getY() == cells[i].getY()) {
13314 crow.end = cells[i];
13331 cells[0].events.push(ev);
13333 this.calevents.push(ev);
13336 clearEvents: function() {
13338 if(!this.calevents){
13342 Roo.each(this.cells.elements, function(c){
13348 Roo.each(this.calevents, function(e) {
13349 Roo.each(e.els, function(el) {
13350 el.un('mouseenter' ,this.onEventEnter, this);
13351 el.un('mouseleave' ,this.onEventLeave, this);
13356 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13362 renderEvents: function()
13366 this.cells.each(function(c) {
13375 if(c.row != c.events.length){
13376 r = 4 - (4 - (c.row - c.events.length));
13379 c.events = ev.slice(0, r);
13380 c.more = ev.slice(r);
13382 if(c.more.length && c.more.length == 1){
13383 c.events.push(c.more.pop());
13386 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13390 this.cells.each(function(c) {
13392 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13395 for (var e = 0; e < c.events.length; e++){
13396 var ev = c.events[e];
13397 var rows = ev.rows;
13399 for(var i = 0; i < rows.length; i++) {
13401 // how many rows should it span..
13404 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13405 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13407 unselectable : "on",
13410 cls: 'fc-event-inner',
13414 // cls: 'fc-event-time',
13415 // html : cells.length > 1 ? '' : ev.time
13419 cls: 'fc-event-title',
13420 html : String.format('{0}', ev.title)
13427 cls: 'ui-resizable-handle ui-resizable-e',
13428 html : '  '
13435 cfg.cls += ' fc-event-start';
13437 if ((i+1) == rows.length) {
13438 cfg.cls += ' fc-event-end';
13441 var ctr = _this.el.select('.fc-event-container',true).first();
13442 var cg = ctr.createChild(cfg);
13444 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13445 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13447 var r = (c.more.length) ? 1 : 0;
13448 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
13449 cg.setWidth(ebox.right - sbox.x -2);
13451 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13452 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13453 cg.on('click', _this.onEventClick, _this, ev);
13464 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13465 style : 'position: absolute',
13466 unselectable : "on",
13469 cls: 'fc-event-inner',
13473 cls: 'fc-event-title',
13481 cls: 'ui-resizable-handle ui-resizable-e',
13482 html : '  '
13488 var ctr = _this.el.select('.fc-event-container',true).first();
13489 var cg = ctr.createChild(cfg);
13491 var sbox = c.select('.fc-day-content',true).first().getBox();
13492 var ebox = c.select('.fc-day-content',true).first().getBox();
13494 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
13495 cg.setWidth(ebox.right - sbox.x -2);
13497 cg.on('click', _this.onMoreEventClick, _this, c.more);
13507 onEventEnter: function (e, el,event,d) {
13508 this.fireEvent('evententer', this, el, event);
13511 onEventLeave: function (e, el,event,d) {
13512 this.fireEvent('eventleave', this, el, event);
13515 onEventClick: function (e, el,event,d) {
13516 this.fireEvent('eventclick', this, el, event);
13519 onMonthChange: function () {
13523 onMoreEventClick: function(e, el, more)
13527 this.calpopover.placement = 'right';
13528 this.calpopover.setTitle('More');
13530 this.calpopover.setContent('');
13532 var ctr = this.calpopover.el.select('.popover-content', true).first();
13534 Roo.each(more, function(m){
13536 cls : 'fc-event-hori fc-event-draggable',
13539 var cg = ctr.createChild(cfg);
13541 cg.on('click', _this.onEventClick, _this, m);
13544 this.calpopover.show(el);
13549 onLoad: function ()
13551 this.calevents = [];
13554 if(this.store.getCount() > 0){
13555 this.store.data.each(function(d){
13558 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13559 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13560 time : d.data.start_time,
13561 title : d.data.title,
13562 description : d.data.description,
13563 venue : d.data.venue
13568 this.renderEvents();
13570 if(this.calevents.length && this.loadMask){
13571 this.maskEl.hide();
13575 onBeforeLoad: function()
13577 this.clearEvents();
13579 this.maskEl.show();
13593 * @class Roo.bootstrap.Popover
13594 * @extends Roo.bootstrap.Component
13595 * Bootstrap Popover class
13596 * @cfg {String} html contents of the popover (or false to use children..)
13597 * @cfg {String} title of popover (or false to hide)
13598 * @cfg {String} placement how it is placed
13599 * @cfg {String} trigger click || hover (or false to trigger manually)
13600 * @cfg {String} over what (parent or false to trigger manually.)
13603 * Create a new Popover
13604 * @param {Object} config The config object
13607 Roo.bootstrap.Popover = function(config){
13608 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13611 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
13613 title: 'Fill in a title',
13616 placement : 'right',
13617 trigger : 'hover', // hover
13621 can_build_overlaid : false,
13623 getChildContainer : function()
13625 return this.el.select('.popover-content',true).first();
13628 getAutoCreate : function(){
13629 Roo.log('make popover?');
13631 cls : 'popover roo-dynamic',
13632 style: 'display:block',
13638 cls : 'popover-inner',
13642 cls: 'popover-title',
13646 cls : 'popover-content',
13657 setTitle: function(str)
13659 this.el.select('.popover-title',true).first().dom.innerHTML = str;
13661 setContent: function(str)
13663 this.el.select('.popover-content',true).first().dom.innerHTML = str;
13665 // as it get's added to the bottom of the page.
13666 onRender : function(ct, position)
13668 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13670 var cfg = Roo.apply({}, this.getAutoCreate());
13674 cfg.cls += ' ' + this.cls;
13677 cfg.style = this.style;
13679 Roo.log("adding to ")
13680 this.el = Roo.get(document.body).createChild(cfg, position);
13686 initEvents : function()
13688 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13689 this.el.enableDisplayMode('block');
13691 if (this.over === false) {
13694 if (this.triggers === false) {
13697 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13698 var triggers = this.trigger ? this.trigger.split(' ') : [];
13699 Roo.each(triggers, function(trigger) {
13701 if (trigger == 'click') {
13702 on_el.on('click', this.toggle, this);
13703 } else if (trigger != 'manual') {
13704 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
13705 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13707 on_el.on(eventIn ,this.enter, this);
13708 on_el.on(eventOut, this.leave, this);
13719 toggle : function () {
13720 this.hoverState == 'in' ? this.leave() : this.enter();
13723 enter : function () {
13726 clearTimeout(this.timeout);
13728 this.hoverState = 'in'
13730 if (!this.delay || !this.delay.show) {
13735 this.timeout = setTimeout(function () {
13736 if (_t.hoverState == 'in') {
13739 }, this.delay.show)
13741 leave : function() {
13742 clearTimeout(this.timeout);
13744 this.hoverState = 'out'
13746 if (!this.delay || !this.delay.hide) {
13751 this.timeout = setTimeout(function () {
13752 if (_t.hoverState == 'out') {
13755 }, this.delay.hide)
13758 show : function (on_el)
13761 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13764 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13765 if (this.html !== false) {
13766 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13768 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13769 if (!this.title.length) {
13770 this.el.select('.popover-title',true).hide();
13773 var placement = typeof this.placement == 'function' ?
13774 this.placement.call(this, this.el, on_el) :
13777 var autoToken = /\s?auto?\s?/i;
13778 var autoPlace = autoToken.test(placement);
13780 placement = placement.replace(autoToken, '') || 'top';
13784 //this.el.setXY([0,0]);
13786 this.el.dom.style.display='block';
13787 this.el.addClass(placement);
13789 //this.el.appendTo(on_el);
13791 var p = this.getPosition();
13792 var box = this.el.getBox();
13797 var align = Roo.bootstrap.Popover.alignment[placement]
13798 this.el.alignTo(on_el, align[0],align[1]);
13799 //var arrow = this.el.select('.arrow',true).first();
13800 //arrow.set(align[2],
13802 this.el.addClass('in');
13803 this.hoverState = null;
13805 if (this.el.hasClass('fade')) {
13812 this.el.setXY([0,0]);
13813 this.el.removeClass('in');
13820 Roo.bootstrap.Popover.alignment = {
13821 'left' : ['r-l', [-10,0], 'right'],
13822 'right' : ['l-r', [10,0], 'left'],
13823 'bottom' : ['t-b', [0,10], 'top'],
13824 'top' : [ 'b-t', [0,-10], 'bottom']
13835 * @class Roo.bootstrap.Progress
13836 * @extends Roo.bootstrap.Component
13837 * Bootstrap Progress class
13838 * @cfg {Boolean} striped striped of the progress bar
13839 * @cfg {Boolean} active animated of the progress bar
13843 * Create a new Progress
13844 * @param {Object} config The config object
13847 Roo.bootstrap.Progress = function(config){
13848 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13851 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
13856 getAutoCreate : function(){
13864 cfg.cls += ' progress-striped';
13868 cfg.cls += ' active';
13887 * @class Roo.bootstrap.ProgressBar
13888 * @extends Roo.bootstrap.Component
13889 * Bootstrap ProgressBar class
13890 * @cfg {Number} aria_valuenow aria-value now
13891 * @cfg {Number} aria_valuemin aria-value min
13892 * @cfg {Number} aria_valuemax aria-value max
13893 * @cfg {String} label label for the progress bar
13894 * @cfg {String} panel (success | info | warning | danger )
13895 * @cfg {String} role role of the progress bar
13896 * @cfg {String} sr_only text
13900 * Create a new ProgressBar
13901 * @param {Object} config The config object
13904 Roo.bootstrap.ProgressBar = function(config){
13905 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13908 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
13912 aria_valuemax : 100,
13918 getAutoCreate : function()
13923 cls: 'progress-bar',
13924 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13936 cfg.role = this.role;
13939 if(this.aria_valuenow){
13940 cfg['aria-valuenow'] = this.aria_valuenow;
13943 if(this.aria_valuemin){
13944 cfg['aria-valuemin'] = this.aria_valuemin;
13947 if(this.aria_valuemax){
13948 cfg['aria-valuemax'] = this.aria_valuemax;
13951 if(this.label && !this.sr_only){
13952 cfg.html = this.label;
13956 cfg.cls += ' progress-bar-' + this.panel;
13962 update : function(aria_valuenow)
13964 this.aria_valuenow = aria_valuenow;
13966 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13981 * @class Roo.bootstrap.TabGroup
13982 * @extends Roo.bootstrap.Column
13983 * Bootstrap Column class
13984 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13985 * @cfg {Boolean} carousel true to make the group behave like a carousel
13988 * Create a new TabGroup
13989 * @param {Object} config The config object
13992 Roo.bootstrap.TabGroup = function(config){
13993 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13995 this.navId = Roo.id();
13998 Roo.bootstrap.TabGroup.register(this);
14002 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
14005 transition : false,
14007 getAutoCreate : function()
14009 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14011 cfg.cls += ' tab-content';
14013 if (this.carousel) {
14014 cfg.cls += ' carousel slide';
14016 cls : 'carousel-inner'
14023 getChildContainer : function()
14025 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14029 * register a Navigation item
14030 * @param {Roo.bootstrap.NavItem} the navitem to add
14032 register : function(item)
14034 this.tabs.push( item);
14035 item.navId = this.navId; // not really needed..
14039 getActivePanel : function()
14042 Roo.each(this.tabs, function(t) {
14052 getPanelByName : function(n)
14055 Roo.each(this.tabs, function(t) {
14056 if (t.tabId == n) {
14064 indexOfPanel : function(p)
14067 Roo.each(this.tabs, function(t,i) {
14068 if (t.tabId == p.tabId) {
14077 * show a specific panel
14078 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14079 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14081 showPanel : function (pan)
14084 if (typeof(pan) == 'number') {
14085 pan = this.tabs[pan];
14087 if (typeof(pan) == 'string') {
14088 pan = this.getPanelByName(pan);
14090 if (pan.tabId == this.getActivePanel().tabId) {
14093 var cur = this.getActivePanel();
14095 if (false === cur.fireEvent('beforedeactivate')) {
14099 if (this.carousel) {
14100 this.transition = true;
14101 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
14102 var lr = dir == 'next' ? 'left' : 'right';
14103 pan.el.addClass(dir); // or prev
14104 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14105 cur.el.addClass(lr); // or right
14106 pan.el.addClass(lr);
14109 cur.el.on('transitionend', function() {
14110 Roo.log("trans end?");
14112 pan.el.removeClass([lr,dir]);
14113 pan.setActive(true);
14115 cur.el.removeClass([lr]);
14116 cur.setActive(false);
14118 _this.transition = false;
14120 }, this, { single: true } );
14124 cur.setActive(false);
14125 pan.setActive(true);
14129 showPanelNext : function()
14131 var i = this.indexOfPanel(this.getActivePanel());
14132 if (i > this.tabs.length) {
14135 this.showPanel(this.tabs[i+1]);
14137 showPanelPrev : function()
14139 var i = this.indexOfPanel(this.getActivePanel());
14143 this.showPanel(this.tabs[i-1]);
14154 Roo.apply(Roo.bootstrap.TabGroup, {
14158 * register a Navigation Group
14159 * @param {Roo.bootstrap.NavGroup} the navgroup to add
14161 register : function(navgrp)
14163 this.groups[navgrp.navId] = navgrp;
14167 * fetch a Navigation Group based on the navigation ID
14168 * if one does not exist , it will get created.
14169 * @param {string} the navgroup to add
14170 * @returns {Roo.bootstrap.NavGroup} the navgroup
14172 get: function(navId) {
14173 if (typeof(this.groups[navId]) == 'undefined') {
14174 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14176 return this.groups[navId] ;
14191 * @class Roo.bootstrap.TabPanel
14192 * @extends Roo.bootstrap.Component
14193 * Bootstrap TabPanel class
14194 * @cfg {Boolean} active panel active
14195 * @cfg {String} html panel content
14196 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14197 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14201 * Create a new TabPanel
14202 * @param {Object} config The config object
14205 Roo.bootstrap.TabPanel = function(config){
14206 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14210 * Fires when the active status changes
14211 * @param {Roo.bootstrap.TabPanel} this
14212 * @param {Boolean} state the new state
14217 * @event beforedeactivate
14218 * Fires before a tab is de-activated - can be used to do validation on a form.
14219 * @param {Roo.bootstrap.TabPanel} this
14220 * @return {Boolean} false if there is an error
14223 'beforedeactivate': true
14226 this.tabId = this.tabId || Roo.id();
14230 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
14237 getAutoCreate : function(){
14240 // item is needed for carousel - not sure if it has any effect otherwise
14241 cls: 'tab-pane item',
14242 html: this.html || ''
14246 cfg.cls += ' active';
14250 cfg.tabId = this.tabId;
14257 initEvents: function()
14259 Roo.log('-------- init events on tab panel ---------');
14261 var p = this.parent();
14262 this.navId = this.navId || p.navId;
14264 if (typeof(this.navId) != 'undefined') {
14265 // not really needed.. but just in case.. parent should be a NavGroup.
14266 var tg = Roo.bootstrap.TabGroup.get(this.navId);
14267 Roo.log(['register', tg, this]);
14273 onRender : function(ct, position)
14275 // Roo.log("Call onRender: " + this.xtype);
14277 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14285 setActive: function(state)
14287 Roo.log("panel - set active " + this.tabId + "=" + state);
14289 this.active = state;
14291 this.el.removeClass('active');
14293 } else if (!this.el.hasClass('active')) {
14294 this.el.addClass('active');
14296 this.fireEvent('changed', this, state);
14313 * @class Roo.bootstrap.DateField
14314 * @extends Roo.bootstrap.Input
14315 * Bootstrap DateField class
14316 * @cfg {Number} weekStart default 0
14317 * @cfg {Number} weekStart default 0
14318 * @cfg {Number} viewMode default empty, (months|years)
14319 * @cfg {Number} minViewMode default empty, (months|years)
14320 * @cfg {Number} startDate default -Infinity
14321 * @cfg {Number} endDate default Infinity
14322 * @cfg {Boolean} todayHighlight default false
14323 * @cfg {Boolean} todayBtn default false
14324 * @cfg {Boolean} calendarWeeks default false
14325 * @cfg {Object} daysOfWeekDisabled default empty
14327 * @cfg {Boolean} keyboardNavigation default true
14328 * @cfg {String} language default en
14331 * Create a new DateField
14332 * @param {Object} config The config object
14335 Roo.bootstrap.DateField = function(config){
14336 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14340 * Fires when this field show.
14341 * @param {Roo.bootstrap.DateField} this
14342 * @param {Mixed} date The date value
14347 * Fires when this field hide.
14348 * @param {Roo.bootstrap.DateField} this
14349 * @param {Mixed} date The date value
14354 * Fires when select a date.
14355 * @param {Roo.bootstrap.DateField} this
14356 * @param {Mixed} date The date value
14362 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
14365 * @cfg {String} format
14366 * The default date format string which can be overriden for localization support. The format must be
14367 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14371 * @cfg {String} altFormats
14372 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14373 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14375 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14383 todayHighlight : false,
14389 keyboardNavigation: true,
14391 calendarWeeks: false,
14393 startDate: -Infinity,
14397 daysOfWeekDisabled: [],
14401 UTCDate: function()
14403 return new Date(Date.UTC.apply(Date, arguments));
14406 UTCToday: function()
14408 var today = new Date();
14409 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14412 getDate: function() {
14413 var d = this.getUTCDate();
14414 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14417 getUTCDate: function() {
14421 setDate: function(d) {
14422 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14425 setUTCDate: function(d) {
14427 this.setValue(this.formatDate(this.date));
14430 onRender: function(ct, position)
14433 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14435 this.language = this.language || 'en';
14436 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14437 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14439 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14440 this.format = this.format || 'm/d/y';
14441 this.isInline = false;
14442 this.isInput = true;
14443 this.component = this.el.select('.add-on', true).first() || false;
14444 this.component = (this.component && this.component.length === 0) ? false : this.component;
14445 this.hasInput = this.component && this.inputEL().length;
14447 if (typeof(this.minViewMode === 'string')) {
14448 switch (this.minViewMode) {
14450 this.minViewMode = 1;
14453 this.minViewMode = 2;
14456 this.minViewMode = 0;
14461 if (typeof(this.viewMode === 'string')) {
14462 switch (this.viewMode) {
14475 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14477 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14479 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14481 this.picker().on('mousedown', this.onMousedown, this);
14482 this.picker().on('click', this.onClick, this);
14484 this.picker().addClass('datepicker-dropdown');
14486 this.startViewMode = this.viewMode;
14489 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14490 if(!this.calendarWeeks){
14495 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14496 v.attr('colspan', function(i, val){
14497 return parseInt(val) + 1;
14502 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14504 this.setStartDate(this.startDate);
14505 this.setEndDate(this.endDate);
14507 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14514 if(this.isInline) {
14519 picker : function()
14521 return this.pickerEl;
14522 // return this.el.select('.datepicker', true).first();
14525 fillDow: function()
14527 var dowCnt = this.weekStart;
14536 if(this.calendarWeeks){
14544 while (dowCnt < this.weekStart + 7) {
14548 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14552 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14555 fillMonths: function()
14558 var months = this.picker().select('>.datepicker-months td', true).first();
14560 months.dom.innerHTML = '';
14566 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14569 months.createChild(month);
14576 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;
14578 if (this.date < this.startDate) {
14579 this.viewDate = new Date(this.startDate);
14580 } else if (this.date > this.endDate) {
14581 this.viewDate = new Date(this.endDate);
14583 this.viewDate = new Date(this.date);
14591 var d = new Date(this.viewDate),
14592 year = d.getUTCFullYear(),
14593 month = d.getUTCMonth(),
14594 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14595 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14596 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14597 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14598 currentDate = this.date && this.date.valueOf(),
14599 today = this.UTCToday();
14601 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14603 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14605 // this.picker.select('>tfoot th.today').
14606 // .text(dates[this.language].today)
14607 // .toggle(this.todayBtn !== false);
14609 this.updateNavArrows();
14612 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14614 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14616 prevMonth.setUTCDate(day);
14618 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14620 var nextMonth = new Date(prevMonth);
14622 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14624 nextMonth = nextMonth.valueOf();
14626 var fillMonths = false;
14628 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14630 while(prevMonth.valueOf() < nextMonth) {
14633 if (prevMonth.getUTCDay() === this.weekStart) {
14635 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14643 if(this.calendarWeeks){
14644 // ISO 8601: First week contains first thursday.
14645 // ISO also states week starts on Monday, but we can be more abstract here.
14647 // Start of current week: based on weekstart/current date
14648 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14649 // Thursday of this week
14650 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14651 // First Thursday of year, year from thursday
14652 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14653 // Calendar week: ms between thursdays, div ms per day, div 7 days
14654 calWeek = (th - yth) / 864e5 / 7 + 1;
14656 fillMonths.cn.push({
14664 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14666 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14669 if (this.todayHighlight &&
14670 prevMonth.getUTCFullYear() == today.getFullYear() &&
14671 prevMonth.getUTCMonth() == today.getMonth() &&
14672 prevMonth.getUTCDate() == today.getDate()) {
14673 clsName += ' today';
14676 if (currentDate && prevMonth.valueOf() === currentDate) {
14677 clsName += ' active';
14680 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14681 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14682 clsName += ' disabled';
14685 fillMonths.cn.push({
14687 cls: 'day ' + clsName,
14688 html: prevMonth.getDate()
14691 prevMonth.setDate(prevMonth.getDate()+1);
14694 var currentYear = this.date && this.date.getUTCFullYear();
14695 var currentMonth = this.date && this.date.getUTCMonth();
14697 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14699 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14700 v.removeClass('active');
14702 if(currentYear === year && k === currentMonth){
14703 v.addClass('active');
14706 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14707 v.addClass('disabled');
14713 year = parseInt(year/10, 10) * 10;
14715 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14717 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14720 for (var i = -1; i < 11; i++) {
14721 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14723 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14731 showMode: function(dir)
14734 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14736 Roo.each(this.picker().select('>div',true).elements, function(v){
14737 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14740 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14745 if(this.isInline) return;
14747 this.picker().removeClass(['bottom', 'top']);
14749 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14751 * place to the top of element!
14755 this.picker().addClass('top');
14756 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14761 this.picker().addClass('bottom');
14763 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14766 parseDate : function(value)
14768 if(!value || value instanceof Date){
14771 var v = Date.parseDate(value, this.format);
14772 if (!v && this.useIso) {
14773 v = Date.parseDate(value, 'Y-m-d');
14775 if(!v && this.altFormats){
14776 if(!this.altFormatsArray){
14777 this.altFormatsArray = this.altFormats.split("|");
14779 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14780 v = Date.parseDate(value, this.altFormatsArray[i]);
14786 formatDate : function(date, fmt)
14788 return (!date || !(date instanceof Date)) ?
14789 date : date.dateFormat(fmt || this.format);
14792 onFocus : function()
14794 Roo.bootstrap.DateField.superclass.onFocus.call(this);
14798 onBlur : function()
14800 Roo.bootstrap.DateField.superclass.onBlur.call(this);
14802 var d = this.inputEl().getValue();
14811 this.picker().show();
14815 this.fireEvent('show', this, this.date);
14820 if(this.isInline) return;
14821 this.picker().hide();
14822 this.viewMode = this.startViewMode;
14825 this.fireEvent('hide', this, this.date);
14829 onMousedown: function(e)
14831 e.stopPropagation();
14832 e.preventDefault();
14837 Roo.bootstrap.DateField.superclass.keyup.call(this);
14841 setValue: function(v)
14843 var d = new Date(v).clearTime();
14845 if(isNaN(d.getTime())){
14846 this.date = this.viewDate = '';
14847 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14851 v = this.formatDate(d);
14853 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14855 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14859 this.fireEvent('select', this, this.date);
14863 getValue: function()
14865 return this.formatDate(this.date);
14868 fireKey: function(e)
14870 if (!this.picker().isVisible()){
14871 if (e.keyCode == 27) // allow escape to hide and re-show picker
14876 var dateChanged = false,
14878 newDate, newViewDate;
14883 e.preventDefault();
14887 if (!this.keyboardNavigation) break;
14888 dir = e.keyCode == 37 ? -1 : 1;
14891 newDate = this.moveYear(this.date, dir);
14892 newViewDate = this.moveYear(this.viewDate, dir);
14893 } else if (e.shiftKey){
14894 newDate = this.moveMonth(this.date, dir);
14895 newViewDate = this.moveMonth(this.viewDate, dir);
14897 newDate = new Date(this.date);
14898 newDate.setUTCDate(this.date.getUTCDate() + dir);
14899 newViewDate = new Date(this.viewDate);
14900 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14902 if (this.dateWithinRange(newDate)){
14903 this.date = newDate;
14904 this.viewDate = newViewDate;
14905 this.setValue(this.formatDate(this.date));
14907 e.preventDefault();
14908 dateChanged = true;
14913 if (!this.keyboardNavigation) break;
14914 dir = e.keyCode == 38 ? -1 : 1;
14916 newDate = this.moveYear(this.date, dir);
14917 newViewDate = this.moveYear(this.viewDate, dir);
14918 } else if (e.shiftKey){
14919 newDate = this.moveMonth(this.date, dir);
14920 newViewDate = this.moveMonth(this.viewDate, dir);
14922 newDate = new Date(this.date);
14923 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14924 newViewDate = new Date(this.viewDate);
14925 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14927 if (this.dateWithinRange(newDate)){
14928 this.date = newDate;
14929 this.viewDate = newViewDate;
14930 this.setValue(this.formatDate(this.date));
14932 e.preventDefault();
14933 dateChanged = true;
14937 this.setValue(this.formatDate(this.date));
14939 e.preventDefault();
14942 this.setValue(this.formatDate(this.date));
14956 onClick: function(e)
14958 e.stopPropagation();
14959 e.preventDefault();
14961 var target = e.getTarget();
14963 if(target.nodeName.toLowerCase() === 'i'){
14964 target = Roo.get(target).dom.parentNode;
14967 var nodeName = target.nodeName;
14968 var className = target.className;
14969 var html = target.innerHTML;
14971 switch(nodeName.toLowerCase()) {
14973 switch(className) {
14979 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14980 switch(this.viewMode){
14982 this.viewDate = this.moveMonth(this.viewDate, dir);
14986 this.viewDate = this.moveYear(this.viewDate, dir);
14992 var date = new Date();
14993 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14995 this.setValue(this.formatDate(this.date));
15002 if (className.indexOf('disabled') === -1) {
15003 this.viewDate.setUTCDate(1);
15004 if (className.indexOf('month') !== -1) {
15005 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15007 var year = parseInt(html, 10) || 0;
15008 this.viewDate.setUTCFullYear(year);
15017 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
15018 var day = parseInt(html, 10) || 1;
15019 var year = this.viewDate.getUTCFullYear(),
15020 month = this.viewDate.getUTCMonth();
15022 if (className.indexOf('old') !== -1) {
15029 } else if (className.indexOf('new') !== -1) {
15037 this.date = this.UTCDate(year, month, day,0,0,0,0);
15038 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15040 this.setValue(this.formatDate(this.date));
15047 setStartDate: function(startDate)
15049 this.startDate = startDate || -Infinity;
15050 if (this.startDate !== -Infinity) {
15051 this.startDate = this.parseDate(this.startDate);
15054 this.updateNavArrows();
15057 setEndDate: function(endDate)
15059 this.endDate = endDate || Infinity;
15060 if (this.endDate !== Infinity) {
15061 this.endDate = this.parseDate(this.endDate);
15064 this.updateNavArrows();
15067 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15069 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15070 if (typeof(this.daysOfWeekDisabled) !== 'object') {
15071 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15073 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15074 return parseInt(d, 10);
15077 this.updateNavArrows();
15080 updateNavArrows: function()
15082 var d = new Date(this.viewDate),
15083 year = d.getUTCFullYear(),
15084 month = d.getUTCMonth();
15086 Roo.each(this.picker().select('.prev', true).elements, function(v){
15088 switch (this.viewMode) {
15091 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15097 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15104 Roo.each(this.picker().select('.next', true).elements, function(v){
15106 switch (this.viewMode) {
15109 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15115 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15123 moveMonth: function(date, dir)
15125 if (!dir) return date;
15126 var new_date = new Date(date.valueOf()),
15127 day = new_date.getUTCDate(),
15128 month = new_date.getUTCMonth(),
15129 mag = Math.abs(dir),
15131 dir = dir > 0 ? 1 : -1;
15134 // If going back one month, make sure month is not current month
15135 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15137 return new_date.getUTCMonth() == month;
15139 // If going forward one month, make sure month is as expected
15140 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15142 return new_date.getUTCMonth() != new_month;
15144 new_month = month + dir;
15145 new_date.setUTCMonth(new_month);
15146 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15147 if (new_month < 0 || new_month > 11)
15148 new_month = (new_month + 12) % 12;
15150 // For magnitudes >1, move one month at a time...
15151 for (var i=0; i<mag; i++)
15152 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15153 new_date = this.moveMonth(new_date, dir);
15154 // ...then reset the day, keeping it in the new month
15155 new_month = new_date.getUTCMonth();
15156 new_date.setUTCDate(day);
15158 return new_month != new_date.getUTCMonth();
15161 // Common date-resetting loop -- if date is beyond end of month, make it
15164 new_date.setUTCDate(--day);
15165 new_date.setUTCMonth(new_month);
15170 moveYear: function(date, dir)
15172 return this.moveMonth(date, dir*12);
15175 dateWithinRange: function(date)
15177 return date >= this.startDate && date <= this.endDate;
15183 this.picker().remove();
15188 Roo.apply(Roo.bootstrap.DateField, {
15199 html: '<i class="fa fa-arrow-left"/>'
15209 html: '<i class="fa fa-arrow-right"/>'
15251 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15252 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15253 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15254 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15255 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15268 navFnc: 'FullYear',
15273 navFnc: 'FullYear',
15278 Roo.apply(Roo.bootstrap.DateField, {
15282 cls: 'datepicker dropdown-menu',
15286 cls: 'datepicker-days',
15290 cls: 'table-condensed',
15292 Roo.bootstrap.DateField.head,
15296 Roo.bootstrap.DateField.footer
15303 cls: 'datepicker-months',
15307 cls: 'table-condensed',
15309 Roo.bootstrap.DateField.head,
15310 Roo.bootstrap.DateField.content,
15311 Roo.bootstrap.DateField.footer
15318 cls: 'datepicker-years',
15322 cls: 'table-condensed',
15324 Roo.bootstrap.DateField.head,
15325 Roo.bootstrap.DateField.content,
15326 Roo.bootstrap.DateField.footer
15345 * @class Roo.bootstrap.TimeField
15346 * @extends Roo.bootstrap.Input
15347 * Bootstrap DateField class
15351 * Create a new TimeField
15352 * @param {Object} config The config object
15355 Roo.bootstrap.TimeField = function(config){
15356 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15360 * Fires when this field show.
15361 * @param {Roo.bootstrap.DateField} this
15362 * @param {Mixed} date The date value
15367 * Fires when this field hide.
15368 * @param {Roo.bootstrap.DateField} this
15369 * @param {Mixed} date The date value
15374 * Fires when select a date.
15375 * @param {Roo.bootstrap.DateField} this
15376 * @param {Mixed} date The date value
15382 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
15385 * @cfg {String} format
15386 * The default time format string which can be overriden for localization support. The format must be
15387 * valid according to {@link Date#parseDate} (defaults to 'H:i').
15391 onRender: function(ct, position)
15394 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15396 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15398 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15400 this.pop = this.picker().select('>.datepicker-time',true).first();
15401 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
15403 this.picker().on('mousedown', this.onMousedown, this);
15404 this.picker().on('click', this.onClick, this);
15406 this.picker().addClass('datepicker-dropdown');
15411 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15412 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15413 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15414 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15415 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15416 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15420 fireKey: function(e){
15421 if (!this.picker().isVisible()){
15422 if (e.keyCode == 27) // allow escape to hide and re-show picker
15427 e.preventDefault();
15435 this.onTogglePeriod();
15438 this.onIncrementMinutes();
15441 this.onDecrementMinutes();
15450 onClick: function(e) {
15451 e.stopPropagation();
15452 e.preventDefault();
15455 picker : function()
15457 return this.el.select('.datepicker', true).first();
15460 fillTime: function()
15462 var time = this.pop.select('tbody', true).first();
15464 time.dom.innerHTML = '';
15479 cls: 'hours-up glyphicon glyphicon-chevron-up'
15499 cls: 'minutes-up glyphicon glyphicon-chevron-up'
15520 cls: 'timepicker-hour',
15535 cls: 'timepicker-minute',
15550 cls: 'btn btn-primary period',
15572 cls: 'hours-down glyphicon glyphicon-chevron-down'
15592 cls: 'minutes-down glyphicon glyphicon-chevron-down'
15610 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15617 var hours = this.time.getHours();
15618 var minutes = this.time.getMinutes();
15631 hours = hours - 12;
15635 hours = '0' + hours;
15639 minutes = '0' + minutes;
15642 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15643 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15644 this.pop.select('button', true).first().dom.innerHTML = period;
15650 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15652 var cls = ['bottom'];
15654 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15661 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15666 this.picker().addClass(cls.join('-'));
15670 Roo.each(cls, function(c){
15672 _this.picker().setTop(_this.inputEl().getHeight());
15676 _this.picker().setTop(0 - _this.picker().getHeight());
15681 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15685 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15692 onFocus : function()
15694 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15698 onBlur : function()
15700 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15706 this.picker().show();
15711 this.fireEvent('show', this, this.date);
15716 this.picker().hide();
15719 this.fireEvent('hide', this, this.date);
15722 setTime : function()
15725 this.setValue(this.time.format(this.format));
15727 this.fireEvent('select', this, this.date);
15732 onMousedown: function(e){
15733 e.stopPropagation();
15734 e.preventDefault();
15737 onIncrementHours: function()
15739 Roo.log('onIncrementHours');
15740 this.time = this.time.add(Date.HOUR, 1);
15745 onDecrementHours: function()
15747 Roo.log('onDecrementHours');
15748 this.time = this.time.add(Date.HOUR, -1);
15752 onIncrementMinutes: function()
15754 Roo.log('onIncrementMinutes');
15755 this.time = this.time.add(Date.MINUTE, 1);
15759 onDecrementMinutes: function()
15761 Roo.log('onDecrementMinutes');
15762 this.time = this.time.add(Date.MINUTE, -1);
15766 onTogglePeriod: function()
15768 Roo.log('onTogglePeriod');
15769 this.time = this.time.add(Date.HOUR, 12);
15776 Roo.apply(Roo.bootstrap.TimeField, {
15806 cls: 'btn btn-info ok',
15818 Roo.apply(Roo.bootstrap.TimeField, {
15822 cls: 'datepicker dropdown-menu',
15826 cls: 'datepicker-time',
15830 cls: 'table-condensed',
15832 Roo.bootstrap.TimeField.content,
15833 Roo.bootstrap.TimeField.footer
15852 * @class Roo.bootstrap.CheckBox
15853 * @extends Roo.bootstrap.Input
15854 * Bootstrap CheckBox class
15856 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15857 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15858 * @cfg {String} boxLabel The text that appears beside the checkbox
15859 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15860 * @cfg {Boolean} checked initnal the element
15864 * Create a new CheckBox
15865 * @param {Object} config The config object
15868 Roo.bootstrap.CheckBox = function(config){
15869 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15874 * Fires when the element is checked or unchecked.
15875 * @param {Roo.bootstrap.CheckBox} this This input
15876 * @param {Boolean} checked The new checked value
15882 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
15884 inputType: 'checkbox',
15891 getAutoCreate : function()
15893 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15899 cfg.cls = 'form-group checkbox' //input-group
15907 type : this.inputType,
15908 value : (!this.checked) ? this.valueOff : this.inputValue,
15909 cls : 'roo-checkbox', //'form-box',
15910 placeholder : this.placeholder || ''
15914 if (this.weight) { // Validity check?
15915 cfg.cls += " checkbox-" + this.weight;
15918 if (this.disabled) {
15919 input.disabled=true;
15923 input.checked = this.checked;
15927 input.name = this.name;
15931 input.cls += ' input-' + this.size;
15935 ['xs','sm','md','lg'].map(function(size){
15936 if (settings[size]) {
15937 cfg.cls += ' col-' + size + '-' + settings[size];
15943 var inputblock = input;
15948 if (this.before || this.after) {
15951 cls : 'input-group',
15955 inputblock.cn.push({
15957 cls : 'input-group-addon',
15961 inputblock.cn.push(input);
15963 inputblock.cn.push({
15965 cls : 'input-group-addon',
15972 if (align ==='left' && this.fieldLabel.length) {
15973 Roo.log("left and has label");
15979 cls : 'control-label col-md-' + this.labelWidth,
15980 html : this.fieldLabel
15984 cls : "col-md-" + (12 - this.labelWidth),
15991 } else if ( this.fieldLabel.length) {
15996 tag: this.boxLabel ? 'span' : 'label',
15998 cls: 'control-label box-input-label',
15999 //cls : 'input-group-addon',
16000 html : this.fieldLabel
16010 Roo.log(" no label && no align");
16011 cfg.cn = [ inputblock ] ;
16020 html: this.boxLabel
16032 * return the real input element.
16034 inputEl: function ()
16036 return this.el.select('input.roo-checkbox',true).first();
16041 return this.el.select('label.control-label',true).first();
16044 initEvents : function()
16046 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16048 this.inputEl().on('click', this.onClick, this);
16052 onClick : function()
16054 this.setChecked(!this.checked);
16057 setChecked : function(state,suppressEvent)
16059 this.checked = state;
16061 this.inputEl().dom.checked = state;
16063 if(suppressEvent !== true){
16064 this.fireEvent('check', this, state);
16067 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16071 setValue : function(v,suppressEvent)
16073 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16087 * @class Roo.bootstrap.Radio
16088 * @extends Roo.bootstrap.CheckBox
16089 * Bootstrap Radio class
16092 * Create a new Radio
16093 * @param {Object} config The config object
16096 Roo.bootstrap.Radio = function(config){
16097 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16101 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
16103 inputType: 'radio',
16107 getAutoCreate : function()
16109 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16115 cfg.cls = 'form-group radio' //input-group
16120 type : this.inputType,
16121 value : (!this.checked) ? this.valueOff : this.inputValue,
16123 placeholder : this.placeholder || ''
16126 if (this.weight) { // Validity check?
16127 cfg.cls += " radio-" + this.weight;
16129 if (this.disabled) {
16130 input.disabled=true;
16134 input.checked = this.checked;
16138 input.name = this.name;
16142 input.cls += ' input-' + this.size;
16146 ['xs','sm','md','lg'].map(function(size){
16147 if (settings[size]) {
16148 cfg.cls += ' col-' + size + '-' + settings[size];
16152 var inputblock = input;
16154 if (this.before || this.after) {
16157 cls : 'input-group',
16161 inputblock.cn.push({
16163 cls : 'input-group-addon',
16167 inputblock.cn.push(input);
16169 inputblock.cn.push({
16171 cls : 'input-group-addon',
16178 if (align ==='left' && this.fieldLabel.length) {
16179 Roo.log("left and has label");
16185 cls : 'control-label col-md-' + this.labelWidth,
16186 html : this.fieldLabel
16190 cls : "col-md-" + (12 - this.labelWidth),
16197 } else if ( this.fieldLabel.length) {
16204 cls: 'control-label box-input-label',
16205 //cls : 'input-group-addon',
16206 html : this.fieldLabel
16216 Roo.log(" no label && no align");
16231 html: this.boxLabel
16238 inputEl: function ()
16240 return this.el.select('input.roo-radio',true).first();
16242 onClick : function()
16244 this.setChecked(true);
16247 setChecked : function(state,suppressEvent)
16250 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16251 v.dom.checked = false;
16255 this.checked = state;
16256 this.inputEl().dom.checked = state;
16258 if(suppressEvent !== true){
16259 this.fireEvent('check', this, state);
16262 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16266 getGroupValue : function()
16269 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16270 if(v.dom.checked == true){
16271 value = v.dom.value;
16279 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
16280 * @return {Mixed} value The field value
16282 getValue : function(){
16283 return this.getGroupValue();
16289 //<script type="text/javascript">
16292 * Based Ext JS Library 1.1.1
16293 * Copyright(c) 2006-2007, Ext JS, LLC.
16299 * @class Roo.HtmlEditorCore
16300 * @extends Roo.Component
16301 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16303 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16306 Roo.HtmlEditorCore = function(config){
16309 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16314 * @event initialize
16315 * Fires when the editor is fully initialized (including the iframe)
16316 * @param {Roo.HtmlEditorCore} this
16321 * Fires when the editor is first receives the focus. Any insertion must wait
16322 * until after this event.
16323 * @param {Roo.HtmlEditorCore} this
16327 * @event beforesync
16328 * Fires before the textarea is updated with content from the editor iframe. Return false
16329 * to cancel the sync.
16330 * @param {Roo.HtmlEditorCore} this
16331 * @param {String} html
16335 * @event beforepush
16336 * Fires before the iframe editor is updated with content from the textarea. Return false
16337 * to cancel the push.
16338 * @param {Roo.HtmlEditorCore} this
16339 * @param {String} html
16344 * Fires when the textarea is updated with content from the editor iframe.
16345 * @param {Roo.HtmlEditorCore} this
16346 * @param {String} html
16351 * Fires when the iframe editor is updated with content from the textarea.
16352 * @param {Roo.HtmlEditorCore} this
16353 * @param {String} html
16358 * @event editorevent
16359 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16360 * @param {Roo.HtmlEditorCore} this
16365 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16367 // defaults : white / black...
16368 this.applyBlacklists();
16375 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
16379 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
16385 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
16390 * @cfg {Number} height (in pixels)
16394 * @cfg {Number} width (in pixels)
16399 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16402 stylesheets: false,
16407 // private properties
16408 validationEvent : false,
16410 initialized : false,
16412 sourceEditMode : false,
16413 onFocus : Roo.emptyFn,
16415 hideMode:'offsets',
16419 // blacklist + whitelisted elements..
16426 * Protected method that will not generally be called directly. It
16427 * is called when the editor initializes the iframe with HTML contents. Override this method if you
16428 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16430 getDocMarkup : function(){
16433 Roo.log(this.stylesheets);
16435 // inherit styels from page...??
16436 if (this.stylesheets === false) {
16438 Roo.get(document.head).select('style').each(function(node) {
16439 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16442 Roo.get(document.head).select('link').each(function(node) {
16443 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16446 } else if (!this.stylesheets.length) {
16448 st = '<style type="text/css">' +
16449 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16452 Roo.each(this.stylesheets, function(s) {
16453 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16458 st += '<style type="text/css">' +
16459 'IMG { cursor: pointer } ' +
16463 return '<html><head>' + st +
16464 //<style type="text/css">' +
16465 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16467 ' </head><body class="roo-htmleditor-body"></body></html>';
16471 onRender : function(ct, position)
16474 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16475 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16478 this.el.dom.style.border = '0 none';
16479 this.el.dom.setAttribute('tabIndex', -1);
16480 this.el.addClass('x-hidden hide');
16484 if(Roo.isIE){ // fix IE 1px bogus margin
16485 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16489 this.frameId = Roo.id();
16493 var iframe = this.owner.wrap.createChild({
16495 cls: 'form-control', // bootstrap..
16497 name: this.frameId,
16498 frameBorder : 'no',
16499 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
16504 this.iframe = iframe.dom;
16506 this.assignDocWin();
16508 this.doc.designMode = 'on';
16511 this.doc.write(this.getDocMarkup());
16515 var task = { // must defer to wait for browser to be ready
16517 //console.log("run task?" + this.doc.readyState);
16518 this.assignDocWin();
16519 if(this.doc.body || this.doc.readyState == 'complete'){
16521 this.doc.designMode="on";
16525 Roo.TaskMgr.stop(task);
16526 this.initEditor.defer(10, this);
16533 Roo.TaskMgr.start(task);
16540 onResize : function(w, h)
16542 Roo.log('resize: ' +w + ',' + h );
16543 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16547 if(typeof w == 'number'){
16549 this.iframe.style.width = w + 'px';
16551 if(typeof h == 'number'){
16553 this.iframe.style.height = h + 'px';
16555 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16562 * Toggles the editor between standard and source edit mode.
16563 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16565 toggleSourceEdit : function(sourceEditMode){
16567 this.sourceEditMode = sourceEditMode === true;
16569 if(this.sourceEditMode){
16571 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
16574 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16575 //this.iframe.className = '';
16578 //this.setSize(this.owner.wrap.getSize());
16579 //this.fireEvent('editmodechange', this, this.sourceEditMode);
16586 * Protected method that will not generally be called directly. If you need/want
16587 * custom HTML cleanup, this is the method you should override.
16588 * @param {String} html The HTML to be cleaned
16589 * return {String} The cleaned HTML
16591 cleanHtml : function(html){
16592 html = String(html);
16593 if(html.length > 5){
16594 if(Roo.isSafari){ // strip safari nonsense
16595 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16598 if(html == ' '){
16605 * HTML Editor -> Textarea
16606 * Protected method that will not generally be called directly. Syncs the contents
16607 * of the editor iframe with the textarea.
16609 syncValue : function(){
16610 if(this.initialized){
16611 var bd = (this.doc.body || this.doc.documentElement);
16612 //this.cleanUpPaste(); -- this is done else where and causes havoc..
16613 var html = bd.innerHTML;
16615 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16616 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16618 html = '<div style="'+m[0]+'">' + html + '</div>';
16621 html = this.cleanHtml(html);
16622 // fix up the special chars.. normaly like back quotes in word...
16623 // however we do not want to do this with chinese..
16624 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16625 var cc = b.charCodeAt();
16627 (cc >= 0x4E00 && cc < 0xA000 ) ||
16628 (cc >= 0x3400 && cc < 0x4E00 ) ||
16629 (cc >= 0xf900 && cc < 0xfb00 )
16635 if(this.owner.fireEvent('beforesync', this, html) !== false){
16636 this.el.dom.value = html;
16637 this.owner.fireEvent('sync', this, html);
16643 * Protected method that will not generally be called directly. Pushes the value of the textarea
16644 * into the iframe editor.
16646 pushValue : function(){
16647 if(this.initialized){
16648 var v = this.el.dom.value.trim();
16650 // if(v.length < 1){
16654 if(this.owner.fireEvent('beforepush', this, v) !== false){
16655 var d = (this.doc.body || this.doc.documentElement);
16657 this.cleanUpPaste();
16658 this.el.dom.value = d.innerHTML;
16659 this.owner.fireEvent('push', this, v);
16665 deferFocus : function(){
16666 this.focus.defer(10, this);
16670 focus : function(){
16671 if(this.win && !this.sourceEditMode){
16678 assignDocWin: function()
16680 var iframe = this.iframe;
16683 this.doc = iframe.contentWindow.document;
16684 this.win = iframe.contentWindow;
16686 // if (!Roo.get(this.frameId)) {
16689 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16690 // this.win = Roo.get(this.frameId).dom.contentWindow;
16692 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16696 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16697 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16702 initEditor : function(){
16703 //console.log("INIT EDITOR");
16704 this.assignDocWin();
16708 this.doc.designMode="on";
16710 this.doc.write(this.getDocMarkup());
16713 var dbody = (this.doc.body || this.doc.documentElement);
16714 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16715 // this copies styles from the containing element into thsi one..
16716 // not sure why we need all of this..
16717 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16719 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16720 //ss['background-attachment'] = 'fixed'; // w3c
16721 dbody.bgProperties = 'fixed'; // ie
16722 //Roo.DomHelper.applyStyles(dbody, ss);
16723 Roo.EventManager.on(this.doc, {
16724 //'mousedown': this.onEditorEvent,
16725 'mouseup': this.onEditorEvent,
16726 'dblclick': this.onEditorEvent,
16727 'click': this.onEditorEvent,
16728 'keyup': this.onEditorEvent,
16733 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16735 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16736 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16738 this.initialized = true;
16740 this.owner.fireEvent('initialize', this);
16745 onDestroy : function(){
16751 //for (var i =0; i < this.toolbars.length;i++) {
16752 // // fixme - ask toolbars for heights?
16753 // this.toolbars[i].onDestroy();
16756 //this.wrap.dom.innerHTML = '';
16757 //this.wrap.remove();
16762 onFirstFocus : function(){
16764 this.assignDocWin();
16767 this.activated = true;
16770 if(Roo.isGecko){ // prevent silly gecko errors
16772 var s = this.win.getSelection();
16773 if(!s.focusNode || s.focusNode.nodeType != 3){
16774 var r = s.getRangeAt(0);
16775 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16780 this.execCmd('useCSS', true);
16781 this.execCmd('styleWithCSS', false);
16784 this.owner.fireEvent('activate', this);
16788 adjustFont: function(btn){
16789 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16790 //if(Roo.isSafari){ // safari
16793 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16794 if(Roo.isSafari){ // safari
16795 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16796 v = (v < 10) ? 10 : v;
16797 v = (v > 48) ? 48 : v;
16798 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16803 v = Math.max(1, v+adjust);
16805 this.execCmd('FontSize', v );
16808 onEditorEvent : function(e){
16809 this.owner.fireEvent('editorevent', this, e);
16810 // this.updateToolbar();
16811 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16814 insertTag : function(tg)
16816 // could be a bit smarter... -> wrap the current selected tRoo..
16817 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16819 range = this.createRange(this.getSelection());
16820 var wrappingNode = this.doc.createElement(tg.toLowerCase());
16821 wrappingNode.appendChild(range.extractContents());
16822 range.insertNode(wrappingNode);
16829 this.execCmd("formatblock", tg);
16833 insertText : function(txt)
16837 var range = this.createRange();
16838 range.deleteContents();
16839 //alert(Sender.getAttribute('label'));
16841 range.insertNode(this.doc.createTextNode(txt));
16847 * Executes a Midas editor command on the editor document and performs necessary focus and
16848 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16849 * @param {String} cmd The Midas command
16850 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16852 relayCmd : function(cmd, value){
16854 this.execCmd(cmd, value);
16855 this.owner.fireEvent('editorevent', this);
16856 //this.updateToolbar();
16857 this.owner.deferFocus();
16861 * Executes a Midas editor command directly on the editor document.
16862 * For visual commands, you should use {@link #relayCmd} instead.
16863 * <b>This should only be called after the editor is initialized.</b>
16864 * @param {String} cmd The Midas command
16865 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16867 execCmd : function(cmd, value){
16868 this.doc.execCommand(cmd, false, value === undefined ? null : value);
16875 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16877 * @param {String} text | dom node..
16879 insertAtCursor : function(text)
16884 if(!this.activated){
16890 var r = this.doc.selection.createRange();
16901 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16905 // from jquery ui (MIT licenced)
16907 var win = this.win;
16909 if (win.getSelection && win.getSelection().getRangeAt) {
16910 range = win.getSelection().getRangeAt(0);
16911 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16912 range.insertNode(node);
16913 } else if (win.document.selection && win.document.selection.createRange) {
16914 // no firefox support
16915 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16916 win.document.selection.createRange().pasteHTML(txt);
16918 // no firefox support
16919 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16920 this.execCmd('InsertHTML', txt);
16929 mozKeyPress : function(e){
16931 var c = e.getCharCode(), cmd;
16934 c = String.fromCharCode(c).toLowerCase();
16948 this.cleanUpPaste.defer(100, this);
16956 e.preventDefault();
16964 fixKeys : function(){ // load time branching for fastest keydown performance
16966 return function(e){
16967 var k = e.getKey(), r;
16970 r = this.doc.selection.createRange();
16973 r.pasteHTML('    ');
16980 r = this.doc.selection.createRange();
16982 var target = r.parentElement();
16983 if(!target || target.tagName.toLowerCase() != 'li'){
16985 r.pasteHTML('<br />');
16991 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16992 this.cleanUpPaste.defer(100, this);
16998 }else if(Roo.isOpera){
16999 return function(e){
17000 var k = e.getKey();
17004 this.execCmd('InsertHTML','    ');
17007 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17008 this.cleanUpPaste.defer(100, this);
17013 }else if(Roo.isSafari){
17014 return function(e){
17015 var k = e.getKey();
17019 this.execCmd('InsertText','\t');
17023 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17024 this.cleanUpPaste.defer(100, this);
17032 getAllAncestors: function()
17034 var p = this.getSelectedNode();
17037 a.push(p); // push blank onto stack..
17038 p = this.getParentElement();
17042 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17046 a.push(this.doc.body);
17050 lastSelNode : false,
17053 getSelection : function()
17055 this.assignDocWin();
17056 return Roo.isIE ? this.doc.selection : this.win.getSelection();
17059 getSelectedNode: function()
17061 // this may only work on Gecko!!!
17063 // should we cache this!!!!
17068 var range = this.createRange(this.getSelection()).cloneRange();
17071 var parent = range.parentElement();
17073 var testRange = range.duplicate();
17074 testRange.moveToElementText(parent);
17075 if (testRange.inRange(range)) {
17078 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17081 parent = parent.parentElement;
17086 // is ancestor a text element.
17087 var ac = range.commonAncestorContainer;
17088 if (ac.nodeType == 3) {
17089 ac = ac.parentNode;
17092 var ar = ac.childNodes;
17095 var other_nodes = [];
17096 var has_other_nodes = false;
17097 for (var i=0;i<ar.length;i++) {
17098 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
17101 // fullly contained node.
17103 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17108 // probably selected..
17109 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17110 other_nodes.push(ar[i]);
17114 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
17119 has_other_nodes = true;
17121 if (!nodes.length && other_nodes.length) {
17122 nodes= other_nodes;
17124 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17130 createRange: function(sel)
17132 // this has strange effects when using with
17133 // top toolbar - not sure if it's a great idea.
17134 //this.editor.contentWindow.focus();
17135 if (typeof sel != "undefined") {
17137 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17139 return this.doc.createRange();
17142 return this.doc.createRange();
17145 getParentElement: function()
17148 this.assignDocWin();
17149 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17151 var range = this.createRange(sel);
17154 var p = range.commonAncestorContainer;
17155 while (p.nodeType == 3) { // text node
17166 * Range intersection.. the hard stuff...
17170 * [ -- selected range --- ]
17174 * if end is before start or hits it. fail.
17175 * if start is after end or hits it fail.
17177 * if either hits (but other is outside. - then it's not
17183 // @see http://www.thismuchiknow.co.uk/?p=64.
17184 rangeIntersectsNode : function(range, node)
17186 var nodeRange = node.ownerDocument.createRange();
17188 nodeRange.selectNode(node);
17190 nodeRange.selectNodeContents(node);
17193 var rangeStartRange = range.cloneRange();
17194 rangeStartRange.collapse(true);
17196 var rangeEndRange = range.cloneRange();
17197 rangeEndRange.collapse(false);
17199 var nodeStartRange = nodeRange.cloneRange();
17200 nodeStartRange.collapse(true);
17202 var nodeEndRange = nodeRange.cloneRange();
17203 nodeEndRange.collapse(false);
17205 return rangeStartRange.compareBoundaryPoints(
17206 Range.START_TO_START, nodeEndRange) == -1 &&
17207 rangeEndRange.compareBoundaryPoints(
17208 Range.START_TO_START, nodeStartRange) == 1;
17212 rangeCompareNode : function(range, node)
17214 var nodeRange = node.ownerDocument.createRange();
17216 nodeRange.selectNode(node);
17218 nodeRange.selectNodeContents(node);
17222 range.collapse(true);
17224 nodeRange.collapse(true);
17226 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17227 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
17229 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17231 var nodeIsBefore = ss == 1;
17232 var nodeIsAfter = ee == -1;
17234 if (nodeIsBefore && nodeIsAfter)
17236 if (!nodeIsBefore && nodeIsAfter)
17237 return 1; //right trailed.
17239 if (nodeIsBefore && !nodeIsAfter)
17240 return 2; // left trailed.
17245 // private? - in a new class?
17246 cleanUpPaste : function()
17248 // cleans up the whole document..
17249 Roo.log('cleanuppaste');
17251 this.cleanUpChildren(this.doc.body);
17252 var clean = this.cleanWordChars(this.doc.body.innerHTML);
17253 if (clean != this.doc.body.innerHTML) {
17254 this.doc.body.innerHTML = clean;
17259 cleanWordChars : function(input) {// change the chars to hex code
17260 var he = Roo.HtmlEditorCore;
17262 var output = input;
17263 Roo.each(he.swapCodes, function(sw) {
17264 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17266 output = output.replace(swapper, sw[1]);
17273 cleanUpChildren : function (n)
17275 if (!n.childNodes.length) {
17278 for (var i = n.childNodes.length-1; i > -1 ; i--) {
17279 this.cleanUpChild(n.childNodes[i]);
17286 cleanUpChild : function (node)
17289 //console.log(node);
17290 if (node.nodeName == "#text") {
17291 // clean up silly Windows -- stuff?
17294 if (node.nodeName == "#comment") {
17295 node.parentNode.removeChild(node);
17296 // clean up silly Windows -- stuff?
17299 var lcname = node.tagName.toLowerCase();
17300 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17301 // whitelist of tags..
17303 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17305 node.parentNode.removeChild(node);
17310 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17312 // remove <a name=....> as rendering on yahoo mailer is borked with this.
17313 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17315 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17316 // remove_keep_children = true;
17319 if (remove_keep_children) {
17320 this.cleanUpChildren(node);
17321 // inserts everything just before this node...
17322 while (node.childNodes.length) {
17323 var cn = node.childNodes[0];
17324 node.removeChild(cn);
17325 node.parentNode.insertBefore(cn, node);
17327 node.parentNode.removeChild(node);
17331 if (!node.attributes || !node.attributes.length) {
17332 this.cleanUpChildren(node);
17336 function cleanAttr(n,v)
17339 if (v.match(/^\./) || v.match(/^\//)) {
17342 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17345 if (v.match(/^#/)) {
17348 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17349 node.removeAttribute(n);
17353 var cwhite = this.cwhite;
17354 var cblack = this.cblack;
17356 function cleanStyle(n,v)
17358 if (v.match(/expression/)) { //XSS?? should we even bother..
17359 node.removeAttribute(n);
17363 var parts = v.split(/;/);
17366 Roo.each(parts, function(p) {
17367 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17371 var l = p.split(':').shift().replace(/\s+/g,'');
17372 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17374 if ( cwhite.length && cblack.indexOf(l) > -1) {
17375 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17376 //node.removeAttribute(n);
17380 // only allow 'c whitelisted system attributes'
17381 if ( cwhite.length && cwhite.indexOf(l) < 0) {
17382 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17383 //node.removeAttribute(n);
17393 if (clean.length) {
17394 node.setAttribute(n, clean.join(';'));
17396 node.removeAttribute(n);
17402 for (var i = node.attributes.length-1; i > -1 ; i--) {
17403 var a = node.attributes[i];
17406 if (a.name.toLowerCase().substr(0,2)=='on') {
17407 node.removeAttribute(a.name);
17410 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17411 node.removeAttribute(a.name);
17414 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17415 cleanAttr(a.name,a.value); // fixme..
17418 if (a.name == 'style') {
17419 cleanStyle(a.name,a.value);
17422 /// clean up MS crap..
17423 // tecnically this should be a list of valid class'es..
17426 if (a.name == 'class') {
17427 if (a.value.match(/^Mso/)) {
17428 node.className = '';
17431 if (a.value.match(/body/)) {
17432 node.className = '';
17443 this.cleanUpChildren(node);
17448 * Clean up MS wordisms...
17450 cleanWord : function(node)
17453 var cleanWordChildren = function()
17455 if (!node.childNodes.length) {
17458 for (var i = node.childNodes.length-1; i > -1 ; i--) {
17459 _t.cleanWord(node.childNodes[i]);
17465 this.cleanWord(this.doc.body);
17468 if (node.nodeName == "#text") {
17469 // clean up silly Windows -- stuff?
17472 if (node.nodeName == "#comment") {
17473 node.parentNode.removeChild(node);
17474 // clean up silly Windows -- stuff?
17478 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17479 node.parentNode.removeChild(node);
17483 // remove - but keep children..
17484 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17485 while (node.childNodes.length) {
17486 var cn = node.childNodes[0];
17487 node.removeChild(cn);
17488 node.parentNode.insertBefore(cn, node);
17490 node.parentNode.removeChild(node);
17491 cleanWordChildren();
17495 if (node.className.length) {
17497 var cn = node.className.split(/\W+/);
17499 Roo.each(cn, function(cls) {
17500 if (cls.match(/Mso[a-zA-Z]+/)) {
17505 node.className = cna.length ? cna.join(' ') : '';
17507 node.removeAttribute("class");
17511 if (node.hasAttribute("lang")) {
17512 node.removeAttribute("lang");
17515 if (node.hasAttribute("style")) {
17517 var styles = node.getAttribute("style").split(";");
17519 Roo.each(styles, function(s) {
17520 if (!s.match(/:/)) {
17523 var kv = s.split(":");
17524 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17527 // what ever is left... we allow.
17530 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17531 if (!nstyle.length) {
17532 node.removeAttribute('style');
17536 cleanWordChildren();
17540 domToHTML : function(currentElement, depth, nopadtext) {
17542 depth = depth || 0;
17543 nopadtext = nopadtext || false;
17545 if (!currentElement) {
17546 return this.domToHTML(this.doc.body);
17549 //Roo.log(currentElement);
17551 var allText = false;
17552 var nodeName = currentElement.nodeName;
17553 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17555 if (nodeName == '#text') {
17556 return currentElement.nodeValue;
17561 if (nodeName != 'BODY') {
17564 // Prints the node tagName, such as <A>, <IMG>, etc
17567 for(i = 0; i < currentElement.attributes.length;i++) {
17569 var aname = currentElement.attributes.item(i).name;
17570 if (!currentElement.attributes.item(i).value.length) {
17573 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17576 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17585 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17588 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17593 // Traverse the tree
17595 var currentElementChild = currentElement.childNodes.item(i);
17596 var allText = true;
17597 var innerHTML = '';
17599 while (currentElementChild) {
17600 // Formatting code (indent the tree so it looks nice on the screen)
17601 var nopad = nopadtext;
17602 if (lastnode == 'SPAN') {
17606 if (currentElementChild.nodeName == '#text') {
17607 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17608 if (!nopad && toadd.length > 80) {
17609 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
17611 innerHTML += toadd;
17614 currentElementChild = currentElement.childNodes.item(i);
17620 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
17622 // Recursively traverse the tree structure of the child node
17623 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
17624 lastnode = currentElementChild.nodeName;
17626 currentElementChild=currentElement.childNodes.item(i);
17632 // The remaining code is mostly for formatting the tree
17633 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
17638 ret+= "</"+tagName+">";
17644 applyBlacklists : function()
17646 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
17647 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
17651 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17652 if (b.indexOf(tag) > -1) {
17655 this.white.push(tag);
17659 Roo.each(w, function(tag) {
17660 if (b.indexOf(tag) > -1) {
17663 if (this.white.indexOf(tag) > -1) {
17666 this.white.push(tag);
17671 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17672 if (w.indexOf(tag) > -1) {
17675 this.black.push(tag);
17679 Roo.each(b, function(tag) {
17680 if (w.indexOf(tag) > -1) {
17683 if (this.black.indexOf(tag) > -1) {
17686 this.black.push(tag);
17691 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
17692 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
17696 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17697 if (b.indexOf(tag) > -1) {
17700 this.cwhite.push(tag);
17704 Roo.each(w, function(tag) {
17705 if (b.indexOf(tag) > -1) {
17708 if (this.cwhite.indexOf(tag) > -1) {
17711 this.cwhite.push(tag);
17716 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17717 if (w.indexOf(tag) > -1) {
17720 this.cblack.push(tag);
17724 Roo.each(b, function(tag) {
17725 if (w.indexOf(tag) > -1) {
17728 if (this.cblack.indexOf(tag) > -1) {
17731 this.cblack.push(tag);
17736 // hide stuff that is not compatible
17750 * @event specialkey
17754 * @cfg {String} fieldClass @hide
17757 * @cfg {String} focusClass @hide
17760 * @cfg {String} autoCreate @hide
17763 * @cfg {String} inputType @hide
17766 * @cfg {String} invalidClass @hide
17769 * @cfg {String} invalidText @hide
17772 * @cfg {String} msgFx @hide
17775 * @cfg {String} validateOnBlur @hide
17779 Roo.HtmlEditorCore.white = [
17780 'area', 'br', 'img', 'input', 'hr', 'wbr',
17782 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
17783 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
17784 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
17785 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
17786 'table', 'ul', 'xmp',
17788 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
17791 'dir', 'menu', 'ol', 'ul', 'dl',
17797 Roo.HtmlEditorCore.black = [
17798 // 'embed', 'object', // enable - backend responsiblity to clean thiese
17800 'base', 'basefont', 'bgsound', 'blink', 'body',
17801 'frame', 'frameset', 'head', 'html', 'ilayer',
17802 'iframe', 'layer', 'link', 'meta', 'object',
17803 'script', 'style' ,'title', 'xml' // clean later..
17805 Roo.HtmlEditorCore.clean = [
17806 'script', 'style', 'title', 'xml'
17808 Roo.HtmlEditorCore.remove = [
17813 Roo.HtmlEditorCore.ablack = [
17817 Roo.HtmlEditorCore.aclean = [
17818 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
17822 Roo.HtmlEditorCore.pwhite= [
17823 'http', 'https', 'mailto'
17826 // white listed style attributes.
17827 Roo.HtmlEditorCore.cwhite= [
17828 // 'text-align', /// default is to allow most things..
17834 // black listed style attributes.
17835 Roo.HtmlEditorCore.cblack= [
17836 // 'font-size' -- this can be set by the project
17840 Roo.HtmlEditorCore.swapCodes =[
17859 * @class Roo.bootstrap.HtmlEditor
17860 * @extends Roo.bootstrap.TextArea
17861 * Bootstrap HtmlEditor class
17864 * Create a new HtmlEditor
17865 * @param {Object} config The config object
17868 Roo.bootstrap.HtmlEditor = function(config){
17869 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17870 if (!this.toolbars) {
17871 this.toolbars = [];
17873 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17876 * @event initialize
17877 * Fires when the editor is fully initialized (including the iframe)
17878 * @param {HtmlEditor} this
17883 * Fires when the editor is first receives the focus. Any insertion must wait
17884 * until after this event.
17885 * @param {HtmlEditor} this
17889 * @event beforesync
17890 * Fires before the textarea is updated with content from the editor iframe. Return false
17891 * to cancel the sync.
17892 * @param {HtmlEditor} this
17893 * @param {String} html
17897 * @event beforepush
17898 * Fires before the iframe editor is updated with content from the textarea. Return false
17899 * to cancel the push.
17900 * @param {HtmlEditor} this
17901 * @param {String} html
17906 * Fires when the textarea is updated with content from the editor iframe.
17907 * @param {HtmlEditor} this
17908 * @param {String} html
17913 * Fires when the iframe editor is updated with content from the textarea.
17914 * @param {HtmlEditor} this
17915 * @param {String} html
17919 * @event editmodechange
17920 * Fires when the editor switches edit modes
17921 * @param {HtmlEditor} this
17922 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17924 editmodechange: true,
17926 * @event editorevent
17927 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17928 * @param {HtmlEditor} this
17932 * @event firstfocus
17933 * Fires when on first focus - needed by toolbars..
17934 * @param {HtmlEditor} this
17939 * Auto save the htmlEditor value as a file into Events
17940 * @param {HtmlEditor} this
17944 * @event savedpreview
17945 * preview the saved version of htmlEditor
17946 * @param {HtmlEditor} this
17953 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
17957 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17962 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
17967 * @cfg {Number} height (in pixels)
17971 * @cfg {Number} width (in pixels)
17976 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17979 stylesheets: false,
17984 // private properties
17985 validationEvent : false,
17987 initialized : false,
17990 onFocus : Roo.emptyFn,
17992 hideMode:'offsets',
17995 tbContainer : false,
17997 toolbarContainer :function() {
17998 return this.wrap.select('.x-html-editor-tb',true).first();
18002 * Protected method that will not generally be called directly. It
18003 * is called when the editor creates its toolbar. Override this method if you need to
18004 * add custom toolbar buttons.
18005 * @param {HtmlEditor} editor
18007 createToolbar : function(){
18009 Roo.log("create toolbars");
18011 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18012 this.toolbars[0].render(this.toolbarContainer());
18016 // if (!editor.toolbars || !editor.toolbars.length) {
18017 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18020 // for (var i =0 ; i < editor.toolbars.length;i++) {
18021 // editor.toolbars[i] = Roo.factory(
18022 // typeof(editor.toolbars[i]) == 'string' ?
18023 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
18024 // Roo.bootstrap.HtmlEditor);
18025 // editor.toolbars[i].init(editor);
18031 onRender : function(ct, position)
18033 // Roo.log("Call onRender: " + this.xtype);
18035 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18037 this.wrap = this.inputEl().wrap({
18038 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18041 this.editorcore.onRender(ct, position);
18043 if (this.resizable) {
18044 this.resizeEl = new Roo.Resizable(this.wrap, {
18048 minHeight : this.height,
18049 height: this.height,
18050 handles : this.resizable,
18053 resize : function(r, w, h) {
18054 _t.onResize(w,h); // -something
18060 this.createToolbar(this);
18063 if(!this.width && this.resizable){
18064 this.setSize(this.wrap.getSize());
18066 if (this.resizeEl) {
18067 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18068 // should trigger onReize..
18074 onResize : function(w, h)
18076 Roo.log('resize: ' +w + ',' + h );
18077 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18081 if(this.inputEl() ){
18082 if(typeof w == 'number'){
18083 var aw = w - this.wrap.getFrameWidth('lr');
18084 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18087 if(typeof h == 'number'){
18088 var tbh = -11; // fixme it needs to tool bar size!
18089 for (var i =0; i < this.toolbars.length;i++) {
18090 // fixme - ask toolbars for heights?
18091 tbh += this.toolbars[i].el.getHeight();
18092 //if (this.toolbars[i].footer) {
18093 // tbh += this.toolbars[i].footer.el.getHeight();
18101 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18102 ah -= 5; // knock a few pixes off for look..
18103 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18107 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18108 this.editorcore.onResize(ew,eh);
18113 * Toggles the editor between standard and source edit mode.
18114 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18116 toggleSourceEdit : function(sourceEditMode)
18118 this.editorcore.toggleSourceEdit(sourceEditMode);
18120 if(this.editorcore.sourceEditMode){
18121 Roo.log('editor - showing textarea');
18124 // Roo.log(this.syncValue());
18126 this.inputEl().removeClass(['hide', 'x-hidden']);
18127 this.inputEl().dom.removeAttribute('tabIndex');
18128 this.inputEl().focus();
18130 Roo.log('editor - hiding textarea');
18132 // Roo.log(this.pushValue());
18135 this.inputEl().addClass(['hide', 'x-hidden']);
18136 this.inputEl().dom.setAttribute('tabIndex', -1);
18137 //this.deferFocus();
18140 if(this.resizable){
18141 this.setSize(this.wrap.getSize());
18144 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18147 // private (for BoxComponent)
18148 adjustSize : Roo.BoxComponent.prototype.adjustSize,
18150 // private (for BoxComponent)
18151 getResizeEl : function(){
18155 // private (for BoxComponent)
18156 getPositionEl : function(){
18161 initEvents : function(){
18162 this.originalValue = this.getValue();
18166 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18169 // markInvalid : Roo.emptyFn,
18171 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18174 // clearInvalid : Roo.emptyFn,
18176 setValue : function(v){
18177 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18178 this.editorcore.pushValue();
18183 deferFocus : function(){
18184 this.focus.defer(10, this);
18188 focus : function(){
18189 this.editorcore.focus();
18195 onDestroy : function(){
18201 for (var i =0; i < this.toolbars.length;i++) {
18202 // fixme - ask toolbars for heights?
18203 this.toolbars[i].onDestroy();
18206 this.wrap.dom.innerHTML = '';
18207 this.wrap.remove();
18212 onFirstFocus : function(){
18213 //Roo.log("onFirstFocus");
18214 this.editorcore.onFirstFocus();
18215 for (var i =0; i < this.toolbars.length;i++) {
18216 this.toolbars[i].onFirstFocus();
18222 syncValue : function()
18224 this.editorcore.syncValue();
18227 pushValue : function()
18229 this.editorcore.pushValue();
18233 // hide stuff that is not compatible
18247 * @event specialkey
18251 * @cfg {String} fieldClass @hide
18254 * @cfg {String} focusClass @hide
18257 * @cfg {String} autoCreate @hide
18260 * @cfg {String} inputType @hide
18263 * @cfg {String} invalidClass @hide
18266 * @cfg {String} invalidText @hide
18269 * @cfg {String} msgFx @hide
18272 * @cfg {String} validateOnBlur @hide
18281 Roo.namespace('Roo.bootstrap.htmleditor');
18283 * @class Roo.bootstrap.HtmlEditorToolbar1
18288 new Roo.bootstrap.HtmlEditor({
18291 new Roo.bootstrap.HtmlEditorToolbar1({
18292 disable : { fonts: 1 , format: 1, ..., ... , ...],
18298 * @cfg {Object} disable List of elements to disable..
18299 * @cfg {Array} btns List of additional buttons.
18303 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18306 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18309 Roo.apply(this, config);
18311 // default disabled, based on 'good practice'..
18312 this.disable = this.disable || {};
18313 Roo.applyIf(this.disable, {
18316 specialElements : true
18318 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18320 this.editor = config.editor;
18321 this.editorcore = config.editor.editorcore;
18323 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18325 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18326 // dont call parent... till later.
18328 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
18333 editorcore : false,
18338 "h1","h2","h3","h4","h5","h6",
18340 "abbr", "acronym", "address", "cite", "samp", "var",
18344 onRender : function(ct, position)
18346 // Roo.log("Call onRender: " + this.xtype);
18348 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18350 this.el.dom.style.marginBottom = '0';
18352 var editorcore = this.editorcore;
18353 var editor= this.editor;
18356 var btn = function(id,cmd , toggle, handler){
18358 var event = toggle ? 'toggle' : 'click';
18363 xns: Roo.bootstrap,
18366 enableToggle:toggle !== false,
18368 pressed : toggle ? false : null,
18371 a.listeners[toggle ? 'toggle' : 'click'] = function() {
18372 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
18381 xns: Roo.bootstrap,
18382 glyphicon : 'font',
18386 xns: Roo.bootstrap,
18390 Roo.each(this.formats, function(f) {
18391 style.menu.items.push({
18393 xns: Roo.bootstrap,
18394 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18399 editorcore.insertTag(this.tagname);
18406 children.push(style);
18409 btn('bold',false,true);
18410 btn('italic',false,true);
18411 btn('align-left', 'justifyleft',true);
18412 btn('align-center', 'justifycenter',true);
18413 btn('align-right' , 'justifyright',true);
18414 btn('link', false, false, function(btn) {
18415 //Roo.log("create link?");
18416 var url = prompt(this.createLinkText, this.defaultLinkValue);
18417 if(url && url != 'http:/'+'/'){
18418 this.editorcore.relayCmd('createlink', url);
18421 btn('list','insertunorderedlist',true);
18422 btn('pencil', false,true, function(btn){
18425 this.toggleSourceEdit(btn.pressed);
18431 xns: Roo.bootstrap,
18436 xns: Roo.bootstrap,
18441 cog.menu.items.push({
18443 xns: Roo.bootstrap,
18444 html : Clean styles,
18449 editorcore.insertTag(this.tagname);
18458 this.xtype = 'NavSimplebar';
18460 for(var i=0;i< children.length;i++) {
18462 this.buttons.add(this.addxtypeChild(children[i]));
18466 editor.on('editorevent', this.updateToolbar, this);
18468 onBtnClick : function(id)
18470 this.editorcore.relayCmd(id);
18471 this.editorcore.focus();
18475 * Protected method that will not generally be called directly. It triggers
18476 * a toolbar update by reading the markup state of the current selection in the editor.
18478 updateToolbar: function(){
18480 if(!this.editorcore.activated){
18481 this.editor.onFirstFocus(); // is this neeed?
18485 var btns = this.buttons;
18486 var doc = this.editorcore.doc;
18487 btns.get('bold').setActive(doc.queryCommandState('bold'));
18488 btns.get('italic').setActive(doc.queryCommandState('italic'));
18489 //btns.get('underline').setActive(doc.queryCommandState('underline'));
18491 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18492 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18493 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18495 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18496 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18499 var ans = this.editorcore.getAllAncestors();
18500 if (this.formatCombo) {
18503 var store = this.formatCombo.store;
18504 this.formatCombo.setValue("");
18505 for (var i =0; i < ans.length;i++) {
18506 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18508 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18516 // hides menus... - so this cant be on a menu...
18517 Roo.bootstrap.MenuMgr.hideAll();
18519 Roo.bootstrap.MenuMgr.hideAll();
18520 //this.editorsyncValue();
18522 onFirstFocus: function() {
18523 this.buttons.each(function(item){
18527 toggleSourceEdit : function(sourceEditMode){
18530 if(sourceEditMode){
18531 Roo.log("disabling buttons");
18532 this.buttons.each( function(item){
18533 if(item.cmd != 'pencil'){
18539 Roo.log("enabling buttons");
18540 if(this.editorcore.initialized){
18541 this.buttons.each( function(item){
18547 Roo.log("calling toggole on editor");
18548 // tell the editor that it's been pressed..
18549 this.editor.toggleSourceEdit(sourceEditMode);
18559 * @class Roo.bootstrap.Table.AbstractSelectionModel
18560 * @extends Roo.util.Observable
18561 * Abstract base class for grid SelectionModels. It provides the interface that should be
18562 * implemented by descendant classes. This class should not be directly instantiated.
18565 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18566 this.locked = false;
18567 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18571 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
18572 /** @ignore Called by the grid automatically. Do not call directly. */
18573 init : function(grid){
18579 * Locks the selections.
18582 this.locked = true;
18586 * Unlocks the selections.
18588 unlock : function(){
18589 this.locked = false;
18593 * Returns true if the selections are locked.
18594 * @return {Boolean}
18596 isLocked : function(){
18597 return this.locked;
18601 * @extends Roo.bootstrap.Table.AbstractSelectionModel
18602 * @class Roo.bootstrap.Table.RowSelectionModel
18603 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18604 * It supports multiple selections and keyboard selection/navigation.
18606 * @param {Object} config
18609 Roo.bootstrap.Table.RowSelectionModel = function(config){
18610 Roo.apply(this, config);
18611 this.selections = new Roo.util.MixedCollection(false, function(o){
18616 this.lastActive = false;
18620 * @event selectionchange
18621 * Fires when the selection changes
18622 * @param {SelectionModel} this
18624 "selectionchange" : true,
18626 * @event afterselectionchange
18627 * Fires after the selection changes (eg. by key press or clicking)
18628 * @param {SelectionModel} this
18630 "afterselectionchange" : true,
18632 * @event beforerowselect
18633 * Fires when a row is selected being selected, return false to cancel.
18634 * @param {SelectionModel} this
18635 * @param {Number} rowIndex The selected index
18636 * @param {Boolean} keepExisting False if other selections will be cleared
18638 "beforerowselect" : true,
18641 * Fires when a row is selected.
18642 * @param {SelectionModel} this
18643 * @param {Number} rowIndex The selected index
18644 * @param {Roo.data.Record} r The record
18646 "rowselect" : true,
18648 * @event rowdeselect
18649 * Fires when a row is deselected.
18650 * @param {SelectionModel} this
18651 * @param {Number} rowIndex The selected index
18653 "rowdeselect" : true
18655 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18656 this.locked = false;
18659 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
18661 * @cfg {Boolean} singleSelect
18662 * True to allow selection of only one row at a time (defaults to false)
18664 singleSelect : false,
18667 initEvents : function(){
18669 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18670 this.grid.on("mousedown", this.handleMouseDown, this);
18671 }else{ // allow click to work like normal
18672 this.grid.on("rowclick", this.handleDragableRowClick, this);
18675 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18676 "up" : function(e){
18678 this.selectPrevious(e.shiftKey);
18679 }else if(this.last !== false && this.lastActive !== false){
18680 var last = this.last;
18681 this.selectRange(this.last, this.lastActive-1);
18682 this.grid.getView().focusRow(this.lastActive);
18683 if(last !== false){
18687 this.selectFirstRow();
18689 this.fireEvent("afterselectionchange", this);
18691 "down" : function(e){
18693 this.selectNext(e.shiftKey);
18694 }else if(this.last !== false && this.lastActive !== false){
18695 var last = this.last;
18696 this.selectRange(this.last, this.lastActive+1);
18697 this.grid.getView().focusRow(this.lastActive);
18698 if(last !== false){
18702 this.selectFirstRow();
18704 this.fireEvent("afterselectionchange", this);
18709 var view = this.grid.view;
18710 view.on("refresh", this.onRefresh, this);
18711 view.on("rowupdated", this.onRowUpdated, this);
18712 view.on("rowremoved", this.onRemove, this);
18716 onRefresh : function(){
18717 var ds = this.grid.dataSource, i, v = this.grid.view;
18718 var s = this.selections;
18719 s.each(function(r){
18720 if((i = ds.indexOfId(r.id)) != -1){
18729 onRemove : function(v, index, r){
18730 this.selections.remove(r);
18734 onRowUpdated : function(v, index, r){
18735 if(this.isSelected(r)){
18736 v.onRowSelect(index);
18742 * @param {Array} records The records to select
18743 * @param {Boolean} keepExisting (optional) True to keep existing selections
18745 selectRecords : function(records, keepExisting){
18747 this.clearSelections();
18749 var ds = this.grid.dataSource;
18750 for(var i = 0, len = records.length; i < len; i++){
18751 this.selectRow(ds.indexOf(records[i]), true);
18756 * Gets the number of selected rows.
18759 getCount : function(){
18760 return this.selections.length;
18764 * Selects the first row in the grid.
18766 selectFirstRow : function(){
18771 * Select the last row.
18772 * @param {Boolean} keepExisting (optional) True to keep existing selections
18774 selectLastRow : function(keepExisting){
18775 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18779 * Selects the row immediately following the last selected row.
18780 * @param {Boolean} keepExisting (optional) True to keep existing selections
18782 selectNext : function(keepExisting){
18783 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18784 this.selectRow(this.last+1, keepExisting);
18785 this.grid.getView().focusRow(this.last);
18790 * Selects the row that precedes the last selected row.
18791 * @param {Boolean} keepExisting (optional) True to keep existing selections
18793 selectPrevious : function(keepExisting){
18795 this.selectRow(this.last-1, keepExisting);
18796 this.grid.getView().focusRow(this.last);
18801 * Returns the selected records
18802 * @return {Array} Array of selected records
18804 getSelections : function(){
18805 return [].concat(this.selections.items);
18809 * Returns the first selected record.
18812 getSelected : function(){
18813 return this.selections.itemAt(0);
18818 * Clears all selections.
18820 clearSelections : function(fast){
18821 if(this.locked) return;
18823 var ds = this.grid.dataSource;
18824 var s = this.selections;
18825 s.each(function(r){
18826 this.deselectRow(ds.indexOfId(r.id));
18830 this.selections.clear();
18837 * Selects all rows.
18839 selectAll : function(){
18840 if(this.locked) return;
18841 this.selections.clear();
18842 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18843 this.selectRow(i, true);
18848 * Returns True if there is a selection.
18849 * @return {Boolean}
18851 hasSelection : function(){
18852 return this.selections.length > 0;
18856 * Returns True if the specified row is selected.
18857 * @param {Number/Record} record The record or index of the record to check
18858 * @return {Boolean}
18860 isSelected : function(index){
18861 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18862 return (r && this.selections.key(r.id) ? true : false);
18866 * Returns True if the specified record id is selected.
18867 * @param {String} id The id of record to check
18868 * @return {Boolean}
18870 isIdSelected : function(id){
18871 return (this.selections.key(id) ? true : false);
18875 handleMouseDown : function(e, t){
18876 var view = this.grid.getView(), rowIndex;
18877 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18880 if(e.shiftKey && this.last !== false){
18881 var last = this.last;
18882 this.selectRange(last, rowIndex, e.ctrlKey);
18883 this.last = last; // reset the last
18884 view.focusRow(rowIndex);
18886 var isSelected = this.isSelected(rowIndex);
18887 if(e.button !== 0 && isSelected){
18888 view.focusRow(rowIndex);
18889 }else if(e.ctrlKey && isSelected){
18890 this.deselectRow(rowIndex);
18891 }else if(!isSelected){
18892 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18893 view.focusRow(rowIndex);
18896 this.fireEvent("afterselectionchange", this);
18899 handleDragableRowClick : function(grid, rowIndex, e)
18901 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18902 this.selectRow(rowIndex, false);
18903 grid.view.focusRow(rowIndex);
18904 this.fireEvent("afterselectionchange", this);
18909 * Selects multiple rows.
18910 * @param {Array} rows Array of the indexes of the row to select
18911 * @param {Boolean} keepExisting (optional) True to keep existing selections
18913 selectRows : function(rows, keepExisting){
18915 this.clearSelections();
18917 for(var i = 0, len = rows.length; i < len; i++){
18918 this.selectRow(rows[i], true);
18923 * Selects a range of rows. All rows in between startRow and endRow are also selected.
18924 * @param {Number} startRow The index of the first row in the range
18925 * @param {Number} endRow The index of the last row in the range
18926 * @param {Boolean} keepExisting (optional) True to retain existing selections
18928 selectRange : function(startRow, endRow, keepExisting){
18929 if(this.locked) return;
18931 this.clearSelections();
18933 if(startRow <= endRow){
18934 for(var i = startRow; i <= endRow; i++){
18935 this.selectRow(i, true);
18938 for(var i = startRow; i >= endRow; i--){
18939 this.selectRow(i, true);
18945 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18946 * @param {Number} startRow The index of the first row in the range
18947 * @param {Number} endRow The index of the last row in the range
18949 deselectRange : function(startRow, endRow, preventViewNotify){
18950 if(this.locked) return;
18951 for(var i = startRow; i <= endRow; i++){
18952 this.deselectRow(i, preventViewNotify);
18958 * @param {Number} row The index of the row to select
18959 * @param {Boolean} keepExisting (optional) True to keep existing selections
18961 selectRow : function(index, keepExisting, preventViewNotify){
18962 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18963 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18964 if(!keepExisting || this.singleSelect){
18965 this.clearSelections();
18967 var r = this.grid.dataSource.getAt(index);
18968 this.selections.add(r);
18969 this.last = this.lastActive = index;
18970 if(!preventViewNotify){
18971 this.grid.getView().onRowSelect(index);
18973 this.fireEvent("rowselect", this, index, r);
18974 this.fireEvent("selectionchange", this);
18980 * @param {Number} row The index of the row to deselect
18982 deselectRow : function(index, preventViewNotify){
18983 if(this.locked) return;
18984 if(this.last == index){
18987 if(this.lastActive == index){
18988 this.lastActive = false;
18990 var r = this.grid.dataSource.getAt(index);
18991 this.selections.remove(r);
18992 if(!preventViewNotify){
18993 this.grid.getView().onRowDeselect(index);
18995 this.fireEvent("rowdeselect", this, index);
18996 this.fireEvent("selectionchange", this);
19000 restoreLast : function(){
19002 this.last = this._last;
19007 acceptsNav : function(row, col, cm){
19008 return !cm.isHidden(col) && cm.isCellEditable(col, row);
19012 onEditorKey : function(field, e){
19013 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19018 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19020 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19022 }else if(k == e.ENTER && !e.ctrlKey){
19026 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19028 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19030 }else if(k == e.ESC){
19034 g.startEditing(newCell[0], newCell[1]);
19039 * Ext JS Library 1.1.1
19040 * Copyright(c) 2006-2007, Ext JS, LLC.
19042 * Originally Released Under LGPL - original licence link has changed is not relivant.
19045 * <script type="text/javascript">
19049 * @class Roo.bootstrap.PagingToolbar
19051 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19053 * Create a new PagingToolbar
19054 * @param {Object} config The config object
19056 Roo.bootstrap.PagingToolbar = function(config)
19058 // old args format still supported... - xtype is prefered..
19059 // created from xtype...
19060 var ds = config.dataSource;
19061 this.toolbarItems = [];
19062 if (config.items) {
19063 this.toolbarItems = config.items;
19064 // config.items = [];
19067 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19074 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19078 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19080 * @cfg {Roo.data.Store} dataSource
19081 * The underlying data store providing the paged data
19084 * @cfg {String/HTMLElement/Element} container
19085 * container The id or element that will contain the toolbar
19088 * @cfg {Boolean} displayInfo
19089 * True to display the displayMsg (defaults to false)
19092 * @cfg {Number} pageSize
19093 * The number of records to display per page (defaults to 20)
19097 * @cfg {String} displayMsg
19098 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19100 displayMsg : 'Displaying {0} - {1} of {2}',
19102 * @cfg {String} emptyMsg
19103 * The message to display when no records are found (defaults to "No data to display")
19105 emptyMsg : 'No data to display',
19107 * Customizable piece of the default paging text (defaults to "Page")
19110 beforePageText : "Page",
19112 * Customizable piece of the default paging text (defaults to "of %0")
19115 afterPageText : "of {0}",
19117 * Customizable piece of the default paging text (defaults to "First Page")
19120 firstText : "First Page",
19122 * Customizable piece of the default paging text (defaults to "Previous Page")
19125 prevText : "Previous Page",
19127 * Customizable piece of the default paging text (defaults to "Next Page")
19130 nextText : "Next Page",
19132 * Customizable piece of the default paging text (defaults to "Last Page")
19135 lastText : "Last Page",
19137 * Customizable piece of the default paging text (defaults to "Refresh")
19140 refreshText : "Refresh",
19144 onRender : function(ct, position)
19146 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19147 this.navgroup.parentId = this.id;
19148 this.navgroup.onRender(this.el, null);
19149 // add the buttons to the navgroup
19151 if(this.displayInfo){
19152 Roo.log(this.el.select('ul.navbar-nav',true).first());
19153 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19154 this.displayEl = this.el.select('.x-paging-info', true).first();
19155 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19156 // this.displayEl = navel.el.select('span',true).first();
19162 Roo.each(_this.buttons, function(e){
19163 Roo.factory(e).onRender(_this.el, null);
19167 Roo.each(_this.toolbarItems, function(e) {
19168 _this.navgroup.addItem(e);
19171 this.first = this.navgroup.addItem({
19172 tooltip: this.firstText,
19174 icon : 'fa fa-backward',
19176 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19179 this.prev = this.navgroup.addItem({
19180 tooltip: this.prevText,
19182 icon : 'fa fa-step-backward',
19184 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
19186 //this.addSeparator();
19189 var field = this.navgroup.addItem( {
19191 cls : 'x-paging-position',
19193 html : this.beforePageText +
19194 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19195 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
19198 this.field = field.el.select('input', true).first();
19199 this.field.on("keydown", this.onPagingKeydown, this);
19200 this.field.on("focus", function(){this.dom.select();});
19203 this.afterTextEl = field.el.select('.x-paging-after',true).first();
19204 //this.field.setHeight(18);
19205 //this.addSeparator();
19206 this.next = this.navgroup.addItem({
19207 tooltip: this.nextText,
19209 html : ' <i class="fa fa-step-forward">',
19211 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
19213 this.last = this.navgroup.addItem({
19214 tooltip: this.lastText,
19215 icon : 'fa fa-forward',
19218 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
19220 //this.addSeparator();
19221 this.loading = this.navgroup.addItem({
19222 tooltip: this.refreshText,
19223 icon: 'fa fa-refresh',
19225 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19231 updateInfo : function(){
19232 if(this.displayEl){
19233 var count = this.ds.getCount();
19234 var msg = count == 0 ?
19238 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
19240 this.displayEl.update(msg);
19245 onLoad : function(ds, r, o){
19246 this.cursor = o.params ? o.params.start : 0;
19247 var d = this.getPageData(),
19251 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19252 this.field.dom.value = ap;
19253 this.first.setDisabled(ap == 1);
19254 this.prev.setDisabled(ap == 1);
19255 this.next.setDisabled(ap == ps);
19256 this.last.setDisabled(ap == ps);
19257 this.loading.enable();
19262 getPageData : function(){
19263 var total = this.ds.getTotalCount();
19266 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19267 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19272 onLoadError : function(){
19273 this.loading.enable();
19277 onPagingKeydown : function(e){
19278 var k = e.getKey();
19279 var d = this.getPageData();
19281 var v = this.field.dom.value, pageNum;
19282 if(!v || isNaN(pageNum = parseInt(v, 10))){
19283 this.field.dom.value = d.activePage;
19286 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19287 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19290 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))
19292 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19293 this.field.dom.value = pageNum;
19294 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19297 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19299 var v = this.field.dom.value, pageNum;
19300 var increment = (e.shiftKey) ? 10 : 1;
19301 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19303 if(!v || isNaN(pageNum = parseInt(v, 10))) {
19304 this.field.dom.value = d.activePage;
19307 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19309 this.field.dom.value = parseInt(v, 10) + increment;
19310 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19311 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19318 beforeLoad : function(){
19320 this.loading.disable();
19325 onClick : function(which){
19332 ds.load({params:{start: 0, limit: this.pageSize}});
19335 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19338 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19341 var total = ds.getTotalCount();
19342 var extra = total % this.pageSize;
19343 var lastStart = extra ? (total - extra) : total-this.pageSize;
19344 ds.load({params:{start: lastStart, limit: this.pageSize}});
19347 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19353 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19354 * @param {Roo.data.Store} store The data store to unbind
19356 unbind : function(ds){
19357 ds.un("beforeload", this.beforeLoad, this);
19358 ds.un("load", this.onLoad, this);
19359 ds.un("loadexception", this.onLoadError, this);
19360 ds.un("remove", this.updateInfo, this);
19361 ds.un("add", this.updateInfo, this);
19362 this.ds = undefined;
19366 * Binds the paging toolbar to the specified {@link Roo.data.Store}
19367 * @param {Roo.data.Store} store The data store to bind
19369 bind : function(ds){
19370 ds.on("beforeload", this.beforeLoad, this);
19371 ds.on("load", this.onLoad, this);
19372 ds.on("loadexception", this.onLoadError, this);
19373 ds.on("remove", this.updateInfo, this);
19374 ds.on("add", this.updateInfo, this);
19385 * @class Roo.bootstrap.MessageBar
19386 * @extends Roo.bootstrap.Component
19387 * Bootstrap MessageBar class
19388 * @cfg {String} html contents of the MessageBar
19389 * @cfg {String} weight (info | success | warning | danger) default info
19390 * @cfg {String} beforeClass insert the bar before the given class
19391 * @cfg {Boolean} closable (true | false) default false
19392 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19395 * Create a new Element
19396 * @param {Object} config The config object
19399 Roo.bootstrap.MessageBar = function(config){
19400 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19403 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
19409 beforeClass: 'bootstrap-sticky-wrap',
19411 getAutoCreate : function(){
19415 cls: 'alert alert-dismissable alert-' + this.weight,
19420 html: this.html || ''
19426 cfg.cls += ' alert-messages-fixed';
19440 onRender : function(ct, position)
19442 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19445 var cfg = Roo.apply({}, this.getAutoCreate());
19449 cfg.cls += ' ' + this.cls;
19452 cfg.style = this.style;
19454 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19456 this.el.setVisibilityMode(Roo.Element.DISPLAY);
19459 this.el.select('>button.close').on('click', this.hide, this);
19465 if (!this.rendered) {
19471 this.fireEvent('show', this);
19477 if (!this.rendered) {
19483 this.fireEvent('hide', this);
19486 update : function()
19488 // var e = this.el.dom.firstChild;
19490 // if(this.closable){
19491 // e = e.nextSibling;
19494 // e.data = this.html || '';
19496 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19512 * @class Roo.bootstrap.Graph
19513 * @extends Roo.bootstrap.Component
19514 * Bootstrap Graph class
19518 @cfg {String} graphtype bar | vbar | pie
19519 @cfg {number} g_x coodinator | centre x (pie)
19520 @cfg {number} g_y coodinator | centre y (pie)
19521 @cfg {number} g_r radius (pie)
19522 @cfg {number} g_height height of the chart (respected by all elements in the set)
19523 @cfg {number} g_width width of the chart (respected by all elements in the set)
19524 @cfg {Object} title The title of the chart
19527 -opts (object) options for the chart
19529 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19530 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19532 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.
19533 o stacked (boolean) whether or not to tread values as in a stacked bar chart
19535 o stretch (boolean)
19537 -opts (object) options for the pie
19540 o startAngle (number)
19541 o endAngle (number)
19545 * Create a new Input
19546 * @param {Object} config The config object
19549 Roo.bootstrap.Graph = function(config){
19550 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19556 * The img click event for the img.
19557 * @param {Roo.EventObject} e
19563 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
19574 //g_colors: this.colors,
19581 getAutoCreate : function(){
19592 onRender : function(ct,position){
19593 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19594 this.raphael = Raphael(this.el.dom);
19596 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19597 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19598 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19599 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19601 r.text(160, 10, "Single Series Chart").attr(txtattr);
19602 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19603 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19604 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19606 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19607 r.barchart(330, 10, 300, 220, data1);
19608 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19609 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19612 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19613 // r.barchart(30, 30, 560, 250, xdata, {
19614 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19615 // axis : "0 0 1 1",
19616 // axisxlabels : xdata
19617 // //yvalues : cols,
19620 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19622 // this.load(null,xdata,{
19623 // axis : "0 0 1 1",
19624 // axisxlabels : xdata
19629 load : function(graphtype,xdata,opts){
19630 this.raphael.clear();
19632 graphtype = this.graphtype;
19637 var r = this.raphael,
19638 fin = function () {
19639 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19641 fout = function () {
19642 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19644 pfin = function() {
19645 this.sector.stop();
19646 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19649 this.label[0].stop();
19650 this.label[0].attr({ r: 7.5 });
19651 this.label[1].attr({ "font-weight": 800 });
19654 pfout = function() {
19655 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19658 this.label[0].animate({ r: 5 }, 500, "bounce");
19659 this.label[1].attr({ "font-weight": 400 });
19665 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19668 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19671 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
19672 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19674 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19681 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19686 setTitle: function(o)
19691 initEvents: function() {
19694 this.el.on('click', this.onClick, this);
19698 onClick : function(e)
19700 Roo.log('img onclick');
19701 this.fireEvent('click', this, e);
19713 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19716 * @class Roo.bootstrap.dash.NumberBox
19717 * @extends Roo.bootstrap.Component
19718 * Bootstrap NumberBox class
19719 * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19720 * @cfg {String} headline Box headline
19721 * @cfg {String} content Box content
19722 * @cfg {String} icon Box icon
19723 * @cfg {String} footer Footer text
19724 * @cfg {String} fhref Footer href
19727 * Create a new NumberBox
19728 * @param {Object} config The config object
19732 Roo.bootstrap.dash.NumberBox = function(config){
19733 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19737 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
19747 getAutoCreate : function(){
19751 cls : 'small-box bg-' + this.bgcolor,
19759 cls : 'roo-headline',
19760 html : this.headline
19764 cls : 'roo-content',
19765 html : this.content
19779 cls : 'ion ' + this.icon
19788 cls : 'small-box-footer',
19789 href : this.fhref || '#',
19793 cfg.cn.push(footer);
19800 onRender : function(ct,position){
19801 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19808 setHeadline: function (value)
19810 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19813 setFooter: function (value, href)
19815 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19818 this.el.select('a.small-box-footer',true).first().attr('href', href);
19823 setContent: function (value)
19825 this.el.select('.roo-content',true).first().dom.innerHTML = value;
19828 initEvents: function()
19842 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19845 * @class Roo.bootstrap.dash.TabBox
19846 * @extends Roo.bootstrap.Component
19847 * Bootstrap TabBox class
19848 * @cfg {String} title Title of the TabBox
19849 * @cfg {String} icon Icon of the TabBox
19850 * @cfg {Boolean} showtabs (true|false) show the tabs default true
19853 * Create a new TabBox
19854 * @param {Object} config The config object
19858 Roo.bootstrap.dash.TabBox = function(config){
19859 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19864 * When a pane is added
19865 * @param {Roo.bootstrap.dash.TabPane} pane
19872 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
19878 getChildContainer : function()
19880 return this.el.select('.tab-content', true).first();
19883 getAutoCreate : function(){
19887 cls: 'pull-left header',
19895 cls: 'fa ' + this.icon
19902 cls: 'nav-tabs-custom',
19906 cls: 'nav nav-tabs pull-right',
19913 cls: 'tab-content no-padding',
19921 initEvents : function()
19923 //Roo.log('add add pane handler');
19924 this.on('addpane', this.onAddPane, this);
19927 * Updates the box title
19928 * @param {String} html to set the title to.
19930 setTitle : function(value)
19932 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19934 onAddPane : function(pane)
19936 //Roo.log('addpane');
19938 // tabs are rendere left to right..
19939 if(!this.showtabs){
19943 var ctr = this.el.select('.nav-tabs', true).first();
19946 var existing = ctr.select('.nav-tab',true);
19947 var qty = existing.getCount();;
19950 var tab = ctr.createChild({
19952 cls : 'nav-tab' + (qty ? '' : ' active'),
19960 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19963 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19965 pane.el.addClass('active');
19970 onTabClick : function(ev,un,ob,pane)
19972 //Roo.log('tab - prev default');
19973 ev.preventDefault();
19976 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19977 pane.tab.addClass('active');
19978 //Roo.log(pane.title);
19979 this.getChildContainer().select('.tab-pane',true).removeClass('active');
19980 // technically we should have a deactivate event.. but maybe add later.
19981 // and it should not de-activate the selected tab...
19983 pane.el.addClass('active');
19984 pane.fireEvent('activate');
19999 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20001 * @class Roo.bootstrap.TabPane
20002 * @extends Roo.bootstrap.Component
20003 * Bootstrap TabPane class
20004 * @cfg {Boolean} active (false | true) Default false
20005 * @cfg {String} title title of panel
20009 * Create a new TabPane
20010 * @param {Object} config The config object
20013 Roo.bootstrap.dash.TabPane = function(config){
20014 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20020 * When a pane is activated
20021 * @param {Roo.bootstrap.dash.TabPane} pane
20028 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
20033 // the tabBox that this is attached to.
20036 getAutoCreate : function()
20044 cfg.cls += ' active';
20049 initEvents : function()
20051 //Roo.log('trigger add pane handler');
20052 this.parent().fireEvent('addpane', this)
20056 * Updates the tab title
20057 * @param {String} html to set the title to.
20059 setTitle: function(str)
20065 this.tab.select('a', true).first().dom.innerHTML = str;
20082 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20085 * @class Roo.bootstrap.menu.Menu
20086 * @extends Roo.bootstrap.Component
20087 * Bootstrap Menu class - container for Menu
20088 * @cfg {String} html Text of the menu
20089 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20090 * @cfg {String} icon Font awesome icon
20091 * @cfg {String} pos Menu align to (top | bottom) default bottom
20095 * Create a new Menu
20096 * @param {Object} config The config object
20100 Roo.bootstrap.menu.Menu = function(config){
20101 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20105 * @event beforeshow
20106 * Fires before this menu is displayed
20107 * @param {Roo.bootstrap.menu.Menu} this
20111 * @event beforehide
20112 * Fires before this menu is hidden
20113 * @param {Roo.bootstrap.menu.Menu} this
20118 * Fires after this menu is displayed
20119 * @param {Roo.bootstrap.menu.Menu} this
20124 * Fires after this menu is hidden
20125 * @param {Roo.bootstrap.menu.Menu} this
20130 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20131 * @param {Roo.bootstrap.menu.Menu} this
20132 * @param {Roo.EventObject} e
20139 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
20143 weight : 'default',
20148 getChildContainer : function() {
20149 if(this.isSubMenu){
20153 return this.el.select('ul.dropdown-menu', true).first();
20156 getAutoCreate : function()
20161 cls : 'roo-menu-text',
20169 cls : 'fa ' + this.icon
20180 cls : 'dropdown-button btn btn-' + this.weight,
20185 cls : 'dropdown-toggle btn btn-' + this.weight,
20195 cls : 'dropdown-menu'
20201 if(this.pos == 'top'){
20202 cfg.cls += ' dropup';
20205 if(this.isSubMenu){
20208 cls : 'dropdown-menu'
20215 onRender : function(ct, position)
20217 this.isSubMenu = ct.hasClass('dropdown-submenu');
20219 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20222 initEvents : function()
20224 if(this.isSubMenu){
20228 this.hidden = true;
20230 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20231 this.triggerEl.on('click', this.onTriggerPress, this);
20233 this.buttonEl = this.el.select('button.dropdown-button', true).first();
20234 this.buttonEl.on('click', this.onClick, this);
20240 if(this.isSubMenu){
20244 return this.el.select('ul.dropdown-menu', true).first();
20247 onClick : function(e)
20249 this.fireEvent("click", this, e);
20252 onTriggerPress : function(e)
20254 if (this.isVisible()) {
20261 isVisible : function(){
20262 return !this.hidden;
20267 this.fireEvent("beforeshow", this);
20269 this.hidden = false;
20270 this.el.addClass('open');
20272 Roo.get(document).on("mouseup", this.onMouseUp, this);
20274 this.fireEvent("show", this);
20281 this.fireEvent("beforehide", this);
20283 this.hidden = true;
20284 this.el.removeClass('open');
20286 Roo.get(document).un("mouseup", this.onMouseUp);
20288 this.fireEvent("hide", this);
20291 onMouseUp : function()
20305 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20308 * @class Roo.bootstrap.menu.Item
20309 * @extends Roo.bootstrap.Component
20310 * Bootstrap MenuItem class
20311 * @cfg {Boolean} submenu (true | false) default false
20312 * @cfg {String} html text of the item
20313 * @cfg {String} href the link
20314 * @cfg {Boolean} disable (true | false) default false
20315 * @cfg {Boolean} preventDefault (true | false) default true
20316 * @cfg {String} icon Font awesome icon
20317 * @cfg {String} pos Submenu align to (left | right) default right
20321 * Create a new Item
20322 * @param {Object} config The config object
20326 Roo.bootstrap.menu.Item = function(config){
20327 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20331 * Fires when the mouse is hovering over this menu
20332 * @param {Roo.bootstrap.menu.Item} this
20333 * @param {Roo.EventObject} e
20338 * Fires when the mouse exits this menu
20339 * @param {Roo.bootstrap.menu.Item} this
20340 * @param {Roo.EventObject} e
20346 * The raw click event for the entire grid.
20347 * @param {Roo.EventObject} e
20353 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
20358 preventDefault: true,
20363 getAutoCreate : function()
20368 cls : 'roo-menu-item-text',
20376 cls : 'fa ' + this.icon
20385 href : this.href || '#',
20392 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20396 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20398 if(this.pos == 'left'){
20399 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20406 initEvents : function()
20408 this.el.on('mouseover', this.onMouseOver, this);
20409 this.el.on('mouseout', this.onMouseOut, this);
20411 this.el.select('a', true).first().on('click', this.onClick, this);
20415 onClick : function(e)
20417 if(this.preventDefault){
20418 e.preventDefault();
20421 this.fireEvent("click", this, e);
20424 onMouseOver : function(e)
20426 if(this.submenu && this.pos == 'left'){
20427 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20430 this.fireEvent("mouseover", this, e);
20433 onMouseOut : function(e)
20435 this.fireEvent("mouseout", this, e);
20447 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20450 * @class Roo.bootstrap.menu.Separator
20451 * @extends Roo.bootstrap.Component
20452 * Bootstrap Separator class
20455 * Create a new Separator
20456 * @param {Object} config The config object
20460 Roo.bootstrap.menu.Separator = function(config){
20461 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20464 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
20466 getAutoCreate : function(){