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
2032 * @cfg {Boolean} animate (true | false) default true
2035 * Create a new Modal Dialog
2036 * @param {Object} config The config object
2039 Roo.bootstrap.Modal = function(config){
2040 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2045 * The raw btnclick event for the button
2046 * @param {Roo.EventObject} e
2050 this.buttons = this.buttons || [];
2053 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2055 title : 'test dialog',
2062 specificTitle: false,
2064 buttonPosition: 'right',
2068 onRender : function(ct, position)
2070 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2073 var cfg = Roo.apply({}, this.getAutoCreate());
2076 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2078 //if (!cfg.name.length) {
2082 cfg.cls += ' ' + this.cls;
2085 cfg.style = this.style;
2087 this.el = Roo.get(document.body).createChild(cfg, position);
2089 //var type = this.el.dom.type;
2091 if(this.tabIndex !== undefined){
2092 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2097 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2098 this.maskEl.enableDisplayMode("block");
2100 //this.el.addClass("x-dlg-modal");
2102 if (this.buttons.length) {
2103 Roo.each(this.buttons, function(bb) {
2104 b = Roo.apply({}, bb);
2105 b.xns = b.xns || Roo.bootstrap;
2106 b.xtype = b.xtype || 'Button';
2107 if (typeof(b.listeners) == 'undefined') {
2108 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2111 var btn = Roo.factory(b);
2113 btn.onRender(this.el.select('.modal-footer div').first());
2117 // render the children.
2120 if(typeof(this.items) != 'undefined'){
2121 var items = this.items;
2124 for(var i =0;i < items.length;i++) {
2125 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2129 this.items = nitems;
2131 this.body = this.el.select('.modal-body',true).first();
2132 this.close = this.el.select('.modal-header .close', true).first();
2133 this.footer = this.el.select('.modal-footer',true).first();
2135 //this.el.addClass([this.fieldClass, this.cls]);
2138 getAutoCreate : function(){
2143 html : this.html || ''
2148 cls : 'modal-title',
2152 if(this.specificTitle){
2158 style : 'display: none',
2161 cls: "modal-dialog",
2164 cls : "modal-content",
2167 cls : 'modal-header',
2179 cls : 'modal-footer',
2183 cls: 'btn-' + this.buttonPosition
2200 modal.cls += ' fade';
2206 getChildContainer : function() {
2208 return this.el.select('.modal-body',true).first();
2211 getButtonContainer : function() {
2212 return this.el.select('.modal-footer div',true).first();
2215 initEvents : function()
2217 this.el.select('.modal-header .close').on('click', this.hide, this);
2219 // this.addxtype(this);
2223 if (!this.rendered) {
2227 this.el.setStyle('display', 'block');
2231 (function(){ _this.el.addClass('in'); }).defer(50);
2233 this.el.addClass('in');
2236 Roo.get(document.body).addClass("x-body-masked");
2237 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2239 this.el.setStyle('zIndex', '10001');
2240 this.fireEvent('show', this);
2246 Roo.log('Modal hide?!');
2248 Roo.get(document.body).removeClass("x-body-masked");
2249 this.el.removeClass('in');
2253 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2255 this.el.setStyle('display', 'none');
2258 this.fireEvent('hide', this);
2261 addButton : function(str, cb)
2265 var b = Roo.apply({}, { html : str } );
2266 b.xns = b.xns || Roo.bootstrap;
2267 b.xtype = b.xtype || 'Button';
2268 if (typeof(b.listeners) == 'undefined') {
2269 b.listeners = { click : cb.createDelegate(this) };
2272 var btn = Roo.factory(b);
2274 btn.onRender(this.el.select('.modal-footer div').first());
2280 setDefaultButton : function(btn)
2282 //this.el.select('.modal-footer').()
2284 resizeTo: function(w,h)
2288 setContentSize : function(w, h)
2292 onButtonClick: function(btn,e)
2295 this.fireEvent('btnclick', btn.name, e);
2297 setTitle: function(str) {
2298 this.el.select('.modal-title',true).first().dom.innerHTML = str;
2304 Roo.apply(Roo.bootstrap.Modal, {
2306 * Button config that displays a single OK button
2315 * Button config that displays Yes and No buttons
2331 * Button config that displays OK and Cancel buttons
2346 * Button config that displays Yes, No and Cancel buttons
2368 * messagebox - can be used as a replace
2372 * @class Roo.MessageBox
2373 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2377 Roo.Msg.alert('Status', 'Changes saved successfully.');
2379 // Prompt for user data:
2380 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2382 // process text value...
2386 // Show a dialog using config options:
2388 title:'Save Changes?',
2389 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2390 buttons: Roo.Msg.YESNOCANCEL,
2397 Roo.bootstrap.MessageBox = function(){
2398 var dlg, opt, mask, waitTimer;
2399 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2400 var buttons, activeTextEl, bwidth;
2404 var handleButton = function(button){
2406 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2410 var handleHide = function(){
2412 dlg.el.removeClass(opt.cls);
2415 // Roo.TaskMgr.stop(waitTimer);
2416 // waitTimer = null;
2421 var updateButtons = function(b){
2424 buttons["ok"].hide();
2425 buttons["cancel"].hide();
2426 buttons["yes"].hide();
2427 buttons["no"].hide();
2428 //dlg.footer.dom.style.display = 'none';
2431 dlg.footer.dom.style.display = '';
2432 for(var k in buttons){
2433 if(typeof buttons[k] != "function"){
2436 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2437 width += buttons[k].el.getWidth()+15;
2447 var handleEsc = function(d, k, e){
2448 if(opt && opt.closable !== false){
2458 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2459 * @return {Roo.BasicDialog} The BasicDialog element
2461 getDialog : function(){
2463 dlg = new Roo.bootstrap.Modal( {
2466 //constraintoviewport:false,
2468 //collapsible : false,
2473 //buttonAlign:"center",
2474 closeClick : function(){
2475 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2478 handleButton("cancel");
2483 dlg.on("hide", handleHide);
2485 //dlg.addKeyListener(27, handleEsc);
2487 this.buttons = buttons;
2488 var bt = this.buttonText;
2489 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2490 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2491 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2492 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2494 bodyEl = dlg.body.createChild({
2496 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2497 '<textarea class="roo-mb-textarea"></textarea>' +
2498 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2500 msgEl = bodyEl.dom.firstChild;
2501 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2502 textboxEl.enableDisplayMode();
2503 textboxEl.addKeyListener([10,13], function(){
2504 if(dlg.isVisible() && opt && opt.buttons){
2507 }else if(opt.buttons.yes){
2508 handleButton("yes");
2512 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2513 textareaEl.enableDisplayMode();
2514 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2515 progressEl.enableDisplayMode();
2516 var pf = progressEl.dom.firstChild;
2518 pp = Roo.get(pf.firstChild);
2519 pp.setHeight(pf.offsetHeight);
2527 * Updates the message box body text
2528 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2529 * the XHTML-compliant non-breaking space character '&#160;')
2530 * @return {Roo.MessageBox} This message box
2532 updateText : function(text){
2533 if(!dlg.isVisible() && !opt.width){
2534 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2536 msgEl.innerHTML = text || ' ';
2538 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2539 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2541 Math.min(opt.width || cw , this.maxWidth),
2542 Math.max(opt.minWidth || this.minWidth, bwidth)
2545 activeTextEl.setWidth(w);
2547 if(dlg.isVisible()){
2548 dlg.fixedcenter = false;
2550 // to big, make it scroll. = But as usual stupid IE does not support
2553 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2554 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2555 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2557 bodyEl.dom.style.height = '';
2558 bodyEl.dom.style.overflowY = '';
2561 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2563 bodyEl.dom.style.overflowX = '';
2566 dlg.setContentSize(w, bodyEl.getHeight());
2567 if(dlg.isVisible()){
2568 dlg.fixedcenter = true;
2574 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2575 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2576 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2577 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2578 * @return {Roo.MessageBox} This message box
2580 updateProgress : function(value, text){
2582 this.updateText(text);
2584 if (pp) { // weird bug on my firefox - for some reason this is not defined
2585 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2591 * Returns true if the message box is currently displayed
2592 * @return {Boolean} True if the message box is visible, else false
2594 isVisible : function(){
2595 return dlg && dlg.isVisible();
2599 * Hides the message box if it is displayed
2602 if(this.isVisible()){
2608 * Displays a new message box, or reinitializes an existing message box, based on the config options
2609 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2610 * The following config object properties are supported:
2612 Property Type Description
2613 ---------- --------------- ------------------------------------------------------------------------------------
2614 animEl String/Element An id or Element from which the message box should animate as it opens and
2615 closes (defaults to undefined)
2616 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2617 cancel:'Bar'}), or false to not show any buttons (defaults to false)
2618 closable Boolean False to hide the top-right close button (defaults to true). Note that
2619 progress and wait dialogs will ignore this property and always hide the
2620 close button as they can only be closed programmatically.
2621 cls String A custom CSS class to apply to the message box element
2622 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
2623 displayed (defaults to 75)
2624 fn Function A callback function to execute after closing the dialog. The arguments to the
2625 function will be btn (the name of the button that was clicked, if applicable,
2626 e.g. "ok"), and text (the value of the active text field, if applicable).
2627 Progress and wait dialogs will ignore this option since they do not respond to
2628 user actions and can only be closed programmatically, so any required function
2629 should be called by the same code after it closes the dialog.
2630 icon String A CSS class that provides a background image to be used as an icon for
2631 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2632 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
2633 minWidth Number The minimum width in pixels of the message box (defaults to 100)
2634 modal Boolean False to allow user interaction with the page while the message box is
2635 displayed (defaults to true)
2636 msg String A string that will replace the existing message box body text (defaults
2637 to the XHTML-compliant non-breaking space character ' ')
2638 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
2639 progress Boolean True to display a progress bar (defaults to false)
2640 progressText String The text to display inside the progress bar if progress = true (defaults to '')
2641 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
2642 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
2643 title String The title text
2644 value String The string value to set into the active textbox element if displayed
2645 wait Boolean True to display a progress bar (defaults to false)
2646 width Number The width of the dialog in pixels
2653 msg: 'Please enter your address:',
2655 buttons: Roo.MessageBox.OKCANCEL,
2658 animEl: 'addAddressBtn'
2661 * @param {Object} config Configuration options
2662 * @return {Roo.MessageBox} This message box
2664 show : function(options)
2667 // this causes nightmares if you show one dialog after another
2668 // especially on callbacks..
2670 if(this.isVisible()){
2673 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2674 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
2675 Roo.log("New Dialog Message:" + options.msg )
2676 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2677 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2680 var d = this.getDialog();
2682 d.setTitle(opt.title || " ");
2683 d.close.setDisplayed(opt.closable !== false);
2684 activeTextEl = textboxEl;
2685 opt.prompt = opt.prompt || (opt.multiline ? true : false);
2690 textareaEl.setHeight(typeof opt.multiline == "number" ?
2691 opt.multiline : this.defaultTextHeight);
2692 activeTextEl = textareaEl;
2701 progressEl.setDisplayed(opt.progress === true);
2702 this.updateProgress(0);
2703 activeTextEl.dom.value = opt.value || "";
2705 dlg.setDefaultButton(activeTextEl);
2707 var bs = opt.buttons;
2711 }else if(bs && bs.yes){
2712 db = buttons["yes"];
2714 dlg.setDefaultButton(db);
2716 bwidth = updateButtons(opt.buttons);
2717 this.updateText(opt.msg);
2719 d.el.addClass(opt.cls);
2721 d.proxyDrag = opt.proxyDrag === true;
2722 d.modal = opt.modal !== false;
2723 d.mask = opt.modal !== false ? mask : false;
2725 // force it to the end of the z-index stack so it gets a cursor in FF
2726 document.body.appendChild(dlg.el.dom);
2727 d.animateTarget = null;
2728 d.show(options.animEl);
2734 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
2735 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2736 * and closing the message box when the process is complete.
2737 * @param {String} title The title bar text
2738 * @param {String} msg The message box body text
2739 * @return {Roo.MessageBox} This message box
2741 progress : function(title, msg){
2748 minWidth: this.minProgressWidth,
2755 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2756 * If a callback function is passed it will be called after the user clicks the button, and the
2757 * id of the button that was clicked will be passed as the only parameter to the callback
2758 * (could also be the top-right close button).
2759 * @param {String} title The title bar text
2760 * @param {String} msg The message box body text
2761 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2762 * @param {Object} scope (optional) The scope of the callback function
2763 * @return {Roo.MessageBox} This message box
2765 alert : function(title, msg, fn, scope){
2778 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
2779 * interaction while waiting for a long-running process to complete that does not have defined intervals.
2780 * You are responsible for closing the message box when the process is complete.
2781 * @param {String} msg The message box body text
2782 * @param {String} title (optional) The title bar text
2783 * @return {Roo.MessageBox} This message box
2785 wait : function(msg, title){
2796 waitTimer = Roo.TaskMgr.start({
2798 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2806 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2807 * If a callback function is passed it will be called after the user clicks either button, and the id of the
2808 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2809 * @param {String} title The title bar text
2810 * @param {String} msg The message box body text
2811 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2812 * @param {Object} scope (optional) The scope of the callback function
2813 * @return {Roo.MessageBox} This message box
2815 confirm : function(title, msg, fn, scope){
2819 buttons: this.YESNO,
2828 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2829 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
2830 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2831 * (could also be the top-right close button) and the text that was entered will be passed as the two
2832 * parameters to the callback.
2833 * @param {String} title The title bar text
2834 * @param {String} msg The message box body text
2835 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2836 * @param {Object} scope (optional) The scope of the callback function
2837 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2838 * property, or the height in pixels to create the textbox (defaults to false / single-line)
2839 * @return {Roo.MessageBox} This message box
2841 prompt : function(title, msg, fn, scope, multiline){
2845 buttons: this.OKCANCEL,
2850 multiline: multiline,
2857 * Button config that displays a single OK button
2862 * Button config that displays Yes and No buttons
2865 YESNO : {yes:true, no:true},
2867 * Button config that displays OK and Cancel buttons
2870 OKCANCEL : {ok:true, cancel:true},
2872 * Button config that displays Yes, No and Cancel buttons
2875 YESNOCANCEL : {yes:true, no:true, cancel:true},
2878 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2881 defaultTextHeight : 75,
2883 * The maximum width in pixels of the message box (defaults to 600)
2888 * The minimum width in pixels of the message box (defaults to 100)
2893 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
2894 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2897 minProgressWidth : 250,
2899 * An object containing the default button text strings that can be overriden for localized language support.
2900 * Supported properties are: ok, cancel, yes and no.
2901 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2914 * Shorthand for {@link Roo.MessageBox}
2916 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox
2917 Roo.Msg = Roo.Msg || Roo.MessageBox;
2926 * @class Roo.bootstrap.Navbar
2927 * @extends Roo.bootstrap.Component
2928 * Bootstrap Navbar class
2931 * Create a new Navbar
2932 * @param {Object} config The config object
2936 Roo.bootstrap.Navbar = function(config){
2937 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2941 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
2950 getAutoCreate : function(){
2953 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2957 initEvents :function ()
2959 //Roo.log(this.el.select('.navbar-toggle',true));
2960 this.el.select('.navbar-toggle',true).on('click', function() {
2961 // Roo.log('click');
2962 this.el.select('.navbar-collapse',true).toggleClass('in');
2970 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2972 var size = this.el.getSize();
2973 this.maskEl.setSize(size.width, size.height);
2974 this.maskEl.enableDisplayMode("block");
2983 getChildContainer : function()
2985 if (this.el.select('.collapse').getCount()) {
2986 return this.el.select('.collapse',true).first();
3019 * @class Roo.bootstrap.NavSimplebar
3020 * @extends Roo.bootstrap.Navbar
3021 * Bootstrap Sidebar class
3023 * @cfg {Boolean} inverse is inverted color
3025 * @cfg {String} type (nav | pills | tabs)
3026 * @cfg {Boolean} arrangement stacked | justified
3027 * @cfg {String} align (left | right) alignment
3029 * @cfg {Boolean} main (true|false) main nav bar? default false
3030 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3032 * @cfg {String} tag (header|footer|nav|div) default is nav
3038 * Create a new Sidebar
3039 * @param {Object} config The config object
3043 Roo.bootstrap.NavSimplebar = function(config){
3044 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3047 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3063 getAutoCreate : function(){
3067 tag : this.tag || 'div',
3080 this.type = this.type || 'nav';
3081 if (['tabs','pills'].indexOf(this.type)!==-1) {
3082 cfg.cn[0].cls += ' nav-' + this.type
3086 if (this.type!=='nav') {
3087 Roo.log('nav type must be nav/tabs/pills')
3089 cfg.cn[0].cls += ' navbar-nav'
3095 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3096 cfg.cn[0].cls += ' nav-' + this.arrangement;
3100 if (this.align === 'right') {
3101 cfg.cn[0].cls += ' navbar-right';
3105 cfg.cls += ' navbar-inverse';
3132 * @class Roo.bootstrap.NavHeaderbar
3133 * @extends Roo.bootstrap.NavSimplebar
3134 * Bootstrap Sidebar class
3136 * @cfg {String} brand what is brand
3137 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3138 * @cfg {String} brand_href href of the brand
3139 * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3140 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3143 * Create a new Sidebar
3144 * @param {Object} config The config object
3148 Roo.bootstrap.NavHeaderbar = function(config){
3149 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3152 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3160 getAutoCreate : function(){
3163 tag: this.nav || 'nav',
3172 cls: 'navbar-header',
3177 cls: 'navbar-toggle',
3178 'data-toggle': 'collapse',
3183 html: 'Toggle navigation'
3205 cls: 'collapse navbar-collapse',
3209 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3211 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3212 cfg.cls += ' navbar-' + this.position;
3214 // tag can override this..
3216 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3219 if (this.brand !== '') {
3222 href: this.brand_href ? this.brand_href : '#',
3223 cls: 'navbar-brand',
3231 cfg.cls += ' main-nav';
3239 initEvents : function()
3241 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3243 if (this.autohide) {
3248 Roo.get(document).on('scroll',function(e) {
3249 var ns = Roo.get(document).getScroll().top;
3250 var os = prevScroll;
3254 ft.removeClass('slideDown');
3255 ft.addClass('slideUp');
3258 ft.removeClass('slideUp');
3259 ft.addClass('slideDown');
3283 * @class Roo.bootstrap.NavSidebar
3284 * @extends Roo.bootstrap.Navbar
3285 * Bootstrap Sidebar class
3288 * Create a new Sidebar
3289 * @param {Object} config The config object
3293 Roo.bootstrap.NavSidebar = function(config){
3294 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3297 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3299 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3301 getAutoCreate : function(){
3306 cls: 'sidebar sidebar-nav'
3328 * @class Roo.bootstrap.NavGroup
3329 * @extends Roo.bootstrap.Component
3330 * Bootstrap NavGroup class
3331 * @cfg {String} align left | right
3332 * @cfg {Boolean} inverse false | true
3333 * @cfg {String} type (nav|pills|tab) default nav
3334 * @cfg {String} navId - reference Id for navbar.
3338 * Create a new nav group
3339 * @param {Object} config The config object
3342 Roo.bootstrap.NavGroup = function(config){
3343 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3346 Roo.bootstrap.NavGroup.register(this);
3350 * Fires when the active item changes
3351 * @param {Roo.bootstrap.NavGroup} this
3352 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3353 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3360 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3371 getAutoCreate : function()
3373 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3380 if (['tabs','pills'].indexOf(this.type)!==-1) {
3381 cfg.cls += ' nav-' + this.type
3383 if (this.type!=='nav') {
3384 Roo.log('nav type must be nav/tabs/pills')
3386 cfg.cls += ' navbar-nav'
3389 if (this.parent().sidebar) {
3392 cls: 'dashboard-menu sidebar-menu'
3398 if (this.form === true) {
3404 if (this.align === 'right') {
3405 cfg.cls += ' navbar-right';
3407 cfg.cls += ' navbar-left';
3411 if (this.align === 'right') {
3412 cfg.cls += ' navbar-right';
3416 cfg.cls += ' navbar-inverse';
3424 * sets the active Navigation item
3425 * @param {Roo.bootstrap.NavItem} the new current navitem
3427 setActiveItem : function(item)
3430 Roo.each(this.navItems, function(v){
3435 v.setActive(false, true);
3442 item.setActive(true, true);
3443 this.fireEvent('changed', this, item, prev);
3448 * gets the active Navigation item
3449 * @return {Roo.bootstrap.NavItem} the current navitem
3451 getActive : function()
3455 Roo.each(this.navItems, function(v){
3466 indexOfNav : function()
3470 Roo.each(this.navItems, function(v,i){
3481 * adds a Navigation item
3482 * @param {Roo.bootstrap.NavItem} the navitem to add
3484 addItem : function(cfg)
3486 var cn = new Roo.bootstrap.NavItem(cfg);
3488 cn.parentId = this.id;
3489 cn.onRender(this.el, null);
3493 * register a Navigation item
3494 * @param {Roo.bootstrap.NavItem} the navitem to add
3496 register : function(item)
3498 this.navItems.push( item);
3499 item.navId = this.navId;
3504 * clear all the Navigation item
3507 clearAll : function()
3510 this.el.dom.innerHTML = '';
3513 getNavItem: function(tabId)
3516 Roo.each(this.navItems, function(e) {
3517 if (e.tabId == tabId) {
3527 setActiveNext : function()
3529 var i = this.indexOfNav(this.getActive());
3530 if (i > this.navItems.length) {
3533 this.setActiveItem(this.navItems[i+1]);
3535 setActivePrev : function()
3537 var i = this.indexOfNav(this.getActive());
3541 this.setActiveItem(this.navItems[i-1]);
3543 clearWasActive : function(except) {
3544 Roo.each(this.navItems, function(e) {
3545 if (e.tabId != except.tabId && e.was_active) {
3546 e.was_active = false;
3553 getWasActive : function ()
3556 Roo.each(this.navItems, function(e) {
3571 Roo.apply(Roo.bootstrap.NavGroup, {
3575 * register a Navigation Group
3576 * @param {Roo.bootstrap.NavGroup} the navgroup to add
3578 register : function(navgrp)
3580 this.groups[navgrp.navId] = navgrp;
3584 * fetch a Navigation Group based on the navigation ID
3585 * @param {string} the navgroup to add
3586 * @returns {Roo.bootstrap.NavGroup} the navgroup
3588 get: function(navId) {
3589 if (typeof(this.groups[navId]) == 'undefined') {
3591 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3593 return this.groups[navId] ;
3608 * @class Roo.bootstrap.NavItem
3609 * @extends Roo.bootstrap.Component
3610 * Bootstrap Navbar.NavItem class
3611 * @cfg {String} href link to
3612 * @cfg {String} html content of button
3613 * @cfg {String} badge text inside badge
3614 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3615 * @cfg {String} glyphicon name of glyphicon
3616 * @cfg {String} icon name of font awesome icon
3617 * @cfg {Boolean} active Is item active
3618 * @cfg {Boolean} disabled Is item disabled
3620 * @cfg {Boolean} preventDefault (true | false) default false
3621 * @cfg {String} tabId the tab that this item activates.
3622 * @cfg {String} tagtype (a|span) render as a href or span?
3625 * Create a new Navbar Item
3626 * @param {Object} config The config object
3628 Roo.bootstrap.NavItem = function(config){
3629 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3634 * The raw click event for the entire grid.
3635 * @param {Roo.EventObject} e
3640 * Fires when the active item active state changes
3641 * @param {Roo.bootstrap.NavItem} this
3642 * @param {boolean} state the new state
3650 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
3658 preventDefault : false,
3665 getAutoCreate : function(){
3673 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3675 if (this.disabled) {
3676 cfg.cls += ' disabled';
3679 if (this.href || this.html || this.glyphicon || this.icon) {
3683 href : this.href || "#",
3684 html: this.html || ''
3689 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3692 if(this.glyphicon) {
3693 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
3698 cfg.cn[0].html += " <span class='caret'></span>";
3702 if (this.badge !== '') {
3704 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3712 initEvents: function() {
3713 // Roo.log('init events?');
3714 // Roo.log(this.el.dom);
3715 if (typeof (this.menu) != 'undefined') {
3716 this.menu.parentType = this.xtype;
3717 this.menu.triggerEl = this.el;
3718 this.addxtype(Roo.apply({}, this.menu));
3722 this.el.select('a',true).on('click', this.onClick, this);
3723 // at this point parent should be available..
3724 this.parent().register(this);
3727 onClick : function(e)
3730 if(this.preventDefault){
3733 if (this.disabled) {
3737 var tg = Roo.bootstrap.TabGroup.get(this.navId);
3738 if (tg && tg.transition) {
3739 Roo.log("waiting for the transitionend");
3743 Roo.log("fire event clicked");
3744 if(this.fireEvent('click', this, e) === false){
3747 var p = this.parent();
3748 if (['tabs','pills'].indexOf(p.type)!==-1) {
3749 if (typeof(p.setActiveItem) !== 'undefined') {
3750 p.setActiveItem(this);
3753 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3754 if (p.parentType == 'NavHeaderbar' && !this.menu) {
3755 // remove the collapsed menu expand...
3756 p.parent().el.select('.navbar-collapse',true).removeClass('in');
3761 isActive: function () {
3764 setActive : function(state, fire, is_was_active)
3766 if (this.active && !state & this.navId) {
3767 this.was_active = true;
3768 var nv = Roo.bootstrap.NavGroup.get(this.navId);
3770 nv.clearWasActive(this);
3774 this.active = state;
3777 this.el.removeClass('active');
3778 } else if (!this.el.hasClass('active')) {
3779 this.el.addClass('active');
3782 this.fireEvent('changed', this, state);
3785 // show a panel if it's registered and related..
3787 if (!this.navId || !this.tabId || !state || is_was_active) {
3791 var tg = Roo.bootstrap.TabGroup.get(this.navId);
3795 var pan = tg.getPanelByName(this.tabId);
3799 // if we can not flip to new panel - go back to old nav highlight..
3800 if (false == tg.showPanel(pan)) {
3801 var nv = Roo.bootstrap.NavGroup.get(this.navId);
3803 var onav = nv.getWasActive();
3805 onav.setActive(true, false, true);
3814 // this should not be here...
3815 setDisabled : function(state)
3817 this.disabled = state;
3819 this.el.removeClass('disabled');
3820 } else if (!this.el.hasClass('disabled')) {
3821 this.el.addClass('disabled');
3834 * <span> icon </span>
3835 * <span> text </span>
3836 * <span>badge </span>
3840 * @class Roo.bootstrap.NavSidebarItem
3841 * @extends Roo.bootstrap.NavItem
3842 * Bootstrap Navbar.NavSidebarItem class
3844 * Create a new Navbar Button
3845 * @param {Object} config The config object
3847 Roo.bootstrap.NavSidebarItem = function(config){
3848 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3853 * The raw click event for the entire grid.
3854 * @param {Roo.EventObject} e
3859 * Fires when the active item active state changes
3860 * @param {Roo.bootstrap.NavSidebarItem} this
3861 * @param {boolean} state the new state
3869 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
3872 getAutoCreate : function(){
3877 href : this.href || '#',
3889 html : this.html || ''
3894 cfg.cls += ' active';
3898 if (this.glyphicon || this.icon) {
3899 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
3900 a.cn.push({ tag : 'i', cls : c }) ;
3905 if (this.badge !== '') {
3906 a.cn.push({ tag: 'span', cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge });
3910 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3911 a.cls += 'dropdown-toggle treeview' ;
3935 * @class Roo.bootstrap.Row
3936 * @extends Roo.bootstrap.Component
3937 * Bootstrap Row class (contains columns...)
3941 * @param {Object} config The config object
3944 Roo.bootstrap.Row = function(config){
3945 Roo.bootstrap.Row.superclass.constructor.call(this, config);
3948 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
3950 getAutoCreate : function(){
3969 * @class Roo.bootstrap.Element
3970 * @extends Roo.bootstrap.Component
3971 * Bootstrap Element class
3972 * @cfg {String} html contents of the element
3973 * @cfg {String} tag tag of the element
3974 * @cfg {String} cls class of the element
3977 * Create a new Element
3978 * @param {Object} config The config object
3981 Roo.bootstrap.Element = function(config){
3982 Roo.bootstrap.Element.superclass.constructor.call(this, config);
3985 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
3992 getAutoCreate : function(){
4017 * @class Roo.bootstrap.Pagination
4018 * @extends Roo.bootstrap.Component
4019 * Bootstrap Pagination class
4020 * @cfg {String} size xs | sm | md | lg
4021 * @cfg {Boolean} inverse false | true
4024 * Create a new Pagination
4025 * @param {Object} config The config object
4028 Roo.bootstrap.Pagination = function(config){
4029 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4032 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4038 getAutoCreate : function(){
4044 cfg.cls += ' inverse';
4050 cfg.cls += " " + this.cls;
4068 * @class Roo.bootstrap.PaginationItem
4069 * @extends Roo.bootstrap.Component
4070 * Bootstrap PaginationItem class
4071 * @cfg {String} html text
4072 * @cfg {String} href the link
4073 * @cfg {Boolean} preventDefault (true | false) default true
4074 * @cfg {Boolean} active (true | false) default false
4078 * Create a new PaginationItem
4079 * @param {Object} config The config object
4083 Roo.bootstrap.PaginationItem = function(config){
4084 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4089 * The raw click event for the entire grid.
4090 * @param {Roo.EventObject} e
4096 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4100 preventDefault: true,
4104 getAutoCreate : function(){
4110 href : this.href ? this.href : '#',
4111 html : this.html ? this.html : ''
4121 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4127 initEvents: function() {
4129 this.el.on('click', this.onClick, this);
4132 onClick : function(e)
4134 Roo.log('PaginationItem on click ');
4135 if(this.preventDefault){
4139 this.fireEvent('click', this, e);
4155 * @class Roo.bootstrap.Slider
4156 * @extends Roo.bootstrap.Component
4157 * Bootstrap Slider class
4160 * Create a new Slider
4161 * @param {Object} config The config object
4164 Roo.bootstrap.Slider = function(config){
4165 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4168 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4170 getAutoCreate : function(){
4174 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4178 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4190 * Ext JS Library 1.1.1
4191 * Copyright(c) 2006-2007, Ext JS, LLC.
4193 * Originally Released Under LGPL - original licence link has changed is not relivant.
4196 * <script type="text/javascript">
4201 * @class Roo.grid.ColumnModel
4202 * @extends Roo.util.Observable
4203 * This is the default implementation of a ColumnModel used by the Grid. It defines
4204 * the columns in the grid.
4207 var colModel = new Roo.grid.ColumnModel([
4208 {header: "Ticker", width: 60, sortable: true, locked: true},
4209 {header: "Company Name", width: 150, sortable: true},
4210 {header: "Market Cap.", width: 100, sortable: true},
4211 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4212 {header: "Employees", width: 100, sortable: true, resizable: false}
4217 * The config options listed for this class are options which may appear in each
4218 * individual column definition.
4219 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4221 * @param {Object} config An Array of column config objects. See this class's
4222 * config objects for details.
4224 Roo.grid.ColumnModel = function(config){
4226 * The config passed into the constructor
4228 this.config = config;
4231 // if no id, create one
4232 // if the column does not have a dataIndex mapping,
4233 // map it to the order it is in the config
4234 for(var i = 0, len = config.length; i < len; i++){
4236 if(typeof c.dataIndex == "undefined"){
4239 if(typeof c.renderer == "string"){
4240 c.renderer = Roo.util.Format[c.renderer];
4242 if(typeof c.id == "undefined"){
4245 if(c.editor && c.editor.xtype){
4246 c.editor = Roo.factory(c.editor, Roo.grid);
4248 if(c.editor && c.editor.isFormField){
4249 c.editor = new Roo.grid.GridEditor(c.editor);
4251 this.lookup[c.id] = c;
4255 * The width of columns which have no width specified (defaults to 100)
4258 this.defaultWidth = 100;
4261 * Default sortable of columns which have no sortable specified (defaults to false)
4264 this.defaultSortable = false;
4268 * @event widthchange
4269 * Fires when the width of a column changes.
4270 * @param {ColumnModel} this
4271 * @param {Number} columnIndex The column index
4272 * @param {Number} newWidth The new width
4274 "widthchange": true,
4276 * @event headerchange
4277 * Fires when the text of a header changes.
4278 * @param {ColumnModel} this
4279 * @param {Number} columnIndex The column index
4280 * @param {Number} newText The new header text
4282 "headerchange": true,
4284 * @event hiddenchange
4285 * Fires when a column is hidden or "unhidden".
4286 * @param {ColumnModel} this
4287 * @param {Number} columnIndex The column index
4288 * @param {Boolean} hidden true if hidden, false otherwise
4290 "hiddenchange": true,
4292 * @event columnmoved
4293 * Fires when a column is moved.
4294 * @param {ColumnModel} this
4295 * @param {Number} oldIndex
4296 * @param {Number} newIndex
4298 "columnmoved" : true,
4300 * @event columlockchange
4301 * Fires when a column's locked state is changed
4302 * @param {ColumnModel} this
4303 * @param {Number} colIndex
4304 * @param {Boolean} locked true if locked
4306 "columnlockchange" : true
4308 Roo.grid.ColumnModel.superclass.constructor.call(this);
4310 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4312 * @cfg {String} header The header text to display in the Grid view.
4315 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4316 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4317 * specified, the column's index is used as an index into the Record's data Array.
4320 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4321 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4324 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4325 * Defaults to the value of the {@link #defaultSortable} property.
4326 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4329 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4332 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4335 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4338 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4341 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4342 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4343 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4344 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4347 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4350 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4354 * Returns the id of the column at the specified index.
4355 * @param {Number} index The column index
4356 * @return {String} the id
4358 getColumnId : function(index){
4359 return this.config[index].id;
4363 * Returns the column for a specified id.
4364 * @param {String} id The column id
4365 * @return {Object} the column
4367 getColumnById : function(id){
4368 return this.lookup[id];
4373 * Returns the column for a specified dataIndex.
4374 * @param {String} dataIndex The column dataIndex
4375 * @return {Object|Boolean} the column or false if not found
4377 getColumnByDataIndex: function(dataIndex){
4378 var index = this.findColumnIndex(dataIndex);
4379 return index > -1 ? this.config[index] : false;
4383 * Returns the index for a specified column id.
4384 * @param {String} id The column id
4385 * @return {Number} the index, or -1 if not found
4387 getIndexById : function(id){
4388 for(var i = 0, len = this.config.length; i < len; i++){
4389 if(this.config[i].id == id){
4397 * Returns the index for a specified column dataIndex.
4398 * @param {String} dataIndex The column dataIndex
4399 * @return {Number} the index, or -1 if not found
4402 findColumnIndex : function(dataIndex){
4403 for(var i = 0, len = this.config.length; i < len; i++){
4404 if(this.config[i].dataIndex == dataIndex){
4412 moveColumn : function(oldIndex, newIndex){
4413 var c = this.config[oldIndex];
4414 this.config.splice(oldIndex, 1);
4415 this.config.splice(newIndex, 0, c);
4416 this.dataMap = null;
4417 this.fireEvent("columnmoved", this, oldIndex, newIndex);
4420 isLocked : function(colIndex){
4421 return this.config[colIndex].locked === true;
4424 setLocked : function(colIndex, value, suppressEvent){
4425 if(this.isLocked(colIndex) == value){
4428 this.config[colIndex].locked = value;
4430 this.fireEvent("columnlockchange", this, colIndex, value);
4434 getTotalLockedWidth : function(){
4436 for(var i = 0; i < this.config.length; i++){
4437 if(this.isLocked(i) && !this.isHidden(i)){
4438 this.totalWidth += this.getColumnWidth(i);
4444 getLockedCount : function(){
4445 for(var i = 0, len = this.config.length; i < len; i++){
4446 if(!this.isLocked(i)){
4453 * Returns the number of columns.
4456 getColumnCount : function(visibleOnly){
4457 if(visibleOnly === true){
4459 for(var i = 0, len = this.config.length; i < len; i++){
4460 if(!this.isHidden(i)){
4466 return this.config.length;
4470 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4471 * @param {Function} fn
4472 * @param {Object} scope (optional)
4473 * @return {Array} result
4475 getColumnsBy : function(fn, scope){
4477 for(var i = 0, len = this.config.length; i < len; i++){
4478 var c = this.config[i];
4479 if(fn.call(scope||this, c, i) === true){
4487 * Returns true if the specified column is sortable.
4488 * @param {Number} col The column index
4491 isSortable : function(col){
4492 if(typeof this.config[col].sortable == "undefined"){
4493 return this.defaultSortable;
4495 return this.config[col].sortable;
4499 * Returns the rendering (formatting) function defined for the column.
4500 * @param {Number} col The column index.
4501 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4503 getRenderer : function(col){
4504 if(!this.config[col].renderer){
4505 return Roo.grid.ColumnModel.defaultRenderer;
4507 return this.config[col].renderer;
4511 * Sets the rendering (formatting) function for a column.
4512 * @param {Number} col The column index
4513 * @param {Function} fn The function to use to process the cell's raw data
4514 * to return HTML markup for the grid view. The render function is called with
4515 * the following parameters:<ul>
4516 * <li>Data value.</li>
4517 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4518 * <li>css A CSS style string to apply to the table cell.</li>
4519 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4520 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4521 * <li>Row index</li>
4522 * <li>Column index</li>
4523 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4525 setRenderer : function(col, fn){
4526 this.config[col].renderer = fn;
4530 * Returns the width for the specified column.
4531 * @param {Number} col The column index
4534 getColumnWidth : function(col){
4535 return this.config[col].width * 1 || this.defaultWidth;
4539 * Sets the width for a column.
4540 * @param {Number} col The column index
4541 * @param {Number} width The new width
4543 setColumnWidth : function(col, width, suppressEvent){
4544 this.config[col].width = width;
4545 this.totalWidth = null;
4547 this.fireEvent("widthchange", this, col, width);
4552 * Returns the total width of all columns.
4553 * @param {Boolean} includeHidden True to include hidden column widths
4556 getTotalWidth : function(includeHidden){
4557 if(!this.totalWidth){
4558 this.totalWidth = 0;
4559 for(var i = 0, len = this.config.length; i < len; i++){
4560 if(includeHidden || !this.isHidden(i)){
4561 this.totalWidth += this.getColumnWidth(i);
4565 return this.totalWidth;
4569 * Returns the header for the specified column.
4570 * @param {Number} col The column index
4573 getColumnHeader : function(col){
4574 return this.config[col].header;
4578 * Sets the header for a column.
4579 * @param {Number} col The column index
4580 * @param {String} header The new header
4582 setColumnHeader : function(col, header){
4583 this.config[col].header = header;
4584 this.fireEvent("headerchange", this, col, header);
4588 * Returns the tooltip for the specified column.
4589 * @param {Number} col The column index
4592 getColumnTooltip : function(col){
4593 return this.config[col].tooltip;
4596 * Sets the tooltip for a column.
4597 * @param {Number} col The column index
4598 * @param {String} tooltip The new tooltip
4600 setColumnTooltip : function(col, tooltip){
4601 this.config[col].tooltip = tooltip;
4605 * Returns the dataIndex for the specified column.
4606 * @param {Number} col The column index
4609 getDataIndex : function(col){
4610 return this.config[col].dataIndex;
4614 * Sets the dataIndex for a column.
4615 * @param {Number} col The column index
4616 * @param {Number} dataIndex The new dataIndex
4618 setDataIndex : function(col, dataIndex){
4619 this.config[col].dataIndex = dataIndex;
4625 * Returns true if the cell is editable.
4626 * @param {Number} colIndex The column index
4627 * @param {Number} rowIndex The row index
4630 isCellEditable : function(colIndex, rowIndex){
4631 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4635 * Returns the editor defined for the cell/column.
4636 * return false or null to disable editing.
4637 * @param {Number} colIndex The column index
4638 * @param {Number} rowIndex The row index
4641 getCellEditor : function(colIndex, rowIndex){
4642 return this.config[colIndex].editor;
4646 * Sets if a column is editable.
4647 * @param {Number} col The column index
4648 * @param {Boolean} editable True if the column is editable
4650 setEditable : function(col, editable){
4651 this.config[col].editable = editable;
4656 * Returns true if the column is hidden.
4657 * @param {Number} colIndex The column index
4660 isHidden : function(colIndex){
4661 return this.config[colIndex].hidden;
4666 * Returns true if the column width cannot be changed
4668 isFixed : function(colIndex){
4669 return this.config[colIndex].fixed;
4673 * Returns true if the column can be resized
4676 isResizable : function(colIndex){
4677 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4680 * Sets if a column is hidden.
4681 * @param {Number} colIndex The column index
4682 * @param {Boolean} hidden True if the column is hidden
4684 setHidden : function(colIndex, hidden){
4685 this.config[colIndex].hidden = hidden;
4686 this.totalWidth = null;
4687 this.fireEvent("hiddenchange", this, colIndex, hidden);
4691 * Sets the editor for a column.
4692 * @param {Number} col The column index
4693 * @param {Object} editor The editor object
4695 setEditor : function(col, editor){
4696 this.config[col].editor = editor;
4700 Roo.grid.ColumnModel.defaultRenderer = function(value){
4701 if(typeof value == "string" && value.length < 1){
4707 // Alias for backwards compatibility
4708 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4711 * Ext JS Library 1.1.1
4712 * Copyright(c) 2006-2007, Ext JS, LLC.
4714 * Originally Released Under LGPL - original licence link has changed is not relivant.
4717 * <script type="text/javascript">
4721 * @class Roo.LoadMask
4722 * A simple utility class for generically masking elements while loading data. If the element being masked has
4723 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4724 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
4725 * element's UpdateManager load indicator and will be destroyed after the initial load.
4727 * Create a new LoadMask
4728 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4729 * @param {Object} config The config object
4731 Roo.LoadMask = function(el, config){
4732 this.el = Roo.get(el);
4733 Roo.apply(this, config);
4735 this.store.on('beforeload', this.onBeforeLoad, this);
4736 this.store.on('load', this.onLoad, this);
4737 this.store.on('loadexception', this.onLoadException, this);
4738 this.removeMask = false;
4740 var um = this.el.getUpdateManager();
4741 um.showLoadIndicator = false; // disable the default indicator
4742 um.on('beforeupdate', this.onBeforeLoad, this);
4743 um.on('update', this.onLoad, this);
4744 um.on('failure', this.onLoad, this);
4745 this.removeMask = true;
4749 Roo.LoadMask.prototype = {
4751 * @cfg {Boolean} removeMask
4752 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4753 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
4757 * The text to display in a centered loading message box (defaults to 'Loading...')
4761 * @cfg {String} msgCls
4762 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4764 msgCls : 'x-mask-loading',
4767 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4773 * Disables the mask to prevent it from being displayed
4775 disable : function(){
4776 this.disabled = true;
4780 * Enables the mask so that it can be displayed
4782 enable : function(){
4783 this.disabled = false;
4786 onLoadException : function()
4790 if (typeof(arguments[3]) != 'undefined') {
4791 Roo.MessageBox.alert("Error loading",arguments[3]);
4795 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4796 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4805 this.el.unmask(this.removeMask);
4810 this.el.unmask(this.removeMask);
4814 onBeforeLoad : function(){
4816 this.el.mask(this.msg, this.msgCls);
4821 destroy : function(){
4823 this.store.un('beforeload', this.onBeforeLoad, this);
4824 this.store.un('load', this.onLoad, this);
4825 this.store.un('loadexception', this.onLoadException, this);
4827 var um = this.el.getUpdateManager();
4828 um.un('beforeupdate', this.onBeforeLoad, this);
4829 um.un('update', this.onLoad, this);
4830 um.un('failure', this.onLoad, this);
4841 * @class Roo.bootstrap.Table
4842 * @extends Roo.bootstrap.Component
4843 * Bootstrap Table class
4844 * @cfg {String} cls table class
4845 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4846 * @cfg {String} bgcolor Specifies the background color for a table
4847 * @cfg {Number} border Specifies whether the table cells should have borders or not
4848 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4849 * @cfg {Number} cellspacing Specifies the space between cells
4850 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4851 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4852 * @cfg {String} sortable Specifies that the table should be sortable
4853 * @cfg {String} summary Specifies a summary of the content of a table
4854 * @cfg {Number} width Specifies the width of a table
4855 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4857 * @cfg {boolean} striped Should the rows be alternative striped
4858 * @cfg {boolean} bordered Add borders to the table
4859 * @cfg {boolean} hover Add hover highlighting
4860 * @cfg {boolean} condensed Format condensed
4861 * @cfg {boolean} responsive Format condensed
4862 * @cfg {Boolean} loadMask (true|false) default false
4863 * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4864 * @cfg {Boolean} thead (true|false) generate thead, default true
4865 * @cfg {Boolean} RowSelection (true|false) default false
4866 * @cfg {Boolean} CellSelection (true|false) default false
4868 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
4872 * Create a new Table
4873 * @param {Object} config The config object
4876 Roo.bootstrap.Table = function(config){
4877 Roo.bootstrap.Table.superclass.constructor.call(this, config);
4880 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4881 this.sm = this.selModel;
4882 this.sm.xmodule = this.xmodule || false;
4884 if (this.cm && typeof(this.cm.config) == 'undefined') {
4885 this.colModel = new Roo.grid.ColumnModel(this.cm);
4886 this.cm = this.colModel;
4887 this.cm.xmodule = this.xmodule || false;
4890 this.store= Roo.factory(this.store, Roo.data);
4891 this.ds = this.store;
4892 this.ds.xmodule = this.xmodule || false;
4895 if (this.footer && this.store) {
4896 this.footer.dataSource = this.ds;
4897 this.footer = Roo.factory(this.footer);
4904 * Fires when a cell is clicked
4905 * @param {Roo.bootstrap.Table} this
4906 * @param {Roo.Element} el
4907 * @param {Number} rowIndex
4908 * @param {Number} columnIndex
4909 * @param {Roo.EventObject} e
4913 * @event celldblclick
4914 * Fires when a cell is double clicked
4915 * @param {Roo.bootstrap.Table} this
4916 * @param {Roo.Element} el
4917 * @param {Number} rowIndex
4918 * @param {Number} columnIndex
4919 * @param {Roo.EventObject} e
4921 "celldblclick" : true,
4924 * Fires when a row is clicked
4925 * @param {Roo.bootstrap.Table} this
4926 * @param {Roo.Element} el
4927 * @param {Number} rowIndex
4928 * @param {Roo.EventObject} e
4932 * @event rowdblclick
4933 * Fires when a row is double clicked
4934 * @param {Roo.bootstrap.Table} this
4935 * @param {Roo.Element} el
4936 * @param {Number} rowIndex
4937 * @param {Roo.EventObject} e
4939 "rowdblclick" : true,
4942 * Fires when a mouseover occur
4943 * @param {Roo.bootstrap.Table} this
4944 * @param {Roo.Element} el
4945 * @param {Number} rowIndex
4946 * @param {Number} columnIndex
4947 * @param {Roo.EventObject} e
4952 * Fires when a mouseout occur
4953 * @param {Roo.bootstrap.Table} this
4954 * @param {Roo.Element} el
4955 * @param {Number} rowIndex
4956 * @param {Number} columnIndex
4957 * @param {Roo.EventObject} e
4962 * Fires when a row is rendered, so you can change add a style to it.
4963 * @param {Roo.bootstrap.Table} this
4964 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
4971 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
4995 RowSelection : false,
4996 CellSelection : false,
4999 // Roo.Element - the tbody
5002 getAutoCreate : function(){
5003 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5012 cfg.cls += ' table-striped';
5016 cfg.cls += ' table-hover';
5018 if (this.bordered) {
5019 cfg.cls += ' table-bordered';
5021 if (this.condensed) {
5022 cfg.cls += ' table-condensed';
5024 if (this.responsive) {
5025 cfg.cls += ' table-responsive';
5029 cfg.cls+= ' ' +this.cls;
5032 // this lot should be simplifed...
5035 cfg.align=this.align;
5038 cfg.bgcolor=this.bgcolor;
5041 cfg.border=this.border;
5043 if (this.cellpadding) {
5044 cfg.cellpadding=this.cellpadding;
5046 if (this.cellspacing) {
5047 cfg.cellspacing=this.cellspacing;
5050 cfg.frame=this.frame;
5053 cfg.rules=this.rules;
5055 if (this.sortable) {
5056 cfg.sortable=this.sortable;
5059 cfg.summary=this.summary;
5062 cfg.width=this.width;
5065 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5068 if(this.store || this.cm){
5070 cfg.cn.push(this.renderHeader());
5073 cfg.cn.push(this.renderBody());
5076 cfg.cn.push(this.renderFooter());
5079 cfg.cls+= ' TableGrid';
5082 return { cn : [ cfg ] };
5085 initEvents : function()
5087 if(!this.store || !this.cm){
5091 //Roo.log('initEvents with ds!!!!');
5093 this.mainBody = this.el.select('tbody', true).first();
5098 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5099 e.on('click', _this.sort, _this);
5102 this.el.on("click", this.onClick, this);
5103 this.el.on("dblclick", this.onDblClick, this);
5105 this.parent().el.setStyle('position', 'relative');
5107 this.footer.parentId = this.id;
5108 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5111 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5113 this.store.on('load', this.onLoad, this);
5114 this.store.on('beforeload', this.onBeforeLoad, this);
5115 this.store.on('update', this.onUpdate, this);
5119 onMouseover : function(e, el)
5121 var cell = Roo.get(el);
5127 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5128 cell = cell.findParent('td', false, true);
5131 var row = cell.findParent('tr', false, true);
5132 var cellIndex = cell.dom.cellIndex;
5133 var rowIndex = row.dom.rowIndex - 1; // start from 0
5135 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5139 onMouseout : function(e, el)
5141 var cell = Roo.get(el);
5147 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5148 cell = cell.findParent('td', false, true);
5151 var row = cell.findParent('tr', false, true);
5152 var cellIndex = cell.dom.cellIndex;
5153 var rowIndex = row.dom.rowIndex - 1; // start from 0
5155 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5159 onClick : function(e, el)
5161 var cell = Roo.get(el);
5163 if(!cell || (!this.CellSelection && !this.RowSelection)){
5168 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5169 cell = cell.findParent('td', false, true);
5172 var row = cell.findParent('tr', false, true);
5173 var cellIndex = cell.dom.cellIndex;
5174 var rowIndex = row.dom.rowIndex - 1;
5176 if(this.CellSelection){
5177 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5180 if(this.RowSelection){
5181 this.fireEvent('rowclick', this, row, rowIndex, e);
5187 onDblClick : function(e,el)
5189 var cell = Roo.get(el);
5191 if(!cell || (!this.CellSelection && !this.RowSelection)){
5195 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5196 cell = cell.findParent('td', false, true);
5199 var row = cell.findParent('tr', false, true);
5200 var cellIndex = cell.dom.cellIndex;
5201 var rowIndex = row.dom.rowIndex - 1;
5203 if(this.CellSelection){
5204 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5207 if(this.RowSelection){
5208 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5212 sort : function(e,el)
5214 var col = Roo.get(el)
5216 if(!col.hasClass('sortable')){
5220 var sort = col.attr('sort');
5223 if(col.hasClass('glyphicon-arrow-up')){
5227 this.store.sortInfo = {field : sort, direction : dir};
5230 Roo.log("calling footer first");
5231 this.footer.onClick('first');
5234 this.store.load({ params : { start : 0 } });
5238 renderHeader : function()
5247 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5249 var config = cm.config[i];
5254 html: cm.getColumnHeader(i)
5257 if(typeof(config.hidden) != 'undefined' && config.hidden){
5258 c.style += ' display:none;';
5261 if(typeof(config.dataIndex) != 'undefined'){
5262 c.sort = config.dataIndex;
5265 if(typeof(config.sortable) != 'undefined' && config.sortable){
5269 if(typeof(config.align) != 'undefined' && config.align.length){
5270 c.style += ' text-align:' + config.align + ';';
5273 if(typeof(config.width) != 'undefined'){
5274 c.style += ' width:' + config.width + 'px;';
5283 renderBody : function()
5293 colspan : this.cm.getColumnCount()
5303 renderFooter : function()
5313 colspan : this.cm.getColumnCount()
5327 Roo.log('ds onload');
5332 var ds = this.store;
5334 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5335 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5337 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5338 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5341 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5342 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5346 var tbody = this.mainBody;
5348 if(ds.getCount() > 0){
5349 ds.data.each(function(d,rowIndex){
5350 var row = this.renderRow(cm, ds, rowIndex);
5352 tbody.createChild(row);
5356 if(row.cellObjects.length){
5357 Roo.each(row.cellObjects, function(r){
5358 _this.renderCellObject(r);
5365 Roo.each(this.el.select('tbody td', true).elements, function(e){
5366 e.on('mouseover', _this.onMouseover, _this);
5369 Roo.each(this.el.select('tbody td', true).elements, function(e){
5370 e.on('mouseout', _this.onMouseout, _this);
5373 //if(this.loadMask){
5374 // this.maskEl.hide();
5379 onUpdate : function(ds,record)
5381 this.refreshRow(record);
5383 onRemove : function(ds, record, index, isUpdate){
5384 if(isUpdate !== true){
5385 this.fireEvent("beforerowremoved", this, index, record);
5387 var bt = this.mainBody.dom;
5389 bt.removeChild(bt.rows[index]);
5392 if(isUpdate !== true){
5393 //this.stripeRows(index);
5394 //this.syncRowHeights(index, index);
5396 this.fireEvent("rowremoved", this, index, record);
5401 refreshRow : function(record){
5402 var ds = this.store, index;
5403 if(typeof record == 'number'){
5405 record = ds.getAt(index);
5407 index = ds.indexOf(record);
5409 this.insertRow(ds, index, true);
5410 this.onRemove(ds, record, index+1, true);
5411 //this.syncRowHeights(index, index);
5413 this.fireEvent("rowupdated", this, index, record);
5416 insertRow : function(dm, rowIndex, isUpdate){
5419 this.fireEvent("beforerowsinserted", this, rowIndex);
5421 //var s = this.getScrollState();
5422 var row = this.renderRow(this.cm, this.store, rowIndex);
5423 // insert before rowIndex..
5424 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5428 if(row.cellObjects.length){
5429 Roo.each(row.cellObjects, function(r){
5430 _this.renderCellObject(r);
5435 this.fireEvent("rowsinserted", this, rowIndex);
5436 //this.syncRowHeights(firstRow, lastRow);
5437 //this.stripeRows(firstRow);
5444 getRowDom : function(rowIndex)
5446 // not sure if I need to check this.. but let's do it anyway..
5447 return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5448 this.mainBody.dom.rows[rowIndex] : false
5450 // returns the object tree for a tr..
5453 renderRow : function(cm, ds, rowIndex) {
5455 var d = ds.getAt(rowIndex);
5462 var cellObjects = [];
5464 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5465 var config = cm.config[i];
5467 var renderer = cm.getRenderer(i);
5471 if(typeof(renderer) !== 'undefined'){
5472 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5474 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5475 // and are rendered into the cells after the row is rendered - using the id for the element.
5477 if(typeof(value) === 'object'){
5487 rowIndex : rowIndex,
5492 this.fireEvent('rowclass', this, rowcfg);
5496 cls : rowcfg.rowClass,
5498 html: (typeof(value) === 'object') ? '' : value
5505 if(typeof(config.hidden) != 'undefined' && config.hidden){
5506 td.style += ' display:none;';
5509 if(typeof(config.align) != 'undefined' && config.align.length){
5510 td.style += ' text-align:' + config.align + ';';
5513 if(typeof(config.width) != 'undefined'){
5514 td.style += ' width:' + config.width + 'px;';
5521 row.cellObjects = cellObjects;
5529 onBeforeLoad : function()
5531 //Roo.log('ds onBeforeLoad');
5535 //if(this.loadMask){
5536 // this.maskEl.show();
5542 this.el.select('tbody', true).first().dom.innerHTML = '';
5545 getSelectionModel : function(){
5547 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5549 return this.selModel;
5552 * Render the Roo.bootstrap object from renderder
5554 renderCellObject : function(r)
5558 var t = r.cfg.render(r.container);
5561 Roo.each(r.cfg.cn, function(c){
5563 container: t.getChildContainer(),
5566 _this.renderCellObject(child);
5583 * @class Roo.bootstrap.TableCell
5584 * @extends Roo.bootstrap.Component
5585 * Bootstrap TableCell class
5586 * @cfg {String} html cell contain text
5587 * @cfg {String} cls cell class
5588 * @cfg {String} tag cell tag (td|th) default td
5589 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5590 * @cfg {String} align Aligns the content in a cell
5591 * @cfg {String} axis Categorizes cells
5592 * @cfg {String} bgcolor Specifies the background color of a cell
5593 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5594 * @cfg {Number} colspan Specifies the number of columns a cell should span
5595 * @cfg {String} headers Specifies one or more header cells a cell is related to
5596 * @cfg {Number} height Sets the height of a cell
5597 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5598 * @cfg {Number} rowspan Sets the number of rows a cell should span
5599 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5600 * @cfg {String} valign Vertical aligns the content in a cell
5601 * @cfg {Number} width Specifies the width of a cell
5604 * Create a new TableCell
5605 * @param {Object} config The config object
5608 Roo.bootstrap.TableCell = function(config){
5609 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5612 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
5632 getAutoCreate : function(){
5633 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5653 cfg.align=this.align
5659 cfg.bgcolor=this.bgcolor
5662 cfg.charoff=this.charoff
5665 cfg.colspan=this.colspan
5668 cfg.headers=this.headers
5671 cfg.height=this.height
5674 cfg.nowrap=this.nowrap
5677 cfg.rowspan=this.rowspan
5680 cfg.scope=this.scope
5683 cfg.valign=this.valign
5686 cfg.width=this.width
5705 * @class Roo.bootstrap.TableRow
5706 * @extends Roo.bootstrap.Component
5707 * Bootstrap TableRow class
5708 * @cfg {String} cls row class
5709 * @cfg {String} align Aligns the content in a table row
5710 * @cfg {String} bgcolor Specifies a background color for a table row
5711 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5712 * @cfg {String} valign Vertical aligns the content in a table row
5715 * Create a new TableRow
5716 * @param {Object} config The config object
5719 Roo.bootstrap.TableRow = function(config){
5720 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5723 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
5731 getAutoCreate : function(){
5732 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5742 cfg.align = this.align;
5745 cfg.bgcolor = this.bgcolor;
5748 cfg.charoff = this.charoff;
5751 cfg.valign = this.valign;
5769 * @class Roo.bootstrap.TableBody
5770 * @extends Roo.bootstrap.Component
5771 * Bootstrap TableBody class
5772 * @cfg {String} cls element class
5773 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5774 * @cfg {String} align Aligns the content inside the element
5775 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5776 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5779 * Create a new TableBody
5780 * @param {Object} config The config object
5783 Roo.bootstrap.TableBody = function(config){
5784 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5787 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
5795 getAutoCreate : function(){
5796 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5810 cfg.align = this.align;
5813 cfg.charoff = this.charoff;
5816 cfg.valign = this.valign;
5823 // initEvents : function()
5830 // this.store = Roo.factory(this.store, Roo.data);
5831 // this.store.on('load', this.onLoad, this);
5833 // this.store.load();
5837 // onLoad: function ()
5839 // this.fireEvent('load', this);
5849 * Ext JS Library 1.1.1
5850 * Copyright(c) 2006-2007, Ext JS, LLC.
5852 * Originally Released Under LGPL - original licence link has changed is not relivant.
5855 * <script type="text/javascript">
5858 // as we use this in bootstrap.
5859 Roo.namespace('Roo.form');
5861 * @class Roo.form.Action
5862 * Internal Class used to handle form actions
5864 * @param {Roo.form.BasicForm} el The form element or its id
5865 * @param {Object} config Configuration options
5870 // define the action interface
5871 Roo.form.Action = function(form, options){
5873 this.options = options || {};
5876 * Client Validation Failed
5879 Roo.form.Action.CLIENT_INVALID = 'client';
5881 * Server Validation Failed
5884 Roo.form.Action.SERVER_INVALID = 'server';
5886 * Connect to Server Failed
5889 Roo.form.Action.CONNECT_FAILURE = 'connect';
5891 * Reading Data from Server Failed
5894 Roo.form.Action.LOAD_FAILURE = 'load';
5896 Roo.form.Action.prototype = {
5898 failureType : undefined,
5899 response : undefined,
5903 run : function(options){
5908 success : function(response){
5913 handleResponse : function(response){
5917 // default connection failure
5918 failure : function(response){
5920 this.response = response;
5921 this.failureType = Roo.form.Action.CONNECT_FAILURE;
5922 this.form.afterAction(this, false);
5925 processResponse : function(response){
5926 this.response = response;
5927 if(!response.responseText){
5930 this.result = this.handleResponse(response);
5934 // utility functions used internally
5935 getUrl : function(appendParams){
5936 var url = this.options.url || this.form.url || this.form.el.dom.action;
5938 var p = this.getParams();
5940 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5946 getMethod : function(){
5947 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5950 getParams : function(){
5951 var bp = this.form.baseParams;
5952 var p = this.options.params;
5954 if(typeof p == "object"){
5955 p = Roo.urlEncode(Roo.applyIf(p, bp));
5956 }else if(typeof p == 'string' && bp){
5957 p += '&' + Roo.urlEncode(bp);
5960 p = Roo.urlEncode(bp);
5965 createCallback : function(){
5967 success: this.success,
5968 failure: this.failure,
5970 timeout: (this.form.timeout*1000),
5971 upload: this.form.fileUpload ? this.success : undefined
5976 Roo.form.Action.Submit = function(form, options){
5977 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5980 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5983 haveProgress : false,
5984 uploadComplete : false,
5986 // uploadProgress indicator.
5987 uploadProgress : function()
5989 if (!this.form.progressUrl) {
5993 if (!this.haveProgress) {
5994 Roo.MessageBox.progress("Uploading", "Uploading");
5996 if (this.uploadComplete) {
5997 Roo.MessageBox.hide();
6001 this.haveProgress = true;
6003 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6005 var c = new Roo.data.Connection();
6007 url : this.form.progressUrl,
6012 success : function(req){
6013 //console.log(data);
6017 rdata = Roo.decode(req.responseText)
6019 Roo.log("Invalid data from server..");
6023 if (!rdata || !rdata.success) {
6025 Roo.MessageBox.alert(Roo.encode(rdata));
6028 var data = rdata.data;
6030 if (this.uploadComplete) {
6031 Roo.MessageBox.hide();
6036 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6037 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6040 this.uploadProgress.defer(2000,this);
6043 failure: function(data) {
6044 Roo.log('progress url failed ');
6055 // run get Values on the form, so it syncs any secondary forms.
6056 this.form.getValues();
6058 var o = this.options;
6059 var method = this.getMethod();
6060 var isPost = method == 'POST';
6061 if(o.clientValidation === false || this.form.isValid()){
6063 if (this.form.progressUrl) {
6064 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6065 (new Date() * 1) + '' + Math.random());
6070 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6071 form:this.form.el.dom,
6072 url:this.getUrl(!isPost),
6074 params:isPost ? this.getParams() : null,
6075 isUpload: this.form.fileUpload
6078 this.uploadProgress();
6080 }else if (o.clientValidation !== false){ // client validation failed
6081 this.failureType = Roo.form.Action.CLIENT_INVALID;
6082 this.form.afterAction(this, false);
6086 success : function(response)
6088 this.uploadComplete= true;
6089 if (this.haveProgress) {
6090 Roo.MessageBox.hide();
6094 var result = this.processResponse(response);
6095 if(result === true || result.success){
6096 this.form.afterAction(this, true);
6100 this.form.markInvalid(result.errors);
6101 this.failureType = Roo.form.Action.SERVER_INVALID;
6103 this.form.afterAction(this, false);
6105 failure : function(response)
6107 this.uploadComplete= true;
6108 if (this.haveProgress) {
6109 Roo.MessageBox.hide();
6112 this.response = response;
6113 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6114 this.form.afterAction(this, false);
6117 handleResponse : function(response){
6118 if(this.form.errorReader){
6119 var rs = this.form.errorReader.read(response);
6122 for(var i = 0, len = rs.records.length; i < len; i++) {
6123 var r = rs.records[i];
6127 if(errors.length < 1){
6131 success : rs.success,
6137 ret = Roo.decode(response.responseText);
6141 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6151 Roo.form.Action.Load = function(form, options){
6152 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6153 this.reader = this.form.reader;
6156 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6161 Roo.Ajax.request(Roo.apply(
6162 this.createCallback(), {
6163 method:this.getMethod(),
6164 url:this.getUrl(false),
6165 params:this.getParams()
6169 success : function(response){
6171 var result = this.processResponse(response);
6172 if(result === true || !result.success || !result.data){
6173 this.failureType = Roo.form.Action.LOAD_FAILURE;
6174 this.form.afterAction(this, false);
6177 this.form.clearInvalid();
6178 this.form.setValues(result.data);
6179 this.form.afterAction(this, true);
6182 handleResponse : function(response){
6183 if(this.form.reader){
6184 var rs = this.form.reader.read(response);
6185 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6187 success : rs.success,
6191 return Roo.decode(response.responseText);
6195 Roo.form.Action.ACTION_TYPES = {
6196 'load' : Roo.form.Action.Load,
6197 'submit' : Roo.form.Action.Submit
6206 * @class Roo.bootstrap.Form
6207 * @extends Roo.bootstrap.Component
6208 * Bootstrap Form class
6209 * @cfg {String} method GET | POST (default POST)
6210 * @cfg {String} labelAlign top | left (default top)
6211 * @cfg {String} align left | right - for navbars
6212 * @cfg {Boolean} loadMask load mask when submit (default true)
6217 * @param {Object} config The config object
6221 Roo.bootstrap.Form = function(config){
6222 Roo.bootstrap.Form.superclass.constructor.call(this, config);
6225 * @event clientvalidation
6226 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6227 * @param {Form} this
6228 * @param {Boolean} valid true if the form has passed client-side validation
6230 clientvalidation: true,
6232 * @event beforeaction
6233 * Fires before any action is performed. Return false to cancel the action.
6234 * @param {Form} this
6235 * @param {Action} action The action to be performed
6239 * @event actionfailed
6240 * Fires when an action fails.
6241 * @param {Form} this
6242 * @param {Action} action The action that failed
6244 actionfailed : true,
6246 * @event actioncomplete
6247 * Fires when an action is completed.
6248 * @param {Form} this
6249 * @param {Action} action The action that completed
6251 actioncomplete : true
6256 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
6259 * @cfg {String} method
6260 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6265 * The URL to use for form actions if one isn't supplied in the action options.
6268 * @cfg {Boolean} fileUpload
6269 * Set to true if this form is a file upload.
6273 * @cfg {Object} baseParams
6274 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6278 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6282 * @cfg {Sting} align (left|right) for navbar forms
6287 activeAction : null,
6290 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6291 * element by passing it or its id or mask the form itself by passing in true.
6294 waitMsgTarget : false,
6298 getAutoCreate : function(){
6302 method : this.method || 'POST',
6303 id : this.id || Roo.id(),
6306 if (this.parent().xtype.match(/^Nav/)) {
6307 cfg.cls = 'navbar-form navbar-' + this.align;
6311 if (this.labelAlign == 'left' ) {
6312 cfg.cls += ' form-horizontal';
6318 initEvents : function()
6320 this.el.on('submit', this.onSubmit, this);
6321 // this was added as random key presses on the form where triggering form submit.
6322 this.el.on('keypress', function(e) {
6323 if (e.getCharCode() != 13) {
6326 // we might need to allow it for textareas.. and some other items.
6327 // check e.getTarget().
6329 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6333 Roo.log("keypress blocked");
6341 onSubmit : function(e){
6346 * Returns true if client-side validation on the form is successful.
6349 isValid : function(){
6350 var items = this.getItems();
6352 items.each(function(f){
6361 * Returns true if any fields in this form have changed since their original load.
6364 isDirty : function(){
6366 var items = this.getItems();
6367 items.each(function(f){
6377 * Performs a predefined action (submit or load) or custom actions you define on this form.
6378 * @param {String} actionName The name of the action type
6379 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
6380 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6381 * accept other config options):
6383 Property Type Description
6384 ---------------- --------------- ----------------------------------------------------------------------------------
6385 url String The url for the action (defaults to the form's url)
6386 method String The form method to use (defaults to the form's method, or POST if not defined)
6387 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
6388 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
6389 validate the form on the client (defaults to false)
6391 * @return {BasicForm} this
6393 doAction : function(action, options){
6394 if(typeof action == 'string'){
6395 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6397 if(this.fireEvent('beforeaction', this, action) !== false){
6398 this.beforeAction(action);
6399 action.run.defer(100, action);
6405 beforeAction : function(action){
6406 var o = action.options;
6409 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6411 // not really supported yet.. ??
6413 //if(this.waitMsgTarget === true){
6414 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6415 //}else if(this.waitMsgTarget){
6416 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6417 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6419 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6425 afterAction : function(action, success){
6426 this.activeAction = null;
6427 var o = action.options;
6429 //if(this.waitMsgTarget === true){
6431 //}else if(this.waitMsgTarget){
6432 // this.waitMsgTarget.unmask();
6434 // Roo.MessageBox.updateProgress(1);
6435 // Roo.MessageBox.hide();
6442 Roo.callback(o.success, o.scope, [this, action]);
6443 this.fireEvent('actioncomplete', this, action);
6447 // failure condition..
6448 // we have a scenario where updates need confirming.
6449 // eg. if a locking scenario exists..
6450 // we look for { errors : { needs_confirm : true }} in the response.
6452 (typeof(action.result) != 'undefined') &&
6453 (typeof(action.result.errors) != 'undefined') &&
6454 (typeof(action.result.errors.needs_confirm) != 'undefined')
6457 Roo.log("not supported yet");
6460 Roo.MessageBox.confirm(
6461 "Change requires confirmation",
6462 action.result.errorMsg,
6467 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
6477 Roo.callback(o.failure, o.scope, [this, action]);
6478 // show an error message if no failed handler is set..
6479 if (!this.hasListener('actionfailed')) {
6480 Roo.log("need to add dialog support");
6482 Roo.MessageBox.alert("Error",
6483 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6484 action.result.errorMsg :
6485 "Saving Failed, please check your entries or try again"
6490 this.fireEvent('actionfailed', this, action);
6495 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6496 * @param {String} id The value to search for
6499 findField : function(id){
6500 var items = this.getItems();
6501 var field = items.get(id);
6503 items.each(function(f){
6504 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6511 return field || null;
6514 * Mark fields in this form invalid in bulk.
6515 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6516 * @return {BasicForm} this
6518 markInvalid : function(errors){
6519 if(errors instanceof Array){
6520 for(var i = 0, len = errors.length; i < len; i++){
6521 var fieldError = errors[i];
6522 var f = this.findField(fieldError.id);
6524 f.markInvalid(fieldError.msg);
6530 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6531 field.markInvalid(errors[id]);
6535 //Roo.each(this.childForms || [], function (f) {
6536 // f.markInvalid(errors);
6543 * Set values for fields in this form in bulk.
6544 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6545 * @return {BasicForm} this
6547 setValues : function(values){
6548 if(values instanceof Array){ // array of objects
6549 for(var i = 0, len = values.length; i < len; i++){
6551 var f = this.findField(v.id);
6553 f.setValue(v.value);
6554 if(this.trackResetOnLoad){
6555 f.originalValue = f.getValue();
6559 }else{ // object hash
6562 if(typeof values[id] != 'function' && (field = this.findField(id))){
6564 if (field.setFromData &&
6566 field.displayField &&
6567 // combos' with local stores can
6568 // be queried via setValue()
6569 // to set their value..
6570 (field.store && !field.store.isLocal)
6574 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6575 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6576 field.setFromData(sd);
6579 field.setValue(values[id]);
6583 if(this.trackResetOnLoad){
6584 field.originalValue = field.getValue();
6590 //Roo.each(this.childForms || [], function (f) {
6591 // f.setValues(values);
6598 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6599 * they are returned as an array.
6600 * @param {Boolean} asString
6603 getValues : function(asString){
6604 //if (this.childForms) {
6605 // copy values from the child forms
6606 // Roo.each(this.childForms, function (f) {
6607 // this.setValues(f.getValues());
6613 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6614 if(asString === true){
6617 return Roo.urlDecode(fs);
6621 * Returns the fields in this form as an object with key/value pairs.
6622 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6625 getFieldValues : function(with_hidden)
6627 var items = this.getItems();
6629 items.each(function(f){
6633 var v = f.getValue();
6634 if (f.inputType =='radio') {
6635 if (typeof(ret[f.getName()]) == 'undefined') {
6636 ret[f.getName()] = ''; // empty..
6639 if (!f.el.dom.checked) {
6647 // not sure if this supported any more..
6648 if ((typeof(v) == 'object') && f.getRawValue) {
6649 v = f.getRawValue() ; // dates..
6651 // combo boxes where name != hiddenName...
6652 if (f.name != f.getName()) {
6653 ret[f.name] = f.getRawValue();
6655 ret[f.getName()] = v;
6662 * Clears all invalid messages in this form.
6663 * @return {BasicForm} this
6665 clearInvalid : function(){
6666 var items = this.getItems();
6668 items.each(function(f){
6679 * @return {BasicForm} this
6682 var items = this.getItems();
6683 items.each(function(f){
6687 Roo.each(this.childForms || [], function (f) {
6694 getItems : function()
6696 var r=new Roo.util.MixedCollection(false, function(o){
6697 return o.id || (o.id = Roo.id());
6699 var iter = function(el) {
6706 Roo.each(el.items,function(e) {
6725 * Ext JS Library 1.1.1
6726 * Copyright(c) 2006-2007, Ext JS, LLC.
6728 * Originally Released Under LGPL - original licence link has changed is not relivant.
6731 * <script type="text/javascript">
6734 * @class Roo.form.VTypes
6735 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6738 Roo.form.VTypes = function(){
6739 // closure these in so they are only created once.
6740 var alpha = /^[a-zA-Z_]+$/;
6741 var alphanum = /^[a-zA-Z0-9_]+$/;
6742 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6743 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6745 // All these messages and functions are configurable
6748 * The function used to validate email addresses
6749 * @param {String} value The email address
6751 'email' : function(v){
6752 return email.test(v);
6755 * The error text to display when the email validation function returns false
6758 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6760 * The keystroke filter mask to be applied on email input
6763 'emailMask' : /[a-z0-9_\.\-@]/i,
6766 * The function used to validate URLs
6767 * @param {String} value The URL
6769 'url' : function(v){
6773 * The error text to display when the url validation function returns false
6776 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6779 * The function used to validate alpha values
6780 * @param {String} value The value
6782 'alpha' : function(v){
6783 return alpha.test(v);
6786 * The error text to display when the alpha validation function returns false
6789 'alphaText' : 'This field should only contain letters and _',
6791 * The keystroke filter mask to be applied on alpha input
6794 'alphaMask' : /[a-z_]/i,
6797 * The function used to validate alphanumeric values
6798 * @param {String} value The value
6800 'alphanum' : function(v){
6801 return alphanum.test(v);
6804 * The error text to display when the alphanumeric validation function returns false
6807 'alphanumText' : 'This field should only contain letters, numbers and _',
6809 * The keystroke filter mask to be applied on alphanumeric input
6812 'alphanumMask' : /[a-z0-9_]/i
6822 * @class Roo.bootstrap.Input
6823 * @extends Roo.bootstrap.Component
6824 * Bootstrap Input class
6825 * @cfg {Boolean} disabled is it disabled
6826 * @cfg {String} fieldLabel - the label associated
6827 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6828 * @cfg {String} name name of the input
6829 * @cfg {string} fieldLabel - the label associated
6830 * @cfg {string} inputType - input / file submit ...
6831 * @cfg {string} placeholder - placeholder to put in text.
6832 * @cfg {string} before - input group add on before
6833 * @cfg {string} after - input group add on after
6834 * @cfg {string} size - (lg|sm) or leave empty..
6835 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6836 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6837 * @cfg {Number} md colspan out of 12 for computer-sized screens
6838 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6839 * @cfg {string} value default value of the input
6840 * @cfg {Number} labelWidth set the width of label (0-12)
6841 * @cfg {String} labelAlign (top|left)
6842 * @cfg {Boolean} readOnly Specifies that the field should be read-only
6843 * @cfg {String} align (left|center|right) Default left
6847 * Create a new Input
6848 * @param {Object} config The config object
6851 Roo.bootstrap.Input = function(config){
6852 Roo.bootstrap.Input.superclass.constructor.call(this, config);
6857 * Fires when this field receives input focus.
6858 * @param {Roo.form.Field} this
6863 * Fires when this field loses input focus.
6864 * @param {Roo.form.Field} this
6869 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
6870 * {@link Roo.EventObject#getKey} to determine which key was pressed.
6871 * @param {Roo.form.Field} this
6872 * @param {Roo.EventObject} e The event object
6877 * Fires just before the field blurs if the field value has changed.
6878 * @param {Roo.form.Field} this
6879 * @param {Mixed} newValue The new value
6880 * @param {Mixed} oldValue The original value
6885 * Fires after the field has been marked as invalid.
6886 * @param {Roo.form.Field} this
6887 * @param {String} msg The validation message
6892 * Fires after the field has been validated with no errors.
6893 * @param {Roo.form.Field} this
6898 * Fires after the key up
6899 * @param {Roo.form.Field} this
6900 * @param {Roo.EventObject} e The event Object
6906 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
6908 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6909 automatic validation (defaults to "keyup").
6911 validationEvent : "keyup",
6913 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6915 validateOnBlur : true,
6917 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6919 validationDelay : 250,
6921 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6923 focusClass : "x-form-focus", // not needed???
6927 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6929 invalidClass : "has-error",
6932 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6934 selectOnFocus : false,
6937 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6941 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6946 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6948 disableKeyFilter : false,
6951 * @cfg {Boolean} disabled True to disable the field (defaults to false).
6955 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6959 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6961 blankText : "This field is required",
6964 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6968 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6970 maxLength : Number.MAX_VALUE,
6972 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6974 minLengthText : "The minimum length for this field is {0}",
6976 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6978 maxLengthText : "The maximum length for this field is {0}",
6982 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6983 * If available, this function will be called only after the basic validators all return true, and will be passed the
6984 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6988 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6989 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6990 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
6994 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7017 formatedValue : false,
7019 parentLabelAlign : function()
7022 while (parent.parent()) {
7023 parent = parent.parent();
7024 if (typeof(parent.labelAlign) !='undefined') {
7025 return parent.labelAlign;
7032 getAutoCreate : function(){
7034 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7040 if(this.inputType != 'hidden'){
7041 cfg.cls = 'form-group' //input-group
7047 type : this.inputType,
7049 cls : 'form-control',
7050 placeholder : this.placeholder || ''
7055 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7058 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7059 input.maxLength = this.maxLength;
7062 if (this.disabled) {
7063 input.disabled=true;
7066 if (this.readOnly) {
7067 input.readonly=true;
7071 input.name = this.name;
7074 input.cls += ' input-' + this.size;
7077 ['xs','sm','md','lg'].map(function(size){
7078 if (settings[size]) {
7079 cfg.cls += ' col-' + size + '-' + settings[size];
7083 var inputblock = input;
7085 if (this.before || this.after) {
7088 cls : 'input-group',
7091 if (this.before && typeof(this.before) == 'string') {
7093 inputblock.cn.push({
7095 cls : 'roo-input-before input-group-addon',
7099 if (this.before && typeof(this.before) == 'object') {
7100 this.before = Roo.factory(this.before);
7101 Roo.log(this.before);
7102 inputblock.cn.push({
7104 cls : 'roo-input-before input-group-' +
7105 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7109 inputblock.cn.push(input);
7111 if (this.after && typeof(this.after) == 'string') {
7112 inputblock.cn.push({
7114 cls : 'roo-input-after input-group-addon',
7118 if (this.after && typeof(this.after) == 'object') {
7119 this.after = Roo.factory(this.after);
7120 Roo.log(this.after);
7121 inputblock.cn.push({
7123 cls : 'roo-input-after input-group-' +
7124 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7129 if (align ==='left' && this.fieldLabel.length) {
7130 Roo.log("left and has label");
7136 cls : 'control-label col-sm-' + this.labelWidth,
7137 html : this.fieldLabel
7141 cls : "col-sm-" + (12 - this.labelWidth),
7148 } else if ( this.fieldLabel.length) {
7154 //cls : 'input-group-addon',
7155 html : this.fieldLabel
7165 Roo.log(" no label && no align");
7174 Roo.log('input-parentType: ' + this.parentType);
7176 if (this.parentType === 'Navbar' && this.parent().bar) {
7177 cfg.cls += ' navbar-form';
7185 * return the real input element.
7187 inputEl: function ()
7189 return this.el.select('input.form-control',true).first();
7191 setDisabled : function(v)
7193 var i = this.inputEl().dom;
7195 i.removeAttribute('disabled');
7199 i.setAttribute('disabled','true');
7201 initEvents : function()
7204 this.inputEl().on("keydown" , this.fireKey, this);
7205 this.inputEl().on("focus", this.onFocus, this);
7206 this.inputEl().on("blur", this.onBlur, this);
7208 this.inputEl().relayEvent('keyup', this);
7210 // reference to original value for reset
7211 this.originalValue = this.getValue();
7212 //Roo.form.TextField.superclass.initEvents.call(this);
7213 if(this.validationEvent == 'keyup'){
7214 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7215 this.inputEl().on('keyup', this.filterValidation, this);
7217 else if(this.validationEvent !== false){
7218 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7221 if(this.selectOnFocus){
7222 this.on("focus", this.preFocus, this);
7225 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7226 this.inputEl().on("keypress", this.filterKeys, this);
7229 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
7230 this.el.on("click", this.autoSize, this);
7233 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7234 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7237 if (typeof(this.before) == 'object') {
7238 this.before.render(this.el.select('.roo-input-before',true).first());
7240 if (typeof(this.after) == 'object') {
7241 this.after.render(this.el.select('.roo-input-after',true).first());
7246 filterValidation : function(e){
7247 if(!e.isNavKeyPress()){
7248 this.validationTask.delay(this.validationDelay);
7252 * Validates the field value
7253 * @return {Boolean} True if the value is valid, else false
7255 validate : function(){
7256 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7257 if(this.disabled || this.validateValue(this.getRawValue())){
7258 this.clearInvalid();
7266 * Validates a value according to the field's validation rules and marks the field as invalid
7267 * if the validation fails
7268 * @param {Mixed} value The value to validate
7269 * @return {Boolean} True if the value is valid, else false
7271 validateValue : function(value){
7272 if(value.length < 1) { // if it's blank
7273 if(this.allowBlank){
7274 this.clearInvalid();
7277 this.markInvalid(this.blankText);
7281 if(value.length < this.minLength){
7282 this.markInvalid(String.format(this.minLengthText, this.minLength));
7285 if(value.length > this.maxLength){
7286 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7290 var vt = Roo.form.VTypes;
7291 if(!vt[this.vtype](value, this)){
7292 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7296 if(typeof this.validator == "function"){
7297 var msg = this.validator(value);
7299 this.markInvalid(msg);
7303 if(this.regex && !this.regex.test(value)){
7304 this.markInvalid(this.regexText);
7313 fireKey : function(e){
7314 //Roo.log('field ' + e.getKey());
7315 if(e.isNavKeyPress()){
7316 this.fireEvent("specialkey", this, e);
7319 focus : function (selectText){
7321 this.inputEl().focus();
7322 if(selectText === true){
7323 this.inputEl().dom.select();
7329 onFocus : function(){
7330 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7331 // this.el.addClass(this.focusClass);
7334 this.hasFocus = true;
7335 this.startValue = this.getValue();
7336 this.fireEvent("focus", this);
7340 beforeBlur : Roo.emptyFn,
7344 onBlur : function(){
7346 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7347 //this.el.removeClass(this.focusClass);
7349 this.hasFocus = false;
7350 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7353 var v = this.getValue();
7354 if(String(v) !== String(this.startValue)){
7355 this.fireEvent('change', this, v, this.startValue);
7357 this.fireEvent("blur", this);
7361 * Resets the current field value to the originally loaded value and clears any validation messages
7364 this.setValue(this.originalValue);
7365 this.clearInvalid();
7368 * Returns the name of the field
7369 * @return {Mixed} name The name field
7371 getName: function(){
7375 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
7376 * @return {Mixed} value The field value
7378 getValue : function(){
7380 var v = this.inputEl().getValue();
7385 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
7386 * @return {Mixed} value The field value
7388 getRawValue : function(){
7389 var v = this.inputEl().getValue();
7395 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
7396 * @param {Mixed} value The value to set
7398 setRawValue : function(v){
7399 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7402 selectText : function(start, end){
7403 var v = this.getRawValue();
7405 start = start === undefined ? 0 : start;
7406 end = end === undefined ? v.length : end;
7407 var d = this.inputEl().dom;
7408 if(d.setSelectionRange){
7409 d.setSelectionRange(start, end);
7410 }else if(d.createTextRange){
7411 var range = d.createTextRange();
7412 range.moveStart("character", start);
7413 range.moveEnd("character", v.length-end);
7420 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
7421 * @param {Mixed} value The value to set
7423 setValue : function(v){
7426 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7432 processValue : function(value){
7433 if(this.stripCharsRe){
7434 var newValue = value.replace(this.stripCharsRe, '');
7435 if(newValue !== value){
7436 this.setRawValue(newValue);
7443 preFocus : function(){
7445 if(this.selectOnFocus){
7446 this.inputEl().dom.select();
7449 filterKeys : function(e){
7451 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7454 var c = e.getCharCode(), cc = String.fromCharCode(c);
7455 if(Roo.isIE && (e.isSpecialKey() || !cc)){
7458 if(!this.maskRe.test(cc)){
7463 * Clear any invalid styles/messages for this field
7465 clearInvalid : function(){
7467 if(!this.el || this.preventMark){ // not rendered
7470 this.el.removeClass(this.invalidClass);
7472 switch(this.msgTarget){
7474 this.el.dom.qtip = '';
7477 this.el.dom.title = '';
7481 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7486 this.errorIcon.dom.qtip = '';
7487 this.errorIcon.hide();
7488 this.un('resize', this.alignErrorIcon, this);
7492 var t = Roo.getDom(this.msgTarget);
7494 t.style.display = 'none';
7498 this.fireEvent('valid', this);
7501 * Mark this field as invalid
7502 * @param {String} msg The validation message
7504 markInvalid : function(msg){
7505 if(!this.el || this.preventMark){ // not rendered
7508 this.el.addClass(this.invalidClass);
7510 msg = msg || this.invalidText;
7511 switch(this.msgTarget){
7513 this.el.dom.qtip = msg;
7514 this.el.dom.qclass = 'x-form-invalid-tip';
7515 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7516 Roo.QuickTips.enable();
7520 this.el.dom.title = msg;
7524 var elp = this.el.findParent('.x-form-element', 5, true);
7525 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7526 this.errorEl.setWidth(elp.getWidth(true)-20);
7528 this.errorEl.update(msg);
7529 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7532 if(!this.errorIcon){
7533 var elp = this.el.findParent('.x-form-element', 5, true);
7534 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7536 this.alignErrorIcon();
7537 this.errorIcon.dom.qtip = msg;
7538 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7539 this.errorIcon.show();
7540 this.on('resize', this.alignErrorIcon, this);
7543 var t = Roo.getDom(this.msgTarget);
7545 t.style.display = this.msgDisplay;
7549 this.fireEvent('invalid', this, msg);
7552 SafariOnKeyDown : function(event)
7554 // this is a workaround for a password hang bug on chrome/ webkit.
7556 var isSelectAll = false;
7558 if(this.inputEl().dom.selectionEnd > 0){
7559 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7561 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7562 event.preventDefault();
7567 if(isSelectAll){ // backspace and delete key
7569 event.preventDefault();
7570 // this is very hacky as keydown always get's upper case.
7572 var cc = String.fromCharCode(event.getCharCode());
7573 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
7577 adjustWidth : function(tag, w){
7578 tag = tag.toLowerCase();
7579 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7580 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7584 if(tag == 'textarea'){
7587 }else if(Roo.isOpera){
7591 if(tag == 'textarea'){
7610 * @class Roo.bootstrap.TextArea
7611 * @extends Roo.bootstrap.Input
7612 * Bootstrap TextArea class
7613 * @cfg {Number} cols Specifies the visible width of a text area
7614 * @cfg {Number} rows Specifies the visible number of lines in a text area
7615 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7616 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7617 * @cfg {string} html text
7620 * Create a new TextArea
7621 * @param {Object} config The config object
7624 Roo.bootstrap.TextArea = function(config){
7625 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7629 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
7639 getAutoCreate : function(){
7641 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7652 value : this.value || '',
7653 html: this.html || '',
7654 cls : 'form-control',
7655 placeholder : this.placeholder || ''
7659 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7660 input.maxLength = this.maxLength;
7664 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7668 input.cols = this.cols;
7671 if (this.readOnly) {
7672 input.readonly = true;
7676 input.name = this.name;
7680 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7684 ['xs','sm','md','lg'].map(function(size){
7685 if (settings[size]) {
7686 cfg.cls += ' col-' + size + '-' + settings[size];
7690 var inputblock = input;
7692 if (this.before || this.after) {
7695 cls : 'input-group',
7699 inputblock.cn.push({
7701 cls : 'input-group-addon',
7705 inputblock.cn.push(input);
7707 inputblock.cn.push({
7709 cls : 'input-group-addon',
7716 if (align ==='left' && this.fieldLabel.length) {
7717 Roo.log("left and has label");
7723 cls : 'control-label col-sm-' + this.labelWidth,
7724 html : this.fieldLabel
7728 cls : "col-sm-" + (12 - this.labelWidth),
7735 } else if ( this.fieldLabel.length) {
7741 //cls : 'input-group-addon',
7742 html : this.fieldLabel
7752 Roo.log(" no label && no align");
7762 if (this.disabled) {
7763 input.disabled=true;
7770 * return the real textarea element.
7772 inputEl: function ()
7774 return this.el.select('textarea.form-control',true).first();
7782 * trigger field - base class for combo..
7787 * @class Roo.bootstrap.TriggerField
7788 * @extends Roo.bootstrap.Input
7789 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7790 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7791 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7792 * for which you can provide a custom implementation. For example:
7794 var trigger = new Roo.bootstrap.TriggerField();
7795 trigger.onTriggerClick = myTriggerFn;
7796 trigger.applyTo('my-field');
7799 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7800 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7801 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
7802 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7804 * Create a new TriggerField.
7805 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7806 * to the base TextField)
7808 Roo.bootstrap.TriggerField = function(config){
7809 this.mimicing = false;
7810 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7813 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
7815 * @cfg {String} triggerClass A CSS class to apply to the trigger
7818 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7822 /** @cfg {Boolean} grow @hide */
7823 /** @cfg {Number} growMin @hide */
7824 /** @cfg {Number} growMax @hide */
7830 autoSize: Roo.emptyFn,
7837 actionMode : 'wrap',
7841 getAutoCreate : function(){
7843 var align = this.labelAlign || this.parentLabelAlign();
7848 cls: 'form-group' //input-group
7855 type : this.inputType,
7856 cls : 'form-control',
7857 autocomplete: 'off',
7858 placeholder : this.placeholder || ''
7862 input.name = this.name;
7865 input.cls += ' input-' + this.size;
7868 if (this.disabled) {
7869 input.disabled=true;
7872 var inputblock = input;
7874 if (this.before || this.after) {
7877 cls : 'input-group',
7881 inputblock.cn.push({
7883 cls : 'input-group-addon',
7887 inputblock.cn.push(input);
7889 inputblock.cn.push({
7891 cls : 'input-group-addon',
7904 cls: 'form-hidden-field'
7912 Roo.log('multiple');
7920 cls: 'form-hidden-field'
7924 cls: 'select2-choices',
7928 cls: 'select2-search-field',
7941 cls: 'select2-container input-group',
7946 // cls: 'typeahead typeahead-long dropdown-menu',
7947 // style: 'display:none'
7952 if(!this.multiple && this.showToggleBtn){
7955 cls : 'input-group-addon btn dropdown-toggle',
7963 cls: 'combobox-clear',
7977 combobox.cls += ' select2-container-multi';
7980 if (align ==='left' && this.fieldLabel.length) {
7982 Roo.log("left and has label");
7988 cls : 'control-label col-sm-' + this.labelWidth,
7989 html : this.fieldLabel
7993 cls : "col-sm-" + (12 - this.labelWidth),
8000 } else if ( this.fieldLabel.length) {
8006 //cls : 'input-group-addon',
8007 html : this.fieldLabel
8017 Roo.log(" no label && no align");
8024 ['xs','sm','md','lg'].map(function(size){
8025 if (settings[size]) {
8026 cfg.cls += ' col-' + size + '-' + settings[size];
8037 onResize : function(w, h){
8038 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8039 // if(typeof w == 'number'){
8040 // var x = w - this.trigger.getWidth();
8041 // this.inputEl().setWidth(this.adjustWidth('input', x));
8042 // this.trigger.setStyle('left', x+'px');
8047 adjustSize : Roo.BoxComponent.prototype.adjustSize,
8050 getResizeEl : function(){
8051 return this.inputEl();
8055 getPositionEl : function(){
8056 return this.inputEl();
8060 alignErrorIcon : function(){
8061 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8065 initEvents : function(){
8069 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8070 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8071 if(!this.multiple && this.showToggleBtn){
8072 this.trigger = this.el.select('span.dropdown-toggle',true).first();
8073 if(this.hideTrigger){
8074 this.trigger.setDisplayed(false);
8076 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8080 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8083 //this.trigger.addClassOnOver('x-form-trigger-over');
8084 //this.trigger.addClassOnClick('x-form-trigger-click');
8087 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8091 createList : function()
8093 this.list = Roo.get(document.body).createChild({
8095 cls: 'typeahead typeahead-long dropdown-menu',
8096 style: 'display:none'
8099 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8104 initTrigger : function(){
8109 onDestroy : function(){
8111 this.trigger.removeAllListeners();
8112 // this.trigger.remove();
8115 // this.wrap.remove();
8117 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8121 onFocus : function(){
8122 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8125 this.wrap.addClass('x-trigger-wrap-focus');
8126 this.mimicing = true;
8127 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8128 if(this.monitorTab){
8129 this.el.on("keydown", this.checkTab, this);
8136 checkTab : function(e){
8137 if(e.getKey() == e.TAB){
8143 onBlur : function(){
8148 mimicBlur : function(e, t){
8150 if(!this.wrap.contains(t) && this.validateBlur()){
8157 triggerBlur : function(){
8158 this.mimicing = false;
8159 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8160 if(this.monitorTab){
8161 this.el.un("keydown", this.checkTab, this);
8163 //this.wrap.removeClass('x-trigger-wrap-focus');
8164 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8168 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8169 validateBlur : function(e, t){
8174 onDisable : function(){
8175 this.inputEl().dom.disabled = true;
8176 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8178 // this.wrap.addClass('x-item-disabled');
8183 onEnable : function(){
8184 this.inputEl().dom.disabled = false;
8185 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8187 // this.el.removeClass('x-item-disabled');
8192 onShow : function(){
8193 var ae = this.getActionEl();
8196 ae.dom.style.display = '';
8197 ae.dom.style.visibility = 'visible';
8203 onHide : function(){
8204 var ae = this.getActionEl();
8205 ae.dom.style.display = 'none';
8209 * The function that should handle the trigger's click event. This method does nothing by default until overridden
8210 * by an implementing function.
8212 * @param {EventObject} e
8214 onTriggerClick : Roo.emptyFn
8218 * Ext JS Library 1.1.1
8219 * Copyright(c) 2006-2007, Ext JS, LLC.
8221 * Originally Released Under LGPL - original licence link has changed is not relivant.
8224 * <script type="text/javascript">
8229 * @class Roo.data.SortTypes
8231 * Defines the default sorting (casting?) comparison functions used when sorting data.
8233 Roo.data.SortTypes = {
8235 * Default sort that does nothing
8236 * @param {Mixed} s The value being converted
8237 * @return {Mixed} The comparison value
8244 * The regular expression used to strip tags
8248 stripTagsRE : /<\/?[^>]+>/gi,
8251 * Strips all HTML tags to sort on text only
8252 * @param {Mixed} s The value being converted
8253 * @return {String} The comparison value
8255 asText : function(s){
8256 return String(s).replace(this.stripTagsRE, "");
8260 * Strips all HTML tags to sort on text only - Case insensitive
8261 * @param {Mixed} s The value being converted
8262 * @return {String} The comparison value
8264 asUCText : function(s){
8265 return String(s).toUpperCase().replace(this.stripTagsRE, "");
8269 * Case insensitive string
8270 * @param {Mixed} s The value being converted
8271 * @return {String} The comparison value
8273 asUCString : function(s) {
8274 return String(s).toUpperCase();
8279 * @param {Mixed} s The value being converted
8280 * @return {Number} The comparison value
8282 asDate : function(s) {
8286 if(s instanceof Date){
8289 return Date.parse(String(s));
8294 * @param {Mixed} s The value being converted
8295 * @return {Float} The comparison value
8297 asFloat : function(s) {
8298 var val = parseFloat(String(s).replace(/,/g, ""));
8299 if(isNaN(val)) val = 0;
8305 * @param {Mixed} s The value being converted
8306 * @return {Number} The comparison value
8308 asInt : function(s) {
8309 var val = parseInt(String(s).replace(/,/g, ""));
8310 if(isNaN(val)) val = 0;
8315 * Ext JS Library 1.1.1
8316 * Copyright(c) 2006-2007, Ext JS, LLC.
8318 * Originally Released Under LGPL - original licence link has changed is not relivant.
8321 * <script type="text/javascript">
8325 * @class Roo.data.Record
8326 * Instances of this class encapsulate both record <em>definition</em> information, and record
8327 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8328 * to access Records cached in an {@link Roo.data.Store} object.<br>
8330 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8331 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8334 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8336 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8337 * {@link #create}. The parameters are the same.
8338 * @param {Array} data An associative Array of data values keyed by the field name.
8339 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8340 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8341 * not specified an integer id is generated.
8343 Roo.data.Record = function(data, id){
8344 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8349 * Generate a constructor for a specific record layout.
8350 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8351 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8352 * Each field definition object may contain the following properties: <ul>
8353 * <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,
8354 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8355 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8356 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8357 * is being used, then this is a string containing the javascript expression to reference the data relative to
8358 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8359 * to the data item relative to the record element. If the mapping expression is the same as the field name,
8360 * this may be omitted.</p></li>
8361 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8362 * <ul><li>auto (Default, implies no conversion)</li>
8367 * <li>date</li></ul></p></li>
8368 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8369 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8370 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8371 * by the Reader into an object that will be stored in the Record. It is passed the
8372 * following parameters:<ul>
8373 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8375 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8377 * <br>usage:<br><pre><code>
8378 var TopicRecord = Roo.data.Record.create(
8379 {name: 'title', mapping: 'topic_title'},
8380 {name: 'author', mapping: 'username'},
8381 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8382 {name: 'lastPost', mapping: 'post_time', type: 'date'},
8383 {name: 'lastPoster', mapping: 'user2'},
8384 {name: 'excerpt', mapping: 'post_text'}
8387 var myNewRecord = new TopicRecord({
8388 title: 'Do my job please',
8391 lastPost: new Date(),
8392 lastPoster: 'Animal',
8393 excerpt: 'No way dude!'
8395 myStore.add(myNewRecord);
8400 Roo.data.Record.create = function(o){
8402 f.superclass.constructor.apply(this, arguments);
8404 Roo.extend(f, Roo.data.Record);
8405 var p = f.prototype;
8406 p.fields = new Roo.util.MixedCollection(false, function(field){
8409 for(var i = 0, len = o.length; i < len; i++){
8410 p.fields.add(new Roo.data.Field(o[i]));
8412 f.getField = function(name){
8413 return p.fields.get(name);
8418 Roo.data.Record.AUTO_ID = 1000;
8419 Roo.data.Record.EDIT = 'edit';
8420 Roo.data.Record.REJECT = 'reject';
8421 Roo.data.Record.COMMIT = 'commit';
8423 Roo.data.Record.prototype = {
8425 * Readonly flag - true if this record has been modified.
8434 join : function(store){
8439 * Set the named field to the specified value.
8440 * @param {String} name The name of the field to set.
8441 * @param {Object} value The value to set the field to.
8443 set : function(name, value){
8444 if(this.data[name] == value){
8451 if(typeof this.modified[name] == 'undefined'){
8452 this.modified[name] = this.data[name];
8454 this.data[name] = value;
8455 if(!this.editing && this.store){
8456 this.store.afterEdit(this);
8461 * Get the value of the named field.
8462 * @param {String} name The name of the field to get the value of.
8463 * @return {Object} The value of the field.
8465 get : function(name){
8466 return this.data[name];
8470 beginEdit : function(){
8471 this.editing = true;
8476 cancelEdit : function(){
8477 this.editing = false;
8478 delete this.modified;
8482 endEdit : function(){
8483 this.editing = false;
8484 if(this.dirty && this.store){
8485 this.store.afterEdit(this);
8490 * Usually called by the {@link Roo.data.Store} which owns the Record.
8491 * Rejects all changes made to the Record since either creation, or the last commit operation.
8492 * Modified fields are reverted to their original values.
8494 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8495 * of reject operations.
8497 reject : function(){
8498 var m = this.modified;
8500 if(typeof m[n] != "function"){
8501 this.data[n] = m[n];
8505 delete this.modified;
8506 this.editing = false;
8508 this.store.afterReject(this);
8513 * Usually called by the {@link Roo.data.Store} which owns the Record.
8514 * Commits all changes made to the Record since either creation, or the last commit operation.
8516 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8517 * of commit operations.
8519 commit : function(){
8521 delete this.modified;
8522 this.editing = false;
8524 this.store.afterCommit(this);
8529 hasError : function(){
8530 return this.error != null;
8534 clearError : function(){
8539 * Creates a copy of this record.
8540 * @param {String} id (optional) A new record id if you don't want to use this record's id
8543 copy : function(newId) {
8544 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8548 * Ext JS Library 1.1.1
8549 * Copyright(c) 2006-2007, Ext JS, LLC.
8551 * Originally Released Under LGPL - original licence link has changed is not relivant.
8554 * <script type="text/javascript">
8560 * @class Roo.data.Store
8561 * @extends Roo.util.Observable
8562 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8563 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8565 * 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
8566 * has no knowledge of the format of the data returned by the Proxy.<br>
8568 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8569 * instances from the data object. These records are cached and made available through accessor functions.
8571 * Creates a new Store.
8572 * @param {Object} config A config object containing the objects needed for the Store to access data,
8573 * and read the data into Records.
8575 Roo.data.Store = function(config){
8576 this.data = new Roo.util.MixedCollection(false);
8577 this.data.getKey = function(o){
8580 this.baseParams = {};
8587 "multisort" : "_multisort"
8590 if(config && config.data){
8591 this.inlineData = config.data;
8595 Roo.apply(this, config);
8597 if(this.reader){ // reader passed
8598 this.reader = Roo.factory(this.reader, Roo.data);
8599 this.reader.xmodule = this.xmodule || false;
8600 if(!this.recordType){
8601 this.recordType = this.reader.recordType;
8603 if(this.reader.onMetaChange){
8604 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8608 if(this.recordType){
8609 this.fields = this.recordType.prototype.fields;
8615 * @event datachanged
8616 * Fires when the data cache has changed, and a widget which is using this Store
8617 * as a Record cache should refresh its view.
8618 * @param {Store} this
8623 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8624 * @param {Store} this
8625 * @param {Object} meta The JSON metadata
8630 * Fires when Records have been added to the Store
8631 * @param {Store} this
8632 * @param {Roo.data.Record[]} records The array of Records added
8633 * @param {Number} index The index at which the record(s) were added
8638 * Fires when a Record has been removed from the Store
8639 * @param {Store} this
8640 * @param {Roo.data.Record} record The Record that was removed
8641 * @param {Number} index The index at which the record was removed
8646 * Fires when a Record has been updated
8647 * @param {Store} this
8648 * @param {Roo.data.Record} record The Record that was updated
8649 * @param {String} operation The update operation being performed. Value may be one of:
8651 Roo.data.Record.EDIT
8652 Roo.data.Record.REJECT
8653 Roo.data.Record.COMMIT
8659 * Fires when the data cache has been cleared.
8660 * @param {Store} this
8665 * Fires before a request is made for a new data object. If the beforeload handler returns false
8666 * the load action will be canceled.
8667 * @param {Store} this
8668 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8672 * @event beforeloadadd
8673 * Fires after a new set of Records has been loaded.
8674 * @param {Store} this
8675 * @param {Roo.data.Record[]} records The Records that were loaded
8676 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8678 beforeloadadd : true,
8681 * Fires after a new set of Records has been loaded, before they are added to the store.
8682 * @param {Store} this
8683 * @param {Roo.data.Record[]} records The Records that were loaded
8684 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8685 * @params {Object} return from reader
8689 * @event loadexception
8690 * Fires if an exception occurs in the Proxy during loading.
8691 * Called with the signature of the Proxy's "loadexception" event.
8692 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8695 * @param {Object} return from JsonData.reader() - success, totalRecords, records
8696 * @param {Object} load options
8697 * @param {Object} jsonData from your request (normally this contains the Exception)
8699 loadexception : true
8703 this.proxy = Roo.factory(this.proxy, Roo.data);
8704 this.proxy.xmodule = this.xmodule || false;
8705 this.relayEvents(this.proxy, ["loadexception"]);
8707 this.sortToggle = {};
8708 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8710 Roo.data.Store.superclass.constructor.call(this);
8712 if(this.inlineData){
8713 this.loadData(this.inlineData);
8714 delete this.inlineData;
8718 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8720 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
8721 * without a remote query - used by combo/forms at present.
8725 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8728 * @cfg {Array} data Inline data to be loaded when the store is initialized.
8731 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8732 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8735 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8736 * on any HTTP request
8739 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8742 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8746 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8747 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8752 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8753 * loaded or when a record is removed. (defaults to false).
8755 pruneModifiedRecords : false,
8761 * Add Records to the Store and fires the add event.
8762 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8764 add : function(records){
8765 records = [].concat(records);
8766 for(var i = 0, len = records.length; i < len; i++){
8767 records[i].join(this);
8769 var index = this.data.length;
8770 this.data.addAll(records);
8771 this.fireEvent("add", this, records, index);
8775 * Remove a Record from the Store and fires the remove event.
8776 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8778 remove : function(record){
8779 var index = this.data.indexOf(record);
8780 this.data.removeAt(index);
8781 if(this.pruneModifiedRecords){
8782 this.modified.remove(record);
8784 this.fireEvent("remove", this, record, index);
8788 * Remove all Records from the Store and fires the clear event.
8790 removeAll : function(){
8792 if(this.pruneModifiedRecords){
8795 this.fireEvent("clear", this);
8799 * Inserts Records to the Store at the given index and fires the add event.
8800 * @param {Number} index The start index at which to insert the passed Records.
8801 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8803 insert : function(index, records){
8804 records = [].concat(records);
8805 for(var i = 0, len = records.length; i < len; i++){
8806 this.data.insert(index, records[i]);
8807 records[i].join(this);
8809 this.fireEvent("add", this, records, index);
8813 * Get the index within the cache of the passed Record.
8814 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8815 * @return {Number} The index of the passed Record. Returns -1 if not found.
8817 indexOf : function(record){
8818 return this.data.indexOf(record);
8822 * Get the index within the cache of the Record with the passed id.
8823 * @param {String} id The id of the Record to find.
8824 * @return {Number} The index of the Record. Returns -1 if not found.
8826 indexOfId : function(id){
8827 return this.data.indexOfKey(id);
8831 * Get the Record with the specified id.
8832 * @param {String} id The id of the Record to find.
8833 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8835 getById : function(id){
8836 return this.data.key(id);
8840 * Get the Record at the specified index.
8841 * @param {Number} index The index of the Record to find.
8842 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8844 getAt : function(index){
8845 return this.data.itemAt(index);
8849 * Returns a range of Records between specified indices.
8850 * @param {Number} startIndex (optional) The starting index (defaults to 0)
8851 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8852 * @return {Roo.data.Record[]} An array of Records
8854 getRange : function(start, end){
8855 return this.data.getRange(start, end);
8859 storeOptions : function(o){
8860 o = Roo.apply({}, o);
8863 this.lastOptions = o;
8867 * Loads the Record cache from the configured Proxy using the configured Reader.
8869 * If using remote paging, then the first load call must specify the <em>start</em>
8870 * and <em>limit</em> properties in the options.params property to establish the initial
8871 * position within the dataset, and the number of Records to cache on each read from the Proxy.
8873 * <strong>It is important to note that for remote data sources, loading is asynchronous,
8874 * and this call will return before the new data has been loaded. Perform any post-processing
8875 * in a callback function, or in a "load" event handler.</strong>
8877 * @param {Object} options An object containing properties which control loading options:<ul>
8878 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8879 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8880 * passed the following arguments:<ul>
8881 * <li>r : Roo.data.Record[]</li>
8882 * <li>options: Options object from the load call</li>
8883 * <li>success: Boolean success indicator</li></ul></li>
8884 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8885 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8888 load : function(options){
8889 options = options || {};
8890 if(this.fireEvent("beforeload", this, options) !== false){
8891 this.storeOptions(options);
8892 var p = Roo.apply(options.params || {}, this.baseParams);
8893 // if meta was not loaded from remote source.. try requesting it.
8894 if (!this.reader.metaFromRemote) {
8897 if(this.sortInfo && this.remoteSort){
8898 var pn = this.paramNames;
8899 p[pn["sort"]] = this.sortInfo.field;
8900 p[pn["dir"]] = this.sortInfo.direction;
8902 if (this.multiSort) {
8903 var pn = this.paramNames;
8904 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8907 this.proxy.load(p, this.reader, this.loadRecords, this, options);
8912 * Reloads the Record cache from the configured Proxy using the configured Reader and
8913 * the options from the last load operation performed.
8914 * @param {Object} options (optional) An object containing properties which may override the options
8915 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8916 * the most recently used options are reused).
8918 reload : function(options){
8919 this.load(Roo.applyIf(options||{}, this.lastOptions));
8923 // Called as a callback by the Reader during a load operation.
8924 loadRecords : function(o, options, success){
8925 if(!o || success === false){
8926 if(success !== false){
8927 this.fireEvent("load", this, [], options, o);
8929 if(options.callback){
8930 options.callback.call(options.scope || this, [], options, false);
8934 // if data returned failure - throw an exception.
8935 if (o.success === false) {
8936 // show a message if no listener is registered.
8937 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8938 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8940 // loadmask wil be hooked into this..
8941 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8944 var r = o.records, t = o.totalRecords || r.length;
8946 this.fireEvent("beforeloadadd", this, r, options, o);
8948 if(!options || options.add !== true){
8949 if(this.pruneModifiedRecords){
8952 for(var i = 0, len = r.length; i < len; i++){
8956 this.data = this.snapshot;
8957 delete this.snapshot;
8960 this.data.addAll(r);
8961 this.totalLength = t;
8963 this.fireEvent("datachanged", this);
8965 this.totalLength = Math.max(t, this.data.length+r.length);
8968 this.fireEvent("load", this, r, options, o);
8969 if(options.callback){
8970 options.callback.call(options.scope || this, r, options, true);
8976 * Loads data from a passed data block. A Reader which understands the format of the data
8977 * must have been configured in the constructor.
8978 * @param {Object} data The data block from which to read the Records. The format of the data expected
8979 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8980 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8982 loadData : function(o, append){
8983 var r = this.reader.readRecords(o);
8984 this.loadRecords(r, {add: append}, true);
8988 * Gets the number of cached records.
8990 * <em>If using paging, this may not be the total size of the dataset. If the data object
8991 * used by the Reader contains the dataset size, then the getTotalCount() function returns
8992 * the data set size</em>
8994 getCount : function(){
8995 return this.data.length || 0;
8999 * Gets the total number of records in the dataset as returned by the server.
9001 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9002 * the dataset size</em>
9004 getTotalCount : function(){
9005 return this.totalLength || 0;
9009 * Returns the sort state of the Store as an object with two properties:
9011 field {String} The name of the field by which the Records are sorted
9012 direction {String} The sort order, "ASC" or "DESC"
9015 getSortState : function(){
9016 return this.sortInfo;
9020 applySort : function(){
9021 if(this.sortInfo && !this.remoteSort){
9022 var s = this.sortInfo, f = s.field;
9023 var st = this.fields.get(f).sortType;
9024 var fn = function(r1, r2){
9025 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9026 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9028 this.data.sort(s.direction, fn);
9029 if(this.snapshot && this.snapshot != this.data){
9030 this.snapshot.sort(s.direction, fn);
9036 * Sets the default sort column and order to be used by the next load operation.
9037 * @param {String} fieldName The name of the field to sort by.
9038 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9040 setDefaultSort : function(field, dir){
9041 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9046 * If remote sorting is used, the sort is performed on the server, and the cache is
9047 * reloaded. If local sorting is used, the cache is sorted internally.
9048 * @param {String} fieldName The name of the field to sort by.
9049 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9051 sort : function(fieldName, dir){
9052 var f = this.fields.get(fieldName);
9054 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9056 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9057 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9062 this.sortToggle[f.name] = dir;
9063 this.sortInfo = {field: f.name, direction: dir};
9064 if(!this.remoteSort){
9066 this.fireEvent("datachanged", this);
9068 this.load(this.lastOptions);
9073 * Calls the specified function for each of the Records in the cache.
9074 * @param {Function} fn The function to call. The Record is passed as the first parameter.
9075 * Returning <em>false</em> aborts and exits the iteration.
9076 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9078 each : function(fn, scope){
9079 this.data.each(fn, scope);
9083 * Gets all records modified since the last commit. Modified records are persisted across load operations
9084 * (e.g., during paging).
9085 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9087 getModifiedRecords : function(){
9088 return this.modified;
9092 createFilterFn : function(property, value, anyMatch){
9093 if(!value.exec){ // not a regex
9094 value = String(value);
9095 if(value.length == 0){
9098 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9101 return value.test(r.data[property]);
9106 * Sums the value of <i>property</i> for each record between start and end and returns the result.
9107 * @param {String} property A field on your records
9108 * @param {Number} start The record index to start at (defaults to 0)
9109 * @param {Number} end The last record index to include (defaults to length - 1)
9110 * @return {Number} The sum
9112 sum : function(property, start, end){
9113 var rs = this.data.items, v = 0;
9115 end = (end || end === 0) ? end : rs.length-1;
9117 for(var i = start; i <= end; i++){
9118 v += (rs[i].data[property] || 0);
9124 * Filter the records by a specified property.
9125 * @param {String} field A field on your records
9126 * @param {String/RegExp} value Either a string that the field
9127 * should start with or a RegExp to test against the field
9128 * @param {Boolean} anyMatch True to match any part not just the beginning
9130 filter : function(property, value, anyMatch){
9131 var fn = this.createFilterFn(property, value, anyMatch);
9132 return fn ? this.filterBy(fn) : this.clearFilter();
9136 * Filter by a function. The specified function will be called with each
9137 * record in this data source. If the function returns true the record is included,
9138 * otherwise it is filtered.
9139 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9140 * @param {Object} scope (optional) The scope of the function (defaults to this)
9142 filterBy : function(fn, scope){
9143 this.snapshot = this.snapshot || this.data;
9144 this.data = this.queryBy(fn, scope||this);
9145 this.fireEvent("datachanged", this);
9149 * Query the records by a specified property.
9150 * @param {String} field A field on your records
9151 * @param {String/RegExp} value Either a string that the field
9152 * should start with or a RegExp to test against the field
9153 * @param {Boolean} anyMatch True to match any part not just the beginning
9154 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9156 query : function(property, value, anyMatch){
9157 var fn = this.createFilterFn(property, value, anyMatch);
9158 return fn ? this.queryBy(fn) : this.data.clone();
9162 * Query by a function. The specified function will be called with each
9163 * record in this data source. If the function returns true the record is included
9165 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9166 * @param {Object} scope (optional) The scope of the function (defaults to this)
9167 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9169 queryBy : function(fn, scope){
9170 var data = this.snapshot || this.data;
9171 return data.filterBy(fn, scope||this);
9175 * Collects unique values for a particular dataIndex from this store.
9176 * @param {String} dataIndex The property to collect
9177 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9178 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9179 * @return {Array} An array of the unique values
9181 collect : function(dataIndex, allowNull, bypassFilter){
9182 var d = (bypassFilter === true && this.snapshot) ?
9183 this.snapshot.items : this.data.items;
9184 var v, sv, r = [], l = {};
9185 for(var i = 0, len = d.length; i < len; i++){
9186 v = d[i].data[dataIndex];
9188 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9197 * Revert to a view of the Record cache with no filtering applied.
9198 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9200 clearFilter : function(suppressEvent){
9201 if(this.snapshot && this.snapshot != this.data){
9202 this.data = this.snapshot;
9203 delete this.snapshot;
9204 if(suppressEvent !== true){
9205 this.fireEvent("datachanged", this);
9211 afterEdit : function(record){
9212 if(this.modified.indexOf(record) == -1){
9213 this.modified.push(record);
9215 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9219 afterReject : function(record){
9220 this.modified.remove(record);
9221 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9225 afterCommit : function(record){
9226 this.modified.remove(record);
9227 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9231 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9232 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9234 commitChanges : function(){
9235 var m = this.modified.slice(0);
9237 for(var i = 0, len = m.length; i < len; i++){
9243 * Cancel outstanding changes on all changed records.
9245 rejectChanges : function(){
9246 var m = this.modified.slice(0);
9248 for(var i = 0, len = m.length; i < len; i++){
9253 onMetaChange : function(meta, rtype, o){
9254 this.recordType = rtype;
9255 this.fields = rtype.prototype.fields;
9256 delete this.snapshot;
9257 this.sortInfo = meta.sortInfo || this.sortInfo;
9259 this.fireEvent('metachange', this, this.reader.meta);
9262 moveIndex : function(data, type)
9264 var index = this.indexOf(data);
9266 var newIndex = index + type;
9270 this.insert(newIndex, data);
9275 * Ext JS Library 1.1.1
9276 * Copyright(c) 2006-2007, Ext JS, LLC.
9278 * Originally Released Under LGPL - original licence link has changed is not relivant.
9281 * <script type="text/javascript">
9285 * @class Roo.data.SimpleStore
9286 * @extends Roo.data.Store
9287 * Small helper class to make creating Stores from Array data easier.
9288 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9289 * @cfg {Array} fields An array of field definition objects, or field name strings.
9290 * @cfg {Array} data The multi-dimensional array of data
9292 * @param {Object} config
9294 Roo.data.SimpleStore = function(config){
9295 Roo.data.SimpleStore.superclass.constructor.call(this, {
9297 reader: new Roo.data.ArrayReader({
9300 Roo.data.Record.create(config.fields)
9302 proxy : new Roo.data.MemoryProxy(config.data)
9306 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9308 * Ext JS Library 1.1.1
9309 * Copyright(c) 2006-2007, Ext JS, LLC.
9311 * Originally Released Under LGPL - original licence link has changed is not relivant.
9314 * <script type="text/javascript">
9319 * @extends Roo.data.Store
9320 * @class Roo.data.JsonStore
9321 * Small helper class to make creating Stores for JSON data easier. <br/>
9323 var store = new Roo.data.JsonStore({
9324 url: 'get-images.php',
9326 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9329 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9330 * JsonReader and HttpProxy (unless inline data is provided).</b>
9331 * @cfg {Array} fields An array of field definition objects, or field name strings.
9333 * @param {Object} config
9335 Roo.data.JsonStore = function(c){
9336 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9337 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9338 reader: new Roo.data.JsonReader(c, c.fields)
9341 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9343 * Ext JS Library 1.1.1
9344 * Copyright(c) 2006-2007, Ext JS, LLC.
9346 * Originally Released Under LGPL - original licence link has changed is not relivant.
9349 * <script type="text/javascript">
9353 Roo.data.Field = function(config){
9354 if(typeof config == "string"){
9355 config = {name: config};
9357 Roo.apply(this, config);
9363 var st = Roo.data.SortTypes;
9364 // named sortTypes are supported, here we look them up
9365 if(typeof this.sortType == "string"){
9366 this.sortType = st[this.sortType];
9369 // set default sortType for strings and dates
9373 this.sortType = st.asUCString;
9376 this.sortType = st.asDate;
9379 this.sortType = st.none;
9384 var stripRe = /[\$,%]/g;
9386 // prebuilt conversion function for this field, instead of
9387 // switching every time we're reading a value
9389 var cv, dateFormat = this.dateFormat;
9394 cv = function(v){ return v; };
9397 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9401 return v !== undefined && v !== null && v !== '' ?
9402 parseInt(String(v).replace(stripRe, ""), 10) : '';
9407 return v !== undefined && v !== null && v !== '' ?
9408 parseFloat(String(v).replace(stripRe, ""), 10) : '';
9413 cv = function(v){ return v === true || v === "true" || v == 1; };
9420 if(v instanceof Date){
9424 if(dateFormat == "timestamp"){
9425 return new Date(v*1000);
9427 return Date.parseDate(v, dateFormat);
9429 var parsed = Date.parse(v);
9430 return parsed ? new Date(parsed) : null;
9439 Roo.data.Field.prototype = {
9447 * Ext JS Library 1.1.1
9448 * Copyright(c) 2006-2007, Ext JS, LLC.
9450 * Originally Released Under LGPL - original licence link has changed is not relivant.
9453 * <script type="text/javascript">
9456 // Base class for reading structured data from a data source. This class is intended to be
9457 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9460 * @class Roo.data.DataReader
9461 * Base class for reading structured data from a data source. This class is intended to be
9462 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9465 Roo.data.DataReader = function(meta, recordType){
9469 this.recordType = recordType instanceof Array ?
9470 Roo.data.Record.create(recordType) : recordType;
9473 Roo.data.DataReader.prototype = {
9475 * Create an empty record
9476 * @param {Object} data (optional) - overlay some values
9477 * @return {Roo.data.Record} record created.
9479 newRow : function(d) {
9481 this.recordType.prototype.fields.each(function(c) {
9483 case 'int' : da[c.name] = 0; break;
9484 case 'date' : da[c.name] = new Date(); break;
9485 case 'float' : da[c.name] = 0.0; break;
9486 case 'boolean' : da[c.name] = false; break;
9487 default : da[c.name] = ""; break;
9491 return new this.recordType(Roo.apply(da, d));
9496 * Ext JS Library 1.1.1
9497 * Copyright(c) 2006-2007, Ext JS, LLC.
9499 * Originally Released Under LGPL - original licence link has changed is not relivant.
9502 * <script type="text/javascript">
9506 * @class Roo.data.DataProxy
9507 * @extends Roo.data.Observable
9508 * This class is an abstract base class for implementations which provide retrieval of
9509 * unformatted data objects.<br>
9511 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9512 * (of the appropriate type which knows how to parse the data object) to provide a block of
9513 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9515 * Custom implementations must implement the load method as described in
9516 * {@link Roo.data.HttpProxy#load}.
9518 Roo.data.DataProxy = function(){
9522 * Fires before a network request is made to retrieve a data object.
9523 * @param {Object} This DataProxy object.
9524 * @param {Object} params The params parameter to the load function.
9529 * Fires before the load method's callback is called.
9530 * @param {Object} This DataProxy object.
9531 * @param {Object} o The data object.
9532 * @param {Object} arg The callback argument object passed to the load function.
9536 * @event loadexception
9537 * Fires if an Exception occurs during data retrieval.
9538 * @param {Object} This DataProxy object.
9539 * @param {Object} o The data object.
9540 * @param {Object} arg The callback argument object passed to the load function.
9541 * @param {Object} e The Exception.
9543 loadexception : true
9545 Roo.data.DataProxy.superclass.constructor.call(this);
9548 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9551 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9555 * Ext JS Library 1.1.1
9556 * Copyright(c) 2006-2007, Ext JS, LLC.
9558 * Originally Released Under LGPL - original licence link has changed is not relivant.
9561 * <script type="text/javascript">
9564 * @class Roo.data.MemoryProxy
9565 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9566 * to the Reader when its load method is called.
9568 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9570 Roo.data.MemoryProxy = function(data){
9574 Roo.data.MemoryProxy.superclass.constructor.call(this);
9578 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9580 * Load data from the requested source (in this case an in-memory
9581 * data object passed to the constructor), read the data object into
9582 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9583 * process that block using the passed callback.
9584 * @param {Object} params This parameter is not used by the MemoryProxy class.
9585 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9586 * object into a block of Roo.data.Records.
9587 * @param {Function} callback The function into which to pass the block of Roo.data.records.
9588 * The function must be passed <ul>
9589 * <li>The Record block object</li>
9590 * <li>The "arg" argument from the load function</li>
9591 * <li>A boolean success indicator</li>
9593 * @param {Object} scope The scope in which to call the callback
9594 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9596 load : function(params, reader, callback, scope, arg){
9597 params = params || {};
9600 result = reader.readRecords(this.data);
9602 this.fireEvent("loadexception", this, arg, null, e);
9603 callback.call(scope, null, arg, false);
9606 callback.call(scope, result, arg, true);
9610 update : function(params, records){
9615 * Ext JS Library 1.1.1
9616 * Copyright(c) 2006-2007, Ext JS, LLC.
9618 * Originally Released Under LGPL - original licence link has changed is not relivant.
9621 * <script type="text/javascript">
9624 * @class Roo.data.HttpProxy
9625 * @extends Roo.data.DataProxy
9626 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9627 * configured to reference a certain URL.<br><br>
9629 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9630 * from which the running page was served.<br><br>
9632 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9634 * Be aware that to enable the browser to parse an XML document, the server must set
9635 * the Content-Type header in the HTTP response to "text/xml".
9637 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9638 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
9639 * will be used to make the request.
9641 Roo.data.HttpProxy = function(conn){
9642 Roo.data.HttpProxy.superclass.constructor.call(this);
9643 // is conn a conn config or a real conn?
9645 this.useAjax = !conn || !conn.events;
9649 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9650 // thse are take from connection...
9653 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9656 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9657 * extra parameters to each request made by this object. (defaults to undefined)
9660 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9661 * to each request made by this object. (defaults to undefined)
9664 * @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)
9667 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9670 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9676 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9680 * Return the {@link Roo.data.Connection} object being used by this Proxy.
9681 * @return {Connection} The Connection object. This object may be used to subscribe to events on
9682 * a finer-grained basis than the DataProxy events.
9684 getConnection : function(){
9685 return this.useAjax ? Roo.Ajax : this.conn;
9689 * Load data from the configured {@link Roo.data.Connection}, read the data object into
9690 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9691 * process that block using the passed callback.
9692 * @param {Object} params An object containing properties which are to be used as HTTP parameters
9693 * for the request to the remote server.
9694 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9695 * object into a block of Roo.data.Records.
9696 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9697 * The function must be passed <ul>
9698 * <li>The Record block object</li>
9699 * <li>The "arg" argument from the load function</li>
9700 * <li>A boolean success indicator</li>
9702 * @param {Object} scope The scope in which to call the callback
9703 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9705 load : function(params, reader, callback, scope, arg){
9706 if(this.fireEvent("beforeload", this, params) !== false){
9708 params : params || {},
9710 callback : callback,
9715 callback : this.loadResponse,
9719 Roo.applyIf(o, this.conn);
9720 if(this.activeRequest){
9721 Roo.Ajax.abort(this.activeRequest);
9723 this.activeRequest = Roo.Ajax.request(o);
9725 this.conn.request(o);
9728 callback.call(scope||this, null, arg, false);
9733 loadResponse : function(o, success, response){
9734 delete this.activeRequest;
9736 this.fireEvent("loadexception", this, o, response);
9737 o.request.callback.call(o.request.scope, null, o.request.arg, false);
9742 result = o.reader.read(response);
9744 this.fireEvent("loadexception", this, o, response, e);
9745 o.request.callback.call(o.request.scope, null, o.request.arg, false);
9749 this.fireEvent("load", this, o, o.request.arg);
9750 o.request.callback.call(o.request.scope, result, o.request.arg, true);
9754 update : function(dataSet){
9759 updateResponse : function(dataSet){
9764 * Ext JS Library 1.1.1
9765 * Copyright(c) 2006-2007, Ext JS, LLC.
9767 * Originally Released Under LGPL - original licence link has changed is not relivant.
9770 * <script type="text/javascript">
9774 * @class Roo.data.ScriptTagProxy
9775 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9776 * other than the originating domain of the running page.<br><br>
9778 * <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
9779 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9781 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9782 * source code that is used as the source inside a <script> tag.<br><br>
9784 * In order for the browser to process the returned data, the server must wrap the data object
9785 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9786 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9787 * depending on whether the callback name was passed:
9790 boolean scriptTag = false;
9791 String cb = request.getParameter("callback");
9794 response.setContentType("text/javascript");
9796 response.setContentType("application/x-json");
9798 Writer out = response.getWriter();
9800 out.write(cb + "(");
9802 out.print(dataBlock.toJsonString());
9809 * @param {Object} config A configuration object.
9811 Roo.data.ScriptTagProxy = function(config){
9812 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9813 Roo.apply(this, config);
9814 this.head = document.getElementsByTagName("head")[0];
9817 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9819 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9821 * @cfg {String} url The URL from which to request the data object.
9824 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9828 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9829 * the server the name of the callback function set up by the load call to process the returned data object.
9830 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9831 * javascript output which calls this named function passing the data object as its only parameter.
9833 callbackParam : "callback",
9835 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9836 * name to the request.
9841 * Load data from the configured URL, read the data object into
9842 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9843 * process that block using the passed callback.
9844 * @param {Object} params An object containing properties which are to be used as HTTP parameters
9845 * for the request to the remote server.
9846 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9847 * object into a block of Roo.data.Records.
9848 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9849 * The function must be passed <ul>
9850 * <li>The Record block object</li>
9851 * <li>The "arg" argument from the load function</li>
9852 * <li>A boolean success indicator</li>
9854 * @param {Object} scope The scope in which to call the callback
9855 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9857 load : function(params, reader, callback, scope, arg){
9858 if(this.fireEvent("beforeload", this, params) !== false){
9860 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9863 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9865 url += "&_dc=" + (new Date().getTime());
9867 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9870 cb : "stcCallback"+transId,
9871 scriptId : "stcScript"+transId,
9875 callback : callback,
9881 window[trans.cb] = function(o){
9882 conn.handleResponse(o, trans);
9885 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9887 if(this.autoAbort !== false){
9891 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9893 var script = document.createElement("script");
9894 script.setAttribute("src", url);
9895 script.setAttribute("type", "text/javascript");
9896 script.setAttribute("id", trans.scriptId);
9897 this.head.appendChild(script);
9901 callback.call(scope||this, null, arg, false);
9906 isLoading : function(){
9907 return this.trans ? true : false;
9911 * Abort the current server request.
9914 if(this.isLoading()){
9915 this.destroyTrans(this.trans);
9920 destroyTrans : function(trans, isLoaded){
9921 this.head.removeChild(document.getElementById(trans.scriptId));
9922 clearTimeout(trans.timeoutId);
9924 window[trans.cb] = undefined;
9926 delete window[trans.cb];
9929 // if hasn't been loaded, wait for load to remove it to prevent script error
9930 window[trans.cb] = function(){
9931 window[trans.cb] = undefined;
9933 delete window[trans.cb];
9940 handleResponse : function(o, trans){
9942 this.destroyTrans(trans, true);
9945 result = trans.reader.readRecords(o);
9947 this.fireEvent("loadexception", this, o, trans.arg, e);
9948 trans.callback.call(trans.scope||window, null, trans.arg, false);
9951 this.fireEvent("load", this, o, trans.arg);
9952 trans.callback.call(trans.scope||window, result, trans.arg, true);
9956 handleFailure : function(trans){
9958 this.destroyTrans(trans, false);
9959 this.fireEvent("loadexception", this, null, trans.arg);
9960 trans.callback.call(trans.scope||window, null, trans.arg, false);
9964 * Ext JS Library 1.1.1
9965 * Copyright(c) 2006-2007, Ext JS, LLC.
9967 * Originally Released Under LGPL - original licence link has changed is not relivant.
9970 * <script type="text/javascript">
9974 * @class Roo.data.JsonReader
9975 * @extends Roo.data.DataReader
9976 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9977 * based on mappings in a provided Roo.data.Record constructor.
9979 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9980 * in the reply previously.
9985 var RecordDef = Roo.data.Record.create([
9986 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
9987 {name: 'occupation'} // This field will use "occupation" as the mapping.
9989 var myReader = new Roo.data.JsonReader({
9990 totalProperty: "results", // The property which contains the total dataset size (optional)
9991 root: "rows", // The property which contains an Array of row objects
9992 id: "id" // The property within each row object that provides an ID for the record (optional)
9996 * This would consume a JSON file like this:
9998 { 'results': 2, 'rows': [
9999 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10000 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10003 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10004 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10005 * paged from the remote server.
10006 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10007 * @cfg {String} root name of the property which contains the Array of row objects.
10008 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10010 * Create a new JsonReader
10011 * @param {Object} meta Metadata configuration options
10012 * @param {Object} recordType Either an Array of field definition objects,
10013 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10015 Roo.data.JsonReader = function(meta, recordType){
10018 // set some defaults:
10019 Roo.applyIf(meta, {
10020 totalProperty: 'total',
10021 successProperty : 'success',
10026 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10028 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10031 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
10032 * Used by Store query builder to append _requestMeta to params.
10035 metaFromRemote : false,
10037 * This method is only used by a DataProxy which has retrieved data from a remote server.
10038 * @param {Object} response The XHR object which contains the JSON data in its responseText.
10039 * @return {Object} data A data block which is used by an Roo.data.Store object as
10040 * a cache of Roo.data.Records.
10042 read : function(response){
10043 var json = response.responseText;
10045 var o = /* eval:var:o */ eval("("+json+")");
10047 throw {message: "JsonReader.read: Json object not found"};
10053 this.metaFromRemote = true;
10054 this.meta = o.metaData;
10055 this.recordType = Roo.data.Record.create(o.metaData.fields);
10056 this.onMetaChange(this.meta, this.recordType, o);
10058 return this.readRecords(o);
10061 // private function a store will implement
10062 onMetaChange : function(meta, recordType, o){
10069 simpleAccess: function(obj, subsc) {
10076 getJsonAccessor: function(){
10078 return function(expr) {
10080 return(re.test(expr))
10081 ? new Function("obj", "return obj." + expr)
10086 return Roo.emptyFn;
10091 * Create a data block containing Roo.data.Records from an XML document.
10092 * @param {Object} o An object which contains an Array of row objects in the property specified
10093 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10094 * which contains the total size of the dataset.
10095 * @return {Object} data A data block which is used by an Roo.data.Store object as
10096 * a cache of Roo.data.Records.
10098 readRecords : function(o){
10100 * After any data loads, the raw JSON data is available for further custom processing.
10104 var s = this.meta, Record = this.recordType,
10105 f = Record.prototype.fields, fi = f.items, fl = f.length;
10107 // Generate extraction functions for the totalProperty, the root, the id, and for each field
10109 if(s.totalProperty) {
10110 this.getTotal = this.getJsonAccessor(s.totalProperty);
10112 if(s.successProperty) {
10113 this.getSuccess = this.getJsonAccessor(s.successProperty);
10115 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10117 var g = this.getJsonAccessor(s.id);
10118 this.getId = function(rec) {
10120 return (r === undefined || r === "") ? null : r;
10123 this.getId = function(){return null;};
10126 for(var jj = 0; jj < fl; jj++){
10128 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10129 this.ef[jj] = this.getJsonAccessor(map);
10133 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10134 if(s.totalProperty){
10135 var vt = parseInt(this.getTotal(o), 10);
10140 if(s.successProperty){
10141 var vs = this.getSuccess(o);
10142 if(vs === false || vs === 'false'){
10147 for(var i = 0; i < c; i++){
10150 var id = this.getId(n);
10151 for(var j = 0; j < fl; j++){
10153 var v = this.ef[j](n);
10155 Roo.log('missing convert for ' + f.name);
10159 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10161 var record = new Record(values, id);
10163 records[i] = record;
10169 totalRecords : totalRecords
10174 * Ext JS Library 1.1.1
10175 * Copyright(c) 2006-2007, Ext JS, LLC.
10177 * Originally Released Under LGPL - original licence link has changed is not relivant.
10180 * <script type="text/javascript">
10184 * @class Roo.data.ArrayReader
10185 * @extends Roo.data.DataReader
10186 * Data reader class to create an Array of Roo.data.Record objects from an Array.
10187 * Each element of that Array represents a row of data fields. The
10188 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10189 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10193 var RecordDef = Roo.data.Record.create([
10194 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
10195 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
10197 var myReader = new Roo.data.ArrayReader({
10198 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
10202 * This would consume an Array like this:
10204 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10206 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10208 * Create a new JsonReader
10209 * @param {Object} meta Metadata configuration options.
10210 * @param {Object} recordType Either an Array of field definition objects
10211 * as specified to {@link Roo.data.Record#create},
10212 * or an {@link Roo.data.Record} object
10213 * created using {@link Roo.data.Record#create}.
10215 Roo.data.ArrayReader = function(meta, recordType){
10216 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10219 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10221 * Create a data block containing Roo.data.Records from an XML document.
10222 * @param {Object} o An Array of row objects which represents the dataset.
10223 * @return {Object} data A data block which is used by an Roo.data.Store object as
10224 * a cache of Roo.data.Records.
10226 readRecords : function(o){
10227 var sid = this.meta ? this.meta.id : null;
10228 var recordType = this.recordType, fields = recordType.prototype.fields;
10231 for(var i = 0; i < root.length; i++){
10234 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10235 for(var j = 0, jlen = fields.length; j < jlen; j++){
10236 var f = fields.items[j];
10237 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10238 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10240 values[f.name] = v;
10242 var record = new recordType(values, id);
10244 records[records.length] = record;
10248 totalRecords : records.length
10257 * @class Roo.bootstrap.ComboBox
10258 * @extends Roo.bootstrap.TriggerField
10259 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10260 * @cfg {Boolean} append (true|false) default false
10261 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10262 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10263 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10264 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10265 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10267 * Create a new ComboBox.
10268 * @param {Object} config Configuration options
10270 Roo.bootstrap.ComboBox = function(config){
10271 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10275 * Fires when the dropdown list is expanded
10276 * @param {Roo.bootstrap.ComboBox} combo This combo box
10281 * Fires when the dropdown list is collapsed
10282 * @param {Roo.bootstrap.ComboBox} combo This combo box
10286 * @event beforeselect
10287 * Fires before a list item is selected. Return false to cancel the selection.
10288 * @param {Roo.bootstrap.ComboBox} combo This combo box
10289 * @param {Roo.data.Record} record The data record returned from the underlying store
10290 * @param {Number} index The index of the selected item in the dropdown list
10292 'beforeselect' : true,
10295 * Fires when a list item is selected
10296 * @param {Roo.bootstrap.ComboBox} combo This combo box
10297 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10298 * @param {Number} index The index of the selected item in the dropdown list
10302 * @event beforequery
10303 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10304 * The event object passed has these properties:
10305 * @param {Roo.bootstrap.ComboBox} combo This combo box
10306 * @param {String} query The query
10307 * @param {Boolean} forceAll true to force "all" query
10308 * @param {Boolean} cancel true to cancel the query
10309 * @param {Object} e The query event object
10311 'beforequery': true,
10314 * Fires when the 'add' icon is pressed (add a listener to enable add button)
10315 * @param {Roo.bootstrap.ComboBox} combo This combo box
10320 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10321 * @param {Roo.bootstrap.ComboBox} combo This combo box
10322 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10327 * Fires when the remove value from the combobox array
10328 * @param {Roo.bootstrap.ComboBox} combo This combo box
10335 this.tickItems = [];
10337 this.selectedIndex = -1;
10338 if(this.mode == 'local'){
10339 if(config.queryDelay === undefined){
10340 this.queryDelay = 10;
10342 if(config.minChars === undefined){
10348 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10351 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10352 * rendering into an Roo.Editor, defaults to false)
10355 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10356 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10359 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10362 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10363 * the dropdown list (defaults to undefined, with no header element)
10367 * @cfg {String/Roo.Template} tpl The template to use to render the output
10371 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10373 listWidth: undefined,
10375 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10376 * mode = 'remote' or 'text' if mode = 'local')
10378 displayField: undefined,
10380 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10381 * mode = 'remote' or 'value' if mode = 'local').
10382 * Note: use of a valueField requires the user make a selection
10383 * in order for a value to be mapped.
10385 valueField: undefined,
10389 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10390 * field's data value (defaults to the underlying DOM element's name)
10392 hiddenName: undefined,
10394 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10398 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10400 selectedClass: 'active',
10403 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10407 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10408 * anchor positions (defaults to 'tl-bl')
10410 listAlign: 'tl-bl?',
10412 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10416 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
10417 * query specified by the allQuery config option (defaults to 'query')
10419 triggerAction: 'query',
10421 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10422 * (defaults to 4, does not apply if editable = false)
10426 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10427 * delay (typeAheadDelay) if it matches a known value (defaults to false)
10431 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10432 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10436 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10437 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
10441 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
10442 * when editable = true (defaults to false)
10444 selectOnFocus:false,
10446 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10448 queryParam: 'query',
10450 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
10451 * when mode = 'remote' (defaults to 'Loading...')
10453 loadingText: 'Loading...',
10455 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10459 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10463 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10464 * traditional select (defaults to true)
10468 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10472 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10476 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10477 * listWidth has a higher value)
10481 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10482 * allow the user to set arbitrary text into the field (defaults to false)
10484 forceSelection:false,
10486 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10487 * if typeAhead = true (defaults to 250)
10489 typeAheadDelay : 250,
10491 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10492 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10494 valueNotFoundText : undefined,
10496 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10498 blockFocus : false,
10501 * @cfg {Boolean} disableClear Disable showing of clear button.
10503 disableClear : false,
10505 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
10507 alwaysQuery : false,
10510 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
10524 btnPosition : 'right',
10525 triggerList : true,
10526 showToggleBtn : true,
10527 // element that contains real text value.. (when hidden is used..)
10529 getAutoCreate : function()
10536 if(!this.tickable){
10537 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10542 * ComboBox with tickable selections
10545 var align = this.labelAlign || this.parentLabelAlign();
10548 cls : 'form-group roo-combobox-tickable' //input-group
10554 cls : 'tickable-buttons',
10559 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10566 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10573 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10580 Roo.each(buttons.cn, function(c){
10582 c.cls += ' btn-' + _this.size;
10585 if (_this.disabled) {
10596 cls: 'form-hidden-field'
10600 cls: 'select2-choices',
10604 cls: 'select2-search-field',
10616 cls: 'select2-container input-group select2-container-multi',
10621 // cls: 'typeahead typeahead-long dropdown-menu',
10622 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
10627 if (align ==='left' && this.fieldLabel.length) {
10629 Roo.log("left and has label");
10635 cls : 'control-label col-sm-' + this.labelWidth,
10636 html : this.fieldLabel
10640 cls : "col-sm-" + (12 - this.labelWidth),
10647 } else if ( this.fieldLabel.length) {
10653 //cls : 'input-group-addon',
10654 html : this.fieldLabel
10664 Roo.log(" no label && no align");
10671 ['xs','sm','md','lg'].map(function(size){
10672 if (settings[size]) {
10673 cfg.cls += ' col-' + size + '-' + settings[size];
10682 initEvents: function()
10686 throw "can not find store for combo";
10688 this.store = Roo.factory(this.store, Roo.data);
10691 this.initTickableEvents();
10695 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10697 if(this.hiddenName){
10699 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10701 this.hiddenField.dom.value =
10702 this.hiddenValue !== undefined ? this.hiddenValue :
10703 this.value !== undefined ? this.value : '';
10705 // prevent input submission
10706 this.el.dom.removeAttribute('name');
10707 this.hiddenField.dom.setAttribute('name', this.hiddenName);
10712 // this.el.dom.setAttribute('autocomplete', 'off');
10715 var cls = 'x-combo-list';
10717 //this.list = new Roo.Layer({
10718 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10724 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10725 _this.list.setWidth(lw);
10728 this.list.on('mouseover', this.onViewOver, this);
10729 this.list.on('mousemove', this.onViewMove, this);
10731 this.list.on('scroll', this.onViewScroll, this);
10734 this.list.swallowEvent('mousewheel');
10735 this.assetHeight = 0;
10738 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10739 this.assetHeight += this.header.getHeight();
10742 this.innerList = this.list.createChild({cls:cls+'-inner'});
10743 this.innerList.on('mouseover', this.onViewOver, this);
10744 this.innerList.on('mousemove', this.onViewMove, this);
10745 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10747 if(this.allowBlank && !this.pageSize && !this.disableClear){
10748 this.footer = this.list.createChild({cls:cls+'-ft'});
10749 this.pageTb = new Roo.Toolbar(this.footer);
10753 this.footer = this.list.createChild({cls:cls+'-ft'});
10754 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10755 {pageSize: this.pageSize});
10759 if (this.pageTb && this.allowBlank && !this.disableClear) {
10761 this.pageTb.add(new Roo.Toolbar.Fill(), {
10762 cls: 'x-btn-icon x-btn-clear',
10764 handler: function()
10767 _this.clearValue();
10768 _this.onSelect(false, -1);
10773 this.assetHeight += this.footer.getHeight();
10778 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10781 this.view = new Roo.View(this.list, this.tpl, {
10782 singleSelect:true, store: this.store, selectedClass: this.selectedClass
10784 //this.view.wrapEl.setDisplayed(false);
10785 this.view.on('click', this.onViewClick, this);
10789 this.store.on('beforeload', this.onBeforeLoad, this);
10790 this.store.on('load', this.onLoad, this);
10791 this.store.on('loadexception', this.onLoadException, this);
10793 if(this.resizable){
10794 this.resizer = new Roo.Resizable(this.list, {
10795 pinned:true, handles:'se'
10797 this.resizer.on('resize', function(r, w, h){
10798 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10799 this.listWidth = w;
10800 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10801 this.restrictHeight();
10803 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10806 if(!this.editable){
10807 this.editable = true;
10808 this.setEditable(false);
10813 if (typeof(this.events.add.listeners) != 'undefined') {
10815 this.addicon = this.wrap.createChild(
10816 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
10818 this.addicon.on('click', function(e) {
10819 this.fireEvent('add', this);
10822 if (typeof(this.events.edit.listeners) != 'undefined') {
10824 this.editicon = this.wrap.createChild(
10825 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
10826 if (this.addicon) {
10827 this.editicon.setStyle('margin-left', '40px');
10829 this.editicon.on('click', function(e) {
10831 // we fire even if inothing is selected..
10832 this.fireEvent('edit', this, this.lastData );
10838 this.keyNav = new Roo.KeyNav(this.inputEl(), {
10839 "up" : function(e){
10840 this.inKeyMode = true;
10844 "down" : function(e){
10845 if(!this.isExpanded()){
10846 this.onTriggerClick();
10848 this.inKeyMode = true;
10853 "enter" : function(e){
10854 // this.onViewClick();
10858 if(this.fireEvent("specialkey", this, e)){
10859 this.onViewClick(false);
10865 "esc" : function(e){
10869 "tab" : function(e){
10872 if(this.fireEvent("specialkey", this, e)){
10873 this.onViewClick(false);
10881 doRelay : function(foo, bar, hname){
10882 if(hname == 'down' || this.scope.isExpanded()){
10883 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10892 this.queryDelay = Math.max(this.queryDelay || 10,
10893 this.mode == 'local' ? 10 : 250);
10896 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10898 if(this.typeAhead){
10899 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10901 if(this.editable !== false){
10902 this.inputEl().on("keyup", this.onKeyUp, this);
10904 if(this.forceSelection){
10905 this.inputEl().on('blur', this.doForce, this);
10909 this.choices = this.el.select('ul.select2-choices', true).first();
10910 this.searchField = this.el.select('ul li.select2-search-field', true).first();
10914 initTickableEvents: function()
10918 if(this.hiddenName){
10920 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10922 this.hiddenField.dom.value =
10923 this.hiddenValue !== undefined ? this.hiddenValue :
10924 this.value !== undefined ? this.value : '';
10926 // prevent input submission
10927 this.el.dom.removeAttribute('name');
10928 this.hiddenField.dom.setAttribute('name', this.hiddenName);
10933 // this.list = this.el.select('ul.dropdown-menu',true).first();
10935 this.choices = this.el.select('ul.select2-choices', true).first();
10936 this.searchField = this.el.select('ul li.select2-search-field', true).first();
10937 if(this.triggerList){
10938 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10941 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10942 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10944 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10945 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10947 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10948 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10950 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10951 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10952 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10955 this.cancelBtn.hide();
10960 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10961 _this.list.setWidth(lw);
10964 this.list.on('mouseover', this.onViewOver, this);
10965 this.list.on('mousemove', this.onViewMove, this);
10967 this.list.on('scroll', this.onViewScroll, this);
10970 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>';
10973 this.view = new Roo.View(this.list, this.tpl, {
10974 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10977 //this.view.wrapEl.setDisplayed(false);
10978 this.view.on('click', this.onViewClick, this);
10982 this.store.on('beforeload', this.onBeforeLoad, this);
10983 this.store.on('load', this.onLoad, this);
10984 this.store.on('loadexception', this.onLoadException, this);
10986 // this.keyNav = new Roo.KeyNav(this.inputEl(), {
10987 // "up" : function(e){
10988 // this.inKeyMode = true;
10989 // this.selectPrev();
10992 // "down" : function(e){
10993 // if(!this.isExpanded()){
10994 // this.onTriggerClick();
10996 // this.inKeyMode = true;
10997 // this.selectNext();
11001 // "enter" : function(e){
11002 //// this.onViewClick();
11004 // this.collapse();
11006 // if(this.fireEvent("specialkey", this, e)){
11007 // this.onViewClick(false);
11013 // "esc" : function(e){
11014 // this.collapse();
11017 // "tab" : function(e){
11018 // this.collapse();
11020 // if(this.fireEvent("specialkey", this, e)){
11021 // this.onViewClick(false);
11029 // doRelay : function(foo, bar, hname){
11030 // if(hname == 'down' || this.scope.isExpanded()){
11031 // return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11036 // forceKeyDown: true
11040 this.queryDelay = Math.max(this.queryDelay || 10,
11041 this.mode == 'local' ? 10 : 250);
11044 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11046 if(this.typeAhead){
11047 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11051 onDestroy : function(){
11053 this.view.setStore(null);
11054 this.view.el.removeAllListeners();
11055 this.view.el.remove();
11056 this.view.purgeListeners();
11059 this.list.dom.innerHTML = '';
11063 this.store.un('beforeload', this.onBeforeLoad, this);
11064 this.store.un('load', this.onLoad, this);
11065 this.store.un('loadexception', this.onLoadException, this);
11067 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11071 fireKey : function(e){
11072 if(e.isNavKeyPress() && !this.list.isVisible()){
11073 this.fireEvent("specialkey", this, e);
11078 onResize: function(w, h){
11079 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11081 // if(typeof w != 'number'){
11082 // // we do not handle it!?!?
11085 // var tw = this.trigger.getWidth();
11086 // // tw += this.addicon ? this.addicon.getWidth() : 0;
11087 // // tw += this.editicon ? this.editicon.getWidth() : 0;
11089 // this.inputEl().setWidth( this.adjustWidth('input', x));
11091 // //this.trigger.setStyle('left', x+'px');
11093 // if(this.list && this.listWidth === undefined){
11094 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11095 // this.list.setWidth(lw);
11096 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11104 * Allow or prevent the user from directly editing the field text. If false is passed,
11105 * the user will only be able to select from the items defined in the dropdown list. This method
11106 * is the runtime equivalent of setting the 'editable' config option at config time.
11107 * @param {Boolean} value True to allow the user to directly edit the field text
11109 setEditable : function(value){
11110 if(value == this.editable){
11113 this.editable = value;
11115 this.inputEl().dom.setAttribute('readOnly', true);
11116 this.inputEl().on('mousedown', this.onTriggerClick, this);
11117 this.inputEl().addClass('x-combo-noedit');
11119 this.inputEl().dom.setAttribute('readOnly', false);
11120 this.inputEl().un('mousedown', this.onTriggerClick, this);
11121 this.inputEl().removeClass('x-combo-noedit');
11127 onBeforeLoad : function(combo,opts){
11128 if(!this.hasFocus){
11132 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11134 this.restrictHeight();
11135 this.selectedIndex = -1;
11139 onLoad : function(){
11141 this.hasQuery = false;
11143 if(!this.hasFocus){
11147 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11148 this.loading.hide();
11151 if(this.store.getCount() > 0){
11153 // this.restrictHeight();
11154 if(this.lastQuery == this.allQuery){
11155 if(this.editable && !this.tickable){
11156 this.inputEl().dom.select();
11160 !this.selectByValue(this.value, true) &&
11161 this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' ||
11162 this.store.lastOptions.add != true)
11164 this.select(0, true);
11167 if(this.autoFocus){
11170 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11171 this.taTask.delay(this.typeAheadDelay);
11175 this.onEmptyResults();
11181 onLoadException : function()
11183 this.hasQuery = false;
11185 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11186 this.loading.hide();
11190 Roo.log(this.store.reader.jsonData);
11191 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11193 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11199 onTypeAhead : function(){
11200 if(this.store.getCount() > 0){
11201 var r = this.store.getAt(0);
11202 var newValue = r.data[this.displayField];
11203 var len = newValue.length;
11204 var selStart = this.getRawValue().length;
11206 if(selStart != len){
11207 this.setRawValue(newValue);
11208 this.selectText(selStart, newValue.length);
11214 onSelect : function(record, index){
11216 if(this.fireEvent('beforeselect', this, record, index) !== false){
11218 this.setFromData(index > -1 ? record.data : false);
11221 this.fireEvent('select', this, record, index);
11226 * Returns the currently selected field value or empty string if no value is set.
11227 * @return {String} value The selected value
11229 getValue : function(){
11232 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11235 if(this.valueField){
11236 return typeof this.value != 'undefined' ? this.value : '';
11238 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11243 * Clears any text/value currently set in the field
11245 clearValue : function(){
11246 if(this.hiddenField){
11247 this.hiddenField.dom.value = '';
11250 this.setRawValue('');
11251 this.lastSelectionText = '';
11256 * Sets the specified value into the field. If the value finds a match, the corresponding record text
11257 * will be displayed in the field. If the value does not match the data value of an existing item,
11258 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11259 * Otherwise the field will be blank (although the value will still be set).
11260 * @param {String} value The value to match
11262 setValue : function(v){
11269 if(this.valueField){
11270 var r = this.findRecord(this.valueField, v);
11272 text = r.data[this.displayField];
11273 }else if(this.valueNotFoundText !== undefined){
11274 text = this.valueNotFoundText;
11277 this.lastSelectionText = text;
11278 if(this.hiddenField){
11279 this.hiddenField.dom.value = v;
11281 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11285 * @property {Object} the last set data for the element
11290 * Sets the value of the field based on a object which is related to the record format for the store.
11291 * @param {Object} value the value to set as. or false on reset?
11293 setFromData : function(o){
11296 if(typeof o.display_name !== 'string'){
11297 for(var i=0;i<o.display_name.length;i++){
11298 this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
11306 var dv = ''; // display value
11307 var vv = ''; // value value..
11309 if (this.displayField) {
11310 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11312 // this is an error condition!!!
11313 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
11316 if(this.valueField){
11317 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11320 if(this.hiddenField){
11321 this.hiddenField.dom.value = vv;
11323 this.lastSelectionText = dv;
11324 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11328 // no hidden field.. - we store the value in 'value', but still display
11329 // display field!!!!
11330 this.lastSelectionText = dv;
11331 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11337 reset : function(){
11338 // overridden so that last data is reset..
11339 this.setValue(this.originalValue);
11340 this.clearInvalid();
11341 this.lastData = false;
11343 this.view.clearSelections();
11347 findRecord : function(prop, value){
11349 if(this.store.getCount() > 0){
11350 this.store.each(function(r){
11351 if(r.data[prop] == value){
11361 getName: function()
11363 // returns hidden if it's set..
11364 if (!this.rendered) {return ''};
11365 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
11369 onViewMove : function(e, t){
11370 this.inKeyMode = false;
11374 onViewOver : function(e, t){
11375 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11378 var item = this.view.findItemFromChild(t);
11381 var index = this.view.indexOf(item);
11382 this.select(index, false);
11387 onViewClick : function(view, doFocus, el, e)
11389 var index = this.view.getSelectedIndexes()[0];
11391 var r = this.store.getAt(index);
11395 if(e.getTarget().nodeName.toLowerCase() != 'input'){
11402 Roo.each(this.tickItems, function(v,k){
11404 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11405 _this.tickItems.splice(k, 1);
11415 this.tickItems.push(r.data);
11420 this.onSelect(r, index);
11422 if(doFocus !== false && !this.blockFocus){
11423 this.inputEl().focus();
11428 restrictHeight : function(){
11429 //this.innerList.dom.style.height = '';
11430 //var inner = this.innerList.dom;
11431 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11432 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11433 //this.list.beginUpdate();
11434 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11435 this.list.alignTo(this.inputEl(), this.listAlign);
11436 this.list.alignTo(this.inputEl(), this.listAlign);
11437 //this.list.endUpdate();
11441 onEmptyResults : function(){
11446 * Returns true if the dropdown list is expanded, else false.
11448 isExpanded : function(){
11449 return this.list.isVisible();
11453 * Select an item in the dropdown list by its data value. 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 {String} value The data value of the 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)
11458 * @return {Boolean} True if the value matched an item in the list, else false
11460 selectByValue : function(v, scrollIntoView){
11461 if(v !== undefined && v !== null){
11462 var r = this.findRecord(this.valueField || this.displayField, v);
11464 this.select(this.store.indexOf(r), scrollIntoView);
11472 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11473 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11474 * @param {Number} index The zero-based index of the list item to select
11475 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11476 * selected item if it is not currently in view (defaults to true)
11478 select : function(index, scrollIntoView){
11479 this.selectedIndex = index;
11480 this.view.select(index);
11481 if(scrollIntoView !== false){
11482 var el = this.view.getNode(index);
11483 if(el && !this.multiple && !this.tickable){
11484 this.list.scrollChildIntoView(el, false);
11490 selectNext : function(){
11491 var ct = this.store.getCount();
11493 if(this.selectedIndex == -1){
11495 }else if(this.selectedIndex < ct-1){
11496 this.select(this.selectedIndex+1);
11502 selectPrev : function(){
11503 var ct = this.store.getCount();
11505 if(this.selectedIndex == -1){
11507 }else if(this.selectedIndex != 0){
11508 this.select(this.selectedIndex-1);
11514 onKeyUp : function(e){
11515 if(this.editable !== false && !e.isSpecialKey()){
11516 this.lastKey = e.getKey();
11517 this.dqTask.delay(this.queryDelay);
11522 validateBlur : function(){
11523 return !this.list || !this.list.isVisible();
11527 initQuery : function(){
11528 this.doQuery(this.getRawValue());
11532 doForce : function(){
11533 if(this.inputEl().dom.value.length > 0){
11534 this.inputEl().dom.value =
11535 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11541 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
11542 * query allowing the query action to be canceled if needed.
11543 * @param {String} query The SQL query to execute
11544 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11545 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
11546 * saved in the current store (defaults to false)
11548 doQuery : function(q, forceAll){
11550 if(q === undefined || q === null){
11555 forceAll: forceAll,
11559 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11564 forceAll = qe.forceAll;
11565 if(forceAll === true || (q.length >= this.minChars)){
11567 this.hasQuery = true;
11569 if(this.lastQuery != q || this.alwaysQuery){
11570 this.lastQuery = q;
11571 if(this.mode == 'local'){
11572 this.selectedIndex = -1;
11574 this.store.clearFilter();
11576 this.store.filter(this.displayField, q);
11580 this.store.baseParams[this.queryParam] = q;
11582 var options = {params : this.getParams(q)};
11585 options.add = true;
11586 options.params.start = this.page * this.pageSize;
11589 this.store.load(options);
11591 * this code will make the page width larger, at the beginning, the list not align correctly,
11592 * we should expand the list on onLoad
11593 * so command out it
11598 this.selectedIndex = -1;
11603 this.loadNext = false;
11607 getParams : function(q){
11609 //p[this.queryParam] = q;
11613 p.limit = this.pageSize;
11619 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11621 collapse : function(){
11622 if(!this.isExpanded()){
11630 this.cancelBtn.hide();
11631 this.trigger.show();
11634 Roo.get(document).un('mousedown', this.collapseIf, this);
11635 Roo.get(document).un('mousewheel', this.collapseIf, this);
11636 if (!this.editable) {
11637 Roo.get(document).un('keydown', this.listKeyPress, this);
11639 this.fireEvent('collapse', this);
11643 collapseIf : function(e){
11644 var in_combo = e.within(this.el);
11645 var in_list = e.within(this.list);
11646 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11648 if (in_combo || in_list || is_list) {
11649 //e.stopPropagation();
11654 this.onTickableFooterButtonClick(e, false, false);
11662 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11664 expand : function(){
11666 if(this.isExpanded() || !this.hasFocus){
11670 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11671 this.list.setWidth(lw);
11678 this.restrictHeight();
11682 this.tickItems = Roo.apply([], this.item);
11685 this.cancelBtn.show();
11686 this.trigger.hide();
11690 Roo.get(document).on('mousedown', this.collapseIf, this);
11691 Roo.get(document).on('mousewheel', this.collapseIf, this);
11692 if (!this.editable) {
11693 Roo.get(document).on('keydown', this.listKeyPress, this);
11696 this.fireEvent('expand', this);
11700 // Implements the default empty TriggerField.onTriggerClick function
11701 onTriggerClick : function(e)
11703 Roo.log('trigger click');
11705 if(this.disabled || !this.triggerList){
11710 this.loadNext = false;
11712 if(this.isExpanded()){
11714 if (!this.blockFocus) {
11715 this.inputEl().focus();
11719 this.hasFocus = true;
11720 if(this.triggerAction == 'all') {
11721 this.doQuery(this.allQuery, true);
11723 this.doQuery(this.getRawValue());
11725 if (!this.blockFocus) {
11726 this.inputEl().focus();
11731 onTickableTriggerClick : function(e)
11738 this.loadNext = false;
11739 this.hasFocus = true;
11741 if(this.triggerAction == 'all') {
11742 this.doQuery(this.allQuery, true);
11744 this.doQuery(this.getRawValue());
11748 onSearchFieldClick : function(e)
11750 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11755 this.loadNext = false;
11756 this.hasFocus = true;
11758 if(this.triggerAction == 'all') {
11759 this.doQuery(this.allQuery, true);
11761 this.doQuery(this.getRawValue());
11765 listKeyPress : function(e)
11767 //Roo.log('listkeypress');
11768 // scroll to first matching element based on key pres..
11769 if (e.isSpecialKey()) {
11772 var k = String.fromCharCode(e.getKey()).toUpperCase();
11775 var csel = this.view.getSelectedNodes();
11776 var cselitem = false;
11778 var ix = this.view.indexOf(csel[0]);
11779 cselitem = this.store.getAt(ix);
11780 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11786 this.store.each(function(v) {
11788 // start at existing selection.
11789 if (cselitem.id == v.id) {
11795 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11796 match = this.store.indexOf(v);
11802 if (match === false) {
11803 return true; // no more action?
11806 this.view.select(match);
11807 var sn = Roo.get(this.view.getSelectedNodes()[0])
11808 //sn.scrollIntoView(sn.dom.parentNode, false);
11811 onViewScroll : function(e, t){
11813 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){
11817 this.hasQuery = true;
11819 this.loading = this.list.select('.loading', true).first();
11821 if(this.loading === null){
11822 this.list.createChild({
11824 cls: 'loading select2-more-results select2-active',
11825 html: 'Loading more results...'
11828 this.loading = this.list.select('.loading', true).first();
11830 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11832 this.loading.hide();
11835 this.loading.show();
11840 this.loadNext = true;
11842 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11847 addItem : function(o)
11849 var dv = ''; // display value
11851 if (this.displayField) {
11852 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11854 // this is an error condition!!!
11855 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
11862 var choice = this.choices.createChild({
11864 cls: 'select2-search-choice',
11873 cls: 'select2-search-choice-close',
11878 }, this.searchField);
11880 var close = choice.select('a.select2-search-choice-close', true).first()
11882 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11890 this.inputEl().dom.value = '';
11894 onRemoveItem : function(e, _self, o)
11896 e.preventDefault();
11897 var index = this.item.indexOf(o.data) * 1;
11900 Roo.log('not this item?!');
11904 this.item.splice(index, 1);
11909 this.fireEvent('remove', this, e);
11913 syncValue : function()
11915 if(!this.item.length){
11922 Roo.each(this.item, function(i){
11923 if(_this.valueField){
11924 value.push(i[_this.valueField]);
11931 this.value = value.join(',');
11933 if(this.hiddenField){
11934 this.hiddenField.dom.value = this.value;
11938 clearItem : function()
11940 if(!this.multiple){
11946 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11953 inputEl: function ()
11956 return this.searchField;
11958 return this.el.select('input.form-control',true).first();
11962 onTickableFooterButtonClick : function(e, btn, el)
11964 e.preventDefault();
11966 if(btn && btn.name == 'cancel'){
11967 this.tickItems = Roo.apply([], this.item);
11976 Roo.each(this.tickItems, function(o){
11987 * @cfg {Boolean} grow
11991 * @cfg {Number} growMin
11995 * @cfg {Number} growMax
12005 * Ext JS Library 1.1.1
12006 * Copyright(c) 2006-2007, Ext JS, LLC.
12008 * Originally Released Under LGPL - original licence link has changed is not relivant.
12011 * <script type="text/javascript">
12016 * @extends Roo.util.Observable
12017 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
12018 * This class also supports single and multi selection modes. <br>
12019 * Create a data model bound view:
12021 var store = new Roo.data.Store(...);
12023 var view = new Roo.View({
12025 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
12027 singleSelect: true,
12028 selectedClass: "ydataview-selected",
12032 // listen for node click?
12033 view.on("click", function(vw, index, node, e){
12034 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12038 dataModel.load("foobar.xml");
12040 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12042 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12043 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12045 * Note: old style constructor is still suported (container, template, config)
12048 * Create a new View
12049 * @param {Object} config The config object
12052 Roo.View = function(config, depreciated_tpl, depreciated_config){
12054 this.parent = false;
12056 if (typeof(depreciated_tpl) == 'undefined') {
12057 // new way.. - universal constructor.
12058 Roo.apply(this, config);
12059 this.el = Roo.get(this.el);
12062 this.el = Roo.get(config);
12063 this.tpl = depreciated_tpl;
12064 Roo.apply(this, depreciated_config);
12066 this.wrapEl = this.el.wrap().wrap();
12067 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12070 if(typeof(this.tpl) == "string"){
12071 this.tpl = new Roo.Template(this.tpl);
12073 // support xtype ctors..
12074 this.tpl = new Roo.factory(this.tpl, Roo);
12078 this.tpl.compile();
12083 * @event beforeclick
12084 * Fires before a click is processed. Returns false to cancel the default action.
12085 * @param {Roo.View} this
12086 * @param {Number} index The index of the target node
12087 * @param {HTMLElement} node The target node
12088 * @param {Roo.EventObject} e The raw event object
12090 "beforeclick" : true,
12093 * Fires when a template node is clicked.
12094 * @param {Roo.View} this
12095 * @param {Number} index The index of the target node
12096 * @param {HTMLElement} node The target node
12097 * @param {Roo.EventObject} e The raw event object
12102 * Fires when a template node is double clicked.
12103 * @param {Roo.View} this
12104 * @param {Number} index The index of the target node
12105 * @param {HTMLElement} node The target node
12106 * @param {Roo.EventObject} e The raw event object
12110 * @event contextmenu
12111 * Fires when a template node is right clicked.
12112 * @param {Roo.View} this
12113 * @param {Number} index The index of the target node
12114 * @param {HTMLElement} node The target node
12115 * @param {Roo.EventObject} e The raw event object
12117 "contextmenu" : true,
12119 * @event selectionchange
12120 * Fires when the selected nodes change.
12121 * @param {Roo.View} this
12122 * @param {Array} selections Array of the selected nodes
12124 "selectionchange" : true,
12127 * @event beforeselect
12128 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12129 * @param {Roo.View} this
12130 * @param {HTMLElement} node The node to be selected
12131 * @param {Array} selections Array of currently selected nodes
12133 "beforeselect" : true,
12135 * @event preparedata
12136 * Fires on every row to render, to allow you to change the data.
12137 * @param {Roo.View} this
12138 * @param {Object} data to be rendered (change this)
12140 "preparedata" : true
12148 "click": this.onClick,
12149 "dblclick": this.onDblClick,
12150 "contextmenu": this.onContextMenu,
12154 this.selections = [];
12156 this.cmp = new Roo.CompositeElementLite([]);
12158 this.store = Roo.factory(this.store, Roo.data);
12159 this.setStore(this.store, true);
12162 if ( this.footer && this.footer.xtype) {
12164 var fctr = this.wrapEl.appendChild(document.createElement("div"));
12166 this.footer.dataSource = this.store
12167 this.footer.container = fctr;
12168 this.footer = Roo.factory(this.footer, Roo);
12169 fctr.insertFirst(this.el);
12171 // this is a bit insane - as the paging toolbar seems to detach the el..
12172 // dom.parentNode.parentNode.parentNode
12173 // they get detached?
12177 Roo.View.superclass.constructor.call(this);
12182 Roo.extend(Roo.View, Roo.util.Observable, {
12185 * @cfg {Roo.data.Store} store Data store to load data from.
12190 * @cfg {String|Roo.Element} el The container element.
12195 * @cfg {String|Roo.Template} tpl The template used by this View
12199 * @cfg {String} dataName the named area of the template to use as the data area
12200 * Works with domtemplates roo-name="name"
12204 * @cfg {String} selectedClass The css class to add to selected nodes
12206 selectedClass : "x-view-selected",
12208 * @cfg {String} emptyText The empty text to show when nothing is loaded.
12213 * @cfg {String} text to display on mask (default Loading)
12217 * @cfg {Boolean} multiSelect Allow multiple selection
12219 multiSelect : false,
12221 * @cfg {Boolean} singleSelect Allow single selection
12223 singleSelect: false,
12226 * @cfg {Boolean} toggleSelect - selecting
12228 toggleSelect : false,
12231 * @cfg {Boolean} tickable - selecting
12236 * Returns the element this view is bound to.
12237 * @return {Roo.Element}
12239 getEl : function(){
12240 return this.wrapEl;
12246 * Refreshes the view. - called by datachanged on the store. - do not call directly.
12248 refresh : function(){
12249 Roo.log('refresh');
12252 // if we are using something like 'domtemplate', then
12253 // the what gets used is:
12254 // t.applySubtemplate(NAME, data, wrapping data..)
12255 // the outer template then get' applied with
12256 // the store 'extra data'
12257 // and the body get's added to the
12258 // roo-name="data" node?
12259 // <span class='roo-tpl-{name}'></span> ?????
12263 this.clearSelections();
12264 this.el.update("");
12266 var records = this.store.getRange();
12267 if(records.length < 1) {
12269 // is this valid?? = should it render a template??
12271 this.el.update(this.emptyText);
12275 if (this.dataName) {
12276 this.el.update(t.apply(this.store.meta)); //????
12277 el = this.el.child('.roo-tpl-' + this.dataName);
12280 for(var i = 0, len = records.length; i < len; i++){
12281 var data = this.prepareData(records[i].data, i, records[i]);
12282 this.fireEvent("preparedata", this, data, i, records[i]);
12284 var d = Roo.apply({}, data);
12287 Roo.apply(d, {'roo-id' : Roo.id()});
12291 Roo.each(this.parent.item, function(item){
12292 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12295 Roo.apply(d, {'roo-data-checked' : 'checked'});
12299 html[html.length] = Roo.util.Format.trim(
12301 t.applySubtemplate(this.dataName, d, this.store.meta) :
12308 el.update(html.join(""));
12309 this.nodes = el.dom.childNodes;
12310 this.updateIndexes(0);
12315 * Function to override to reformat the data that is sent to
12316 * the template for each node.
12317 * DEPRICATED - use the preparedata event handler.
12318 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12319 * a JSON object for an UpdateManager bound view).
12321 prepareData : function(data, index, record)
12323 this.fireEvent("preparedata", this, data, index, record);
12327 onUpdate : function(ds, record){
12328 Roo.log('on update');
12329 this.clearSelections();
12330 var index = this.store.indexOf(record);
12331 var n = this.nodes[index];
12332 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12333 n.parentNode.removeChild(n);
12334 this.updateIndexes(index, index);
12340 onAdd : function(ds, records, index)
12342 Roo.log(['on Add', ds, records, index] );
12343 this.clearSelections();
12344 if(this.nodes.length == 0){
12348 var n = this.nodes[index];
12349 for(var i = 0, len = records.length; i < len; i++){
12350 var d = this.prepareData(records[i].data, i, records[i]);
12352 this.tpl.insertBefore(n, d);
12355 this.tpl.append(this.el, d);
12358 this.updateIndexes(index);
12361 onRemove : function(ds, record, index){
12362 Roo.log('onRemove');
12363 this.clearSelections();
12364 var el = this.dataName ?
12365 this.el.child('.roo-tpl-' + this.dataName) :
12368 el.dom.removeChild(this.nodes[index]);
12369 this.updateIndexes(index);
12373 * Refresh an individual node.
12374 * @param {Number} index
12376 refreshNode : function(index){
12377 this.onUpdate(this.store, this.store.getAt(index));
12380 updateIndexes : function(startIndex, endIndex){
12381 var ns = this.nodes;
12382 startIndex = startIndex || 0;
12383 endIndex = endIndex || ns.length - 1;
12384 for(var i = startIndex; i <= endIndex; i++){
12385 ns[i].nodeIndex = i;
12390 * Changes the data store this view uses and refresh the view.
12391 * @param {Store} store
12393 setStore : function(store, initial){
12394 if(!initial && this.store){
12395 this.store.un("datachanged", this.refresh);
12396 this.store.un("add", this.onAdd);
12397 this.store.un("remove", this.onRemove);
12398 this.store.un("update", this.onUpdate);
12399 this.store.un("clear", this.refresh);
12400 this.store.un("beforeload", this.onBeforeLoad);
12401 this.store.un("load", this.onLoad);
12402 this.store.un("loadexception", this.onLoad);
12406 store.on("datachanged", this.refresh, this);
12407 store.on("add", this.onAdd, this);
12408 store.on("remove", this.onRemove, this);
12409 store.on("update", this.onUpdate, this);
12410 store.on("clear", this.refresh, this);
12411 store.on("beforeload", this.onBeforeLoad, this);
12412 store.on("load", this.onLoad, this);
12413 store.on("loadexception", this.onLoad, this);
12421 * onbeforeLoad - masks the loading area.
12424 onBeforeLoad : function(store,opts)
12426 Roo.log('onBeforeLoad');
12428 this.el.update("");
12430 this.el.mask(this.mask ? this.mask : "Loading" );
12432 onLoad : function ()
12439 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12440 * @param {HTMLElement} node
12441 * @return {HTMLElement} The template node
12443 findItemFromChild : function(node){
12444 var el = this.dataName ?
12445 this.el.child('.roo-tpl-' + this.dataName,true) :
12448 if(!node || node.parentNode == el){
12451 var p = node.parentNode;
12452 while(p && p != el){
12453 if(p.parentNode == el){
12462 onClick : function(e){
12463 var item = this.findItemFromChild(e.getTarget());
12465 var index = this.indexOf(item);
12466 if(this.onItemClick(item, index, e) !== false){
12467 this.fireEvent("click", this, index, item, e);
12470 this.clearSelections();
12475 onContextMenu : function(e){
12476 var item = this.findItemFromChild(e.getTarget());
12478 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12483 onDblClick : function(e){
12484 var item = this.findItemFromChild(e.getTarget());
12486 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12490 onItemClick : function(item, index, e)
12492 if(this.fireEvent("beforeclick", this, index, item, e) === false){
12495 if (this.toggleSelect) {
12496 var m = this.isSelected(item) ? 'unselect' : 'select';
12499 _t[m](item, true, false);
12502 if(this.multiSelect || this.singleSelect){
12503 if(this.multiSelect && e.shiftKey && this.lastSelection){
12504 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12506 this.select(item, this.multiSelect && e.ctrlKey);
12507 this.lastSelection = item;
12510 if(!this.tickable){
12511 e.preventDefault();
12519 * Get the number of selected nodes.
12522 getSelectionCount : function(){
12523 return this.selections.length;
12527 * Get the currently selected nodes.
12528 * @return {Array} An array of HTMLElements
12530 getSelectedNodes : function(){
12531 return this.selections;
12535 * Get the indexes of the selected nodes.
12538 getSelectedIndexes : function(){
12539 var indexes = [], s = this.selections;
12540 for(var i = 0, len = s.length; i < len; i++){
12541 indexes.push(s[i].nodeIndex);
12547 * Clear all selections
12548 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12550 clearSelections : function(suppressEvent){
12551 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12552 this.cmp.elements = this.selections;
12553 this.cmp.removeClass(this.selectedClass);
12554 this.selections = [];
12555 if(!suppressEvent){
12556 this.fireEvent("selectionchange", this, this.selections);
12562 * Returns true if the passed node is selected
12563 * @param {HTMLElement/Number} node The node or node index
12564 * @return {Boolean}
12566 isSelected : function(node){
12567 var s = this.selections;
12571 node = this.getNode(node);
12572 return s.indexOf(node) !== -1;
12577 * @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
12578 * @param {Boolean} keepExisting (optional) true to keep existing selections
12579 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12581 select : function(nodeInfo, keepExisting, suppressEvent){
12582 if(nodeInfo instanceof Array){
12584 this.clearSelections(true);
12586 for(var i = 0, len = nodeInfo.length; i < len; i++){
12587 this.select(nodeInfo[i], true, true);
12591 var node = this.getNode(nodeInfo);
12592 if(!node || this.isSelected(node)){
12593 return; // already selected.
12596 this.clearSelections(true);
12598 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12599 Roo.fly(node).addClass(this.selectedClass);
12600 this.selections.push(node);
12601 if(!suppressEvent){
12602 this.fireEvent("selectionchange", this, this.selections);
12610 * @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
12611 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12612 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12614 unselect : function(nodeInfo, keepExisting, suppressEvent)
12616 if(nodeInfo instanceof Array){
12617 Roo.each(this.selections, function(s) {
12618 this.unselect(s, nodeInfo);
12622 var node = this.getNode(nodeInfo);
12623 if(!node || !this.isSelected(node)){
12624 Roo.log("not selected");
12625 return; // not selected.
12629 Roo.each(this.selections, function(s) {
12631 Roo.fly(node).removeClass(this.selectedClass);
12638 this.selections= ns;
12639 this.fireEvent("selectionchange", this, this.selections);
12643 * Gets a template node.
12644 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12645 * @return {HTMLElement} The node or null if it wasn't found
12647 getNode : function(nodeInfo){
12648 if(typeof nodeInfo == "string"){
12649 return document.getElementById(nodeInfo);
12650 }else if(typeof nodeInfo == "number"){
12651 return this.nodes[nodeInfo];
12657 * Gets a range template nodes.
12658 * @param {Number} startIndex
12659 * @param {Number} endIndex
12660 * @return {Array} An array of nodes
12662 getNodes : function(start, end){
12663 var ns = this.nodes;
12664 start = start || 0;
12665 end = typeof end == "undefined" ? ns.length - 1 : end;
12668 for(var i = start; i <= end; i++){
12672 for(var i = start; i >= end; i--){
12680 * Finds the index of the passed node
12681 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12682 * @return {Number} The index of the node or -1
12684 indexOf : function(node){
12685 node = this.getNode(node);
12686 if(typeof node.nodeIndex == "number"){
12687 return node.nodeIndex;
12689 var ns = this.nodes;
12690 for(var i = 0, len = ns.length; i < len; i++){
12701 * based on jquery fullcalendar
12705 Roo.bootstrap = Roo.bootstrap || {};
12707 * @class Roo.bootstrap.Calendar
12708 * @extends Roo.bootstrap.Component
12709 * Bootstrap Calendar class
12710 * @cfg {Boolean} loadMask (true|false) default false
12711 * @cfg {Object} header generate the user specific header of the calendar, default false
12714 * Create a new Container
12715 * @param {Object} config The config object
12720 Roo.bootstrap.Calendar = function(config){
12721 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12725 * Fires when a date is selected
12726 * @param {DatePicker} this
12727 * @param {Date} date The selected date
12731 * @event monthchange
12732 * Fires when the displayed month changes
12733 * @param {DatePicker} this
12734 * @param {Date} date The selected month
12736 'monthchange': true,
12738 * @event evententer
12739 * Fires when mouse over an event
12740 * @param {Calendar} this
12741 * @param {event} Event
12743 'evententer': true,
12745 * @event eventleave
12746 * Fires when the mouse leaves an
12747 * @param {Calendar} this
12750 'eventleave': true,
12752 * @event eventclick
12753 * Fires when the mouse click an
12754 * @param {Calendar} this
12763 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
12766 * @cfg {Number} startDay
12767 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12775 getAutoCreate : function(){
12778 var fc_button = function(name, corner, style, content ) {
12779 return Roo.apply({},{
12781 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
12783 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12786 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12797 style : 'width:100%',
12804 cls : 'fc-header-left',
12806 fc_button('prev', 'left', 'arrow', '‹' ),
12807 fc_button('next', 'right', 'arrow', '›' ),
12808 { tag: 'span', cls: 'fc-header-space' },
12809 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
12817 cls : 'fc-header-center',
12821 cls: 'fc-header-title',
12824 html : 'month / year'
12832 cls : 'fc-header-right',
12834 /* fc_button('month', 'left', '', 'month' ),
12835 fc_button('week', '', '', 'week' ),
12836 fc_button('day', 'right', '', 'day' )
12848 header = this.header;
12851 var cal_heads = function() {
12853 // fixme - handle this.
12855 for (var i =0; i < Date.dayNames.length; i++) {
12856 var d = Date.dayNames[i];
12859 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12860 html : d.substring(0,3)
12864 ret[0].cls += ' fc-first';
12865 ret[6].cls += ' fc-last';
12868 var cal_cell = function(n) {
12871 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12876 cls: 'fc-day-number',
12880 cls: 'fc-day-content',
12884 style: 'position: relative;' // height: 17px;
12896 var cal_rows = function() {
12899 for (var r = 0; r < 6; r++) {
12906 for (var i =0; i < Date.dayNames.length; i++) {
12907 var d = Date.dayNames[i];
12908 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12911 row.cn[0].cls+=' fc-first';
12912 row.cn[0].cn[0].style = 'min-height:90px';
12913 row.cn[6].cls+=' fc-last';
12917 ret[0].cls += ' fc-first';
12918 ret[4].cls += ' fc-prev-last';
12919 ret[5].cls += ' fc-last';
12926 cls: 'fc-border-separate',
12927 style : 'width:100%',
12935 cls : 'fc-first fc-last',
12953 cls : 'fc-content',
12954 style : "position: relative;",
12957 cls : 'fc-view fc-view-month fc-grid',
12958 style : 'position: relative',
12959 unselectable : 'on',
12962 cls : 'fc-event-container',
12963 style : 'position:absolute;z-index:8;top:0;left:0;'
12981 initEvents : function()
12984 throw "can not find store for calendar";
12990 style: "text-align:center",
12994 style: "background-color:white;width:50%;margin:250 auto",
12998 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
13009 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13011 var size = this.el.select('.fc-content', true).first().getSize();
13012 this.maskEl.setSize(size.width, size.height);
13013 this.maskEl.enableDisplayMode("block");
13014 if(!this.loadMask){
13015 this.maskEl.hide();
13018 this.store = Roo.factory(this.store, Roo.data);
13019 this.store.on('load', this.onLoad, this);
13020 this.store.on('beforeload', this.onBeforeLoad, this);
13024 this.cells = this.el.select('.fc-day',true);
13025 //Roo.log(this.cells);
13026 this.textNodes = this.el.query('.fc-day-number');
13027 this.cells.addClassOnOver('fc-state-hover');
13029 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13030 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13031 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13032 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13034 this.on('monthchange', this.onMonthChange, this);
13036 this.update(new Date().clearTime());
13039 resize : function() {
13040 var sz = this.el.getSize();
13042 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13043 this.el.select('.fc-day-content div',true).setHeight(34);
13048 showPrevMonth : function(e){
13049 this.update(this.activeDate.add("mo", -1));
13051 showToday : function(e){
13052 this.update(new Date().clearTime());
13055 showNextMonth : function(e){
13056 this.update(this.activeDate.add("mo", 1));
13060 showPrevYear : function(){
13061 this.update(this.activeDate.add("y", -1));
13065 showNextYear : function(){
13066 this.update(this.activeDate.add("y", 1));
13071 update : function(date)
13073 var vd = this.activeDate;
13074 this.activeDate = date;
13075 // if(vd && this.el){
13076 // var t = date.getTime();
13077 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13078 // Roo.log('using add remove');
13080 // this.fireEvent('monthchange', this, date);
13082 // this.cells.removeClass("fc-state-highlight");
13083 // this.cells.each(function(c){
13084 // if(c.dateValue == t){
13085 // c.addClass("fc-state-highlight");
13086 // setTimeout(function(){
13087 // try{c.dom.firstChild.focus();}catch(e){}
13097 var days = date.getDaysInMonth();
13099 var firstOfMonth = date.getFirstDateOfMonth();
13100 var startingPos = firstOfMonth.getDay()-this.startDay;
13102 if(startingPos < this.startDay){
13106 var pm = date.add(Date.MONTH, -1);
13107 var prevStart = pm.getDaysInMonth()-startingPos;
13109 this.cells = this.el.select('.fc-day',true);
13110 this.textNodes = this.el.query('.fc-day-number');
13111 this.cells.addClassOnOver('fc-state-hover');
13113 var cells = this.cells.elements;
13114 var textEls = this.textNodes;
13116 Roo.each(cells, function(cell){
13117 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13120 days += startingPos;
13122 // convert everything to numbers so it's fast
13123 var day = 86400000;
13124 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13127 //Roo.log(prevStart);
13129 var today = new Date().clearTime().getTime();
13130 var sel = date.clearTime().getTime();
13131 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13132 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13133 var ddMatch = this.disabledDatesRE;
13134 var ddText = this.disabledDatesText;
13135 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13136 var ddaysText = this.disabledDaysText;
13137 var format = this.format;
13139 var setCellClass = function(cal, cell){
13143 //Roo.log('set Cell Class');
13145 var t = d.getTime();
13149 cell.dateValue = t;
13151 cell.className += " fc-today";
13152 cell.className += " fc-state-highlight";
13153 cell.title = cal.todayText;
13156 // disable highlight in other month..
13157 //cell.className += " fc-state-highlight";
13162 cell.className = " fc-state-disabled";
13163 cell.title = cal.minText;
13167 cell.className = " fc-state-disabled";
13168 cell.title = cal.maxText;
13172 if(ddays.indexOf(d.getDay()) != -1){
13173 cell.title = ddaysText;
13174 cell.className = " fc-state-disabled";
13177 if(ddMatch && format){
13178 var fvalue = d.dateFormat(format);
13179 if(ddMatch.test(fvalue)){
13180 cell.title = ddText.replace("%0", fvalue);
13181 cell.className = " fc-state-disabled";
13185 if (!cell.initialClassName) {
13186 cell.initialClassName = cell.dom.className;
13189 cell.dom.className = cell.initialClassName + ' ' + cell.className;
13194 for(; i < startingPos; i++) {
13195 textEls[i].innerHTML = (++prevStart);
13196 d.setDate(d.getDate()+1);
13198 cells[i].className = "fc-past fc-other-month";
13199 setCellClass(this, cells[i]);
13204 for(; i < days; i++){
13205 intDay = i - startingPos + 1;
13206 textEls[i].innerHTML = (intDay);
13207 d.setDate(d.getDate()+1);
13209 cells[i].className = ''; // "x-date-active";
13210 setCellClass(this, cells[i]);
13214 for(; i < 42; i++) {
13215 textEls[i].innerHTML = (++extraDays);
13216 d.setDate(d.getDate()+1);
13218 cells[i].className = "fc-future fc-other-month";
13219 setCellClass(this, cells[i]);
13222 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13224 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13226 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13227 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13229 if(totalRows != 6){
13230 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13231 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13234 this.fireEvent('monthchange', this, date);
13238 if(!this.internalRender){
13239 var main = this.el.dom.firstChild;
13240 var w = main.offsetWidth;
13241 this.el.setWidth(w + this.el.getBorderWidth("lr"));
13242 Roo.fly(main).setWidth(w);
13243 this.internalRender = true;
13244 // opera does not respect the auto grow header center column
13245 // then, after it gets a width opera refuses to recalculate
13246 // without a second pass
13247 if(Roo.isOpera && !this.secondPass){
13248 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13249 this.secondPass = true;
13250 this.update.defer(10, this, [date]);
13257 findCell : function(dt) {
13258 dt = dt.clearTime().getTime();
13260 this.cells.each(function(c){
13261 //Roo.log("check " +c.dateValue + '?=' + dt);
13262 if(c.dateValue == dt){
13272 findCells : function(ev) {
13273 var s = ev.start.clone().clearTime().getTime();
13275 var e= ev.end.clone().clearTime().getTime();
13278 this.cells.each(function(c){
13279 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13281 if(c.dateValue > e){
13284 if(c.dateValue < s){
13293 // findBestRow: function(cells)
13297 // for (var i =0 ; i < cells.length;i++) {
13298 // ret = Math.max(cells[i].rows || 0,ret);
13305 addItem : function(ev)
13307 // look for vertical location slot in
13308 var cells = this.findCells(ev);
13310 // ev.row = this.findBestRow(cells);
13312 // work out the location.
13316 for(var i =0; i < cells.length; i++) {
13318 cells[i].row = cells[0].row;
13321 cells[i].row = cells[i].row + 1;
13331 if (crow.start.getY() == cells[i].getY()) {
13333 crow.end = cells[i];
13350 cells[0].events.push(ev);
13352 this.calevents.push(ev);
13355 clearEvents: function() {
13357 if(!this.calevents){
13361 Roo.each(this.cells.elements, function(c){
13367 Roo.each(this.calevents, function(e) {
13368 Roo.each(e.els, function(el) {
13369 el.un('mouseenter' ,this.onEventEnter, this);
13370 el.un('mouseleave' ,this.onEventLeave, this);
13375 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13381 renderEvents: function()
13385 this.cells.each(function(c) {
13394 if(c.row != c.events.length){
13395 r = 4 - (4 - (c.row - c.events.length));
13398 c.events = ev.slice(0, r);
13399 c.more = ev.slice(r);
13401 if(c.more.length && c.more.length == 1){
13402 c.events.push(c.more.pop());
13405 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13409 this.cells.each(function(c) {
13411 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13414 for (var e = 0; e < c.events.length; e++){
13415 var ev = c.events[e];
13416 var rows = ev.rows;
13418 for(var i = 0; i < rows.length; i++) {
13420 // how many rows should it span..
13423 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13424 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13426 unselectable : "on",
13429 cls: 'fc-event-inner',
13433 // cls: 'fc-event-time',
13434 // html : cells.length > 1 ? '' : ev.time
13438 cls: 'fc-event-title',
13439 html : String.format('{0}', ev.title)
13446 cls: 'ui-resizable-handle ui-resizable-e',
13447 html : '  '
13454 cfg.cls += ' fc-event-start';
13456 if ((i+1) == rows.length) {
13457 cfg.cls += ' fc-event-end';
13460 var ctr = _this.el.select('.fc-event-container',true).first();
13461 var cg = ctr.createChild(cfg);
13463 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13464 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13466 var r = (c.more.length) ? 1 : 0;
13467 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
13468 cg.setWidth(ebox.right - sbox.x -2);
13470 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13471 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13472 cg.on('click', _this.onEventClick, _this, ev);
13483 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13484 style : 'position: absolute',
13485 unselectable : "on",
13488 cls: 'fc-event-inner',
13492 cls: 'fc-event-title',
13500 cls: 'ui-resizable-handle ui-resizable-e',
13501 html : '  '
13507 var ctr = _this.el.select('.fc-event-container',true).first();
13508 var cg = ctr.createChild(cfg);
13510 var sbox = c.select('.fc-day-content',true).first().getBox();
13511 var ebox = c.select('.fc-day-content',true).first().getBox();
13513 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
13514 cg.setWidth(ebox.right - sbox.x -2);
13516 cg.on('click', _this.onMoreEventClick, _this, c.more);
13526 onEventEnter: function (e, el,event,d) {
13527 this.fireEvent('evententer', this, el, event);
13530 onEventLeave: function (e, el,event,d) {
13531 this.fireEvent('eventleave', this, el, event);
13534 onEventClick: function (e, el,event,d) {
13535 this.fireEvent('eventclick', this, el, event);
13538 onMonthChange: function () {
13542 onMoreEventClick: function(e, el, more)
13546 this.calpopover.placement = 'right';
13547 this.calpopover.setTitle('More');
13549 this.calpopover.setContent('');
13551 var ctr = this.calpopover.el.select('.popover-content', true).first();
13553 Roo.each(more, function(m){
13555 cls : 'fc-event-hori fc-event-draggable',
13558 var cg = ctr.createChild(cfg);
13560 cg.on('click', _this.onEventClick, _this, m);
13563 this.calpopover.show(el);
13568 onLoad: function ()
13570 this.calevents = [];
13573 if(this.store.getCount() > 0){
13574 this.store.data.each(function(d){
13577 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13578 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13579 time : d.data.start_time,
13580 title : d.data.title,
13581 description : d.data.description,
13582 venue : d.data.venue
13587 this.renderEvents();
13589 if(this.calevents.length && this.loadMask){
13590 this.maskEl.hide();
13594 onBeforeLoad: function()
13596 this.clearEvents();
13598 this.maskEl.show();
13612 * @class Roo.bootstrap.Popover
13613 * @extends Roo.bootstrap.Component
13614 * Bootstrap Popover class
13615 * @cfg {String} html contents of the popover (or false to use children..)
13616 * @cfg {String} title of popover (or false to hide)
13617 * @cfg {String} placement how it is placed
13618 * @cfg {String} trigger click || hover (or false to trigger manually)
13619 * @cfg {String} over what (parent or false to trigger manually.)
13622 * Create a new Popover
13623 * @param {Object} config The config object
13626 Roo.bootstrap.Popover = function(config){
13627 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13630 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
13632 title: 'Fill in a title',
13635 placement : 'right',
13636 trigger : 'hover', // hover
13640 can_build_overlaid : false,
13642 getChildContainer : function()
13644 return this.el.select('.popover-content',true).first();
13647 getAutoCreate : function(){
13648 Roo.log('make popover?');
13650 cls : 'popover roo-dynamic',
13651 style: 'display:block',
13657 cls : 'popover-inner',
13661 cls: 'popover-title',
13665 cls : 'popover-content',
13676 setTitle: function(str)
13678 this.el.select('.popover-title',true).first().dom.innerHTML = str;
13680 setContent: function(str)
13682 this.el.select('.popover-content',true).first().dom.innerHTML = str;
13684 // as it get's added to the bottom of the page.
13685 onRender : function(ct, position)
13687 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13689 var cfg = Roo.apply({}, this.getAutoCreate());
13693 cfg.cls += ' ' + this.cls;
13696 cfg.style = this.style;
13698 Roo.log("adding to ")
13699 this.el = Roo.get(document.body).createChild(cfg, position);
13705 initEvents : function()
13707 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13708 this.el.enableDisplayMode('block');
13710 if (this.over === false) {
13713 if (this.triggers === false) {
13716 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13717 var triggers = this.trigger ? this.trigger.split(' ') : [];
13718 Roo.each(triggers, function(trigger) {
13720 if (trigger == 'click') {
13721 on_el.on('click', this.toggle, this);
13722 } else if (trigger != 'manual') {
13723 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
13724 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13726 on_el.on(eventIn ,this.enter, this);
13727 on_el.on(eventOut, this.leave, this);
13738 toggle : function () {
13739 this.hoverState == 'in' ? this.leave() : this.enter();
13742 enter : function () {
13745 clearTimeout(this.timeout);
13747 this.hoverState = 'in'
13749 if (!this.delay || !this.delay.show) {
13754 this.timeout = setTimeout(function () {
13755 if (_t.hoverState == 'in') {
13758 }, this.delay.show)
13760 leave : function() {
13761 clearTimeout(this.timeout);
13763 this.hoverState = 'out'
13765 if (!this.delay || !this.delay.hide) {
13770 this.timeout = setTimeout(function () {
13771 if (_t.hoverState == 'out') {
13774 }, this.delay.hide)
13777 show : function (on_el)
13780 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13783 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13784 if (this.html !== false) {
13785 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13787 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13788 if (!this.title.length) {
13789 this.el.select('.popover-title',true).hide();
13792 var placement = typeof this.placement == 'function' ?
13793 this.placement.call(this, this.el, on_el) :
13796 var autoToken = /\s?auto?\s?/i;
13797 var autoPlace = autoToken.test(placement);
13799 placement = placement.replace(autoToken, '') || 'top';
13803 //this.el.setXY([0,0]);
13805 this.el.dom.style.display='block';
13806 this.el.addClass(placement);
13808 //this.el.appendTo(on_el);
13810 var p = this.getPosition();
13811 var box = this.el.getBox();
13816 var align = Roo.bootstrap.Popover.alignment[placement]
13817 this.el.alignTo(on_el, align[0],align[1]);
13818 //var arrow = this.el.select('.arrow',true).first();
13819 //arrow.set(align[2],
13821 this.el.addClass('in');
13822 this.hoverState = null;
13824 if (this.el.hasClass('fade')) {
13831 this.el.setXY([0,0]);
13832 this.el.removeClass('in');
13839 Roo.bootstrap.Popover.alignment = {
13840 'left' : ['r-l', [-10,0], 'right'],
13841 'right' : ['l-r', [10,0], 'left'],
13842 'bottom' : ['t-b', [0,10], 'top'],
13843 'top' : [ 'b-t', [0,-10], 'bottom']
13854 * @class Roo.bootstrap.Progress
13855 * @extends Roo.bootstrap.Component
13856 * Bootstrap Progress class
13857 * @cfg {Boolean} striped striped of the progress bar
13858 * @cfg {Boolean} active animated of the progress bar
13862 * Create a new Progress
13863 * @param {Object} config The config object
13866 Roo.bootstrap.Progress = function(config){
13867 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13870 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
13875 getAutoCreate : function(){
13883 cfg.cls += ' progress-striped';
13887 cfg.cls += ' active';
13906 * @class Roo.bootstrap.ProgressBar
13907 * @extends Roo.bootstrap.Component
13908 * Bootstrap ProgressBar class
13909 * @cfg {Number} aria_valuenow aria-value now
13910 * @cfg {Number} aria_valuemin aria-value min
13911 * @cfg {Number} aria_valuemax aria-value max
13912 * @cfg {String} label label for the progress bar
13913 * @cfg {String} panel (success | info | warning | danger )
13914 * @cfg {String} role role of the progress bar
13915 * @cfg {String} sr_only text
13919 * Create a new ProgressBar
13920 * @param {Object} config The config object
13923 Roo.bootstrap.ProgressBar = function(config){
13924 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13927 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
13931 aria_valuemax : 100,
13937 getAutoCreate : function()
13942 cls: 'progress-bar',
13943 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13955 cfg.role = this.role;
13958 if(this.aria_valuenow){
13959 cfg['aria-valuenow'] = this.aria_valuenow;
13962 if(this.aria_valuemin){
13963 cfg['aria-valuemin'] = this.aria_valuemin;
13966 if(this.aria_valuemax){
13967 cfg['aria-valuemax'] = this.aria_valuemax;
13970 if(this.label && !this.sr_only){
13971 cfg.html = this.label;
13975 cfg.cls += ' progress-bar-' + this.panel;
13981 update : function(aria_valuenow)
13983 this.aria_valuenow = aria_valuenow;
13985 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14000 * @class Roo.bootstrap.TabGroup
14001 * @extends Roo.bootstrap.Column
14002 * Bootstrap Column class
14003 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14004 * @cfg {Boolean} carousel true to make the group behave like a carousel
14007 * Create a new TabGroup
14008 * @param {Object} config The config object
14011 Roo.bootstrap.TabGroup = function(config){
14012 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14014 this.navId = Roo.id();
14017 Roo.bootstrap.TabGroup.register(this);
14021 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
14024 transition : false,
14026 getAutoCreate : function()
14028 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14030 cfg.cls += ' tab-content';
14032 if (this.carousel) {
14033 cfg.cls += ' carousel slide';
14035 cls : 'carousel-inner'
14042 getChildContainer : function()
14044 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14048 * register a Navigation item
14049 * @param {Roo.bootstrap.NavItem} the navitem to add
14051 register : function(item)
14053 this.tabs.push( item);
14054 item.navId = this.navId; // not really needed..
14058 getActivePanel : function()
14061 Roo.each(this.tabs, function(t) {
14071 getPanelByName : function(n)
14074 Roo.each(this.tabs, function(t) {
14075 if (t.tabId == n) {
14083 indexOfPanel : function(p)
14086 Roo.each(this.tabs, function(t,i) {
14087 if (t.tabId == p.tabId) {
14096 * show a specific panel
14097 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14098 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14100 showPanel : function (pan)
14103 if (typeof(pan) == 'number') {
14104 pan = this.tabs[pan];
14106 if (typeof(pan) == 'string') {
14107 pan = this.getPanelByName(pan);
14109 if (pan.tabId == this.getActivePanel().tabId) {
14112 var cur = this.getActivePanel();
14114 if (false === cur.fireEvent('beforedeactivate')) {
14118 if (this.carousel) {
14119 this.transition = true;
14120 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
14121 var lr = dir == 'next' ? 'left' : 'right';
14122 pan.el.addClass(dir); // or prev
14123 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14124 cur.el.addClass(lr); // or right
14125 pan.el.addClass(lr);
14128 cur.el.on('transitionend', function() {
14129 Roo.log("trans end?");
14131 pan.el.removeClass([lr,dir]);
14132 pan.setActive(true);
14134 cur.el.removeClass([lr]);
14135 cur.setActive(false);
14137 _this.transition = false;
14139 }, this, { single: true } );
14143 cur.setActive(false);
14144 pan.setActive(true);
14148 showPanelNext : function()
14150 var i = this.indexOfPanel(this.getActivePanel());
14151 if (i > this.tabs.length) {
14154 this.showPanel(this.tabs[i+1]);
14156 showPanelPrev : function()
14158 var i = this.indexOfPanel(this.getActivePanel());
14162 this.showPanel(this.tabs[i-1]);
14173 Roo.apply(Roo.bootstrap.TabGroup, {
14177 * register a Navigation Group
14178 * @param {Roo.bootstrap.NavGroup} the navgroup to add
14180 register : function(navgrp)
14182 this.groups[navgrp.navId] = navgrp;
14186 * fetch a Navigation Group based on the navigation ID
14187 * if one does not exist , it will get created.
14188 * @param {string} the navgroup to add
14189 * @returns {Roo.bootstrap.NavGroup} the navgroup
14191 get: function(navId) {
14192 if (typeof(this.groups[navId]) == 'undefined') {
14193 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14195 return this.groups[navId] ;
14210 * @class Roo.bootstrap.TabPanel
14211 * @extends Roo.bootstrap.Component
14212 * Bootstrap TabPanel class
14213 * @cfg {Boolean} active panel active
14214 * @cfg {String} html panel content
14215 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14216 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14220 * Create a new TabPanel
14221 * @param {Object} config The config object
14224 Roo.bootstrap.TabPanel = function(config){
14225 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14229 * Fires when the active status changes
14230 * @param {Roo.bootstrap.TabPanel} this
14231 * @param {Boolean} state the new state
14236 * @event beforedeactivate
14237 * Fires before a tab is de-activated - can be used to do validation on a form.
14238 * @param {Roo.bootstrap.TabPanel} this
14239 * @return {Boolean} false if there is an error
14242 'beforedeactivate': true
14245 this.tabId = this.tabId || Roo.id();
14249 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
14256 getAutoCreate : function(){
14259 // item is needed for carousel - not sure if it has any effect otherwise
14260 cls: 'tab-pane item',
14261 html: this.html || ''
14265 cfg.cls += ' active';
14269 cfg.tabId = this.tabId;
14276 initEvents: function()
14278 Roo.log('-------- init events on tab panel ---------');
14280 var p = this.parent();
14281 this.navId = this.navId || p.navId;
14283 if (typeof(this.navId) != 'undefined') {
14284 // not really needed.. but just in case.. parent should be a NavGroup.
14285 var tg = Roo.bootstrap.TabGroup.get(this.navId);
14286 Roo.log(['register', tg, this]);
14292 onRender : function(ct, position)
14294 // Roo.log("Call onRender: " + this.xtype);
14296 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14304 setActive: function(state)
14306 Roo.log("panel - set active " + this.tabId + "=" + state);
14308 this.active = state;
14310 this.el.removeClass('active');
14312 } else if (!this.el.hasClass('active')) {
14313 this.el.addClass('active');
14315 this.fireEvent('changed', this, state);
14332 * @class Roo.bootstrap.DateField
14333 * @extends Roo.bootstrap.Input
14334 * Bootstrap DateField class
14335 * @cfg {Number} weekStart default 0
14336 * @cfg {Number} weekStart default 0
14337 * @cfg {Number} viewMode default empty, (months|years)
14338 * @cfg {Number} minViewMode default empty, (months|years)
14339 * @cfg {Number} startDate default -Infinity
14340 * @cfg {Number} endDate default Infinity
14341 * @cfg {Boolean} todayHighlight default false
14342 * @cfg {Boolean} todayBtn default false
14343 * @cfg {Boolean} calendarWeeks default false
14344 * @cfg {Object} daysOfWeekDisabled default empty
14346 * @cfg {Boolean} keyboardNavigation default true
14347 * @cfg {String} language default en
14350 * Create a new DateField
14351 * @param {Object} config The config object
14354 Roo.bootstrap.DateField = function(config){
14355 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14359 * Fires when this field show.
14360 * @param {Roo.bootstrap.DateField} this
14361 * @param {Mixed} date The date value
14366 * Fires when this field hide.
14367 * @param {Roo.bootstrap.DateField} this
14368 * @param {Mixed} date The date value
14373 * Fires when select a date.
14374 * @param {Roo.bootstrap.DateField} this
14375 * @param {Mixed} date The date value
14381 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
14384 * @cfg {String} format
14385 * The default date format string which can be overriden for localization support. The format must be
14386 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14390 * @cfg {String} altFormats
14391 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14392 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14394 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14402 todayHighlight : false,
14408 keyboardNavigation: true,
14410 calendarWeeks: false,
14412 startDate: -Infinity,
14416 daysOfWeekDisabled: [],
14420 UTCDate: function()
14422 return new Date(Date.UTC.apply(Date, arguments));
14425 UTCToday: function()
14427 var today = new Date();
14428 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14431 getDate: function() {
14432 var d = this.getUTCDate();
14433 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14436 getUTCDate: function() {
14440 setDate: function(d) {
14441 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14444 setUTCDate: function(d) {
14446 this.setValue(this.formatDate(this.date));
14449 onRender: function(ct, position)
14452 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14454 this.language = this.language || 'en';
14455 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14456 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14458 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14459 this.format = this.format || 'm/d/y';
14460 this.isInline = false;
14461 this.isInput = true;
14462 this.component = this.el.select('.add-on', true).first() || false;
14463 this.component = (this.component && this.component.length === 0) ? false : this.component;
14464 this.hasInput = this.component && this.inputEL().length;
14466 if (typeof(this.minViewMode === 'string')) {
14467 switch (this.minViewMode) {
14469 this.minViewMode = 1;
14472 this.minViewMode = 2;
14475 this.minViewMode = 0;
14480 if (typeof(this.viewMode === 'string')) {
14481 switch (this.viewMode) {
14494 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14496 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14498 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14500 this.picker().on('mousedown', this.onMousedown, this);
14501 this.picker().on('click', this.onClick, this);
14503 this.picker().addClass('datepicker-dropdown');
14505 this.startViewMode = this.viewMode;
14508 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14509 if(!this.calendarWeeks){
14514 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14515 v.attr('colspan', function(i, val){
14516 return parseInt(val) + 1;
14521 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14523 this.setStartDate(this.startDate);
14524 this.setEndDate(this.endDate);
14526 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14533 if(this.isInline) {
14538 picker : function()
14540 return this.pickerEl;
14541 // return this.el.select('.datepicker', true).first();
14544 fillDow: function()
14546 var dowCnt = this.weekStart;
14555 if(this.calendarWeeks){
14563 while (dowCnt < this.weekStart + 7) {
14567 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14571 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14574 fillMonths: function()
14577 var months = this.picker().select('>.datepicker-months td', true).first();
14579 months.dom.innerHTML = '';
14585 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14588 months.createChild(month);
14595 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;
14597 if (this.date < this.startDate) {
14598 this.viewDate = new Date(this.startDate);
14599 } else if (this.date > this.endDate) {
14600 this.viewDate = new Date(this.endDate);
14602 this.viewDate = new Date(this.date);
14610 var d = new Date(this.viewDate),
14611 year = d.getUTCFullYear(),
14612 month = d.getUTCMonth(),
14613 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14614 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14615 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14616 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14617 currentDate = this.date && this.date.valueOf(),
14618 today = this.UTCToday();
14620 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14622 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14624 // this.picker.select('>tfoot th.today').
14625 // .text(dates[this.language].today)
14626 // .toggle(this.todayBtn !== false);
14628 this.updateNavArrows();
14631 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14633 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14635 prevMonth.setUTCDate(day);
14637 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14639 var nextMonth = new Date(prevMonth);
14641 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14643 nextMonth = nextMonth.valueOf();
14645 var fillMonths = false;
14647 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14649 while(prevMonth.valueOf() < nextMonth) {
14652 if (prevMonth.getUTCDay() === this.weekStart) {
14654 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14662 if(this.calendarWeeks){
14663 // ISO 8601: First week contains first thursday.
14664 // ISO also states week starts on Monday, but we can be more abstract here.
14666 // Start of current week: based on weekstart/current date
14667 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14668 // Thursday of this week
14669 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14670 // First Thursday of year, year from thursday
14671 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14672 // Calendar week: ms between thursdays, div ms per day, div 7 days
14673 calWeek = (th - yth) / 864e5 / 7 + 1;
14675 fillMonths.cn.push({
14683 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14685 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14688 if (this.todayHighlight &&
14689 prevMonth.getUTCFullYear() == today.getFullYear() &&
14690 prevMonth.getUTCMonth() == today.getMonth() &&
14691 prevMonth.getUTCDate() == today.getDate()) {
14692 clsName += ' today';
14695 if (currentDate && prevMonth.valueOf() === currentDate) {
14696 clsName += ' active';
14699 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14700 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14701 clsName += ' disabled';
14704 fillMonths.cn.push({
14706 cls: 'day ' + clsName,
14707 html: prevMonth.getDate()
14710 prevMonth.setDate(prevMonth.getDate()+1);
14713 var currentYear = this.date && this.date.getUTCFullYear();
14714 var currentMonth = this.date && this.date.getUTCMonth();
14716 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14718 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14719 v.removeClass('active');
14721 if(currentYear === year && k === currentMonth){
14722 v.addClass('active');
14725 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14726 v.addClass('disabled');
14732 year = parseInt(year/10, 10) * 10;
14734 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14736 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14739 for (var i = -1; i < 11; i++) {
14740 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14742 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14750 showMode: function(dir)
14753 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14755 Roo.each(this.picker().select('>div',true).elements, function(v){
14756 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14759 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14764 if(this.isInline) return;
14766 this.picker().removeClass(['bottom', 'top']);
14768 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14770 * place to the top of element!
14774 this.picker().addClass('top');
14775 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14780 this.picker().addClass('bottom');
14782 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14785 parseDate : function(value)
14787 if(!value || value instanceof Date){
14790 var v = Date.parseDate(value, this.format);
14791 if (!v && this.useIso) {
14792 v = Date.parseDate(value, 'Y-m-d');
14794 if(!v && this.altFormats){
14795 if(!this.altFormatsArray){
14796 this.altFormatsArray = this.altFormats.split("|");
14798 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14799 v = Date.parseDate(value, this.altFormatsArray[i]);
14805 formatDate : function(date, fmt)
14807 return (!date || !(date instanceof Date)) ?
14808 date : date.dateFormat(fmt || this.format);
14811 onFocus : function()
14813 Roo.bootstrap.DateField.superclass.onFocus.call(this);
14817 onBlur : function()
14819 Roo.bootstrap.DateField.superclass.onBlur.call(this);
14821 var d = this.inputEl().getValue();
14830 this.picker().show();
14834 this.fireEvent('show', this, this.date);
14839 if(this.isInline) return;
14840 this.picker().hide();
14841 this.viewMode = this.startViewMode;
14844 this.fireEvent('hide', this, this.date);
14848 onMousedown: function(e)
14850 e.stopPropagation();
14851 e.preventDefault();
14856 Roo.bootstrap.DateField.superclass.keyup.call(this);
14860 setValue: function(v)
14862 var d = new Date(v).clearTime();
14864 if(isNaN(d.getTime())){
14865 this.date = this.viewDate = '';
14866 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14870 v = this.formatDate(d);
14872 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14874 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14878 this.fireEvent('select', this, this.date);
14882 getValue: function()
14884 return this.formatDate(this.date);
14887 fireKey: function(e)
14889 if (!this.picker().isVisible()){
14890 if (e.keyCode == 27) // allow escape to hide and re-show picker
14895 var dateChanged = false,
14897 newDate, newViewDate;
14902 e.preventDefault();
14906 if (!this.keyboardNavigation) break;
14907 dir = e.keyCode == 37 ? -1 : 1;
14910 newDate = this.moveYear(this.date, dir);
14911 newViewDate = this.moveYear(this.viewDate, dir);
14912 } else if (e.shiftKey){
14913 newDate = this.moveMonth(this.date, dir);
14914 newViewDate = this.moveMonth(this.viewDate, dir);
14916 newDate = new Date(this.date);
14917 newDate.setUTCDate(this.date.getUTCDate() + dir);
14918 newViewDate = new Date(this.viewDate);
14919 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14921 if (this.dateWithinRange(newDate)){
14922 this.date = newDate;
14923 this.viewDate = newViewDate;
14924 this.setValue(this.formatDate(this.date));
14926 e.preventDefault();
14927 dateChanged = true;
14932 if (!this.keyboardNavigation) break;
14933 dir = e.keyCode == 38 ? -1 : 1;
14935 newDate = this.moveYear(this.date, dir);
14936 newViewDate = this.moveYear(this.viewDate, dir);
14937 } else if (e.shiftKey){
14938 newDate = this.moveMonth(this.date, dir);
14939 newViewDate = this.moveMonth(this.viewDate, dir);
14941 newDate = new Date(this.date);
14942 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14943 newViewDate = new Date(this.viewDate);
14944 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14946 if (this.dateWithinRange(newDate)){
14947 this.date = newDate;
14948 this.viewDate = newViewDate;
14949 this.setValue(this.formatDate(this.date));
14951 e.preventDefault();
14952 dateChanged = true;
14956 this.setValue(this.formatDate(this.date));
14958 e.preventDefault();
14961 this.setValue(this.formatDate(this.date));
14975 onClick: function(e)
14977 e.stopPropagation();
14978 e.preventDefault();
14980 var target = e.getTarget();
14982 if(target.nodeName.toLowerCase() === 'i'){
14983 target = Roo.get(target).dom.parentNode;
14986 var nodeName = target.nodeName;
14987 var className = target.className;
14988 var html = target.innerHTML;
14990 switch(nodeName.toLowerCase()) {
14992 switch(className) {
14998 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14999 switch(this.viewMode){
15001 this.viewDate = this.moveMonth(this.viewDate, dir);
15005 this.viewDate = this.moveYear(this.viewDate, dir);
15011 var date = new Date();
15012 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15014 this.setValue(this.formatDate(this.date));
15021 if (className.indexOf('disabled') === -1) {
15022 this.viewDate.setUTCDate(1);
15023 if (className.indexOf('month') !== -1) {
15024 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15026 var year = parseInt(html, 10) || 0;
15027 this.viewDate.setUTCFullYear(year);
15036 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
15037 var day = parseInt(html, 10) || 1;
15038 var year = this.viewDate.getUTCFullYear(),
15039 month = this.viewDate.getUTCMonth();
15041 if (className.indexOf('old') !== -1) {
15048 } else if (className.indexOf('new') !== -1) {
15056 this.date = this.UTCDate(year, month, day,0,0,0,0);
15057 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15059 this.setValue(this.formatDate(this.date));
15066 setStartDate: function(startDate)
15068 this.startDate = startDate || -Infinity;
15069 if (this.startDate !== -Infinity) {
15070 this.startDate = this.parseDate(this.startDate);
15073 this.updateNavArrows();
15076 setEndDate: function(endDate)
15078 this.endDate = endDate || Infinity;
15079 if (this.endDate !== Infinity) {
15080 this.endDate = this.parseDate(this.endDate);
15083 this.updateNavArrows();
15086 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15088 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15089 if (typeof(this.daysOfWeekDisabled) !== 'object') {
15090 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15092 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15093 return parseInt(d, 10);
15096 this.updateNavArrows();
15099 updateNavArrows: function()
15101 var d = new Date(this.viewDate),
15102 year = d.getUTCFullYear(),
15103 month = d.getUTCMonth();
15105 Roo.each(this.picker().select('.prev', true).elements, function(v){
15107 switch (this.viewMode) {
15110 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15116 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15123 Roo.each(this.picker().select('.next', true).elements, function(v){
15125 switch (this.viewMode) {
15128 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15134 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15142 moveMonth: function(date, dir)
15144 if (!dir) return date;
15145 var new_date = new Date(date.valueOf()),
15146 day = new_date.getUTCDate(),
15147 month = new_date.getUTCMonth(),
15148 mag = Math.abs(dir),
15150 dir = dir > 0 ? 1 : -1;
15153 // If going back one month, make sure month is not current month
15154 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15156 return new_date.getUTCMonth() == month;
15158 // If going forward one month, make sure month is as expected
15159 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15161 return new_date.getUTCMonth() != new_month;
15163 new_month = month + dir;
15164 new_date.setUTCMonth(new_month);
15165 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15166 if (new_month < 0 || new_month > 11)
15167 new_month = (new_month + 12) % 12;
15169 // For magnitudes >1, move one month at a time...
15170 for (var i=0; i<mag; i++)
15171 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15172 new_date = this.moveMonth(new_date, dir);
15173 // ...then reset the day, keeping it in the new month
15174 new_month = new_date.getUTCMonth();
15175 new_date.setUTCDate(day);
15177 return new_month != new_date.getUTCMonth();
15180 // Common date-resetting loop -- if date is beyond end of month, make it
15183 new_date.setUTCDate(--day);
15184 new_date.setUTCMonth(new_month);
15189 moveYear: function(date, dir)
15191 return this.moveMonth(date, dir*12);
15194 dateWithinRange: function(date)
15196 return date >= this.startDate && date <= this.endDate;
15202 this.picker().remove();
15207 Roo.apply(Roo.bootstrap.DateField, {
15218 html: '<i class="fa fa-arrow-left"/>'
15228 html: '<i class="fa fa-arrow-right"/>'
15270 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15271 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15272 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15273 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15274 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15287 navFnc: 'FullYear',
15292 navFnc: 'FullYear',
15297 Roo.apply(Roo.bootstrap.DateField, {
15301 cls: 'datepicker dropdown-menu',
15305 cls: 'datepicker-days',
15309 cls: 'table-condensed',
15311 Roo.bootstrap.DateField.head,
15315 Roo.bootstrap.DateField.footer
15322 cls: 'datepicker-months',
15326 cls: 'table-condensed',
15328 Roo.bootstrap.DateField.head,
15329 Roo.bootstrap.DateField.content,
15330 Roo.bootstrap.DateField.footer
15337 cls: 'datepicker-years',
15341 cls: 'table-condensed',
15343 Roo.bootstrap.DateField.head,
15344 Roo.bootstrap.DateField.content,
15345 Roo.bootstrap.DateField.footer
15364 * @class Roo.bootstrap.TimeField
15365 * @extends Roo.bootstrap.Input
15366 * Bootstrap DateField class
15370 * Create a new TimeField
15371 * @param {Object} config The config object
15374 Roo.bootstrap.TimeField = function(config){
15375 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15379 * Fires when this field show.
15380 * @param {Roo.bootstrap.DateField} this
15381 * @param {Mixed} date The date value
15386 * Fires when this field hide.
15387 * @param {Roo.bootstrap.DateField} this
15388 * @param {Mixed} date The date value
15393 * Fires when select a date.
15394 * @param {Roo.bootstrap.DateField} this
15395 * @param {Mixed} date The date value
15401 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
15404 * @cfg {String} format
15405 * The default time format string which can be overriden for localization support. The format must be
15406 * valid according to {@link Date#parseDate} (defaults to 'H:i').
15410 onRender: function(ct, position)
15413 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15415 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15417 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15419 this.pop = this.picker().select('>.datepicker-time',true).first();
15420 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
15422 this.picker().on('mousedown', this.onMousedown, this);
15423 this.picker().on('click', this.onClick, this);
15425 this.picker().addClass('datepicker-dropdown');
15430 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15431 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15432 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15433 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15434 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15435 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15439 fireKey: function(e){
15440 if (!this.picker().isVisible()){
15441 if (e.keyCode == 27) // allow escape to hide and re-show picker
15446 e.preventDefault();
15454 this.onTogglePeriod();
15457 this.onIncrementMinutes();
15460 this.onDecrementMinutes();
15469 onClick: function(e) {
15470 e.stopPropagation();
15471 e.preventDefault();
15474 picker : function()
15476 return this.el.select('.datepicker', true).first();
15479 fillTime: function()
15481 var time = this.pop.select('tbody', true).first();
15483 time.dom.innerHTML = '';
15498 cls: 'hours-up glyphicon glyphicon-chevron-up'
15518 cls: 'minutes-up glyphicon glyphicon-chevron-up'
15539 cls: 'timepicker-hour',
15554 cls: 'timepicker-minute',
15569 cls: 'btn btn-primary period',
15591 cls: 'hours-down glyphicon glyphicon-chevron-down'
15611 cls: 'minutes-down glyphicon glyphicon-chevron-down'
15629 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15636 var hours = this.time.getHours();
15637 var minutes = this.time.getMinutes();
15650 hours = hours - 12;
15654 hours = '0' + hours;
15658 minutes = '0' + minutes;
15661 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15662 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15663 this.pop.select('button', true).first().dom.innerHTML = period;
15669 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15671 var cls = ['bottom'];
15673 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15680 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15685 this.picker().addClass(cls.join('-'));
15689 Roo.each(cls, function(c){
15691 _this.picker().setTop(_this.inputEl().getHeight());
15695 _this.picker().setTop(0 - _this.picker().getHeight());
15700 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15704 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15711 onFocus : function()
15713 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15717 onBlur : function()
15719 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15725 this.picker().show();
15730 this.fireEvent('show', this, this.date);
15735 this.picker().hide();
15738 this.fireEvent('hide', this, this.date);
15741 setTime : function()
15744 this.setValue(this.time.format(this.format));
15746 this.fireEvent('select', this, this.date);
15751 onMousedown: function(e){
15752 e.stopPropagation();
15753 e.preventDefault();
15756 onIncrementHours: function()
15758 Roo.log('onIncrementHours');
15759 this.time = this.time.add(Date.HOUR, 1);
15764 onDecrementHours: function()
15766 Roo.log('onDecrementHours');
15767 this.time = this.time.add(Date.HOUR, -1);
15771 onIncrementMinutes: function()
15773 Roo.log('onIncrementMinutes');
15774 this.time = this.time.add(Date.MINUTE, 1);
15778 onDecrementMinutes: function()
15780 Roo.log('onDecrementMinutes');
15781 this.time = this.time.add(Date.MINUTE, -1);
15785 onTogglePeriod: function()
15787 Roo.log('onTogglePeriod');
15788 this.time = this.time.add(Date.HOUR, 12);
15795 Roo.apply(Roo.bootstrap.TimeField, {
15825 cls: 'btn btn-info ok',
15837 Roo.apply(Roo.bootstrap.TimeField, {
15841 cls: 'datepicker dropdown-menu',
15845 cls: 'datepicker-time',
15849 cls: 'table-condensed',
15851 Roo.bootstrap.TimeField.content,
15852 Roo.bootstrap.TimeField.footer
15871 * @class Roo.bootstrap.CheckBox
15872 * @extends Roo.bootstrap.Input
15873 * Bootstrap CheckBox class
15875 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15876 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15877 * @cfg {String} boxLabel The text that appears beside the checkbox
15878 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15879 * @cfg {Boolean} checked initnal the element
15883 * Create a new CheckBox
15884 * @param {Object} config The config object
15887 Roo.bootstrap.CheckBox = function(config){
15888 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15893 * Fires when the element is checked or unchecked.
15894 * @param {Roo.bootstrap.CheckBox} this This input
15895 * @param {Boolean} checked The new checked value
15901 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
15903 inputType: 'checkbox',
15910 getAutoCreate : function()
15912 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15918 cfg.cls = 'form-group checkbox' //input-group
15926 type : this.inputType,
15927 value : (!this.checked) ? this.valueOff : this.inputValue,
15928 cls : 'roo-checkbox', //'form-box',
15929 placeholder : this.placeholder || ''
15933 if (this.weight) { // Validity check?
15934 cfg.cls += " checkbox-" + this.weight;
15937 if (this.disabled) {
15938 input.disabled=true;
15942 input.checked = this.checked;
15946 input.name = this.name;
15950 input.cls += ' input-' + this.size;
15954 ['xs','sm','md','lg'].map(function(size){
15955 if (settings[size]) {
15956 cfg.cls += ' col-' + size + '-' + settings[size];
15962 var inputblock = input;
15967 if (this.before || this.after) {
15970 cls : 'input-group',
15974 inputblock.cn.push({
15976 cls : 'input-group-addon',
15980 inputblock.cn.push(input);
15982 inputblock.cn.push({
15984 cls : 'input-group-addon',
15991 if (align ==='left' && this.fieldLabel.length) {
15992 Roo.log("left and has label");
15998 cls : 'control-label col-md-' + this.labelWidth,
15999 html : this.fieldLabel
16003 cls : "col-md-" + (12 - this.labelWidth),
16010 } else if ( this.fieldLabel.length) {
16015 tag: this.boxLabel ? 'span' : 'label',
16017 cls: 'control-label box-input-label',
16018 //cls : 'input-group-addon',
16019 html : this.fieldLabel
16029 Roo.log(" no label && no align");
16030 cfg.cn = [ inputblock ] ;
16039 html: this.boxLabel
16051 * return the real input element.
16053 inputEl: function ()
16055 return this.el.select('input.roo-checkbox',true).first();
16060 return this.el.select('label.control-label',true).first();
16063 initEvents : function()
16065 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16067 this.inputEl().on('click', this.onClick, this);
16071 onClick : function()
16073 this.setChecked(!this.checked);
16076 setChecked : function(state,suppressEvent)
16078 this.checked = state;
16080 this.inputEl().dom.checked = state;
16082 if(suppressEvent !== true){
16083 this.fireEvent('check', this, state);
16086 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16090 setValue : function(v,suppressEvent)
16092 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16106 * @class Roo.bootstrap.Radio
16107 * @extends Roo.bootstrap.CheckBox
16108 * Bootstrap Radio class
16111 * Create a new Radio
16112 * @param {Object} config The config object
16115 Roo.bootstrap.Radio = function(config){
16116 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16120 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
16122 inputType: 'radio',
16126 getAutoCreate : function()
16128 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16134 cfg.cls = 'form-group radio' //input-group
16139 type : this.inputType,
16140 value : (!this.checked) ? this.valueOff : this.inputValue,
16142 placeholder : this.placeholder || ''
16145 if (this.weight) { // Validity check?
16146 cfg.cls += " radio-" + this.weight;
16148 if (this.disabled) {
16149 input.disabled=true;
16153 input.checked = this.checked;
16157 input.name = this.name;
16161 input.cls += ' input-' + this.size;
16165 ['xs','sm','md','lg'].map(function(size){
16166 if (settings[size]) {
16167 cfg.cls += ' col-' + size + '-' + settings[size];
16171 var inputblock = input;
16173 if (this.before || this.after) {
16176 cls : 'input-group',
16180 inputblock.cn.push({
16182 cls : 'input-group-addon',
16186 inputblock.cn.push(input);
16188 inputblock.cn.push({
16190 cls : 'input-group-addon',
16197 if (align ==='left' && this.fieldLabel.length) {
16198 Roo.log("left and has label");
16204 cls : 'control-label col-md-' + this.labelWidth,
16205 html : this.fieldLabel
16209 cls : "col-md-" + (12 - this.labelWidth),
16216 } else if ( this.fieldLabel.length) {
16223 cls: 'control-label box-input-label',
16224 //cls : 'input-group-addon',
16225 html : this.fieldLabel
16235 Roo.log(" no label && no align");
16250 html: this.boxLabel
16257 inputEl: function ()
16259 return this.el.select('input.roo-radio',true).first();
16261 onClick : function()
16263 this.setChecked(true);
16266 setChecked : function(state,suppressEvent)
16269 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16270 v.dom.checked = false;
16274 this.checked = state;
16275 this.inputEl().dom.checked = state;
16277 if(suppressEvent !== true){
16278 this.fireEvent('check', this, state);
16281 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16285 getGroupValue : function()
16288 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16289 if(v.dom.checked == true){
16290 value = v.dom.value;
16298 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
16299 * @return {Mixed} value The field value
16301 getValue : function(){
16302 return this.getGroupValue();
16308 //<script type="text/javascript">
16311 * Based Ext JS Library 1.1.1
16312 * Copyright(c) 2006-2007, Ext JS, LLC.
16318 * @class Roo.HtmlEditorCore
16319 * @extends Roo.Component
16320 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16322 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16325 Roo.HtmlEditorCore = function(config){
16328 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16333 * @event initialize
16334 * Fires when the editor is fully initialized (including the iframe)
16335 * @param {Roo.HtmlEditorCore} this
16340 * Fires when the editor is first receives the focus. Any insertion must wait
16341 * until after this event.
16342 * @param {Roo.HtmlEditorCore} this
16346 * @event beforesync
16347 * Fires before the textarea is updated with content from the editor iframe. Return false
16348 * to cancel the sync.
16349 * @param {Roo.HtmlEditorCore} this
16350 * @param {String} html
16354 * @event beforepush
16355 * Fires before the iframe editor is updated with content from the textarea. Return false
16356 * to cancel the push.
16357 * @param {Roo.HtmlEditorCore} this
16358 * @param {String} html
16363 * Fires when the textarea is updated with content from the editor iframe.
16364 * @param {Roo.HtmlEditorCore} this
16365 * @param {String} html
16370 * Fires when the iframe editor is updated with content from the textarea.
16371 * @param {Roo.HtmlEditorCore} this
16372 * @param {String} html
16377 * @event editorevent
16378 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16379 * @param {Roo.HtmlEditorCore} this
16384 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16386 // defaults : white / black...
16387 this.applyBlacklists();
16394 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
16398 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
16404 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
16409 * @cfg {Number} height (in pixels)
16413 * @cfg {Number} width (in pixels)
16418 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16421 stylesheets: false,
16426 // private properties
16427 validationEvent : false,
16429 initialized : false,
16431 sourceEditMode : false,
16432 onFocus : Roo.emptyFn,
16434 hideMode:'offsets',
16438 // blacklist + whitelisted elements..
16445 * Protected method that will not generally be called directly. It
16446 * is called when the editor initializes the iframe with HTML contents. Override this method if you
16447 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16449 getDocMarkup : function(){
16452 Roo.log(this.stylesheets);
16454 // inherit styels from page...??
16455 if (this.stylesheets === false) {
16457 Roo.get(document.head).select('style').each(function(node) {
16458 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16461 Roo.get(document.head).select('link').each(function(node) {
16462 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16465 } else if (!this.stylesheets.length) {
16467 st = '<style type="text/css">' +
16468 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16471 Roo.each(this.stylesheets, function(s) {
16472 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16477 st += '<style type="text/css">' +
16478 'IMG { cursor: pointer } ' +
16482 return '<html><head>' + st +
16483 //<style type="text/css">' +
16484 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16486 ' </head><body class="roo-htmleditor-body"></body></html>';
16490 onRender : function(ct, position)
16493 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16494 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16497 this.el.dom.style.border = '0 none';
16498 this.el.dom.setAttribute('tabIndex', -1);
16499 this.el.addClass('x-hidden hide');
16503 if(Roo.isIE){ // fix IE 1px bogus margin
16504 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16508 this.frameId = Roo.id();
16512 var iframe = this.owner.wrap.createChild({
16514 cls: 'form-control', // bootstrap..
16516 name: this.frameId,
16517 frameBorder : 'no',
16518 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
16523 this.iframe = iframe.dom;
16525 this.assignDocWin();
16527 this.doc.designMode = 'on';
16530 this.doc.write(this.getDocMarkup());
16534 var task = { // must defer to wait for browser to be ready
16536 //console.log("run task?" + this.doc.readyState);
16537 this.assignDocWin();
16538 if(this.doc.body || this.doc.readyState == 'complete'){
16540 this.doc.designMode="on";
16544 Roo.TaskMgr.stop(task);
16545 this.initEditor.defer(10, this);
16552 Roo.TaskMgr.start(task);
16559 onResize : function(w, h)
16561 Roo.log('resize: ' +w + ',' + h );
16562 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16566 if(typeof w == 'number'){
16568 this.iframe.style.width = w + 'px';
16570 if(typeof h == 'number'){
16572 this.iframe.style.height = h + 'px';
16574 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16581 * Toggles the editor between standard and source edit mode.
16582 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16584 toggleSourceEdit : function(sourceEditMode){
16586 this.sourceEditMode = sourceEditMode === true;
16588 if(this.sourceEditMode){
16590 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
16593 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16594 //this.iframe.className = '';
16597 //this.setSize(this.owner.wrap.getSize());
16598 //this.fireEvent('editmodechange', this, this.sourceEditMode);
16605 * Protected method that will not generally be called directly. If you need/want
16606 * custom HTML cleanup, this is the method you should override.
16607 * @param {String} html The HTML to be cleaned
16608 * return {String} The cleaned HTML
16610 cleanHtml : function(html){
16611 html = String(html);
16612 if(html.length > 5){
16613 if(Roo.isSafari){ // strip safari nonsense
16614 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16617 if(html == ' '){
16624 * HTML Editor -> Textarea
16625 * Protected method that will not generally be called directly. Syncs the contents
16626 * of the editor iframe with the textarea.
16628 syncValue : function(){
16629 if(this.initialized){
16630 var bd = (this.doc.body || this.doc.documentElement);
16631 //this.cleanUpPaste(); -- this is done else where and causes havoc..
16632 var html = bd.innerHTML;
16634 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16635 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16637 html = '<div style="'+m[0]+'">' + html + '</div>';
16640 html = this.cleanHtml(html);
16641 // fix up the special chars.. normaly like back quotes in word...
16642 // however we do not want to do this with chinese..
16643 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16644 var cc = b.charCodeAt();
16646 (cc >= 0x4E00 && cc < 0xA000 ) ||
16647 (cc >= 0x3400 && cc < 0x4E00 ) ||
16648 (cc >= 0xf900 && cc < 0xfb00 )
16654 if(this.owner.fireEvent('beforesync', this, html) !== false){
16655 this.el.dom.value = html;
16656 this.owner.fireEvent('sync', this, html);
16662 * Protected method that will not generally be called directly. Pushes the value of the textarea
16663 * into the iframe editor.
16665 pushValue : function(){
16666 if(this.initialized){
16667 var v = this.el.dom.value.trim();
16669 // if(v.length < 1){
16673 if(this.owner.fireEvent('beforepush', this, v) !== false){
16674 var d = (this.doc.body || this.doc.documentElement);
16676 this.cleanUpPaste();
16677 this.el.dom.value = d.innerHTML;
16678 this.owner.fireEvent('push', this, v);
16684 deferFocus : function(){
16685 this.focus.defer(10, this);
16689 focus : function(){
16690 if(this.win && !this.sourceEditMode){
16697 assignDocWin: function()
16699 var iframe = this.iframe;
16702 this.doc = iframe.contentWindow.document;
16703 this.win = iframe.contentWindow;
16705 // if (!Roo.get(this.frameId)) {
16708 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16709 // this.win = Roo.get(this.frameId).dom.contentWindow;
16711 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16715 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16716 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16721 initEditor : function(){
16722 //console.log("INIT EDITOR");
16723 this.assignDocWin();
16727 this.doc.designMode="on";
16729 this.doc.write(this.getDocMarkup());
16732 var dbody = (this.doc.body || this.doc.documentElement);
16733 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16734 // this copies styles from the containing element into thsi one..
16735 // not sure why we need all of this..
16736 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16738 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16739 //ss['background-attachment'] = 'fixed'; // w3c
16740 dbody.bgProperties = 'fixed'; // ie
16741 //Roo.DomHelper.applyStyles(dbody, ss);
16742 Roo.EventManager.on(this.doc, {
16743 //'mousedown': this.onEditorEvent,
16744 'mouseup': this.onEditorEvent,
16745 'dblclick': this.onEditorEvent,
16746 'click': this.onEditorEvent,
16747 'keyup': this.onEditorEvent,
16752 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16754 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16755 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16757 this.initialized = true;
16759 this.owner.fireEvent('initialize', this);
16764 onDestroy : function(){
16770 //for (var i =0; i < this.toolbars.length;i++) {
16771 // // fixme - ask toolbars for heights?
16772 // this.toolbars[i].onDestroy();
16775 //this.wrap.dom.innerHTML = '';
16776 //this.wrap.remove();
16781 onFirstFocus : function(){
16783 this.assignDocWin();
16786 this.activated = true;
16789 if(Roo.isGecko){ // prevent silly gecko errors
16791 var s = this.win.getSelection();
16792 if(!s.focusNode || s.focusNode.nodeType != 3){
16793 var r = s.getRangeAt(0);
16794 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16799 this.execCmd('useCSS', true);
16800 this.execCmd('styleWithCSS', false);
16803 this.owner.fireEvent('activate', this);
16807 adjustFont: function(btn){
16808 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16809 //if(Roo.isSafari){ // safari
16812 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16813 if(Roo.isSafari){ // safari
16814 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16815 v = (v < 10) ? 10 : v;
16816 v = (v > 48) ? 48 : v;
16817 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16822 v = Math.max(1, v+adjust);
16824 this.execCmd('FontSize', v );
16827 onEditorEvent : function(e){
16828 this.owner.fireEvent('editorevent', this, e);
16829 // this.updateToolbar();
16830 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16833 insertTag : function(tg)
16835 // could be a bit smarter... -> wrap the current selected tRoo..
16836 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16838 range = this.createRange(this.getSelection());
16839 var wrappingNode = this.doc.createElement(tg.toLowerCase());
16840 wrappingNode.appendChild(range.extractContents());
16841 range.insertNode(wrappingNode);
16848 this.execCmd("formatblock", tg);
16852 insertText : function(txt)
16856 var range = this.createRange();
16857 range.deleteContents();
16858 //alert(Sender.getAttribute('label'));
16860 range.insertNode(this.doc.createTextNode(txt));
16866 * Executes a Midas editor command on the editor document and performs necessary focus and
16867 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16868 * @param {String} cmd The Midas command
16869 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16871 relayCmd : function(cmd, value){
16873 this.execCmd(cmd, value);
16874 this.owner.fireEvent('editorevent', this);
16875 //this.updateToolbar();
16876 this.owner.deferFocus();
16880 * Executes a Midas editor command directly on the editor document.
16881 * For visual commands, you should use {@link #relayCmd} instead.
16882 * <b>This should only be called after the editor is initialized.</b>
16883 * @param {String} cmd The Midas command
16884 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16886 execCmd : function(cmd, value){
16887 this.doc.execCommand(cmd, false, value === undefined ? null : value);
16894 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16896 * @param {String} text | dom node..
16898 insertAtCursor : function(text)
16903 if(!this.activated){
16909 var r = this.doc.selection.createRange();
16920 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16924 // from jquery ui (MIT licenced)
16926 var win = this.win;
16928 if (win.getSelection && win.getSelection().getRangeAt) {
16929 range = win.getSelection().getRangeAt(0);
16930 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16931 range.insertNode(node);
16932 } else if (win.document.selection && win.document.selection.createRange) {
16933 // no firefox support
16934 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16935 win.document.selection.createRange().pasteHTML(txt);
16937 // no firefox support
16938 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16939 this.execCmd('InsertHTML', txt);
16948 mozKeyPress : function(e){
16950 var c = e.getCharCode(), cmd;
16953 c = String.fromCharCode(c).toLowerCase();
16967 this.cleanUpPaste.defer(100, this);
16975 e.preventDefault();
16983 fixKeys : function(){ // load time branching for fastest keydown performance
16985 return function(e){
16986 var k = e.getKey(), r;
16989 r = this.doc.selection.createRange();
16992 r.pasteHTML('    ');
16999 r = this.doc.selection.createRange();
17001 var target = r.parentElement();
17002 if(!target || target.tagName.toLowerCase() != 'li'){
17004 r.pasteHTML('<br />');
17010 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17011 this.cleanUpPaste.defer(100, this);
17017 }else if(Roo.isOpera){
17018 return function(e){
17019 var k = e.getKey();
17023 this.execCmd('InsertHTML','    ');
17026 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17027 this.cleanUpPaste.defer(100, this);
17032 }else if(Roo.isSafari){
17033 return function(e){
17034 var k = e.getKey();
17038 this.execCmd('InsertText','\t');
17042 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17043 this.cleanUpPaste.defer(100, this);
17051 getAllAncestors: function()
17053 var p = this.getSelectedNode();
17056 a.push(p); // push blank onto stack..
17057 p = this.getParentElement();
17061 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17065 a.push(this.doc.body);
17069 lastSelNode : false,
17072 getSelection : function()
17074 this.assignDocWin();
17075 return Roo.isIE ? this.doc.selection : this.win.getSelection();
17078 getSelectedNode: function()
17080 // this may only work on Gecko!!!
17082 // should we cache this!!!!
17087 var range = this.createRange(this.getSelection()).cloneRange();
17090 var parent = range.parentElement();
17092 var testRange = range.duplicate();
17093 testRange.moveToElementText(parent);
17094 if (testRange.inRange(range)) {
17097 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17100 parent = parent.parentElement;
17105 // is ancestor a text element.
17106 var ac = range.commonAncestorContainer;
17107 if (ac.nodeType == 3) {
17108 ac = ac.parentNode;
17111 var ar = ac.childNodes;
17114 var other_nodes = [];
17115 var has_other_nodes = false;
17116 for (var i=0;i<ar.length;i++) {
17117 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
17120 // fullly contained node.
17122 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17127 // probably selected..
17128 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17129 other_nodes.push(ar[i]);
17133 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
17138 has_other_nodes = true;
17140 if (!nodes.length && other_nodes.length) {
17141 nodes= other_nodes;
17143 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17149 createRange: function(sel)
17151 // this has strange effects when using with
17152 // top toolbar - not sure if it's a great idea.
17153 //this.editor.contentWindow.focus();
17154 if (typeof sel != "undefined") {
17156 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17158 return this.doc.createRange();
17161 return this.doc.createRange();
17164 getParentElement: function()
17167 this.assignDocWin();
17168 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17170 var range = this.createRange(sel);
17173 var p = range.commonAncestorContainer;
17174 while (p.nodeType == 3) { // text node
17185 * Range intersection.. the hard stuff...
17189 * [ -- selected range --- ]
17193 * if end is before start or hits it. fail.
17194 * if start is after end or hits it fail.
17196 * if either hits (but other is outside. - then it's not
17202 // @see http://www.thismuchiknow.co.uk/?p=64.
17203 rangeIntersectsNode : function(range, node)
17205 var nodeRange = node.ownerDocument.createRange();
17207 nodeRange.selectNode(node);
17209 nodeRange.selectNodeContents(node);
17212 var rangeStartRange = range.cloneRange();
17213 rangeStartRange.collapse(true);
17215 var rangeEndRange = range.cloneRange();
17216 rangeEndRange.collapse(false);
17218 var nodeStartRange = nodeRange.cloneRange();
17219 nodeStartRange.collapse(true);
17221 var nodeEndRange = nodeRange.cloneRange();
17222 nodeEndRange.collapse(false);
17224 return rangeStartRange.compareBoundaryPoints(
17225 Range.START_TO_START, nodeEndRange) == -1 &&
17226 rangeEndRange.compareBoundaryPoints(
17227 Range.START_TO_START, nodeStartRange) == 1;
17231 rangeCompareNode : function(range, node)
17233 var nodeRange = node.ownerDocument.createRange();
17235 nodeRange.selectNode(node);
17237 nodeRange.selectNodeContents(node);
17241 range.collapse(true);
17243 nodeRange.collapse(true);
17245 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17246 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
17248 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17250 var nodeIsBefore = ss == 1;
17251 var nodeIsAfter = ee == -1;
17253 if (nodeIsBefore && nodeIsAfter)
17255 if (!nodeIsBefore && nodeIsAfter)
17256 return 1; //right trailed.
17258 if (nodeIsBefore && !nodeIsAfter)
17259 return 2; // left trailed.
17264 // private? - in a new class?
17265 cleanUpPaste : function()
17267 // cleans up the whole document..
17268 Roo.log('cleanuppaste');
17270 this.cleanUpChildren(this.doc.body);
17271 var clean = this.cleanWordChars(this.doc.body.innerHTML);
17272 if (clean != this.doc.body.innerHTML) {
17273 this.doc.body.innerHTML = clean;
17278 cleanWordChars : function(input) {// change the chars to hex code
17279 var he = Roo.HtmlEditorCore;
17281 var output = input;
17282 Roo.each(he.swapCodes, function(sw) {
17283 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17285 output = output.replace(swapper, sw[1]);
17292 cleanUpChildren : function (n)
17294 if (!n.childNodes.length) {
17297 for (var i = n.childNodes.length-1; i > -1 ; i--) {
17298 this.cleanUpChild(n.childNodes[i]);
17305 cleanUpChild : function (node)
17308 //console.log(node);
17309 if (node.nodeName == "#text") {
17310 // clean up silly Windows -- stuff?
17313 if (node.nodeName == "#comment") {
17314 node.parentNode.removeChild(node);
17315 // clean up silly Windows -- stuff?
17318 var lcname = node.tagName.toLowerCase();
17319 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17320 // whitelist of tags..
17322 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17324 node.parentNode.removeChild(node);
17329 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17331 // remove <a name=....> as rendering on yahoo mailer is borked with this.
17332 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17334 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17335 // remove_keep_children = true;
17338 if (remove_keep_children) {
17339 this.cleanUpChildren(node);
17340 // inserts everything just before this node...
17341 while (node.childNodes.length) {
17342 var cn = node.childNodes[0];
17343 node.removeChild(cn);
17344 node.parentNode.insertBefore(cn, node);
17346 node.parentNode.removeChild(node);
17350 if (!node.attributes || !node.attributes.length) {
17351 this.cleanUpChildren(node);
17355 function cleanAttr(n,v)
17358 if (v.match(/^\./) || v.match(/^\//)) {
17361 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17364 if (v.match(/^#/)) {
17367 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17368 node.removeAttribute(n);
17372 var cwhite = this.cwhite;
17373 var cblack = this.cblack;
17375 function cleanStyle(n,v)
17377 if (v.match(/expression/)) { //XSS?? should we even bother..
17378 node.removeAttribute(n);
17382 var parts = v.split(/;/);
17385 Roo.each(parts, function(p) {
17386 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17390 var l = p.split(':').shift().replace(/\s+/g,'');
17391 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17393 if ( cwhite.length && cblack.indexOf(l) > -1) {
17394 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17395 //node.removeAttribute(n);
17399 // only allow 'c whitelisted system attributes'
17400 if ( cwhite.length && cwhite.indexOf(l) < 0) {
17401 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17402 //node.removeAttribute(n);
17412 if (clean.length) {
17413 node.setAttribute(n, clean.join(';'));
17415 node.removeAttribute(n);
17421 for (var i = node.attributes.length-1; i > -1 ; i--) {
17422 var a = node.attributes[i];
17425 if (a.name.toLowerCase().substr(0,2)=='on') {
17426 node.removeAttribute(a.name);
17429 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17430 node.removeAttribute(a.name);
17433 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17434 cleanAttr(a.name,a.value); // fixme..
17437 if (a.name == 'style') {
17438 cleanStyle(a.name,a.value);
17441 /// clean up MS crap..
17442 // tecnically this should be a list of valid class'es..
17445 if (a.name == 'class') {
17446 if (a.value.match(/^Mso/)) {
17447 node.className = '';
17450 if (a.value.match(/body/)) {
17451 node.className = '';
17462 this.cleanUpChildren(node);
17467 * Clean up MS wordisms...
17469 cleanWord : function(node)
17472 var cleanWordChildren = function()
17474 if (!node.childNodes.length) {
17477 for (var i = node.childNodes.length-1; i > -1 ; i--) {
17478 _t.cleanWord(node.childNodes[i]);
17484 this.cleanWord(this.doc.body);
17487 if (node.nodeName == "#text") {
17488 // clean up silly Windows -- stuff?
17491 if (node.nodeName == "#comment") {
17492 node.parentNode.removeChild(node);
17493 // clean up silly Windows -- stuff?
17497 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17498 node.parentNode.removeChild(node);
17502 // remove - but keep children..
17503 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17504 while (node.childNodes.length) {
17505 var cn = node.childNodes[0];
17506 node.removeChild(cn);
17507 node.parentNode.insertBefore(cn, node);
17509 node.parentNode.removeChild(node);
17510 cleanWordChildren();
17514 if (node.className.length) {
17516 var cn = node.className.split(/\W+/);
17518 Roo.each(cn, function(cls) {
17519 if (cls.match(/Mso[a-zA-Z]+/)) {
17524 node.className = cna.length ? cna.join(' ') : '';
17526 node.removeAttribute("class");
17530 if (node.hasAttribute("lang")) {
17531 node.removeAttribute("lang");
17534 if (node.hasAttribute("style")) {
17536 var styles = node.getAttribute("style").split(";");
17538 Roo.each(styles, function(s) {
17539 if (!s.match(/:/)) {
17542 var kv = s.split(":");
17543 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17546 // what ever is left... we allow.
17549 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17550 if (!nstyle.length) {
17551 node.removeAttribute('style');
17555 cleanWordChildren();
17559 domToHTML : function(currentElement, depth, nopadtext) {
17561 depth = depth || 0;
17562 nopadtext = nopadtext || false;
17564 if (!currentElement) {
17565 return this.domToHTML(this.doc.body);
17568 //Roo.log(currentElement);
17570 var allText = false;
17571 var nodeName = currentElement.nodeName;
17572 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17574 if (nodeName == '#text') {
17575 return currentElement.nodeValue;
17580 if (nodeName != 'BODY') {
17583 // Prints the node tagName, such as <A>, <IMG>, etc
17586 for(i = 0; i < currentElement.attributes.length;i++) {
17588 var aname = currentElement.attributes.item(i).name;
17589 if (!currentElement.attributes.item(i).value.length) {
17592 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17595 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17604 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17607 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17612 // Traverse the tree
17614 var currentElementChild = currentElement.childNodes.item(i);
17615 var allText = true;
17616 var innerHTML = '';
17618 while (currentElementChild) {
17619 // Formatting code (indent the tree so it looks nice on the screen)
17620 var nopad = nopadtext;
17621 if (lastnode == 'SPAN') {
17625 if (currentElementChild.nodeName == '#text') {
17626 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17627 if (!nopad && toadd.length > 80) {
17628 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
17630 innerHTML += toadd;
17633 currentElementChild = currentElement.childNodes.item(i);
17639 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
17641 // Recursively traverse the tree structure of the child node
17642 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
17643 lastnode = currentElementChild.nodeName;
17645 currentElementChild=currentElement.childNodes.item(i);
17651 // The remaining code is mostly for formatting the tree
17652 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
17657 ret+= "</"+tagName+">";
17663 applyBlacklists : function()
17665 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
17666 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
17670 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17671 if (b.indexOf(tag) > -1) {
17674 this.white.push(tag);
17678 Roo.each(w, function(tag) {
17679 if (b.indexOf(tag) > -1) {
17682 if (this.white.indexOf(tag) > -1) {
17685 this.white.push(tag);
17690 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17691 if (w.indexOf(tag) > -1) {
17694 this.black.push(tag);
17698 Roo.each(b, function(tag) {
17699 if (w.indexOf(tag) > -1) {
17702 if (this.black.indexOf(tag) > -1) {
17705 this.black.push(tag);
17710 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
17711 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
17715 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17716 if (b.indexOf(tag) > -1) {
17719 this.cwhite.push(tag);
17723 Roo.each(w, function(tag) {
17724 if (b.indexOf(tag) > -1) {
17727 if (this.cwhite.indexOf(tag) > -1) {
17730 this.cwhite.push(tag);
17735 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17736 if (w.indexOf(tag) > -1) {
17739 this.cblack.push(tag);
17743 Roo.each(b, function(tag) {
17744 if (w.indexOf(tag) > -1) {
17747 if (this.cblack.indexOf(tag) > -1) {
17750 this.cblack.push(tag);
17755 // hide stuff that is not compatible
17769 * @event specialkey
17773 * @cfg {String} fieldClass @hide
17776 * @cfg {String} focusClass @hide
17779 * @cfg {String} autoCreate @hide
17782 * @cfg {String} inputType @hide
17785 * @cfg {String} invalidClass @hide
17788 * @cfg {String} invalidText @hide
17791 * @cfg {String} msgFx @hide
17794 * @cfg {String} validateOnBlur @hide
17798 Roo.HtmlEditorCore.white = [
17799 'area', 'br', 'img', 'input', 'hr', 'wbr',
17801 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
17802 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
17803 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
17804 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
17805 'table', 'ul', 'xmp',
17807 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
17810 'dir', 'menu', 'ol', 'ul', 'dl',
17816 Roo.HtmlEditorCore.black = [
17817 // 'embed', 'object', // enable - backend responsiblity to clean thiese
17819 'base', 'basefont', 'bgsound', 'blink', 'body',
17820 'frame', 'frameset', 'head', 'html', 'ilayer',
17821 'iframe', 'layer', 'link', 'meta', 'object',
17822 'script', 'style' ,'title', 'xml' // clean later..
17824 Roo.HtmlEditorCore.clean = [
17825 'script', 'style', 'title', 'xml'
17827 Roo.HtmlEditorCore.remove = [
17832 Roo.HtmlEditorCore.ablack = [
17836 Roo.HtmlEditorCore.aclean = [
17837 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
17841 Roo.HtmlEditorCore.pwhite= [
17842 'http', 'https', 'mailto'
17845 // white listed style attributes.
17846 Roo.HtmlEditorCore.cwhite= [
17847 // 'text-align', /// default is to allow most things..
17853 // black listed style attributes.
17854 Roo.HtmlEditorCore.cblack= [
17855 // 'font-size' -- this can be set by the project
17859 Roo.HtmlEditorCore.swapCodes =[
17878 * @class Roo.bootstrap.HtmlEditor
17879 * @extends Roo.bootstrap.TextArea
17880 * Bootstrap HtmlEditor class
17883 * Create a new HtmlEditor
17884 * @param {Object} config The config object
17887 Roo.bootstrap.HtmlEditor = function(config){
17888 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17889 if (!this.toolbars) {
17890 this.toolbars = [];
17892 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17895 * @event initialize
17896 * Fires when the editor is fully initialized (including the iframe)
17897 * @param {HtmlEditor} this
17902 * Fires when the editor is first receives the focus. Any insertion must wait
17903 * until after this event.
17904 * @param {HtmlEditor} this
17908 * @event beforesync
17909 * Fires before the textarea is updated with content from the editor iframe. Return false
17910 * to cancel the sync.
17911 * @param {HtmlEditor} this
17912 * @param {String} html
17916 * @event beforepush
17917 * Fires before the iframe editor is updated with content from the textarea. Return false
17918 * to cancel the push.
17919 * @param {HtmlEditor} this
17920 * @param {String} html
17925 * Fires when the textarea is updated with content from the editor iframe.
17926 * @param {HtmlEditor} this
17927 * @param {String} html
17932 * Fires when the iframe editor is updated with content from the textarea.
17933 * @param {HtmlEditor} this
17934 * @param {String} html
17938 * @event editmodechange
17939 * Fires when the editor switches edit modes
17940 * @param {HtmlEditor} this
17941 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17943 editmodechange: true,
17945 * @event editorevent
17946 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17947 * @param {HtmlEditor} this
17951 * @event firstfocus
17952 * Fires when on first focus - needed by toolbars..
17953 * @param {HtmlEditor} this
17958 * Auto save the htmlEditor value as a file into Events
17959 * @param {HtmlEditor} this
17963 * @event savedpreview
17964 * preview the saved version of htmlEditor
17965 * @param {HtmlEditor} this
17972 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
17976 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17981 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
17986 * @cfg {Number} height (in pixels)
17990 * @cfg {Number} width (in pixels)
17995 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17998 stylesheets: false,
18003 // private properties
18004 validationEvent : false,
18006 initialized : false,
18009 onFocus : Roo.emptyFn,
18011 hideMode:'offsets',
18014 tbContainer : false,
18016 toolbarContainer :function() {
18017 return this.wrap.select('.x-html-editor-tb',true).first();
18021 * Protected method that will not generally be called directly. It
18022 * is called when the editor creates its toolbar. Override this method if you need to
18023 * add custom toolbar buttons.
18024 * @param {HtmlEditor} editor
18026 createToolbar : function(){
18028 Roo.log("create toolbars");
18030 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18031 this.toolbars[0].render(this.toolbarContainer());
18035 // if (!editor.toolbars || !editor.toolbars.length) {
18036 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18039 // for (var i =0 ; i < editor.toolbars.length;i++) {
18040 // editor.toolbars[i] = Roo.factory(
18041 // typeof(editor.toolbars[i]) == 'string' ?
18042 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
18043 // Roo.bootstrap.HtmlEditor);
18044 // editor.toolbars[i].init(editor);
18050 onRender : function(ct, position)
18052 // Roo.log("Call onRender: " + this.xtype);
18054 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18056 this.wrap = this.inputEl().wrap({
18057 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18060 this.editorcore.onRender(ct, position);
18062 if (this.resizable) {
18063 this.resizeEl = new Roo.Resizable(this.wrap, {
18067 minHeight : this.height,
18068 height: this.height,
18069 handles : this.resizable,
18072 resize : function(r, w, h) {
18073 _t.onResize(w,h); // -something
18079 this.createToolbar(this);
18082 if(!this.width && this.resizable){
18083 this.setSize(this.wrap.getSize());
18085 if (this.resizeEl) {
18086 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18087 // should trigger onReize..
18093 onResize : function(w, h)
18095 Roo.log('resize: ' +w + ',' + h );
18096 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18100 if(this.inputEl() ){
18101 if(typeof w == 'number'){
18102 var aw = w - this.wrap.getFrameWidth('lr');
18103 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18106 if(typeof h == 'number'){
18107 var tbh = -11; // fixme it needs to tool bar size!
18108 for (var i =0; i < this.toolbars.length;i++) {
18109 // fixme - ask toolbars for heights?
18110 tbh += this.toolbars[i].el.getHeight();
18111 //if (this.toolbars[i].footer) {
18112 // tbh += this.toolbars[i].footer.el.getHeight();
18120 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18121 ah -= 5; // knock a few pixes off for look..
18122 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18126 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18127 this.editorcore.onResize(ew,eh);
18132 * Toggles the editor between standard and source edit mode.
18133 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18135 toggleSourceEdit : function(sourceEditMode)
18137 this.editorcore.toggleSourceEdit(sourceEditMode);
18139 if(this.editorcore.sourceEditMode){
18140 Roo.log('editor - showing textarea');
18143 // Roo.log(this.syncValue());
18145 this.inputEl().removeClass(['hide', 'x-hidden']);
18146 this.inputEl().dom.removeAttribute('tabIndex');
18147 this.inputEl().focus();
18149 Roo.log('editor - hiding textarea');
18151 // Roo.log(this.pushValue());
18154 this.inputEl().addClass(['hide', 'x-hidden']);
18155 this.inputEl().dom.setAttribute('tabIndex', -1);
18156 //this.deferFocus();
18159 if(this.resizable){
18160 this.setSize(this.wrap.getSize());
18163 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18166 // private (for BoxComponent)
18167 adjustSize : Roo.BoxComponent.prototype.adjustSize,
18169 // private (for BoxComponent)
18170 getResizeEl : function(){
18174 // private (for BoxComponent)
18175 getPositionEl : function(){
18180 initEvents : function(){
18181 this.originalValue = this.getValue();
18185 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18188 // markInvalid : Roo.emptyFn,
18190 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18193 // clearInvalid : Roo.emptyFn,
18195 setValue : function(v){
18196 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18197 this.editorcore.pushValue();
18202 deferFocus : function(){
18203 this.focus.defer(10, this);
18207 focus : function(){
18208 this.editorcore.focus();
18214 onDestroy : function(){
18220 for (var i =0; i < this.toolbars.length;i++) {
18221 // fixme - ask toolbars for heights?
18222 this.toolbars[i].onDestroy();
18225 this.wrap.dom.innerHTML = '';
18226 this.wrap.remove();
18231 onFirstFocus : function(){
18232 //Roo.log("onFirstFocus");
18233 this.editorcore.onFirstFocus();
18234 for (var i =0; i < this.toolbars.length;i++) {
18235 this.toolbars[i].onFirstFocus();
18241 syncValue : function()
18243 this.editorcore.syncValue();
18246 pushValue : function()
18248 this.editorcore.pushValue();
18252 // hide stuff that is not compatible
18266 * @event specialkey
18270 * @cfg {String} fieldClass @hide
18273 * @cfg {String} focusClass @hide
18276 * @cfg {String} autoCreate @hide
18279 * @cfg {String} inputType @hide
18282 * @cfg {String} invalidClass @hide
18285 * @cfg {String} invalidText @hide
18288 * @cfg {String} msgFx @hide
18291 * @cfg {String} validateOnBlur @hide
18300 Roo.namespace('Roo.bootstrap.htmleditor');
18302 * @class Roo.bootstrap.HtmlEditorToolbar1
18307 new Roo.bootstrap.HtmlEditor({
18310 new Roo.bootstrap.HtmlEditorToolbar1({
18311 disable : { fonts: 1 , format: 1, ..., ... , ...],
18317 * @cfg {Object} disable List of elements to disable..
18318 * @cfg {Array} btns List of additional buttons.
18322 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18325 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18328 Roo.apply(this, config);
18330 // default disabled, based on 'good practice'..
18331 this.disable = this.disable || {};
18332 Roo.applyIf(this.disable, {
18335 specialElements : true
18337 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18339 this.editor = config.editor;
18340 this.editorcore = config.editor.editorcore;
18342 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18344 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18345 // dont call parent... till later.
18347 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
18352 editorcore : false,
18357 "h1","h2","h3","h4","h5","h6",
18359 "abbr", "acronym", "address", "cite", "samp", "var",
18363 onRender : function(ct, position)
18365 // Roo.log("Call onRender: " + this.xtype);
18367 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18369 this.el.dom.style.marginBottom = '0';
18371 var editorcore = this.editorcore;
18372 var editor= this.editor;
18375 var btn = function(id,cmd , toggle, handler){
18377 var event = toggle ? 'toggle' : 'click';
18382 xns: Roo.bootstrap,
18385 enableToggle:toggle !== false,
18387 pressed : toggle ? false : null,
18390 a.listeners[toggle ? 'toggle' : 'click'] = function() {
18391 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
18400 xns: Roo.bootstrap,
18401 glyphicon : 'font',
18405 xns: Roo.bootstrap,
18409 Roo.each(this.formats, function(f) {
18410 style.menu.items.push({
18412 xns: Roo.bootstrap,
18413 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18418 editorcore.insertTag(this.tagname);
18425 children.push(style);
18428 btn('bold',false,true);
18429 btn('italic',false,true);
18430 btn('align-left', 'justifyleft',true);
18431 btn('align-center', 'justifycenter',true);
18432 btn('align-right' , 'justifyright',true);
18433 btn('link', false, false, function(btn) {
18434 //Roo.log("create link?");
18435 var url = prompt(this.createLinkText, this.defaultLinkValue);
18436 if(url && url != 'http:/'+'/'){
18437 this.editorcore.relayCmd('createlink', url);
18440 btn('list','insertunorderedlist',true);
18441 btn('pencil', false,true, function(btn){
18444 this.toggleSourceEdit(btn.pressed);
18450 xns: Roo.bootstrap,
18455 xns: Roo.bootstrap,
18460 cog.menu.items.push({
18462 xns: Roo.bootstrap,
18463 html : Clean styles,
18468 editorcore.insertTag(this.tagname);
18477 this.xtype = 'NavSimplebar';
18479 for(var i=0;i< children.length;i++) {
18481 this.buttons.add(this.addxtypeChild(children[i]));
18485 editor.on('editorevent', this.updateToolbar, this);
18487 onBtnClick : function(id)
18489 this.editorcore.relayCmd(id);
18490 this.editorcore.focus();
18494 * Protected method that will not generally be called directly. It triggers
18495 * a toolbar update by reading the markup state of the current selection in the editor.
18497 updateToolbar: function(){
18499 if(!this.editorcore.activated){
18500 this.editor.onFirstFocus(); // is this neeed?
18504 var btns = this.buttons;
18505 var doc = this.editorcore.doc;
18506 btns.get('bold').setActive(doc.queryCommandState('bold'));
18507 btns.get('italic').setActive(doc.queryCommandState('italic'));
18508 //btns.get('underline').setActive(doc.queryCommandState('underline'));
18510 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18511 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18512 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18514 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18515 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18518 var ans = this.editorcore.getAllAncestors();
18519 if (this.formatCombo) {
18522 var store = this.formatCombo.store;
18523 this.formatCombo.setValue("");
18524 for (var i =0; i < ans.length;i++) {
18525 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18527 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18535 // hides menus... - so this cant be on a menu...
18536 Roo.bootstrap.MenuMgr.hideAll();
18538 Roo.bootstrap.MenuMgr.hideAll();
18539 //this.editorsyncValue();
18541 onFirstFocus: function() {
18542 this.buttons.each(function(item){
18546 toggleSourceEdit : function(sourceEditMode){
18549 if(sourceEditMode){
18550 Roo.log("disabling buttons");
18551 this.buttons.each( function(item){
18552 if(item.cmd != 'pencil'){
18558 Roo.log("enabling buttons");
18559 if(this.editorcore.initialized){
18560 this.buttons.each( function(item){
18566 Roo.log("calling toggole on editor");
18567 // tell the editor that it's been pressed..
18568 this.editor.toggleSourceEdit(sourceEditMode);
18578 * @class Roo.bootstrap.Table.AbstractSelectionModel
18579 * @extends Roo.util.Observable
18580 * Abstract base class for grid SelectionModels. It provides the interface that should be
18581 * implemented by descendant classes. This class should not be directly instantiated.
18584 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18585 this.locked = false;
18586 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18590 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
18591 /** @ignore Called by the grid automatically. Do not call directly. */
18592 init : function(grid){
18598 * Locks the selections.
18601 this.locked = true;
18605 * Unlocks the selections.
18607 unlock : function(){
18608 this.locked = false;
18612 * Returns true if the selections are locked.
18613 * @return {Boolean}
18615 isLocked : function(){
18616 return this.locked;
18620 * @extends Roo.bootstrap.Table.AbstractSelectionModel
18621 * @class Roo.bootstrap.Table.RowSelectionModel
18622 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18623 * It supports multiple selections and keyboard selection/navigation.
18625 * @param {Object} config
18628 Roo.bootstrap.Table.RowSelectionModel = function(config){
18629 Roo.apply(this, config);
18630 this.selections = new Roo.util.MixedCollection(false, function(o){
18635 this.lastActive = false;
18639 * @event selectionchange
18640 * Fires when the selection changes
18641 * @param {SelectionModel} this
18643 "selectionchange" : true,
18645 * @event afterselectionchange
18646 * Fires after the selection changes (eg. by key press or clicking)
18647 * @param {SelectionModel} this
18649 "afterselectionchange" : true,
18651 * @event beforerowselect
18652 * Fires when a row is selected being selected, return false to cancel.
18653 * @param {SelectionModel} this
18654 * @param {Number} rowIndex The selected index
18655 * @param {Boolean} keepExisting False if other selections will be cleared
18657 "beforerowselect" : true,
18660 * Fires when a row is selected.
18661 * @param {SelectionModel} this
18662 * @param {Number} rowIndex The selected index
18663 * @param {Roo.data.Record} r The record
18665 "rowselect" : true,
18667 * @event rowdeselect
18668 * Fires when a row is deselected.
18669 * @param {SelectionModel} this
18670 * @param {Number} rowIndex The selected index
18672 "rowdeselect" : true
18674 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18675 this.locked = false;
18678 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
18680 * @cfg {Boolean} singleSelect
18681 * True to allow selection of only one row at a time (defaults to false)
18683 singleSelect : false,
18686 initEvents : function(){
18688 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18689 this.grid.on("mousedown", this.handleMouseDown, this);
18690 }else{ // allow click to work like normal
18691 this.grid.on("rowclick", this.handleDragableRowClick, this);
18694 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18695 "up" : function(e){
18697 this.selectPrevious(e.shiftKey);
18698 }else if(this.last !== false && this.lastActive !== false){
18699 var last = this.last;
18700 this.selectRange(this.last, this.lastActive-1);
18701 this.grid.getView().focusRow(this.lastActive);
18702 if(last !== false){
18706 this.selectFirstRow();
18708 this.fireEvent("afterselectionchange", this);
18710 "down" : function(e){
18712 this.selectNext(e.shiftKey);
18713 }else if(this.last !== false && this.lastActive !== false){
18714 var last = this.last;
18715 this.selectRange(this.last, this.lastActive+1);
18716 this.grid.getView().focusRow(this.lastActive);
18717 if(last !== false){
18721 this.selectFirstRow();
18723 this.fireEvent("afterselectionchange", this);
18728 var view = this.grid.view;
18729 view.on("refresh", this.onRefresh, this);
18730 view.on("rowupdated", this.onRowUpdated, this);
18731 view.on("rowremoved", this.onRemove, this);
18735 onRefresh : function(){
18736 var ds = this.grid.dataSource, i, v = this.grid.view;
18737 var s = this.selections;
18738 s.each(function(r){
18739 if((i = ds.indexOfId(r.id)) != -1){
18748 onRemove : function(v, index, r){
18749 this.selections.remove(r);
18753 onRowUpdated : function(v, index, r){
18754 if(this.isSelected(r)){
18755 v.onRowSelect(index);
18761 * @param {Array} records The records to select
18762 * @param {Boolean} keepExisting (optional) True to keep existing selections
18764 selectRecords : function(records, keepExisting){
18766 this.clearSelections();
18768 var ds = this.grid.dataSource;
18769 for(var i = 0, len = records.length; i < len; i++){
18770 this.selectRow(ds.indexOf(records[i]), true);
18775 * Gets the number of selected rows.
18778 getCount : function(){
18779 return this.selections.length;
18783 * Selects the first row in the grid.
18785 selectFirstRow : function(){
18790 * Select the last row.
18791 * @param {Boolean} keepExisting (optional) True to keep existing selections
18793 selectLastRow : function(keepExisting){
18794 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18798 * Selects the row immediately following the last selected row.
18799 * @param {Boolean} keepExisting (optional) True to keep existing selections
18801 selectNext : function(keepExisting){
18802 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18803 this.selectRow(this.last+1, keepExisting);
18804 this.grid.getView().focusRow(this.last);
18809 * Selects the row that precedes the last selected row.
18810 * @param {Boolean} keepExisting (optional) True to keep existing selections
18812 selectPrevious : function(keepExisting){
18814 this.selectRow(this.last-1, keepExisting);
18815 this.grid.getView().focusRow(this.last);
18820 * Returns the selected records
18821 * @return {Array} Array of selected records
18823 getSelections : function(){
18824 return [].concat(this.selections.items);
18828 * Returns the first selected record.
18831 getSelected : function(){
18832 return this.selections.itemAt(0);
18837 * Clears all selections.
18839 clearSelections : function(fast){
18840 if(this.locked) return;
18842 var ds = this.grid.dataSource;
18843 var s = this.selections;
18844 s.each(function(r){
18845 this.deselectRow(ds.indexOfId(r.id));
18849 this.selections.clear();
18856 * Selects all rows.
18858 selectAll : function(){
18859 if(this.locked) return;
18860 this.selections.clear();
18861 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18862 this.selectRow(i, true);
18867 * Returns True if there is a selection.
18868 * @return {Boolean}
18870 hasSelection : function(){
18871 return this.selections.length > 0;
18875 * Returns True if the specified row is selected.
18876 * @param {Number/Record} record The record or index of the record to check
18877 * @return {Boolean}
18879 isSelected : function(index){
18880 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18881 return (r && this.selections.key(r.id) ? true : false);
18885 * Returns True if the specified record id is selected.
18886 * @param {String} id The id of record to check
18887 * @return {Boolean}
18889 isIdSelected : function(id){
18890 return (this.selections.key(id) ? true : false);
18894 handleMouseDown : function(e, t){
18895 var view = this.grid.getView(), rowIndex;
18896 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18899 if(e.shiftKey && this.last !== false){
18900 var last = this.last;
18901 this.selectRange(last, rowIndex, e.ctrlKey);
18902 this.last = last; // reset the last
18903 view.focusRow(rowIndex);
18905 var isSelected = this.isSelected(rowIndex);
18906 if(e.button !== 0 && isSelected){
18907 view.focusRow(rowIndex);
18908 }else if(e.ctrlKey && isSelected){
18909 this.deselectRow(rowIndex);
18910 }else if(!isSelected){
18911 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18912 view.focusRow(rowIndex);
18915 this.fireEvent("afterselectionchange", this);
18918 handleDragableRowClick : function(grid, rowIndex, e)
18920 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18921 this.selectRow(rowIndex, false);
18922 grid.view.focusRow(rowIndex);
18923 this.fireEvent("afterselectionchange", this);
18928 * Selects multiple rows.
18929 * @param {Array} rows Array of the indexes of the row to select
18930 * @param {Boolean} keepExisting (optional) True to keep existing selections
18932 selectRows : function(rows, keepExisting){
18934 this.clearSelections();
18936 for(var i = 0, len = rows.length; i < len; i++){
18937 this.selectRow(rows[i], true);
18942 * Selects a range of rows. All rows in between startRow and endRow are also selected.
18943 * @param {Number} startRow The index of the first row in the range
18944 * @param {Number} endRow The index of the last row in the range
18945 * @param {Boolean} keepExisting (optional) True to retain existing selections
18947 selectRange : function(startRow, endRow, keepExisting){
18948 if(this.locked) return;
18950 this.clearSelections();
18952 if(startRow <= endRow){
18953 for(var i = startRow; i <= endRow; i++){
18954 this.selectRow(i, true);
18957 for(var i = startRow; i >= endRow; i--){
18958 this.selectRow(i, true);
18964 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18965 * @param {Number} startRow The index of the first row in the range
18966 * @param {Number} endRow The index of the last row in the range
18968 deselectRange : function(startRow, endRow, preventViewNotify){
18969 if(this.locked) return;
18970 for(var i = startRow; i <= endRow; i++){
18971 this.deselectRow(i, preventViewNotify);
18977 * @param {Number} row The index of the row to select
18978 * @param {Boolean} keepExisting (optional) True to keep existing selections
18980 selectRow : function(index, keepExisting, preventViewNotify){
18981 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18982 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18983 if(!keepExisting || this.singleSelect){
18984 this.clearSelections();
18986 var r = this.grid.dataSource.getAt(index);
18987 this.selections.add(r);
18988 this.last = this.lastActive = index;
18989 if(!preventViewNotify){
18990 this.grid.getView().onRowSelect(index);
18992 this.fireEvent("rowselect", this, index, r);
18993 this.fireEvent("selectionchange", this);
18999 * @param {Number} row The index of the row to deselect
19001 deselectRow : function(index, preventViewNotify){
19002 if(this.locked) return;
19003 if(this.last == index){
19006 if(this.lastActive == index){
19007 this.lastActive = false;
19009 var r = this.grid.dataSource.getAt(index);
19010 this.selections.remove(r);
19011 if(!preventViewNotify){
19012 this.grid.getView().onRowDeselect(index);
19014 this.fireEvent("rowdeselect", this, index);
19015 this.fireEvent("selectionchange", this);
19019 restoreLast : function(){
19021 this.last = this._last;
19026 acceptsNav : function(row, col, cm){
19027 return !cm.isHidden(col) && cm.isCellEditable(col, row);
19031 onEditorKey : function(field, e){
19032 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19037 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19039 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19041 }else if(k == e.ENTER && !e.ctrlKey){
19045 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19047 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19049 }else if(k == e.ESC){
19053 g.startEditing(newCell[0], newCell[1]);
19058 * Ext JS Library 1.1.1
19059 * Copyright(c) 2006-2007, Ext JS, LLC.
19061 * Originally Released Under LGPL - original licence link has changed is not relivant.
19064 * <script type="text/javascript">
19068 * @class Roo.bootstrap.PagingToolbar
19070 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19072 * Create a new PagingToolbar
19073 * @param {Object} config The config object
19075 Roo.bootstrap.PagingToolbar = function(config)
19077 // old args format still supported... - xtype is prefered..
19078 // created from xtype...
19079 var ds = config.dataSource;
19080 this.toolbarItems = [];
19081 if (config.items) {
19082 this.toolbarItems = config.items;
19083 // config.items = [];
19086 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19093 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19097 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19099 * @cfg {Roo.data.Store} dataSource
19100 * The underlying data store providing the paged data
19103 * @cfg {String/HTMLElement/Element} container
19104 * container The id or element that will contain the toolbar
19107 * @cfg {Boolean} displayInfo
19108 * True to display the displayMsg (defaults to false)
19111 * @cfg {Number} pageSize
19112 * The number of records to display per page (defaults to 20)
19116 * @cfg {String} displayMsg
19117 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19119 displayMsg : 'Displaying {0} - {1} of {2}',
19121 * @cfg {String} emptyMsg
19122 * The message to display when no records are found (defaults to "No data to display")
19124 emptyMsg : 'No data to display',
19126 * Customizable piece of the default paging text (defaults to "Page")
19129 beforePageText : "Page",
19131 * Customizable piece of the default paging text (defaults to "of %0")
19134 afterPageText : "of {0}",
19136 * Customizable piece of the default paging text (defaults to "First Page")
19139 firstText : "First Page",
19141 * Customizable piece of the default paging text (defaults to "Previous Page")
19144 prevText : "Previous Page",
19146 * Customizable piece of the default paging text (defaults to "Next Page")
19149 nextText : "Next Page",
19151 * Customizable piece of the default paging text (defaults to "Last Page")
19154 lastText : "Last Page",
19156 * Customizable piece of the default paging text (defaults to "Refresh")
19159 refreshText : "Refresh",
19163 onRender : function(ct, position)
19165 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19166 this.navgroup.parentId = this.id;
19167 this.navgroup.onRender(this.el, null);
19168 // add the buttons to the navgroup
19170 if(this.displayInfo){
19171 Roo.log(this.el.select('ul.navbar-nav',true).first());
19172 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19173 this.displayEl = this.el.select('.x-paging-info', true).first();
19174 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19175 // this.displayEl = navel.el.select('span',true).first();
19181 Roo.each(_this.buttons, function(e){
19182 Roo.factory(e).onRender(_this.el, null);
19186 Roo.each(_this.toolbarItems, function(e) {
19187 _this.navgroup.addItem(e);
19190 this.first = this.navgroup.addItem({
19191 tooltip: this.firstText,
19193 icon : 'fa fa-backward',
19195 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19198 this.prev = this.navgroup.addItem({
19199 tooltip: this.prevText,
19201 icon : 'fa fa-step-backward',
19203 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
19205 //this.addSeparator();
19208 var field = this.navgroup.addItem( {
19210 cls : 'x-paging-position',
19212 html : this.beforePageText +
19213 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19214 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
19217 this.field = field.el.select('input', true).first();
19218 this.field.on("keydown", this.onPagingKeydown, this);
19219 this.field.on("focus", function(){this.dom.select();});
19222 this.afterTextEl = field.el.select('.x-paging-after',true).first();
19223 //this.field.setHeight(18);
19224 //this.addSeparator();
19225 this.next = this.navgroup.addItem({
19226 tooltip: this.nextText,
19228 html : ' <i class="fa fa-step-forward">',
19230 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
19232 this.last = this.navgroup.addItem({
19233 tooltip: this.lastText,
19234 icon : 'fa fa-forward',
19237 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
19239 //this.addSeparator();
19240 this.loading = this.navgroup.addItem({
19241 tooltip: this.refreshText,
19242 icon: 'fa fa-refresh',
19244 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19250 updateInfo : function(){
19251 if(this.displayEl){
19252 var count = this.ds.getCount();
19253 var msg = count == 0 ?
19257 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
19259 this.displayEl.update(msg);
19264 onLoad : function(ds, r, o){
19265 this.cursor = o.params ? o.params.start : 0;
19266 var d = this.getPageData(),
19270 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19271 this.field.dom.value = ap;
19272 this.first.setDisabled(ap == 1);
19273 this.prev.setDisabled(ap == 1);
19274 this.next.setDisabled(ap == ps);
19275 this.last.setDisabled(ap == ps);
19276 this.loading.enable();
19281 getPageData : function(){
19282 var total = this.ds.getTotalCount();
19285 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19286 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19291 onLoadError : function(){
19292 this.loading.enable();
19296 onPagingKeydown : function(e){
19297 var k = e.getKey();
19298 var d = this.getPageData();
19300 var v = this.field.dom.value, pageNum;
19301 if(!v || isNaN(pageNum = parseInt(v, 10))){
19302 this.field.dom.value = d.activePage;
19305 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19306 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19309 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))
19311 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19312 this.field.dom.value = pageNum;
19313 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19316 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19318 var v = this.field.dom.value, pageNum;
19319 var increment = (e.shiftKey) ? 10 : 1;
19320 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19322 if(!v || isNaN(pageNum = parseInt(v, 10))) {
19323 this.field.dom.value = d.activePage;
19326 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19328 this.field.dom.value = parseInt(v, 10) + increment;
19329 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19330 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19337 beforeLoad : function(){
19339 this.loading.disable();
19344 onClick : function(which){
19351 ds.load({params:{start: 0, limit: this.pageSize}});
19354 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19357 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19360 var total = ds.getTotalCount();
19361 var extra = total % this.pageSize;
19362 var lastStart = extra ? (total - extra) : total-this.pageSize;
19363 ds.load({params:{start: lastStart, limit: this.pageSize}});
19366 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19372 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19373 * @param {Roo.data.Store} store The data store to unbind
19375 unbind : function(ds){
19376 ds.un("beforeload", this.beforeLoad, this);
19377 ds.un("load", this.onLoad, this);
19378 ds.un("loadexception", this.onLoadError, this);
19379 ds.un("remove", this.updateInfo, this);
19380 ds.un("add", this.updateInfo, this);
19381 this.ds = undefined;
19385 * Binds the paging toolbar to the specified {@link Roo.data.Store}
19386 * @param {Roo.data.Store} store The data store to bind
19388 bind : function(ds){
19389 ds.on("beforeload", this.beforeLoad, this);
19390 ds.on("load", this.onLoad, this);
19391 ds.on("loadexception", this.onLoadError, this);
19392 ds.on("remove", this.updateInfo, this);
19393 ds.on("add", this.updateInfo, this);
19404 * @class Roo.bootstrap.MessageBar
19405 * @extends Roo.bootstrap.Component
19406 * Bootstrap MessageBar class
19407 * @cfg {String} html contents of the MessageBar
19408 * @cfg {String} weight (info | success | warning | danger) default info
19409 * @cfg {String} beforeClass insert the bar before the given class
19410 * @cfg {Boolean} closable (true | false) default false
19411 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19414 * Create a new Element
19415 * @param {Object} config The config object
19418 Roo.bootstrap.MessageBar = function(config){
19419 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19422 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
19428 beforeClass: 'bootstrap-sticky-wrap',
19430 getAutoCreate : function(){
19434 cls: 'alert alert-dismissable alert-' + this.weight,
19439 html: this.html || ''
19445 cfg.cls += ' alert-messages-fixed';
19459 onRender : function(ct, position)
19461 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19464 var cfg = Roo.apply({}, this.getAutoCreate());
19468 cfg.cls += ' ' + this.cls;
19471 cfg.style = this.style;
19473 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19475 this.el.setVisibilityMode(Roo.Element.DISPLAY);
19478 this.el.select('>button.close').on('click', this.hide, this);
19484 if (!this.rendered) {
19490 this.fireEvent('show', this);
19496 if (!this.rendered) {
19502 this.fireEvent('hide', this);
19505 update : function()
19507 // var e = this.el.dom.firstChild;
19509 // if(this.closable){
19510 // e = e.nextSibling;
19513 // e.data = this.html || '';
19515 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19531 * @class Roo.bootstrap.Graph
19532 * @extends Roo.bootstrap.Component
19533 * Bootstrap Graph class
19537 @cfg {String} graphtype bar | vbar | pie
19538 @cfg {number} g_x coodinator | centre x (pie)
19539 @cfg {number} g_y coodinator | centre y (pie)
19540 @cfg {number} g_r radius (pie)
19541 @cfg {number} g_height height of the chart (respected by all elements in the set)
19542 @cfg {number} g_width width of the chart (respected by all elements in the set)
19543 @cfg {Object} title The title of the chart
19546 -opts (object) options for the chart
19548 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19549 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19551 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.
19552 o stacked (boolean) whether or not to tread values as in a stacked bar chart
19554 o stretch (boolean)
19556 -opts (object) options for the pie
19559 o startAngle (number)
19560 o endAngle (number)
19564 * Create a new Input
19565 * @param {Object} config The config object
19568 Roo.bootstrap.Graph = function(config){
19569 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19575 * The img click event for the img.
19576 * @param {Roo.EventObject} e
19582 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
19593 //g_colors: this.colors,
19600 getAutoCreate : function(){
19611 onRender : function(ct,position){
19612 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19613 this.raphael = Raphael(this.el.dom);
19615 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19616 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19617 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19618 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19620 r.text(160, 10, "Single Series Chart").attr(txtattr);
19621 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19622 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19623 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19625 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19626 r.barchart(330, 10, 300, 220, data1);
19627 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19628 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19631 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19632 // r.barchart(30, 30, 560, 250, xdata, {
19633 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19634 // axis : "0 0 1 1",
19635 // axisxlabels : xdata
19636 // //yvalues : cols,
19639 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19641 // this.load(null,xdata,{
19642 // axis : "0 0 1 1",
19643 // axisxlabels : xdata
19648 load : function(graphtype,xdata,opts){
19649 this.raphael.clear();
19651 graphtype = this.graphtype;
19656 var r = this.raphael,
19657 fin = function () {
19658 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19660 fout = function () {
19661 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19663 pfin = function() {
19664 this.sector.stop();
19665 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19668 this.label[0].stop();
19669 this.label[0].attr({ r: 7.5 });
19670 this.label[1].attr({ "font-weight": 800 });
19673 pfout = function() {
19674 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19677 this.label[0].animate({ r: 5 }, 500, "bounce");
19678 this.label[1].attr({ "font-weight": 400 });
19684 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19687 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19690 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
19691 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19693 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19700 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19705 setTitle: function(o)
19710 initEvents: function() {
19713 this.el.on('click', this.onClick, this);
19717 onClick : function(e)
19719 Roo.log('img onclick');
19720 this.fireEvent('click', this, e);
19732 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19735 * @class Roo.bootstrap.dash.NumberBox
19736 * @extends Roo.bootstrap.Component
19737 * Bootstrap NumberBox class
19738 * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19739 * @cfg {String} headline Box headline
19740 * @cfg {String} content Box content
19741 * @cfg {String} icon Box icon
19742 * @cfg {String} footer Footer text
19743 * @cfg {String} fhref Footer href
19746 * Create a new NumberBox
19747 * @param {Object} config The config object
19751 Roo.bootstrap.dash.NumberBox = function(config){
19752 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19756 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
19766 getAutoCreate : function(){
19770 cls : 'small-box bg-' + this.bgcolor,
19778 cls : 'roo-headline',
19779 html : this.headline
19783 cls : 'roo-content',
19784 html : this.content
19798 cls : 'ion ' + this.icon
19807 cls : 'small-box-footer',
19808 href : this.fhref || '#',
19812 cfg.cn.push(footer);
19819 onRender : function(ct,position){
19820 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19827 setHeadline: function (value)
19829 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19832 setFooter: function (value, href)
19834 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19837 this.el.select('a.small-box-footer',true).first().attr('href', href);
19842 setContent: function (value)
19844 this.el.select('.roo-content',true).first().dom.innerHTML = value;
19847 initEvents: function()
19861 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19864 * @class Roo.bootstrap.dash.TabBox
19865 * @extends Roo.bootstrap.Component
19866 * Bootstrap TabBox class
19867 * @cfg {String} title Title of the TabBox
19868 * @cfg {String} icon Icon of the TabBox
19869 * @cfg {Boolean} showtabs (true|false) show the tabs default true
19872 * Create a new TabBox
19873 * @param {Object} config The config object
19877 Roo.bootstrap.dash.TabBox = function(config){
19878 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19883 * When a pane is added
19884 * @param {Roo.bootstrap.dash.TabPane} pane
19891 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
19897 getChildContainer : function()
19899 return this.el.select('.tab-content', true).first();
19902 getAutoCreate : function(){
19906 cls: 'pull-left header',
19914 cls: 'fa ' + this.icon
19921 cls: 'nav-tabs-custom',
19925 cls: 'nav nav-tabs pull-right',
19932 cls: 'tab-content no-padding',
19940 initEvents : function()
19942 //Roo.log('add add pane handler');
19943 this.on('addpane', this.onAddPane, this);
19946 * Updates the box title
19947 * @param {String} html to set the title to.
19949 setTitle : function(value)
19951 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19953 onAddPane : function(pane)
19955 //Roo.log('addpane');
19957 // tabs are rendere left to right..
19958 if(!this.showtabs){
19962 var ctr = this.el.select('.nav-tabs', true).first();
19965 var existing = ctr.select('.nav-tab',true);
19966 var qty = existing.getCount();;
19969 var tab = ctr.createChild({
19971 cls : 'nav-tab' + (qty ? '' : ' active'),
19979 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19982 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19984 pane.el.addClass('active');
19989 onTabClick : function(ev,un,ob,pane)
19991 //Roo.log('tab - prev default');
19992 ev.preventDefault();
19995 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19996 pane.tab.addClass('active');
19997 //Roo.log(pane.title);
19998 this.getChildContainer().select('.tab-pane',true).removeClass('active');
19999 // technically we should have a deactivate event.. but maybe add later.
20000 // and it should not de-activate the selected tab...
20002 pane.el.addClass('active');
20003 pane.fireEvent('activate');
20018 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20020 * @class Roo.bootstrap.TabPane
20021 * @extends Roo.bootstrap.Component
20022 * Bootstrap TabPane class
20023 * @cfg {Boolean} active (false | true) Default false
20024 * @cfg {String} title title of panel
20028 * Create a new TabPane
20029 * @param {Object} config The config object
20032 Roo.bootstrap.dash.TabPane = function(config){
20033 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20039 * When a pane is activated
20040 * @param {Roo.bootstrap.dash.TabPane} pane
20047 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
20052 // the tabBox that this is attached to.
20055 getAutoCreate : function()
20063 cfg.cls += ' active';
20068 initEvents : function()
20070 //Roo.log('trigger add pane handler');
20071 this.parent().fireEvent('addpane', this)
20075 * Updates the tab title
20076 * @param {String} html to set the title to.
20078 setTitle: function(str)
20084 this.tab.select('a', true).first().dom.innerHTML = str;
20101 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20104 * @class Roo.bootstrap.menu.Menu
20105 * @extends Roo.bootstrap.Component
20106 * Bootstrap Menu class - container for Menu
20107 * @cfg {String} html Text of the menu
20108 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20109 * @cfg {String} icon Font awesome icon
20110 * @cfg {String} pos Menu align to (top | bottom) default bottom
20114 * Create a new Menu
20115 * @param {Object} config The config object
20119 Roo.bootstrap.menu.Menu = function(config){
20120 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20124 * @event beforeshow
20125 * Fires before this menu is displayed
20126 * @param {Roo.bootstrap.menu.Menu} this
20130 * @event beforehide
20131 * Fires before this menu is hidden
20132 * @param {Roo.bootstrap.menu.Menu} this
20137 * Fires after this menu is displayed
20138 * @param {Roo.bootstrap.menu.Menu} this
20143 * Fires after this menu is hidden
20144 * @param {Roo.bootstrap.menu.Menu} this
20149 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20150 * @param {Roo.bootstrap.menu.Menu} this
20151 * @param {Roo.EventObject} e
20158 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
20162 weight : 'default',
20167 getChildContainer : function() {
20168 if(this.isSubMenu){
20172 return this.el.select('ul.dropdown-menu', true).first();
20175 getAutoCreate : function()
20180 cls : 'roo-menu-text',
20188 cls : 'fa ' + this.icon
20199 cls : 'dropdown-button btn btn-' + this.weight,
20204 cls : 'dropdown-toggle btn btn-' + this.weight,
20214 cls : 'dropdown-menu'
20220 if(this.pos == 'top'){
20221 cfg.cls += ' dropup';
20224 if(this.isSubMenu){
20227 cls : 'dropdown-menu'
20234 onRender : function(ct, position)
20236 this.isSubMenu = ct.hasClass('dropdown-submenu');
20238 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20241 initEvents : function()
20243 if(this.isSubMenu){
20247 this.hidden = true;
20249 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20250 this.triggerEl.on('click', this.onTriggerPress, this);
20252 this.buttonEl = this.el.select('button.dropdown-button', true).first();
20253 this.buttonEl.on('click', this.onClick, this);
20259 if(this.isSubMenu){
20263 return this.el.select('ul.dropdown-menu', true).first();
20266 onClick : function(e)
20268 this.fireEvent("click", this, e);
20271 onTriggerPress : function(e)
20273 if (this.isVisible()) {
20280 isVisible : function(){
20281 return !this.hidden;
20286 this.fireEvent("beforeshow", this);
20288 this.hidden = false;
20289 this.el.addClass('open');
20291 Roo.get(document).on("mouseup", this.onMouseUp, this);
20293 this.fireEvent("show", this);
20300 this.fireEvent("beforehide", this);
20302 this.hidden = true;
20303 this.el.removeClass('open');
20305 Roo.get(document).un("mouseup", this.onMouseUp);
20307 this.fireEvent("hide", this);
20310 onMouseUp : function()
20324 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20327 * @class Roo.bootstrap.menu.Item
20328 * @extends Roo.bootstrap.Component
20329 * Bootstrap MenuItem class
20330 * @cfg {Boolean} submenu (true | false) default false
20331 * @cfg {String} html text of the item
20332 * @cfg {String} href the link
20333 * @cfg {Boolean} disable (true | false) default false
20334 * @cfg {Boolean} preventDefault (true | false) default true
20335 * @cfg {String} icon Font awesome icon
20336 * @cfg {String} pos Submenu align to (left | right) default right
20340 * Create a new Item
20341 * @param {Object} config The config object
20345 Roo.bootstrap.menu.Item = function(config){
20346 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20350 * Fires when the mouse is hovering over this menu
20351 * @param {Roo.bootstrap.menu.Item} this
20352 * @param {Roo.EventObject} e
20357 * Fires when the mouse exits this menu
20358 * @param {Roo.bootstrap.menu.Item} this
20359 * @param {Roo.EventObject} e
20365 * The raw click event for the entire grid.
20366 * @param {Roo.EventObject} e
20372 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
20377 preventDefault: true,
20382 getAutoCreate : function()
20387 cls : 'roo-menu-item-text',
20395 cls : 'fa ' + this.icon
20404 href : this.href || '#',
20411 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20415 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20417 if(this.pos == 'left'){
20418 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20425 initEvents : function()
20427 this.el.on('mouseover', this.onMouseOver, this);
20428 this.el.on('mouseout', this.onMouseOut, this);
20430 this.el.select('a', true).first().on('click', this.onClick, this);
20434 onClick : function(e)
20436 if(this.preventDefault){
20437 e.preventDefault();
20440 this.fireEvent("click", this, e);
20443 onMouseOver : function(e)
20445 if(this.submenu && this.pos == 'left'){
20446 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20449 this.fireEvent("mouseover", this, e);
20452 onMouseOut : function(e)
20454 this.fireEvent("mouseout", this, e);
20466 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20469 * @class Roo.bootstrap.menu.Separator
20470 * @extends Roo.bootstrap.Component
20471 * Bootstrap Separator class
20474 * Create a new Separator
20475 * @param {Object} config The config object
20479 Roo.bootstrap.menu.Separator = function(config){
20480 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20483 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
20485 getAutoCreate : function(){