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
1888 * @cfg {Boolean} isContainer (true | false) default false
1892 * Create a new MenuItem
1893 * @param {Object} config The config object
1897 Roo.bootstrap.MenuItem = function(config){
1898 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1903 * The raw click event for the entire grid.
1904 * @param {Roo.EventObject} e
1910 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
1914 preventDefault: true,
1915 isContainer : false,
1917 getAutoCreate : function(){
1919 if(this.isContainer){
1922 cls: 'dropdown-menu-item'
1928 cls: 'dropdown-menu-item',
1937 if (this.parent().type == 'treeview') {
1938 cfg.cls = 'treeview-menu';
1941 cfg.cn[0].href = this.href || cfg.cn[0].href ;
1942 cfg.cn[0].html = this.html || cfg.cn[0].html ;
1946 initEvents: function() {
1948 //this.el.select('a').on('click', this.onClick, this);
1951 onClick : function(e)
1953 Roo.log('item on click ');
1954 //if(this.preventDefault){
1955 // e.preventDefault();
1957 //this.parent().hideMenuItems();
1959 this.fireEvent('click', this, e);
1978 * @class Roo.bootstrap.MenuSeparator
1979 * @extends Roo.bootstrap.Component
1980 * Bootstrap MenuSeparator class
1983 * Create a new MenuItem
1984 * @param {Object} config The config object
1988 Roo.bootstrap.MenuSeparator = function(config){
1989 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1992 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
1994 getAutoCreate : function(){
2009 <div class="modal fade">
2010 <div class="modal-dialog">
2011 <div class="modal-content">
2012 <div class="modal-header">
2013 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
2014 <h4 class="modal-title">Modal title</h4>
2016 <div class="modal-body">
2017 <p>One fine body…</p>
2019 <div class="modal-footer">
2020 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2021 <button type="button" class="btn btn-primary">Save changes</button>
2023 </div><!-- /.modal-content -->
2024 </div><!-- /.modal-dialog -->
2025 </div><!-- /.modal -->
2035 * @class Roo.bootstrap.Modal
2036 * @extends Roo.bootstrap.Component
2037 * Bootstrap Modal class
2038 * @cfg {String} title Title of dialog
2039 * @cfg {Boolean} specificTitle (true|false) default false
2040 * @cfg {Array} buttons Array of buttons or standard button set..
2041 * @cfg {String} buttonPosition (left|right|center) default right
2042 * @cfg {Boolean} animate (true | false) default true
2045 * Create a new Modal Dialog
2046 * @param {Object} config The config object
2049 Roo.bootstrap.Modal = function(config){
2050 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2055 * The raw btnclick event for the button
2056 * @param {Roo.EventObject} e
2060 this.buttons = this.buttons || [];
2063 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2065 title : 'test dialog',
2072 specificTitle: false,
2074 buttonPosition: 'right',
2078 onRender : function(ct, position)
2080 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2083 var cfg = Roo.apply({}, this.getAutoCreate());
2086 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2088 //if (!cfg.name.length) {
2092 cfg.cls += ' ' + this.cls;
2095 cfg.style = this.style;
2097 this.el = Roo.get(document.body).createChild(cfg, position);
2099 //var type = this.el.dom.type;
2101 if(this.tabIndex !== undefined){
2102 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2107 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2108 this.maskEl.enableDisplayMode("block");
2110 //this.el.addClass("x-dlg-modal");
2112 if (this.buttons.length) {
2113 Roo.each(this.buttons, function(bb) {
2114 b = Roo.apply({}, bb);
2115 b.xns = b.xns || Roo.bootstrap;
2116 b.xtype = b.xtype || 'Button';
2117 if (typeof(b.listeners) == 'undefined') {
2118 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2121 var btn = Roo.factory(b);
2123 btn.onRender(this.el.select('.modal-footer div').first());
2127 // render the children.
2130 if(typeof(this.items) != 'undefined'){
2131 var items = this.items;
2134 for(var i =0;i < items.length;i++) {
2135 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2139 this.items = nitems;
2141 this.body = this.el.select('.modal-body',true).first();
2142 this.close = this.el.select('.modal-header .close', true).first();
2143 this.footer = this.el.select('.modal-footer',true).first();
2145 //this.el.addClass([this.fieldClass, this.cls]);
2148 getAutoCreate : function(){
2153 html : this.html || ''
2158 cls : 'modal-title',
2162 if(this.specificTitle){
2168 style : 'display: none',
2171 cls: "modal-dialog",
2174 cls : "modal-content",
2177 cls : 'modal-header',
2189 cls : 'modal-footer',
2193 cls: 'btn-' + this.buttonPosition
2210 modal.cls += ' fade';
2216 getChildContainer : function() {
2218 return this.el.select('.modal-body',true).first();
2221 getButtonContainer : function() {
2222 return this.el.select('.modal-footer div',true).first();
2225 initEvents : function()
2227 this.el.select('.modal-header .close').on('click', this.hide, this);
2229 // this.addxtype(this);
2233 if (!this.rendered) {
2237 this.el.setStyle('display', 'block');
2241 (function(){ _this.el.addClass('in'); }).defer(50);
2243 this.el.addClass('in');
2246 Roo.get(document.body).addClass("x-body-masked");
2247 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2249 this.el.setStyle('zIndex', '10001');
2250 this.fireEvent('show', this);
2257 Roo.get(document.body).removeClass("x-body-masked");
2258 this.el.removeClass('in');
2262 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2264 this.el.setStyle('display', 'none');
2267 this.fireEvent('hide', this);
2270 addButton : function(str, cb)
2274 var b = Roo.apply({}, { html : str } );
2275 b.xns = b.xns || Roo.bootstrap;
2276 b.xtype = b.xtype || 'Button';
2277 if (typeof(b.listeners) == 'undefined') {
2278 b.listeners = { click : cb.createDelegate(this) };
2281 var btn = Roo.factory(b);
2283 btn.onRender(this.el.select('.modal-footer div').first());
2289 setDefaultButton : function(btn)
2291 //this.el.select('.modal-footer').()
2293 resizeTo: function(w,h)
2297 setContentSize : function(w, h)
2301 onButtonClick: function(btn,e)
2304 this.fireEvent('btnclick', btn.name, e);
2306 setTitle: function(str) {
2307 this.el.select('.modal-title',true).first().dom.innerHTML = str;
2313 Roo.apply(Roo.bootstrap.Modal, {
2315 * Button config that displays a single OK button
2324 * Button config that displays Yes and No buttons
2340 * Button config that displays OK and Cancel buttons
2355 * Button config that displays Yes, No and Cancel buttons
2377 * messagebox - can be used as a replace
2381 * @class Roo.MessageBox
2382 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2386 Roo.Msg.alert('Status', 'Changes saved successfully.');
2388 // Prompt for user data:
2389 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2391 // process text value...
2395 // Show a dialog using config options:
2397 title:'Save Changes?',
2398 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2399 buttons: Roo.Msg.YESNOCANCEL,
2406 Roo.bootstrap.MessageBox = function(){
2407 var dlg, opt, mask, waitTimer;
2408 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2409 var buttons, activeTextEl, bwidth;
2413 var handleButton = function(button){
2415 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2419 var handleHide = function(){
2421 dlg.el.removeClass(opt.cls);
2424 // Roo.TaskMgr.stop(waitTimer);
2425 // waitTimer = null;
2430 var updateButtons = function(b){
2433 buttons["ok"].hide();
2434 buttons["cancel"].hide();
2435 buttons["yes"].hide();
2436 buttons["no"].hide();
2437 //dlg.footer.dom.style.display = 'none';
2440 dlg.footer.dom.style.display = '';
2441 for(var k in buttons){
2442 if(typeof buttons[k] != "function"){
2445 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2446 width += buttons[k].el.getWidth()+15;
2456 var handleEsc = function(d, k, e){
2457 if(opt && opt.closable !== false){
2467 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2468 * @return {Roo.BasicDialog} The BasicDialog element
2470 getDialog : function(){
2472 dlg = new Roo.bootstrap.Modal( {
2475 //constraintoviewport:false,
2477 //collapsible : false,
2482 //buttonAlign:"center",
2483 closeClick : function(){
2484 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2487 handleButton("cancel");
2492 dlg.on("hide", handleHide);
2494 //dlg.addKeyListener(27, handleEsc);
2496 this.buttons = buttons;
2497 var bt = this.buttonText;
2498 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2499 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2500 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2501 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2503 bodyEl = dlg.body.createChild({
2505 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2506 '<textarea class="roo-mb-textarea"></textarea>' +
2507 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2509 msgEl = bodyEl.dom.firstChild;
2510 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2511 textboxEl.enableDisplayMode();
2512 textboxEl.addKeyListener([10,13], function(){
2513 if(dlg.isVisible() && opt && opt.buttons){
2516 }else if(opt.buttons.yes){
2517 handleButton("yes");
2521 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2522 textareaEl.enableDisplayMode();
2523 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2524 progressEl.enableDisplayMode();
2525 var pf = progressEl.dom.firstChild;
2527 pp = Roo.get(pf.firstChild);
2528 pp.setHeight(pf.offsetHeight);
2536 * Updates the message box body text
2537 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2538 * the XHTML-compliant non-breaking space character '&#160;')
2539 * @return {Roo.MessageBox} This message box
2541 updateText : function(text){
2542 if(!dlg.isVisible() && !opt.width){
2543 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2545 msgEl.innerHTML = text || ' ';
2547 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2548 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2550 Math.min(opt.width || cw , this.maxWidth),
2551 Math.max(opt.minWidth || this.minWidth, bwidth)
2554 activeTextEl.setWidth(w);
2556 if(dlg.isVisible()){
2557 dlg.fixedcenter = false;
2559 // to big, make it scroll. = But as usual stupid IE does not support
2562 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2563 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2564 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2566 bodyEl.dom.style.height = '';
2567 bodyEl.dom.style.overflowY = '';
2570 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2572 bodyEl.dom.style.overflowX = '';
2575 dlg.setContentSize(w, bodyEl.getHeight());
2576 if(dlg.isVisible()){
2577 dlg.fixedcenter = true;
2583 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2584 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2585 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2586 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2587 * @return {Roo.MessageBox} This message box
2589 updateProgress : function(value, text){
2591 this.updateText(text);
2593 if (pp) { // weird bug on my firefox - for some reason this is not defined
2594 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2600 * Returns true if the message box is currently displayed
2601 * @return {Boolean} True if the message box is visible, else false
2603 isVisible : function(){
2604 return dlg && dlg.isVisible();
2608 * Hides the message box if it is displayed
2611 if(this.isVisible()){
2617 * Displays a new message box, or reinitializes an existing message box, based on the config options
2618 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2619 * The following config object properties are supported:
2621 Property Type Description
2622 ---------- --------------- ------------------------------------------------------------------------------------
2623 animEl String/Element An id or Element from which the message box should animate as it opens and
2624 closes (defaults to undefined)
2625 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2626 cancel:'Bar'}), or false to not show any buttons (defaults to false)
2627 closable Boolean False to hide the top-right close button (defaults to true). Note that
2628 progress and wait dialogs will ignore this property and always hide the
2629 close button as they can only be closed programmatically.
2630 cls String A custom CSS class to apply to the message box element
2631 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
2632 displayed (defaults to 75)
2633 fn Function A callback function to execute after closing the dialog. The arguments to the
2634 function will be btn (the name of the button that was clicked, if applicable,
2635 e.g. "ok"), and text (the value of the active text field, if applicable).
2636 Progress and wait dialogs will ignore this option since they do not respond to
2637 user actions and can only be closed programmatically, so any required function
2638 should be called by the same code after it closes the dialog.
2639 icon String A CSS class that provides a background image to be used as an icon for
2640 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2641 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
2642 minWidth Number The minimum width in pixels of the message box (defaults to 100)
2643 modal Boolean False to allow user interaction with the page while the message box is
2644 displayed (defaults to true)
2645 msg String A string that will replace the existing message box body text (defaults
2646 to the XHTML-compliant non-breaking space character ' ')
2647 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
2648 progress Boolean True to display a progress bar (defaults to false)
2649 progressText String The text to display inside the progress bar if progress = true (defaults to '')
2650 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
2651 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
2652 title String The title text
2653 value String The string value to set into the active textbox element if displayed
2654 wait Boolean True to display a progress bar (defaults to false)
2655 width Number The width of the dialog in pixels
2662 msg: 'Please enter your address:',
2664 buttons: Roo.MessageBox.OKCANCEL,
2667 animEl: 'addAddressBtn'
2670 * @param {Object} config Configuration options
2671 * @return {Roo.MessageBox} This message box
2673 show : function(options)
2676 // this causes nightmares if you show one dialog after another
2677 // especially on callbacks..
2679 if(this.isVisible()){
2682 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2683 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
2684 Roo.log("New Dialog Message:" + options.msg )
2685 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2686 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2689 var d = this.getDialog();
2691 d.setTitle(opt.title || " ");
2692 d.close.setDisplayed(opt.closable !== false);
2693 activeTextEl = textboxEl;
2694 opt.prompt = opt.prompt || (opt.multiline ? true : false);
2699 textareaEl.setHeight(typeof opt.multiline == "number" ?
2700 opt.multiline : this.defaultTextHeight);
2701 activeTextEl = textareaEl;
2710 progressEl.setDisplayed(opt.progress === true);
2711 this.updateProgress(0);
2712 activeTextEl.dom.value = opt.value || "";
2714 dlg.setDefaultButton(activeTextEl);
2716 var bs = opt.buttons;
2720 }else if(bs && bs.yes){
2721 db = buttons["yes"];
2723 dlg.setDefaultButton(db);
2725 bwidth = updateButtons(opt.buttons);
2726 this.updateText(opt.msg);
2728 d.el.addClass(opt.cls);
2730 d.proxyDrag = opt.proxyDrag === true;
2731 d.modal = opt.modal !== false;
2732 d.mask = opt.modal !== false ? mask : false;
2734 // force it to the end of the z-index stack so it gets a cursor in FF
2735 document.body.appendChild(dlg.el.dom);
2736 d.animateTarget = null;
2737 d.show(options.animEl);
2743 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
2744 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2745 * and closing the message box when the process is complete.
2746 * @param {String} title The title bar text
2747 * @param {String} msg The message box body text
2748 * @return {Roo.MessageBox} This message box
2750 progress : function(title, msg){
2757 minWidth: this.minProgressWidth,
2764 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2765 * If a callback function is passed it will be called after the user clicks the button, and the
2766 * id of the button that was clicked will be passed as the only parameter to the callback
2767 * (could also be the top-right close button).
2768 * @param {String} title The title bar text
2769 * @param {String} msg The message box body text
2770 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2771 * @param {Object} scope (optional) The scope of the callback function
2772 * @return {Roo.MessageBox} This message box
2774 alert : function(title, msg, fn, scope){
2787 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
2788 * interaction while waiting for a long-running process to complete that does not have defined intervals.
2789 * You are responsible for closing the message box when the process is complete.
2790 * @param {String} msg The message box body text
2791 * @param {String} title (optional) The title bar text
2792 * @return {Roo.MessageBox} This message box
2794 wait : function(msg, title){
2805 waitTimer = Roo.TaskMgr.start({
2807 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2815 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2816 * If a callback function is passed it will be called after the user clicks either button, and the id of the
2817 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2818 * @param {String} title The title bar text
2819 * @param {String} msg The message box body text
2820 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2821 * @param {Object} scope (optional) The scope of the callback function
2822 * @return {Roo.MessageBox} This message box
2824 confirm : function(title, msg, fn, scope){
2828 buttons: this.YESNO,
2837 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2838 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
2839 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2840 * (could also be the top-right close button) and the text that was entered will be passed as the two
2841 * parameters to the callback.
2842 * @param {String} title The title bar text
2843 * @param {String} msg The message box body text
2844 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2845 * @param {Object} scope (optional) The scope of the callback function
2846 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2847 * property, or the height in pixels to create the textbox (defaults to false / single-line)
2848 * @return {Roo.MessageBox} This message box
2850 prompt : function(title, msg, fn, scope, multiline){
2854 buttons: this.OKCANCEL,
2859 multiline: multiline,
2866 * Button config that displays a single OK button
2871 * Button config that displays Yes and No buttons
2874 YESNO : {yes:true, no:true},
2876 * Button config that displays OK and Cancel buttons
2879 OKCANCEL : {ok:true, cancel:true},
2881 * Button config that displays Yes, No and Cancel buttons
2884 YESNOCANCEL : {yes:true, no:true, cancel:true},
2887 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2890 defaultTextHeight : 75,
2892 * The maximum width in pixels of the message box (defaults to 600)
2897 * The minimum width in pixels of the message box (defaults to 100)
2902 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
2903 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2906 minProgressWidth : 250,
2908 * An object containing the default button text strings that can be overriden for localized language support.
2909 * Supported properties are: ok, cancel, yes and no.
2910 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2923 * Shorthand for {@link Roo.MessageBox}
2925 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox
2926 Roo.Msg = Roo.Msg || Roo.MessageBox;
2935 * @class Roo.bootstrap.Navbar
2936 * @extends Roo.bootstrap.Component
2937 * Bootstrap Navbar class
2940 * Create a new Navbar
2941 * @param {Object} config The config object
2945 Roo.bootstrap.Navbar = function(config){
2946 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2950 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
2959 getAutoCreate : function(){
2962 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2966 initEvents :function ()
2968 //Roo.log(this.el.select('.navbar-toggle',true));
2969 this.el.select('.navbar-toggle',true).on('click', function() {
2970 // Roo.log('click');
2971 this.el.select('.navbar-collapse',true).toggleClass('in');
2979 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2981 var size = this.el.getSize();
2982 this.maskEl.setSize(size.width, size.height);
2983 this.maskEl.enableDisplayMode("block");
2992 getChildContainer : function()
2994 if (this.el.select('.collapse').getCount()) {
2995 return this.el.select('.collapse',true).first();
3028 * @class Roo.bootstrap.NavSimplebar
3029 * @extends Roo.bootstrap.Navbar
3030 * Bootstrap Sidebar class
3032 * @cfg {Boolean} inverse is inverted color
3034 * @cfg {String} type (nav | pills | tabs)
3035 * @cfg {Boolean} arrangement stacked | justified
3036 * @cfg {String} align (left | right) alignment
3038 * @cfg {Boolean} main (true|false) main nav bar? default false
3039 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3041 * @cfg {String} tag (header|footer|nav|div) default is nav
3047 * Create a new Sidebar
3048 * @param {Object} config The config object
3052 Roo.bootstrap.NavSimplebar = function(config){
3053 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3056 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3072 getAutoCreate : function(){
3076 tag : this.tag || 'div',
3089 this.type = this.type || 'nav';
3090 if (['tabs','pills'].indexOf(this.type)!==-1) {
3091 cfg.cn[0].cls += ' nav-' + this.type
3095 if (this.type!=='nav') {
3096 Roo.log('nav type must be nav/tabs/pills')
3098 cfg.cn[0].cls += ' navbar-nav'
3104 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3105 cfg.cn[0].cls += ' nav-' + this.arrangement;
3109 if (this.align === 'right') {
3110 cfg.cn[0].cls += ' navbar-right';
3114 cfg.cls += ' navbar-inverse';
3141 * @class Roo.bootstrap.NavHeaderbar
3142 * @extends Roo.bootstrap.NavSimplebar
3143 * Bootstrap Sidebar class
3145 * @cfg {String} brand what is brand
3146 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3147 * @cfg {String} brand_href href of the brand
3148 * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3149 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3152 * Create a new Sidebar
3153 * @param {Object} config The config object
3157 Roo.bootstrap.NavHeaderbar = function(config){
3158 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3161 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3169 getAutoCreate : function(){
3172 tag: this.nav || 'nav',
3181 cls: 'navbar-header',
3186 cls: 'navbar-toggle',
3187 'data-toggle': 'collapse',
3192 html: 'Toggle navigation'
3214 cls: 'collapse navbar-collapse',
3218 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3220 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3221 cfg.cls += ' navbar-' + this.position;
3223 // tag can override this..
3225 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3228 if (this.brand !== '') {
3231 href: this.brand_href ? this.brand_href : '#',
3232 cls: 'navbar-brand',
3240 cfg.cls += ' main-nav';
3248 initEvents : function()
3250 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3252 if (this.autohide) {
3257 Roo.get(document).on('scroll',function(e) {
3258 var ns = Roo.get(document).getScroll().top;
3259 var os = prevScroll;
3263 ft.removeClass('slideDown');
3264 ft.addClass('slideUp');
3267 ft.removeClass('slideUp');
3268 ft.addClass('slideDown');
3292 * @class Roo.bootstrap.NavSidebar
3293 * @extends Roo.bootstrap.Navbar
3294 * Bootstrap Sidebar class
3297 * Create a new Sidebar
3298 * @param {Object} config The config object
3302 Roo.bootstrap.NavSidebar = function(config){
3303 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3306 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3308 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3310 getAutoCreate : function(){
3315 cls: 'sidebar sidebar-nav'
3337 * @class Roo.bootstrap.NavGroup
3338 * @extends Roo.bootstrap.Component
3339 * Bootstrap NavGroup class
3340 * @cfg {String} align left | right
3341 * @cfg {Boolean} inverse false | true
3342 * @cfg {String} type (nav|pills|tab) default nav
3343 * @cfg {String} navId - reference Id for navbar.
3347 * Create a new nav group
3348 * @param {Object} config The config object
3351 Roo.bootstrap.NavGroup = function(config){
3352 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3355 Roo.bootstrap.NavGroup.register(this);
3359 * Fires when the active item changes
3360 * @param {Roo.bootstrap.NavGroup} this
3361 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3362 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3369 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3380 getAutoCreate : function()
3382 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3389 if (['tabs','pills'].indexOf(this.type)!==-1) {
3390 cfg.cls += ' nav-' + this.type
3392 if (this.type!=='nav') {
3393 Roo.log('nav type must be nav/tabs/pills')
3395 cfg.cls += ' navbar-nav'
3398 if (this.parent().sidebar) {
3401 cls: 'dashboard-menu sidebar-menu'
3407 if (this.form === true) {
3413 if (this.align === 'right') {
3414 cfg.cls += ' navbar-right';
3416 cfg.cls += ' navbar-left';
3420 if (this.align === 'right') {
3421 cfg.cls += ' navbar-right';
3425 cfg.cls += ' navbar-inverse';
3433 * sets the active Navigation item
3434 * @param {Roo.bootstrap.NavItem} the new current navitem
3436 setActiveItem : function(item)
3439 Roo.each(this.navItems, function(v){
3444 v.setActive(false, true);
3451 item.setActive(true, true);
3452 this.fireEvent('changed', this, item, prev);
3457 * gets the active Navigation item
3458 * @return {Roo.bootstrap.NavItem} the current navitem
3460 getActive : function()
3464 Roo.each(this.navItems, function(v){
3475 indexOfNav : function()
3479 Roo.each(this.navItems, function(v,i){
3490 * adds a Navigation item
3491 * @param {Roo.bootstrap.NavItem} the navitem to add
3493 addItem : function(cfg)
3495 var cn = new Roo.bootstrap.NavItem(cfg);
3497 cn.parentId = this.id;
3498 cn.onRender(this.el, null);
3502 * register a Navigation item
3503 * @param {Roo.bootstrap.NavItem} the navitem to add
3505 register : function(item)
3507 this.navItems.push( item);
3508 item.navId = this.navId;
3513 * clear all the Navigation item
3516 clearAll : function()
3519 this.el.dom.innerHTML = '';
3522 getNavItem: function(tabId)
3525 Roo.each(this.navItems, function(e) {
3526 if (e.tabId == tabId) {
3536 setActiveNext : function()
3538 var i = this.indexOfNav(this.getActive());
3539 if (i > this.navItems.length) {
3542 this.setActiveItem(this.navItems[i+1]);
3544 setActivePrev : function()
3546 var i = this.indexOfNav(this.getActive());
3550 this.setActiveItem(this.navItems[i-1]);
3552 clearWasActive : function(except) {
3553 Roo.each(this.navItems, function(e) {
3554 if (e.tabId != except.tabId && e.was_active) {
3555 e.was_active = false;
3562 getWasActive : function ()
3565 Roo.each(this.navItems, function(e) {
3580 Roo.apply(Roo.bootstrap.NavGroup, {
3584 * register a Navigation Group
3585 * @param {Roo.bootstrap.NavGroup} the navgroup to add
3587 register : function(navgrp)
3589 this.groups[navgrp.navId] = navgrp;
3593 * fetch a Navigation Group based on the navigation ID
3594 * @param {string} the navgroup to add
3595 * @returns {Roo.bootstrap.NavGroup} the navgroup
3597 get: function(navId) {
3598 if (typeof(this.groups[navId]) == 'undefined') {
3600 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3602 return this.groups[navId] ;
3617 * @class Roo.bootstrap.NavItem
3618 * @extends Roo.bootstrap.Component
3619 * Bootstrap Navbar.NavItem class
3620 * @cfg {String} href link to
3621 * @cfg {String} html content of button
3622 * @cfg {String} badge text inside badge
3623 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3624 * @cfg {String} glyphicon name of glyphicon
3625 * @cfg {String} icon name of font awesome icon
3626 * @cfg {Boolean} active Is item active
3627 * @cfg {Boolean} disabled Is item disabled
3629 * @cfg {Boolean} preventDefault (true | false) default false
3630 * @cfg {String} tabId the tab that this item activates.
3631 * @cfg {String} tagtype (a|span) render as a href or span?
3634 * Create a new Navbar Item
3635 * @param {Object} config The config object
3637 Roo.bootstrap.NavItem = function(config){
3638 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3643 * The raw click event for the entire grid.
3644 * @param {Roo.EventObject} e
3649 * Fires when the active item active state changes
3650 * @param {Roo.bootstrap.NavItem} this
3651 * @param {boolean} state the new state
3659 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
3667 preventDefault : false,
3674 getAutoCreate : function(){
3682 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3684 if (this.disabled) {
3685 cfg.cls += ' disabled';
3688 if (this.href || this.html || this.glyphicon || this.icon) {
3692 href : this.href || "#",
3693 html: this.html || ''
3698 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3701 if(this.glyphicon) {
3702 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
3707 cfg.cn[0].html += " <span class='caret'></span>";
3711 if (this.badge !== '') {
3713 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3721 initEvents: function() {
3722 // Roo.log('init events?');
3723 // Roo.log(this.el.dom);
3724 if (typeof (this.menu) != 'undefined') {
3725 this.menu.parentType = this.xtype;
3726 this.menu.triggerEl = this.el;
3727 this.addxtype(Roo.apply({}, this.menu));
3731 this.el.select('a',true).on('click', this.onClick, this);
3732 // at this point parent should be available..
3733 this.parent().register(this);
3736 onClick : function(e)
3739 if(this.preventDefault){
3742 if (this.disabled) {
3746 var tg = Roo.bootstrap.TabGroup.get(this.navId);
3747 if (tg && tg.transition) {
3748 Roo.log("waiting for the transitionend");
3752 Roo.log("fire event clicked");
3753 if(this.fireEvent('click', this, e) === false){
3756 var p = this.parent();
3757 if (['tabs','pills'].indexOf(p.type)!==-1) {
3758 if (typeof(p.setActiveItem) !== 'undefined') {
3759 p.setActiveItem(this);
3762 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3763 if (p.parentType == 'NavHeaderbar' && !this.menu) {
3764 // remove the collapsed menu expand...
3765 p.parent().el.select('.navbar-collapse',true).removeClass('in');
3770 isActive: function () {
3773 setActive : function(state, fire, is_was_active)
3775 if (this.active && !state & this.navId) {
3776 this.was_active = true;
3777 var nv = Roo.bootstrap.NavGroup.get(this.navId);
3779 nv.clearWasActive(this);
3783 this.active = state;
3786 this.el.removeClass('active');
3787 } else if (!this.el.hasClass('active')) {
3788 this.el.addClass('active');
3791 this.fireEvent('changed', this, state);
3794 // show a panel if it's registered and related..
3796 if (!this.navId || !this.tabId || !state || is_was_active) {
3800 var tg = Roo.bootstrap.TabGroup.get(this.navId);
3804 var pan = tg.getPanelByName(this.tabId);
3808 // if we can not flip to new panel - go back to old nav highlight..
3809 if (false == tg.showPanel(pan)) {
3810 var nv = Roo.bootstrap.NavGroup.get(this.navId);
3812 var onav = nv.getWasActive();
3814 onav.setActive(true, false, true);
3823 // this should not be here...
3824 setDisabled : function(state)
3826 this.disabled = state;
3828 this.el.removeClass('disabled');
3829 } else if (!this.el.hasClass('disabled')) {
3830 this.el.addClass('disabled');
3843 * <span> icon </span>
3844 * <span> text </span>
3845 * <span>badge </span>
3849 * @class Roo.bootstrap.NavSidebarItem
3850 * @extends Roo.bootstrap.NavItem
3851 * Bootstrap Navbar.NavSidebarItem class
3853 * Create a new Navbar Button
3854 * @param {Object} config The config object
3856 Roo.bootstrap.NavSidebarItem = function(config){
3857 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3862 * The raw click event for the entire grid.
3863 * @param {Roo.EventObject} e
3868 * Fires when the active item active state changes
3869 * @param {Roo.bootstrap.NavSidebarItem} this
3870 * @param {boolean} state the new state
3878 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
3881 getAutoCreate : function(){
3886 href : this.href || '#',
3898 html : this.html || ''
3903 cfg.cls += ' active';
3907 if (this.glyphicon || this.icon) {
3908 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
3909 a.cn.push({ tag : 'i', cls : c }) ;
3914 if (this.badge !== '') {
3915 a.cn.push({ tag: 'span', cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge });
3919 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3920 a.cls += 'dropdown-toggle treeview' ;
3944 * @class Roo.bootstrap.Row
3945 * @extends Roo.bootstrap.Component
3946 * Bootstrap Row class (contains columns...)
3950 * @param {Object} config The config object
3953 Roo.bootstrap.Row = function(config){
3954 Roo.bootstrap.Row.superclass.constructor.call(this, config);
3957 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
3959 getAutoCreate : function(){
3978 * @class Roo.bootstrap.Element
3979 * @extends Roo.bootstrap.Component
3980 * Bootstrap Element class
3981 * @cfg {String} html contents of the element
3982 * @cfg {String} tag tag of the element
3983 * @cfg {String} cls class of the element
3986 * Create a new Element
3987 * @param {Object} config The config object
3990 Roo.bootstrap.Element = function(config){
3991 Roo.bootstrap.Element.superclass.constructor.call(this, config);
3994 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4001 getAutoCreate : function(){
4026 * @class Roo.bootstrap.Pagination
4027 * @extends Roo.bootstrap.Component
4028 * Bootstrap Pagination class
4029 * @cfg {String} size xs | sm | md | lg
4030 * @cfg {Boolean} inverse false | true
4033 * Create a new Pagination
4034 * @param {Object} config The config object
4037 Roo.bootstrap.Pagination = function(config){
4038 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4041 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4047 getAutoCreate : function(){
4053 cfg.cls += ' inverse';
4059 cfg.cls += " " + this.cls;
4077 * @class Roo.bootstrap.PaginationItem
4078 * @extends Roo.bootstrap.Component
4079 * Bootstrap PaginationItem class
4080 * @cfg {String} html text
4081 * @cfg {String} href the link
4082 * @cfg {Boolean} preventDefault (true | false) default true
4083 * @cfg {Boolean} active (true | false) default false
4087 * Create a new PaginationItem
4088 * @param {Object} config The config object
4092 Roo.bootstrap.PaginationItem = function(config){
4093 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4098 * The raw click event for the entire grid.
4099 * @param {Roo.EventObject} e
4105 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4109 preventDefault: true,
4113 getAutoCreate : function(){
4119 href : this.href ? this.href : '#',
4120 html : this.html ? this.html : ''
4130 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4136 initEvents: function() {
4138 this.el.on('click', this.onClick, this);
4141 onClick : function(e)
4143 Roo.log('PaginationItem on click ');
4144 if(this.preventDefault){
4148 this.fireEvent('click', this, e);
4164 * @class Roo.bootstrap.Slider
4165 * @extends Roo.bootstrap.Component
4166 * Bootstrap Slider class
4169 * Create a new Slider
4170 * @param {Object} config The config object
4173 Roo.bootstrap.Slider = function(config){
4174 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4177 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4179 getAutoCreate : function(){
4183 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4187 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4199 * Ext JS Library 1.1.1
4200 * Copyright(c) 2006-2007, Ext JS, LLC.
4202 * Originally Released Under LGPL - original licence link has changed is not relivant.
4205 * <script type="text/javascript">
4210 * @class Roo.grid.ColumnModel
4211 * @extends Roo.util.Observable
4212 * This is the default implementation of a ColumnModel used by the Grid. It defines
4213 * the columns in the grid.
4216 var colModel = new Roo.grid.ColumnModel([
4217 {header: "Ticker", width: 60, sortable: true, locked: true},
4218 {header: "Company Name", width: 150, sortable: true},
4219 {header: "Market Cap.", width: 100, sortable: true},
4220 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4221 {header: "Employees", width: 100, sortable: true, resizable: false}
4226 * The config options listed for this class are options which may appear in each
4227 * individual column definition.
4228 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4230 * @param {Object} config An Array of column config objects. See this class's
4231 * config objects for details.
4233 Roo.grid.ColumnModel = function(config){
4235 * The config passed into the constructor
4237 this.config = config;
4240 // if no id, create one
4241 // if the column does not have a dataIndex mapping,
4242 // map it to the order it is in the config
4243 for(var i = 0, len = config.length; i < len; i++){
4245 if(typeof c.dataIndex == "undefined"){
4248 if(typeof c.renderer == "string"){
4249 c.renderer = Roo.util.Format[c.renderer];
4251 if(typeof c.id == "undefined"){
4254 if(c.editor && c.editor.xtype){
4255 c.editor = Roo.factory(c.editor, Roo.grid);
4257 if(c.editor && c.editor.isFormField){
4258 c.editor = new Roo.grid.GridEditor(c.editor);
4260 this.lookup[c.id] = c;
4264 * The width of columns which have no width specified (defaults to 100)
4267 this.defaultWidth = 100;
4270 * Default sortable of columns which have no sortable specified (defaults to false)
4273 this.defaultSortable = false;
4277 * @event widthchange
4278 * Fires when the width of a column changes.
4279 * @param {ColumnModel} this
4280 * @param {Number} columnIndex The column index
4281 * @param {Number} newWidth The new width
4283 "widthchange": true,
4285 * @event headerchange
4286 * Fires when the text of a header changes.
4287 * @param {ColumnModel} this
4288 * @param {Number} columnIndex The column index
4289 * @param {Number} newText The new header text
4291 "headerchange": true,
4293 * @event hiddenchange
4294 * Fires when a column is hidden or "unhidden".
4295 * @param {ColumnModel} this
4296 * @param {Number} columnIndex The column index
4297 * @param {Boolean} hidden true if hidden, false otherwise
4299 "hiddenchange": true,
4301 * @event columnmoved
4302 * Fires when a column is moved.
4303 * @param {ColumnModel} this
4304 * @param {Number} oldIndex
4305 * @param {Number} newIndex
4307 "columnmoved" : true,
4309 * @event columlockchange
4310 * Fires when a column's locked state is changed
4311 * @param {ColumnModel} this
4312 * @param {Number} colIndex
4313 * @param {Boolean} locked true if locked
4315 "columnlockchange" : true
4317 Roo.grid.ColumnModel.superclass.constructor.call(this);
4319 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4321 * @cfg {String} header The header text to display in the Grid view.
4324 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4325 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4326 * specified, the column's index is used as an index into the Record's data Array.
4329 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4330 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4333 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4334 * Defaults to the value of the {@link #defaultSortable} property.
4335 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4338 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4341 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4344 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4347 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4350 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4351 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4352 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4353 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4356 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4359 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4363 * Returns the id of the column at the specified index.
4364 * @param {Number} index The column index
4365 * @return {String} the id
4367 getColumnId : function(index){
4368 return this.config[index].id;
4372 * Returns the column for a specified id.
4373 * @param {String} id The column id
4374 * @return {Object} the column
4376 getColumnById : function(id){
4377 return this.lookup[id];
4382 * Returns the column for a specified dataIndex.
4383 * @param {String} dataIndex The column dataIndex
4384 * @return {Object|Boolean} the column or false if not found
4386 getColumnByDataIndex: function(dataIndex){
4387 var index = this.findColumnIndex(dataIndex);
4388 return index > -1 ? this.config[index] : false;
4392 * Returns the index for a specified column id.
4393 * @param {String} id The column id
4394 * @return {Number} the index, or -1 if not found
4396 getIndexById : function(id){
4397 for(var i = 0, len = this.config.length; i < len; i++){
4398 if(this.config[i].id == id){
4406 * Returns the index for a specified column dataIndex.
4407 * @param {String} dataIndex The column dataIndex
4408 * @return {Number} the index, or -1 if not found
4411 findColumnIndex : function(dataIndex){
4412 for(var i = 0, len = this.config.length; i < len; i++){
4413 if(this.config[i].dataIndex == dataIndex){
4421 moveColumn : function(oldIndex, newIndex){
4422 var c = this.config[oldIndex];
4423 this.config.splice(oldIndex, 1);
4424 this.config.splice(newIndex, 0, c);
4425 this.dataMap = null;
4426 this.fireEvent("columnmoved", this, oldIndex, newIndex);
4429 isLocked : function(colIndex){
4430 return this.config[colIndex].locked === true;
4433 setLocked : function(colIndex, value, suppressEvent){
4434 if(this.isLocked(colIndex) == value){
4437 this.config[colIndex].locked = value;
4439 this.fireEvent("columnlockchange", this, colIndex, value);
4443 getTotalLockedWidth : function(){
4445 for(var i = 0; i < this.config.length; i++){
4446 if(this.isLocked(i) && !this.isHidden(i)){
4447 this.totalWidth += this.getColumnWidth(i);
4453 getLockedCount : function(){
4454 for(var i = 0, len = this.config.length; i < len; i++){
4455 if(!this.isLocked(i)){
4462 * Returns the number of columns.
4465 getColumnCount : function(visibleOnly){
4466 if(visibleOnly === true){
4468 for(var i = 0, len = this.config.length; i < len; i++){
4469 if(!this.isHidden(i)){
4475 return this.config.length;
4479 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4480 * @param {Function} fn
4481 * @param {Object} scope (optional)
4482 * @return {Array} result
4484 getColumnsBy : function(fn, scope){
4486 for(var i = 0, len = this.config.length; i < len; i++){
4487 var c = this.config[i];
4488 if(fn.call(scope||this, c, i) === true){
4496 * Returns true if the specified column is sortable.
4497 * @param {Number} col The column index
4500 isSortable : function(col){
4501 if(typeof this.config[col].sortable == "undefined"){
4502 return this.defaultSortable;
4504 return this.config[col].sortable;
4508 * Returns the rendering (formatting) function defined for the column.
4509 * @param {Number} col The column index.
4510 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4512 getRenderer : function(col){
4513 if(!this.config[col].renderer){
4514 return Roo.grid.ColumnModel.defaultRenderer;
4516 return this.config[col].renderer;
4520 * Sets the rendering (formatting) function for a column.
4521 * @param {Number} col The column index
4522 * @param {Function} fn The function to use to process the cell's raw data
4523 * to return HTML markup for the grid view. The render function is called with
4524 * the following parameters:<ul>
4525 * <li>Data value.</li>
4526 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4527 * <li>css A CSS style string to apply to the table cell.</li>
4528 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4529 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4530 * <li>Row index</li>
4531 * <li>Column index</li>
4532 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4534 setRenderer : function(col, fn){
4535 this.config[col].renderer = fn;
4539 * Returns the width for the specified column.
4540 * @param {Number} col The column index
4543 getColumnWidth : function(col){
4544 return this.config[col].width * 1 || this.defaultWidth;
4548 * Sets the width for a column.
4549 * @param {Number} col The column index
4550 * @param {Number} width The new width
4552 setColumnWidth : function(col, width, suppressEvent){
4553 this.config[col].width = width;
4554 this.totalWidth = null;
4556 this.fireEvent("widthchange", this, col, width);
4561 * Returns the total width of all columns.
4562 * @param {Boolean} includeHidden True to include hidden column widths
4565 getTotalWidth : function(includeHidden){
4566 if(!this.totalWidth){
4567 this.totalWidth = 0;
4568 for(var i = 0, len = this.config.length; i < len; i++){
4569 if(includeHidden || !this.isHidden(i)){
4570 this.totalWidth += this.getColumnWidth(i);
4574 return this.totalWidth;
4578 * Returns the header for the specified column.
4579 * @param {Number} col The column index
4582 getColumnHeader : function(col){
4583 return this.config[col].header;
4587 * Sets the header for a column.
4588 * @param {Number} col The column index
4589 * @param {String} header The new header
4591 setColumnHeader : function(col, header){
4592 this.config[col].header = header;
4593 this.fireEvent("headerchange", this, col, header);
4597 * Returns the tooltip for the specified column.
4598 * @param {Number} col The column index
4601 getColumnTooltip : function(col){
4602 return this.config[col].tooltip;
4605 * Sets the tooltip for a column.
4606 * @param {Number} col The column index
4607 * @param {String} tooltip The new tooltip
4609 setColumnTooltip : function(col, tooltip){
4610 this.config[col].tooltip = tooltip;
4614 * Returns the dataIndex for the specified column.
4615 * @param {Number} col The column index
4618 getDataIndex : function(col){
4619 return this.config[col].dataIndex;
4623 * Sets the dataIndex for a column.
4624 * @param {Number} col The column index
4625 * @param {Number} dataIndex The new dataIndex
4627 setDataIndex : function(col, dataIndex){
4628 this.config[col].dataIndex = dataIndex;
4634 * Returns true if the cell is editable.
4635 * @param {Number} colIndex The column index
4636 * @param {Number} rowIndex The row index
4639 isCellEditable : function(colIndex, rowIndex){
4640 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4644 * Returns the editor defined for the cell/column.
4645 * return false or null to disable editing.
4646 * @param {Number} colIndex The column index
4647 * @param {Number} rowIndex The row index
4650 getCellEditor : function(colIndex, rowIndex){
4651 return this.config[colIndex].editor;
4655 * Sets if a column is editable.
4656 * @param {Number} col The column index
4657 * @param {Boolean} editable True if the column is editable
4659 setEditable : function(col, editable){
4660 this.config[col].editable = editable;
4665 * Returns true if the column is hidden.
4666 * @param {Number} colIndex The column index
4669 isHidden : function(colIndex){
4670 return this.config[colIndex].hidden;
4675 * Returns true if the column width cannot be changed
4677 isFixed : function(colIndex){
4678 return this.config[colIndex].fixed;
4682 * Returns true if the column can be resized
4685 isResizable : function(colIndex){
4686 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4689 * Sets if a column is hidden.
4690 * @param {Number} colIndex The column index
4691 * @param {Boolean} hidden True if the column is hidden
4693 setHidden : function(colIndex, hidden){
4694 this.config[colIndex].hidden = hidden;
4695 this.totalWidth = null;
4696 this.fireEvent("hiddenchange", this, colIndex, hidden);
4700 * Sets the editor for a column.
4701 * @param {Number} col The column index
4702 * @param {Object} editor The editor object
4704 setEditor : function(col, editor){
4705 this.config[col].editor = editor;
4709 Roo.grid.ColumnModel.defaultRenderer = function(value){
4710 if(typeof value == "string" && value.length < 1){
4716 // Alias for backwards compatibility
4717 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4720 * Ext JS Library 1.1.1
4721 * Copyright(c) 2006-2007, Ext JS, LLC.
4723 * Originally Released Under LGPL - original licence link has changed is not relivant.
4726 * <script type="text/javascript">
4730 * @class Roo.LoadMask
4731 * A simple utility class for generically masking elements while loading data. If the element being masked has
4732 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4733 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
4734 * element's UpdateManager load indicator and will be destroyed after the initial load.
4736 * Create a new LoadMask
4737 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4738 * @param {Object} config The config object
4740 Roo.LoadMask = function(el, config){
4741 this.el = Roo.get(el);
4742 Roo.apply(this, config);
4744 this.store.on('beforeload', this.onBeforeLoad, this);
4745 this.store.on('load', this.onLoad, this);
4746 this.store.on('loadexception', this.onLoadException, this);
4747 this.removeMask = false;
4749 var um = this.el.getUpdateManager();
4750 um.showLoadIndicator = false; // disable the default indicator
4751 um.on('beforeupdate', this.onBeforeLoad, this);
4752 um.on('update', this.onLoad, this);
4753 um.on('failure', this.onLoad, this);
4754 this.removeMask = true;
4758 Roo.LoadMask.prototype = {
4760 * @cfg {Boolean} removeMask
4761 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4762 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
4766 * The text to display in a centered loading message box (defaults to 'Loading...')
4770 * @cfg {String} msgCls
4771 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4773 msgCls : 'x-mask-loading',
4776 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4782 * Disables the mask to prevent it from being displayed
4784 disable : function(){
4785 this.disabled = true;
4789 * Enables the mask so that it can be displayed
4791 enable : function(){
4792 this.disabled = false;
4795 onLoadException : function()
4799 if (typeof(arguments[3]) != 'undefined') {
4800 Roo.MessageBox.alert("Error loading",arguments[3]);
4804 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4805 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4814 this.el.unmask(this.removeMask);
4819 this.el.unmask(this.removeMask);
4823 onBeforeLoad : function(){
4825 this.el.mask(this.msg, this.msgCls);
4830 destroy : function(){
4832 this.store.un('beforeload', this.onBeforeLoad, this);
4833 this.store.un('load', this.onLoad, this);
4834 this.store.un('loadexception', this.onLoadException, this);
4836 var um = this.el.getUpdateManager();
4837 um.un('beforeupdate', this.onBeforeLoad, this);
4838 um.un('update', this.onLoad, this);
4839 um.un('failure', this.onLoad, this);
4850 * @class Roo.bootstrap.Table
4851 * @extends Roo.bootstrap.Component
4852 * Bootstrap Table class
4853 * @cfg {String} cls table class
4854 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4855 * @cfg {String} bgcolor Specifies the background color for a table
4856 * @cfg {Number} border Specifies whether the table cells should have borders or not
4857 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4858 * @cfg {Number} cellspacing Specifies the space between cells
4859 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4860 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4861 * @cfg {String} sortable Specifies that the table should be sortable
4862 * @cfg {String} summary Specifies a summary of the content of a table
4863 * @cfg {Number} width Specifies the width of a table
4864 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4866 * @cfg {boolean} striped Should the rows be alternative striped
4867 * @cfg {boolean} bordered Add borders to the table
4868 * @cfg {boolean} hover Add hover highlighting
4869 * @cfg {boolean} condensed Format condensed
4870 * @cfg {boolean} responsive Format condensed
4871 * @cfg {Boolean} loadMask (true|false) default false
4872 * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4873 * @cfg {Boolean} thead (true|false) generate thead, default true
4874 * @cfg {Boolean} RowSelection (true|false) default false
4875 * @cfg {Boolean} CellSelection (true|false) default false
4877 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
4881 * Create a new Table
4882 * @param {Object} config The config object
4885 Roo.bootstrap.Table = function(config){
4886 Roo.bootstrap.Table.superclass.constructor.call(this, config);
4889 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4890 this.sm = this.selModel;
4891 this.sm.xmodule = this.xmodule || false;
4893 if (this.cm && typeof(this.cm.config) == 'undefined') {
4894 this.colModel = new Roo.grid.ColumnModel(this.cm);
4895 this.cm = this.colModel;
4896 this.cm.xmodule = this.xmodule || false;
4899 this.store= Roo.factory(this.store, Roo.data);
4900 this.ds = this.store;
4901 this.ds.xmodule = this.xmodule || false;
4904 if (this.footer && this.store) {
4905 this.footer.dataSource = this.ds;
4906 this.footer = Roo.factory(this.footer);
4913 * Fires when a cell is clicked
4914 * @param {Roo.bootstrap.Table} this
4915 * @param {Roo.Element} el
4916 * @param {Number} rowIndex
4917 * @param {Number} columnIndex
4918 * @param {Roo.EventObject} e
4922 * @event celldblclick
4923 * Fires when a cell is double clicked
4924 * @param {Roo.bootstrap.Table} this
4925 * @param {Roo.Element} el
4926 * @param {Number} rowIndex
4927 * @param {Number} columnIndex
4928 * @param {Roo.EventObject} e
4930 "celldblclick" : true,
4933 * Fires when a row is clicked
4934 * @param {Roo.bootstrap.Table} this
4935 * @param {Roo.Element} el
4936 * @param {Number} rowIndex
4937 * @param {Roo.EventObject} e
4941 * @event rowdblclick
4942 * Fires when a row is double clicked
4943 * @param {Roo.bootstrap.Table} this
4944 * @param {Roo.Element} el
4945 * @param {Number} rowIndex
4946 * @param {Roo.EventObject} e
4948 "rowdblclick" : true,
4951 * Fires when a mouseover occur
4952 * @param {Roo.bootstrap.Table} this
4953 * @param {Roo.Element} el
4954 * @param {Number} rowIndex
4955 * @param {Number} columnIndex
4956 * @param {Roo.EventObject} e
4961 * Fires when a mouseout occur
4962 * @param {Roo.bootstrap.Table} this
4963 * @param {Roo.Element} el
4964 * @param {Number} rowIndex
4965 * @param {Number} columnIndex
4966 * @param {Roo.EventObject} e
4971 * Fires when a row is rendered, so you can change add a style to it.
4972 * @param {Roo.bootstrap.Table} this
4973 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
4980 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5004 RowSelection : false,
5005 CellSelection : false,
5008 // Roo.Element - the tbody
5011 getAutoCreate : function(){
5012 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5021 cfg.cls += ' table-striped';
5025 cfg.cls += ' table-hover';
5027 if (this.bordered) {
5028 cfg.cls += ' table-bordered';
5030 if (this.condensed) {
5031 cfg.cls += ' table-condensed';
5033 if (this.responsive) {
5034 cfg.cls += ' table-responsive';
5038 cfg.cls+= ' ' +this.cls;
5041 // this lot should be simplifed...
5044 cfg.align=this.align;
5047 cfg.bgcolor=this.bgcolor;
5050 cfg.border=this.border;
5052 if (this.cellpadding) {
5053 cfg.cellpadding=this.cellpadding;
5055 if (this.cellspacing) {
5056 cfg.cellspacing=this.cellspacing;
5059 cfg.frame=this.frame;
5062 cfg.rules=this.rules;
5064 if (this.sortable) {
5065 cfg.sortable=this.sortable;
5068 cfg.summary=this.summary;
5071 cfg.width=this.width;
5074 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5077 if(this.store || this.cm){
5079 cfg.cn.push(this.renderHeader());
5082 cfg.cn.push(this.renderBody());
5085 cfg.cn.push(this.renderFooter());
5088 cfg.cls+= ' TableGrid';
5091 return { cn : [ cfg ] };
5094 initEvents : function()
5096 if(!this.store || !this.cm){
5100 //Roo.log('initEvents with ds!!!!');
5102 this.mainBody = this.el.select('tbody', true).first();
5107 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5108 e.on('click', _this.sort, _this);
5111 this.el.on("click", this.onClick, this);
5112 this.el.on("dblclick", this.onDblClick, this);
5114 this.parent().el.setStyle('position', 'relative');
5116 this.footer.parentId = this.id;
5117 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5120 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5122 this.store.on('load', this.onLoad, this);
5123 this.store.on('beforeload', this.onBeforeLoad, this);
5124 this.store.on('update', this.onUpdate, this);
5128 onMouseover : function(e, el)
5130 var cell = Roo.get(el);
5136 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5137 cell = cell.findParent('td', false, true);
5140 var row = cell.findParent('tr', false, true);
5141 var cellIndex = cell.dom.cellIndex;
5142 var rowIndex = row.dom.rowIndex - 1; // start from 0
5144 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5148 onMouseout : function(e, el)
5150 var cell = Roo.get(el);
5156 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5157 cell = cell.findParent('td', false, true);
5160 var row = cell.findParent('tr', false, true);
5161 var cellIndex = cell.dom.cellIndex;
5162 var rowIndex = row.dom.rowIndex - 1; // start from 0
5164 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5168 onClick : function(e, el)
5170 var cell = Roo.get(el);
5172 if(!cell || (!this.CellSelection && !this.RowSelection)){
5177 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5178 cell = cell.findParent('td', false, true);
5181 var row = cell.findParent('tr', false, true);
5182 var cellIndex = cell.dom.cellIndex;
5183 var rowIndex = row.dom.rowIndex - 1;
5185 if(this.CellSelection){
5186 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5189 if(this.RowSelection){
5190 this.fireEvent('rowclick', this, row, rowIndex, e);
5196 onDblClick : function(e,el)
5198 var cell = Roo.get(el);
5200 if(!cell || (!this.CellSelection && !this.RowSelection)){
5204 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5205 cell = cell.findParent('td', false, true);
5208 var row = cell.findParent('tr', false, true);
5209 var cellIndex = cell.dom.cellIndex;
5210 var rowIndex = row.dom.rowIndex - 1;
5212 if(this.CellSelection){
5213 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5216 if(this.RowSelection){
5217 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5221 sort : function(e,el)
5223 var col = Roo.get(el)
5225 if(!col.hasClass('sortable')){
5229 var sort = col.attr('sort');
5232 if(col.hasClass('glyphicon-arrow-up')){
5236 this.store.sortInfo = {field : sort, direction : dir};
5239 Roo.log("calling footer first");
5240 this.footer.onClick('first');
5243 this.store.load({ params : { start : 0 } });
5247 renderHeader : function()
5256 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5258 var config = cm.config[i];
5263 html: cm.getColumnHeader(i)
5266 if(typeof(config.hidden) != 'undefined' && config.hidden){
5267 c.style += ' display:none;';
5270 if(typeof(config.dataIndex) != 'undefined'){
5271 c.sort = config.dataIndex;
5274 if(typeof(config.sortable) != 'undefined' && config.sortable){
5278 if(typeof(config.align) != 'undefined' && config.align.length){
5279 c.style += ' text-align:' + config.align + ';';
5282 if(typeof(config.width) != 'undefined'){
5283 c.style += ' width:' + config.width + 'px;';
5292 renderBody : function()
5302 colspan : this.cm.getColumnCount()
5312 renderFooter : function()
5322 colspan : this.cm.getColumnCount()
5336 Roo.log('ds onload');
5341 var ds = this.store;
5343 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5344 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5346 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5347 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5350 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5351 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5355 var tbody = this.mainBody;
5357 if(ds.getCount() > 0){
5358 ds.data.each(function(d,rowIndex){
5359 var row = this.renderRow(cm, ds, rowIndex);
5361 tbody.createChild(row);
5365 if(row.cellObjects.length){
5366 Roo.each(row.cellObjects, function(r){
5367 _this.renderCellObject(r);
5374 Roo.each(this.el.select('tbody td', true).elements, function(e){
5375 e.on('mouseover', _this.onMouseover, _this);
5378 Roo.each(this.el.select('tbody td', true).elements, function(e){
5379 e.on('mouseout', _this.onMouseout, _this);
5382 //if(this.loadMask){
5383 // this.maskEl.hide();
5388 onUpdate : function(ds,record)
5390 this.refreshRow(record);
5392 onRemove : function(ds, record, index, isUpdate){
5393 if(isUpdate !== true){
5394 this.fireEvent("beforerowremoved", this, index, record);
5396 var bt = this.mainBody.dom;
5398 bt.removeChild(bt.rows[index]);
5401 if(isUpdate !== true){
5402 //this.stripeRows(index);
5403 //this.syncRowHeights(index, index);
5405 this.fireEvent("rowremoved", this, index, record);
5410 refreshRow : function(record){
5411 var ds = this.store, index;
5412 if(typeof record == 'number'){
5414 record = ds.getAt(index);
5416 index = ds.indexOf(record);
5418 this.insertRow(ds, index, true);
5419 this.onRemove(ds, record, index+1, true);
5420 //this.syncRowHeights(index, index);
5422 this.fireEvent("rowupdated", this, index, record);
5425 insertRow : function(dm, rowIndex, isUpdate){
5428 this.fireEvent("beforerowsinserted", this, rowIndex);
5430 //var s = this.getScrollState();
5431 var row = this.renderRow(this.cm, this.store, rowIndex);
5432 // insert before rowIndex..
5433 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5437 if(row.cellObjects.length){
5438 Roo.each(row.cellObjects, function(r){
5439 _this.renderCellObject(r);
5444 this.fireEvent("rowsinserted", this, rowIndex);
5445 //this.syncRowHeights(firstRow, lastRow);
5446 //this.stripeRows(firstRow);
5453 getRowDom : function(rowIndex)
5455 // not sure if I need to check this.. but let's do it anyway..
5456 return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5457 this.mainBody.dom.rows[rowIndex] : false
5459 // returns the object tree for a tr..
5462 renderRow : function(cm, ds, rowIndex) {
5464 var d = ds.getAt(rowIndex);
5471 var cellObjects = [];
5473 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5474 var config = cm.config[i];
5476 var renderer = cm.getRenderer(i);
5480 if(typeof(renderer) !== 'undefined'){
5481 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5483 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5484 // and are rendered into the cells after the row is rendered - using the id for the element.
5486 if(typeof(value) === 'object'){
5496 rowIndex : rowIndex,
5501 this.fireEvent('rowclass', this, rowcfg);
5505 cls : rowcfg.rowClass,
5507 html: (typeof(value) === 'object') ? '' : value
5514 if(typeof(config.hidden) != 'undefined' && config.hidden){
5515 td.style += ' display:none;';
5518 if(typeof(config.align) != 'undefined' && config.align.length){
5519 td.style += ' text-align:' + config.align + ';';
5522 if(typeof(config.width) != 'undefined'){
5523 td.style += ' width:' + config.width + 'px;';
5530 row.cellObjects = cellObjects;
5538 onBeforeLoad : function()
5540 //Roo.log('ds onBeforeLoad');
5544 //if(this.loadMask){
5545 // this.maskEl.show();
5551 this.el.select('tbody', true).first().dom.innerHTML = '';
5554 getSelectionModel : function(){
5556 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5558 return this.selModel;
5561 * Render the Roo.bootstrap object from renderder
5563 renderCellObject : function(r)
5567 var t = r.cfg.render(r.container);
5570 Roo.each(r.cfg.cn, function(c){
5572 container: t.getChildContainer(),
5575 _this.renderCellObject(child);
5592 * @class Roo.bootstrap.TableCell
5593 * @extends Roo.bootstrap.Component
5594 * Bootstrap TableCell class
5595 * @cfg {String} html cell contain text
5596 * @cfg {String} cls cell class
5597 * @cfg {String} tag cell tag (td|th) default td
5598 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5599 * @cfg {String} align Aligns the content in a cell
5600 * @cfg {String} axis Categorizes cells
5601 * @cfg {String} bgcolor Specifies the background color of a cell
5602 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5603 * @cfg {Number} colspan Specifies the number of columns a cell should span
5604 * @cfg {String} headers Specifies one or more header cells a cell is related to
5605 * @cfg {Number} height Sets the height of a cell
5606 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5607 * @cfg {Number} rowspan Sets the number of rows a cell should span
5608 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5609 * @cfg {String} valign Vertical aligns the content in a cell
5610 * @cfg {Number} width Specifies the width of a cell
5613 * Create a new TableCell
5614 * @param {Object} config The config object
5617 Roo.bootstrap.TableCell = function(config){
5618 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5621 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
5641 getAutoCreate : function(){
5642 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5662 cfg.align=this.align
5668 cfg.bgcolor=this.bgcolor
5671 cfg.charoff=this.charoff
5674 cfg.colspan=this.colspan
5677 cfg.headers=this.headers
5680 cfg.height=this.height
5683 cfg.nowrap=this.nowrap
5686 cfg.rowspan=this.rowspan
5689 cfg.scope=this.scope
5692 cfg.valign=this.valign
5695 cfg.width=this.width
5714 * @class Roo.bootstrap.TableRow
5715 * @extends Roo.bootstrap.Component
5716 * Bootstrap TableRow class
5717 * @cfg {String} cls row class
5718 * @cfg {String} align Aligns the content in a table row
5719 * @cfg {String} bgcolor Specifies a background color for a table row
5720 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5721 * @cfg {String} valign Vertical aligns the content in a table row
5724 * Create a new TableRow
5725 * @param {Object} config The config object
5728 Roo.bootstrap.TableRow = function(config){
5729 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5732 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
5740 getAutoCreate : function(){
5741 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5751 cfg.align = this.align;
5754 cfg.bgcolor = this.bgcolor;
5757 cfg.charoff = this.charoff;
5760 cfg.valign = this.valign;
5778 * @class Roo.bootstrap.TableBody
5779 * @extends Roo.bootstrap.Component
5780 * Bootstrap TableBody class
5781 * @cfg {String} cls element class
5782 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5783 * @cfg {String} align Aligns the content inside the element
5784 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5785 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5788 * Create a new TableBody
5789 * @param {Object} config The config object
5792 Roo.bootstrap.TableBody = function(config){
5793 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5796 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
5804 getAutoCreate : function(){
5805 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5819 cfg.align = this.align;
5822 cfg.charoff = this.charoff;
5825 cfg.valign = this.valign;
5832 // initEvents : function()
5839 // this.store = Roo.factory(this.store, Roo.data);
5840 // this.store.on('load', this.onLoad, this);
5842 // this.store.load();
5846 // onLoad: function ()
5848 // this.fireEvent('load', this);
5858 * Ext JS Library 1.1.1
5859 * Copyright(c) 2006-2007, Ext JS, LLC.
5861 * Originally Released Under LGPL - original licence link has changed is not relivant.
5864 * <script type="text/javascript">
5867 // as we use this in bootstrap.
5868 Roo.namespace('Roo.form');
5870 * @class Roo.form.Action
5871 * Internal Class used to handle form actions
5873 * @param {Roo.form.BasicForm} el The form element or its id
5874 * @param {Object} config Configuration options
5879 // define the action interface
5880 Roo.form.Action = function(form, options){
5882 this.options = options || {};
5885 * Client Validation Failed
5888 Roo.form.Action.CLIENT_INVALID = 'client';
5890 * Server Validation Failed
5893 Roo.form.Action.SERVER_INVALID = 'server';
5895 * Connect to Server Failed
5898 Roo.form.Action.CONNECT_FAILURE = 'connect';
5900 * Reading Data from Server Failed
5903 Roo.form.Action.LOAD_FAILURE = 'load';
5905 Roo.form.Action.prototype = {
5907 failureType : undefined,
5908 response : undefined,
5912 run : function(options){
5917 success : function(response){
5922 handleResponse : function(response){
5926 // default connection failure
5927 failure : function(response){
5929 this.response = response;
5930 this.failureType = Roo.form.Action.CONNECT_FAILURE;
5931 this.form.afterAction(this, false);
5934 processResponse : function(response){
5935 this.response = response;
5936 if(!response.responseText){
5939 this.result = this.handleResponse(response);
5943 // utility functions used internally
5944 getUrl : function(appendParams){
5945 var url = this.options.url || this.form.url || this.form.el.dom.action;
5947 var p = this.getParams();
5949 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5955 getMethod : function(){
5956 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5959 getParams : function(){
5960 var bp = this.form.baseParams;
5961 var p = this.options.params;
5963 if(typeof p == "object"){
5964 p = Roo.urlEncode(Roo.applyIf(p, bp));
5965 }else if(typeof p == 'string' && bp){
5966 p += '&' + Roo.urlEncode(bp);
5969 p = Roo.urlEncode(bp);
5974 createCallback : function(){
5976 success: this.success,
5977 failure: this.failure,
5979 timeout: (this.form.timeout*1000),
5980 upload: this.form.fileUpload ? this.success : undefined
5985 Roo.form.Action.Submit = function(form, options){
5986 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5989 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5992 haveProgress : false,
5993 uploadComplete : false,
5995 // uploadProgress indicator.
5996 uploadProgress : function()
5998 if (!this.form.progressUrl) {
6002 if (!this.haveProgress) {
6003 Roo.MessageBox.progress("Uploading", "Uploading");
6005 if (this.uploadComplete) {
6006 Roo.MessageBox.hide();
6010 this.haveProgress = true;
6012 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6014 var c = new Roo.data.Connection();
6016 url : this.form.progressUrl,
6021 success : function(req){
6022 //console.log(data);
6026 rdata = Roo.decode(req.responseText)
6028 Roo.log("Invalid data from server..");
6032 if (!rdata || !rdata.success) {
6034 Roo.MessageBox.alert(Roo.encode(rdata));
6037 var data = rdata.data;
6039 if (this.uploadComplete) {
6040 Roo.MessageBox.hide();
6045 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6046 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6049 this.uploadProgress.defer(2000,this);
6052 failure: function(data) {
6053 Roo.log('progress url failed ');
6064 // run get Values on the form, so it syncs any secondary forms.
6065 this.form.getValues();
6067 var o = this.options;
6068 var method = this.getMethod();
6069 var isPost = method == 'POST';
6070 if(o.clientValidation === false || this.form.isValid()){
6072 if (this.form.progressUrl) {
6073 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6074 (new Date() * 1) + '' + Math.random());
6079 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6080 form:this.form.el.dom,
6081 url:this.getUrl(!isPost),
6083 params:isPost ? this.getParams() : null,
6084 isUpload: this.form.fileUpload
6087 this.uploadProgress();
6089 }else if (o.clientValidation !== false){ // client validation failed
6090 this.failureType = Roo.form.Action.CLIENT_INVALID;
6091 this.form.afterAction(this, false);
6095 success : function(response)
6097 this.uploadComplete= true;
6098 if (this.haveProgress) {
6099 Roo.MessageBox.hide();
6103 var result = this.processResponse(response);
6104 if(result === true || result.success){
6105 this.form.afterAction(this, true);
6109 this.form.markInvalid(result.errors);
6110 this.failureType = Roo.form.Action.SERVER_INVALID;
6112 this.form.afterAction(this, false);
6114 failure : function(response)
6116 this.uploadComplete= true;
6117 if (this.haveProgress) {
6118 Roo.MessageBox.hide();
6121 this.response = response;
6122 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6123 this.form.afterAction(this, false);
6126 handleResponse : function(response){
6127 if(this.form.errorReader){
6128 var rs = this.form.errorReader.read(response);
6131 for(var i = 0, len = rs.records.length; i < len; i++) {
6132 var r = rs.records[i];
6136 if(errors.length < 1){
6140 success : rs.success,
6146 ret = Roo.decode(response.responseText);
6150 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6160 Roo.form.Action.Load = function(form, options){
6161 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6162 this.reader = this.form.reader;
6165 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6170 Roo.Ajax.request(Roo.apply(
6171 this.createCallback(), {
6172 method:this.getMethod(),
6173 url:this.getUrl(false),
6174 params:this.getParams()
6178 success : function(response){
6180 var result = this.processResponse(response);
6181 if(result === true || !result.success || !result.data){
6182 this.failureType = Roo.form.Action.LOAD_FAILURE;
6183 this.form.afterAction(this, false);
6186 this.form.clearInvalid();
6187 this.form.setValues(result.data);
6188 this.form.afterAction(this, true);
6191 handleResponse : function(response){
6192 if(this.form.reader){
6193 var rs = this.form.reader.read(response);
6194 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6196 success : rs.success,
6200 return Roo.decode(response.responseText);
6204 Roo.form.Action.ACTION_TYPES = {
6205 'load' : Roo.form.Action.Load,
6206 'submit' : Roo.form.Action.Submit
6215 * @class Roo.bootstrap.Form
6216 * @extends Roo.bootstrap.Component
6217 * Bootstrap Form class
6218 * @cfg {String} method GET | POST (default POST)
6219 * @cfg {String} labelAlign top | left (default top)
6220 * @cfg {String} align left | right - for navbars
6221 * @cfg {Boolean} loadMask load mask when submit (default true)
6226 * @param {Object} config The config object
6230 Roo.bootstrap.Form = function(config){
6231 Roo.bootstrap.Form.superclass.constructor.call(this, config);
6234 * @event clientvalidation
6235 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6236 * @param {Form} this
6237 * @param {Boolean} valid true if the form has passed client-side validation
6239 clientvalidation: true,
6241 * @event beforeaction
6242 * Fires before any action is performed. Return false to cancel the action.
6243 * @param {Form} this
6244 * @param {Action} action The action to be performed
6248 * @event actionfailed
6249 * Fires when an action fails.
6250 * @param {Form} this
6251 * @param {Action} action The action that failed
6253 actionfailed : true,
6255 * @event actioncomplete
6256 * Fires when an action is completed.
6257 * @param {Form} this
6258 * @param {Action} action The action that completed
6260 actioncomplete : true
6265 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
6268 * @cfg {String} method
6269 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6274 * The URL to use for form actions if one isn't supplied in the action options.
6277 * @cfg {Boolean} fileUpload
6278 * Set to true if this form is a file upload.
6282 * @cfg {Object} baseParams
6283 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6287 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6291 * @cfg {Sting} align (left|right) for navbar forms
6296 activeAction : null,
6299 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6300 * element by passing it or its id or mask the form itself by passing in true.
6303 waitMsgTarget : false,
6307 getAutoCreate : function(){
6311 method : this.method || 'POST',
6312 id : this.id || Roo.id(),
6315 if (this.parent().xtype.match(/^Nav/)) {
6316 cfg.cls = 'navbar-form navbar-' + this.align;
6320 if (this.labelAlign == 'left' ) {
6321 cfg.cls += ' form-horizontal';
6327 initEvents : function()
6329 this.el.on('submit', this.onSubmit, this);
6330 // this was added as random key presses on the form where triggering form submit.
6331 this.el.on('keypress', function(e) {
6332 if (e.getCharCode() != 13) {
6335 // we might need to allow it for textareas.. and some other items.
6336 // check e.getTarget().
6338 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6342 Roo.log("keypress blocked");
6350 onSubmit : function(e){
6355 * Returns true if client-side validation on the form is successful.
6358 isValid : function(){
6359 var items = this.getItems();
6361 items.each(function(f){
6370 * Returns true if any fields in this form have changed since their original load.
6373 isDirty : function(){
6375 var items = this.getItems();
6376 items.each(function(f){
6386 * Performs a predefined action (submit or load) or custom actions you define on this form.
6387 * @param {String} actionName The name of the action type
6388 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
6389 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6390 * accept other config options):
6392 Property Type Description
6393 ---------------- --------------- ----------------------------------------------------------------------------------
6394 url String The url for the action (defaults to the form's url)
6395 method String The form method to use (defaults to the form's method, or POST if not defined)
6396 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
6397 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
6398 validate the form on the client (defaults to false)
6400 * @return {BasicForm} this
6402 doAction : function(action, options){
6403 if(typeof action == 'string'){
6404 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6406 if(this.fireEvent('beforeaction', this, action) !== false){
6407 this.beforeAction(action);
6408 action.run.defer(100, action);
6414 beforeAction : function(action){
6415 var o = action.options;
6418 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6420 // not really supported yet.. ??
6422 //if(this.waitMsgTarget === true){
6423 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6424 //}else if(this.waitMsgTarget){
6425 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6426 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6428 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6434 afterAction : function(action, success){
6435 this.activeAction = null;
6436 var o = action.options;
6438 //if(this.waitMsgTarget === true){
6440 //}else if(this.waitMsgTarget){
6441 // this.waitMsgTarget.unmask();
6443 // Roo.MessageBox.updateProgress(1);
6444 // Roo.MessageBox.hide();
6451 Roo.callback(o.success, o.scope, [this, action]);
6452 this.fireEvent('actioncomplete', this, action);
6456 // failure condition..
6457 // we have a scenario where updates need confirming.
6458 // eg. if a locking scenario exists..
6459 // we look for { errors : { needs_confirm : true }} in the response.
6461 (typeof(action.result) != 'undefined') &&
6462 (typeof(action.result.errors) != 'undefined') &&
6463 (typeof(action.result.errors.needs_confirm) != 'undefined')
6466 Roo.log("not supported yet");
6469 Roo.MessageBox.confirm(
6470 "Change requires confirmation",
6471 action.result.errorMsg,
6476 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
6486 Roo.callback(o.failure, o.scope, [this, action]);
6487 // show an error message if no failed handler is set..
6488 if (!this.hasListener('actionfailed')) {
6489 Roo.log("need to add dialog support");
6491 Roo.MessageBox.alert("Error",
6492 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6493 action.result.errorMsg :
6494 "Saving Failed, please check your entries or try again"
6499 this.fireEvent('actionfailed', this, action);
6504 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6505 * @param {String} id The value to search for
6508 findField : function(id){
6509 var items = this.getItems();
6510 var field = items.get(id);
6512 items.each(function(f){
6513 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6520 return field || null;
6523 * Mark fields in this form invalid in bulk.
6524 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6525 * @return {BasicForm} this
6527 markInvalid : function(errors){
6528 if(errors instanceof Array){
6529 for(var i = 0, len = errors.length; i < len; i++){
6530 var fieldError = errors[i];
6531 var f = this.findField(fieldError.id);
6533 f.markInvalid(fieldError.msg);
6539 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6540 field.markInvalid(errors[id]);
6544 //Roo.each(this.childForms || [], function (f) {
6545 // f.markInvalid(errors);
6552 * Set values for fields in this form in bulk.
6553 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6554 * @return {BasicForm} this
6556 setValues : function(values){
6557 if(values instanceof Array){ // array of objects
6558 for(var i = 0, len = values.length; i < len; i++){
6560 var f = this.findField(v.id);
6562 f.setValue(v.value);
6563 if(this.trackResetOnLoad){
6564 f.originalValue = f.getValue();
6568 }else{ // object hash
6571 if(typeof values[id] != 'function' && (field = this.findField(id))){
6573 if (field.setFromData &&
6575 field.displayField &&
6576 // combos' with local stores can
6577 // be queried via setValue()
6578 // to set their value..
6579 (field.store && !field.store.isLocal)
6583 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6584 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6585 field.setFromData(sd);
6588 field.setValue(values[id]);
6592 if(this.trackResetOnLoad){
6593 field.originalValue = field.getValue();
6599 //Roo.each(this.childForms || [], function (f) {
6600 // f.setValues(values);
6607 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6608 * they are returned as an array.
6609 * @param {Boolean} asString
6612 getValues : function(asString){
6613 //if (this.childForms) {
6614 // copy values from the child forms
6615 // Roo.each(this.childForms, function (f) {
6616 // this.setValues(f.getValues());
6622 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6623 if(asString === true){
6626 return Roo.urlDecode(fs);
6630 * Returns the fields in this form as an object with key/value pairs.
6631 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6634 getFieldValues : function(with_hidden)
6636 var items = this.getItems();
6638 items.each(function(f){
6642 var v = f.getValue();
6643 if (f.inputType =='radio') {
6644 if (typeof(ret[f.getName()]) == 'undefined') {
6645 ret[f.getName()] = ''; // empty..
6648 if (!f.el.dom.checked) {
6656 // not sure if this supported any more..
6657 if ((typeof(v) == 'object') && f.getRawValue) {
6658 v = f.getRawValue() ; // dates..
6660 // combo boxes where name != hiddenName...
6661 if (f.name != f.getName()) {
6662 ret[f.name] = f.getRawValue();
6664 ret[f.getName()] = v;
6671 * Clears all invalid messages in this form.
6672 * @return {BasicForm} this
6674 clearInvalid : function(){
6675 var items = this.getItems();
6677 items.each(function(f){
6688 * @return {BasicForm} this
6691 var items = this.getItems();
6692 items.each(function(f){
6696 Roo.each(this.childForms || [], function (f) {
6703 getItems : function()
6705 var r=new Roo.util.MixedCollection(false, function(o){
6706 return o.id || (o.id = Roo.id());
6708 var iter = function(el) {
6715 Roo.each(el.items,function(e) {
6734 * Ext JS Library 1.1.1
6735 * Copyright(c) 2006-2007, Ext JS, LLC.
6737 * Originally Released Under LGPL - original licence link has changed is not relivant.
6740 * <script type="text/javascript">
6743 * @class Roo.form.VTypes
6744 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6747 Roo.form.VTypes = function(){
6748 // closure these in so they are only created once.
6749 var alpha = /^[a-zA-Z_]+$/;
6750 var alphanum = /^[a-zA-Z0-9_]+$/;
6751 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6752 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6754 // All these messages and functions are configurable
6757 * The function used to validate email addresses
6758 * @param {String} value The email address
6760 'email' : function(v){
6761 return email.test(v);
6764 * The error text to display when the email validation function returns false
6767 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6769 * The keystroke filter mask to be applied on email input
6772 'emailMask' : /[a-z0-9_\.\-@]/i,
6775 * The function used to validate URLs
6776 * @param {String} value The URL
6778 'url' : function(v){
6782 * The error text to display when the url validation function returns false
6785 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6788 * The function used to validate alpha values
6789 * @param {String} value The value
6791 'alpha' : function(v){
6792 return alpha.test(v);
6795 * The error text to display when the alpha validation function returns false
6798 'alphaText' : 'This field should only contain letters and _',
6800 * The keystroke filter mask to be applied on alpha input
6803 'alphaMask' : /[a-z_]/i,
6806 * The function used to validate alphanumeric values
6807 * @param {String} value The value
6809 'alphanum' : function(v){
6810 return alphanum.test(v);
6813 * The error text to display when the alphanumeric validation function returns false
6816 'alphanumText' : 'This field should only contain letters, numbers and _',
6818 * The keystroke filter mask to be applied on alphanumeric input
6821 'alphanumMask' : /[a-z0-9_]/i
6831 * @class Roo.bootstrap.Input
6832 * @extends Roo.bootstrap.Component
6833 * Bootstrap Input class
6834 * @cfg {Boolean} disabled is it disabled
6835 * @cfg {String} fieldLabel - the label associated
6836 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6837 * @cfg {String} name name of the input
6838 * @cfg {string} fieldLabel - the label associated
6839 * @cfg {string} inputType - input / file submit ...
6840 * @cfg {string} placeholder - placeholder to put in text.
6841 * @cfg {string} before - input group add on before
6842 * @cfg {string} after - input group add on after
6843 * @cfg {string} size - (lg|sm) or leave empty..
6844 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6845 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6846 * @cfg {Number} md colspan out of 12 for computer-sized screens
6847 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6848 * @cfg {string} value default value of the input
6849 * @cfg {Number} labelWidth set the width of label (0-12)
6850 * @cfg {String} labelAlign (top|left)
6851 * @cfg {Boolean} readOnly Specifies that the field should be read-only
6852 * @cfg {String} align (left|center|right) Default left
6856 * Create a new Input
6857 * @param {Object} config The config object
6860 Roo.bootstrap.Input = function(config){
6861 Roo.bootstrap.Input.superclass.constructor.call(this, config);
6866 * Fires when this field receives input focus.
6867 * @param {Roo.form.Field} this
6872 * Fires when this field loses input focus.
6873 * @param {Roo.form.Field} this
6878 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
6879 * {@link Roo.EventObject#getKey} to determine which key was pressed.
6880 * @param {Roo.form.Field} this
6881 * @param {Roo.EventObject} e The event object
6886 * Fires just before the field blurs if the field value has changed.
6887 * @param {Roo.form.Field} this
6888 * @param {Mixed} newValue The new value
6889 * @param {Mixed} oldValue The original value
6894 * Fires after the field has been marked as invalid.
6895 * @param {Roo.form.Field} this
6896 * @param {String} msg The validation message
6901 * Fires after the field has been validated with no errors.
6902 * @param {Roo.form.Field} this
6907 * Fires after the key up
6908 * @param {Roo.form.Field} this
6909 * @param {Roo.EventObject} e The event Object
6915 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
6917 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6918 automatic validation (defaults to "keyup").
6920 validationEvent : "keyup",
6922 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6924 validateOnBlur : true,
6926 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6928 validationDelay : 250,
6930 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6932 focusClass : "x-form-focus", // not needed???
6936 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6938 invalidClass : "has-error",
6941 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6943 selectOnFocus : false,
6946 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6950 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6955 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6957 disableKeyFilter : false,
6960 * @cfg {Boolean} disabled True to disable the field (defaults to false).
6964 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6968 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6970 blankText : "This field is required",
6973 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6977 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6979 maxLength : Number.MAX_VALUE,
6981 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6983 minLengthText : "The minimum length for this field is {0}",
6985 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6987 maxLengthText : "The maximum length for this field is {0}",
6991 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6992 * If available, this function will be called only after the basic validators all return true, and will be passed the
6993 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6997 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6998 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6999 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7003 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7026 formatedValue : false,
7028 parentLabelAlign : function()
7031 while (parent.parent()) {
7032 parent = parent.parent();
7033 if (typeof(parent.labelAlign) !='undefined') {
7034 return parent.labelAlign;
7041 getAutoCreate : function(){
7043 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7049 if(this.inputType != 'hidden'){
7050 cfg.cls = 'form-group' //input-group
7056 type : this.inputType,
7058 cls : 'form-control',
7059 placeholder : this.placeholder || ''
7064 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7067 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7068 input.maxLength = this.maxLength;
7071 if (this.disabled) {
7072 input.disabled=true;
7075 if (this.readOnly) {
7076 input.readonly=true;
7080 input.name = this.name;
7083 input.cls += ' input-' + this.size;
7086 ['xs','sm','md','lg'].map(function(size){
7087 if (settings[size]) {
7088 cfg.cls += ' col-' + size + '-' + settings[size];
7092 var inputblock = input;
7094 if (this.before || this.after) {
7097 cls : 'input-group',
7100 if (this.before && typeof(this.before) == 'string') {
7102 inputblock.cn.push({
7104 cls : 'roo-input-before input-group-addon',
7108 if (this.before && typeof(this.before) == 'object') {
7109 this.before = Roo.factory(this.before);
7110 Roo.log(this.before);
7111 inputblock.cn.push({
7113 cls : 'roo-input-before input-group-' +
7114 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7118 inputblock.cn.push(input);
7120 if (this.after && typeof(this.after) == 'string') {
7121 inputblock.cn.push({
7123 cls : 'roo-input-after input-group-addon',
7127 if (this.after && typeof(this.after) == 'object') {
7128 this.after = Roo.factory(this.after);
7129 Roo.log(this.after);
7130 inputblock.cn.push({
7132 cls : 'roo-input-after input-group-' +
7133 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7138 if (align ==='left' && this.fieldLabel.length) {
7139 Roo.log("left and has label");
7145 cls : 'control-label col-sm-' + this.labelWidth,
7146 html : this.fieldLabel
7150 cls : "col-sm-" + (12 - this.labelWidth),
7157 } else if ( this.fieldLabel.length) {
7163 //cls : 'input-group-addon',
7164 html : this.fieldLabel
7174 Roo.log(" no label && no align");
7183 Roo.log('input-parentType: ' + this.parentType);
7185 if (this.parentType === 'Navbar' && this.parent().bar) {
7186 cfg.cls += ' navbar-form';
7194 * return the real input element.
7196 inputEl: function ()
7198 return this.el.select('input.form-control',true).first();
7200 setDisabled : function(v)
7202 var i = this.inputEl().dom;
7204 i.removeAttribute('disabled');
7208 i.setAttribute('disabled','true');
7210 initEvents : function()
7213 this.inputEl().on("keydown" , this.fireKey, this);
7214 this.inputEl().on("focus", this.onFocus, this);
7215 this.inputEl().on("blur", this.onBlur, this);
7217 this.inputEl().relayEvent('keyup', this);
7219 // reference to original value for reset
7220 this.originalValue = this.getValue();
7221 //Roo.form.TextField.superclass.initEvents.call(this);
7222 if(this.validationEvent == 'keyup'){
7223 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7224 this.inputEl().on('keyup', this.filterValidation, this);
7226 else if(this.validationEvent !== false){
7227 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7230 if(this.selectOnFocus){
7231 this.on("focus", this.preFocus, this);
7234 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7235 this.inputEl().on("keypress", this.filterKeys, this);
7238 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
7239 this.el.on("click", this.autoSize, this);
7242 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7243 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7246 if (typeof(this.before) == 'object') {
7247 this.before.render(this.el.select('.roo-input-before',true).first());
7249 if (typeof(this.after) == 'object') {
7250 this.after.render(this.el.select('.roo-input-after',true).first());
7255 filterValidation : function(e){
7256 if(!e.isNavKeyPress()){
7257 this.validationTask.delay(this.validationDelay);
7261 * Validates the field value
7262 * @return {Boolean} True if the value is valid, else false
7264 validate : function(){
7265 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7266 if(this.disabled || this.validateValue(this.getRawValue())){
7267 this.clearInvalid();
7275 * Validates a value according to the field's validation rules and marks the field as invalid
7276 * if the validation fails
7277 * @param {Mixed} value The value to validate
7278 * @return {Boolean} True if the value is valid, else false
7280 validateValue : function(value){
7281 if(value.length < 1) { // if it's blank
7282 if(this.allowBlank){
7283 this.clearInvalid();
7286 this.markInvalid(this.blankText);
7290 if(value.length < this.minLength){
7291 this.markInvalid(String.format(this.minLengthText, this.minLength));
7294 if(value.length > this.maxLength){
7295 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7299 var vt = Roo.form.VTypes;
7300 if(!vt[this.vtype](value, this)){
7301 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7305 if(typeof this.validator == "function"){
7306 var msg = this.validator(value);
7308 this.markInvalid(msg);
7312 if(this.regex && !this.regex.test(value)){
7313 this.markInvalid(this.regexText);
7322 fireKey : function(e){
7323 //Roo.log('field ' + e.getKey());
7324 if(e.isNavKeyPress()){
7325 this.fireEvent("specialkey", this, e);
7328 focus : function (selectText){
7330 this.inputEl().focus();
7331 if(selectText === true){
7332 this.inputEl().dom.select();
7338 onFocus : function(){
7339 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7340 // this.el.addClass(this.focusClass);
7343 this.hasFocus = true;
7344 this.startValue = this.getValue();
7345 this.fireEvent("focus", this);
7349 beforeBlur : Roo.emptyFn,
7353 onBlur : function(){
7355 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7356 //this.el.removeClass(this.focusClass);
7358 this.hasFocus = false;
7359 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7362 var v = this.getValue();
7363 if(String(v) !== String(this.startValue)){
7364 this.fireEvent('change', this, v, this.startValue);
7366 this.fireEvent("blur", this);
7370 * Resets the current field value to the originally loaded value and clears any validation messages
7373 this.setValue(this.originalValue);
7374 this.clearInvalid();
7377 * Returns the name of the field
7378 * @return {Mixed} name The name field
7380 getName: function(){
7384 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
7385 * @return {Mixed} value The field value
7387 getValue : function(){
7389 var v = this.inputEl().getValue();
7394 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
7395 * @return {Mixed} value The field value
7397 getRawValue : function(){
7398 var v = this.inputEl().getValue();
7404 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
7405 * @param {Mixed} value The value to set
7407 setRawValue : function(v){
7408 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7411 selectText : function(start, end){
7412 var v = this.getRawValue();
7414 start = start === undefined ? 0 : start;
7415 end = end === undefined ? v.length : end;
7416 var d = this.inputEl().dom;
7417 if(d.setSelectionRange){
7418 d.setSelectionRange(start, end);
7419 }else if(d.createTextRange){
7420 var range = d.createTextRange();
7421 range.moveStart("character", start);
7422 range.moveEnd("character", v.length-end);
7429 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
7430 * @param {Mixed} value The value to set
7432 setValue : function(v){
7435 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7441 processValue : function(value){
7442 if(this.stripCharsRe){
7443 var newValue = value.replace(this.stripCharsRe, '');
7444 if(newValue !== value){
7445 this.setRawValue(newValue);
7452 preFocus : function(){
7454 if(this.selectOnFocus){
7455 this.inputEl().dom.select();
7458 filterKeys : function(e){
7460 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7463 var c = e.getCharCode(), cc = String.fromCharCode(c);
7464 if(Roo.isIE && (e.isSpecialKey() || !cc)){
7467 if(!this.maskRe.test(cc)){
7472 * Clear any invalid styles/messages for this field
7474 clearInvalid : function(){
7476 if(!this.el || this.preventMark){ // not rendered
7479 this.el.removeClass(this.invalidClass);
7481 switch(this.msgTarget){
7483 this.el.dom.qtip = '';
7486 this.el.dom.title = '';
7490 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7495 this.errorIcon.dom.qtip = '';
7496 this.errorIcon.hide();
7497 this.un('resize', this.alignErrorIcon, this);
7501 var t = Roo.getDom(this.msgTarget);
7503 t.style.display = 'none';
7507 this.fireEvent('valid', this);
7510 * Mark this field as invalid
7511 * @param {String} msg The validation message
7513 markInvalid : function(msg){
7514 if(!this.el || this.preventMark){ // not rendered
7517 this.el.addClass(this.invalidClass);
7519 msg = msg || this.invalidText;
7520 switch(this.msgTarget){
7522 this.el.dom.qtip = msg;
7523 this.el.dom.qclass = 'x-form-invalid-tip';
7524 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7525 Roo.QuickTips.enable();
7529 this.el.dom.title = msg;
7533 var elp = this.el.findParent('.x-form-element', 5, true);
7534 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7535 this.errorEl.setWidth(elp.getWidth(true)-20);
7537 this.errorEl.update(msg);
7538 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7541 if(!this.errorIcon){
7542 var elp = this.el.findParent('.x-form-element', 5, true);
7543 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7545 this.alignErrorIcon();
7546 this.errorIcon.dom.qtip = msg;
7547 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7548 this.errorIcon.show();
7549 this.on('resize', this.alignErrorIcon, this);
7552 var t = Roo.getDom(this.msgTarget);
7554 t.style.display = this.msgDisplay;
7558 this.fireEvent('invalid', this, msg);
7561 SafariOnKeyDown : function(event)
7563 // this is a workaround for a password hang bug on chrome/ webkit.
7565 var isSelectAll = false;
7567 if(this.inputEl().dom.selectionEnd > 0){
7568 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7570 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7571 event.preventDefault();
7576 if(isSelectAll){ // backspace and delete key
7578 event.preventDefault();
7579 // this is very hacky as keydown always get's upper case.
7581 var cc = String.fromCharCode(event.getCharCode());
7582 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
7586 adjustWidth : function(tag, w){
7587 tag = tag.toLowerCase();
7588 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7589 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7593 if(tag == 'textarea'){
7596 }else if(Roo.isOpera){
7600 if(tag == 'textarea'){
7619 * @class Roo.bootstrap.TextArea
7620 * @extends Roo.bootstrap.Input
7621 * Bootstrap TextArea class
7622 * @cfg {Number} cols Specifies the visible width of a text area
7623 * @cfg {Number} rows Specifies the visible number of lines in a text area
7624 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7625 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7626 * @cfg {string} html text
7629 * Create a new TextArea
7630 * @param {Object} config The config object
7633 Roo.bootstrap.TextArea = function(config){
7634 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7638 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
7648 getAutoCreate : function(){
7650 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7661 value : this.value || '',
7662 html: this.html || '',
7663 cls : 'form-control',
7664 placeholder : this.placeholder || ''
7668 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7669 input.maxLength = this.maxLength;
7673 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7677 input.cols = this.cols;
7680 if (this.readOnly) {
7681 input.readonly = true;
7685 input.name = this.name;
7689 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7693 ['xs','sm','md','lg'].map(function(size){
7694 if (settings[size]) {
7695 cfg.cls += ' col-' + size + '-' + settings[size];
7699 var inputblock = input;
7701 if (this.before || this.after) {
7704 cls : 'input-group',
7708 inputblock.cn.push({
7710 cls : 'input-group-addon',
7714 inputblock.cn.push(input);
7716 inputblock.cn.push({
7718 cls : 'input-group-addon',
7725 if (align ==='left' && this.fieldLabel.length) {
7726 Roo.log("left and has label");
7732 cls : 'control-label col-sm-' + this.labelWidth,
7733 html : this.fieldLabel
7737 cls : "col-sm-" + (12 - this.labelWidth),
7744 } else if ( this.fieldLabel.length) {
7750 //cls : 'input-group-addon',
7751 html : this.fieldLabel
7761 Roo.log(" no label && no align");
7771 if (this.disabled) {
7772 input.disabled=true;
7779 * return the real textarea element.
7781 inputEl: function ()
7783 return this.el.select('textarea.form-control',true).first();
7791 * trigger field - base class for combo..
7796 * @class Roo.bootstrap.TriggerField
7797 * @extends Roo.bootstrap.Input
7798 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7799 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7800 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7801 * for which you can provide a custom implementation. For example:
7803 var trigger = new Roo.bootstrap.TriggerField();
7804 trigger.onTriggerClick = myTriggerFn;
7805 trigger.applyTo('my-field');
7808 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7809 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7810 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
7811 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7813 * Create a new TriggerField.
7814 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7815 * to the base TextField)
7817 Roo.bootstrap.TriggerField = function(config){
7818 this.mimicing = false;
7819 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7822 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
7824 * @cfg {String} triggerClass A CSS class to apply to the trigger
7827 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7831 /** @cfg {Boolean} grow @hide */
7832 /** @cfg {Number} growMin @hide */
7833 /** @cfg {Number} growMax @hide */
7839 autoSize: Roo.emptyFn,
7846 actionMode : 'wrap',
7850 getAutoCreate : function(){
7852 var align = this.labelAlign || this.parentLabelAlign();
7857 cls: 'form-group' //input-group
7864 type : this.inputType,
7865 cls : 'form-control',
7866 autocomplete: 'off',
7867 placeholder : this.placeholder || ''
7871 input.name = this.name;
7874 input.cls += ' input-' + this.size;
7877 if (this.disabled) {
7878 input.disabled=true;
7881 var inputblock = input;
7883 if (this.before || this.after) {
7886 cls : 'input-group',
7890 inputblock.cn.push({
7892 cls : 'input-group-addon',
7896 inputblock.cn.push(input);
7898 inputblock.cn.push({
7900 cls : 'input-group-addon',
7913 cls: 'form-hidden-field'
7921 Roo.log('multiple');
7929 cls: 'form-hidden-field'
7933 cls: 'select2-choices',
7937 cls: 'select2-search-field',
7950 cls: 'select2-container input-group',
7955 // cls: 'typeahead typeahead-long dropdown-menu',
7956 // style: 'display:none'
7961 if(!this.multiple && this.showToggleBtn){
7964 cls : 'input-group-addon btn dropdown-toggle',
7972 cls: 'combobox-clear',
7986 combobox.cls += ' select2-container-multi';
7989 if (align ==='left' && this.fieldLabel.length) {
7991 Roo.log("left and has label");
7997 cls : 'control-label col-sm-' + this.labelWidth,
7998 html : this.fieldLabel
8002 cls : "col-sm-" + (12 - this.labelWidth),
8009 } else if ( this.fieldLabel.length) {
8015 //cls : 'input-group-addon',
8016 html : this.fieldLabel
8026 Roo.log(" no label && no align");
8033 ['xs','sm','md','lg'].map(function(size){
8034 if (settings[size]) {
8035 cfg.cls += ' col-' + size + '-' + settings[size];
8046 onResize : function(w, h){
8047 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8048 // if(typeof w == 'number'){
8049 // var x = w - this.trigger.getWidth();
8050 // this.inputEl().setWidth(this.adjustWidth('input', x));
8051 // this.trigger.setStyle('left', x+'px');
8056 adjustSize : Roo.BoxComponent.prototype.adjustSize,
8059 getResizeEl : function(){
8060 return this.inputEl();
8064 getPositionEl : function(){
8065 return this.inputEl();
8069 alignErrorIcon : function(){
8070 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8074 initEvents : function(){
8078 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8079 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8080 if(!this.multiple && this.showToggleBtn){
8081 this.trigger = this.el.select('span.dropdown-toggle',true).first();
8082 if(this.hideTrigger){
8083 this.trigger.setDisplayed(false);
8085 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8089 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8092 //this.trigger.addClassOnOver('x-form-trigger-over');
8093 //this.trigger.addClassOnClick('x-form-trigger-click');
8096 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8100 createList : function()
8102 this.list = Roo.get(document.body).createChild({
8104 cls: 'typeahead typeahead-long dropdown-menu',
8105 style: 'display:none'
8108 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8113 initTrigger : function(){
8118 onDestroy : function(){
8120 this.trigger.removeAllListeners();
8121 // this.trigger.remove();
8124 // this.wrap.remove();
8126 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8130 onFocus : function(){
8131 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8134 this.wrap.addClass('x-trigger-wrap-focus');
8135 this.mimicing = true;
8136 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8137 if(this.monitorTab){
8138 this.el.on("keydown", this.checkTab, this);
8145 checkTab : function(e){
8146 if(e.getKey() == e.TAB){
8152 onBlur : function(){
8157 mimicBlur : function(e, t){
8159 if(!this.wrap.contains(t) && this.validateBlur()){
8166 triggerBlur : function(){
8167 this.mimicing = false;
8168 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8169 if(this.monitorTab){
8170 this.el.un("keydown", this.checkTab, this);
8172 //this.wrap.removeClass('x-trigger-wrap-focus');
8173 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8177 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8178 validateBlur : function(e, t){
8183 onDisable : function(){
8184 this.inputEl().dom.disabled = true;
8185 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8187 // this.wrap.addClass('x-item-disabled');
8192 onEnable : function(){
8193 this.inputEl().dom.disabled = false;
8194 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8196 // this.el.removeClass('x-item-disabled');
8201 onShow : function(){
8202 var ae = this.getActionEl();
8205 ae.dom.style.display = '';
8206 ae.dom.style.visibility = 'visible';
8212 onHide : function(){
8213 var ae = this.getActionEl();
8214 ae.dom.style.display = 'none';
8218 * The function that should handle the trigger's click event. This method does nothing by default until overridden
8219 * by an implementing function.
8221 * @param {EventObject} e
8223 onTriggerClick : Roo.emptyFn
8227 * Ext JS Library 1.1.1
8228 * Copyright(c) 2006-2007, Ext JS, LLC.
8230 * Originally Released Under LGPL - original licence link has changed is not relivant.
8233 * <script type="text/javascript">
8238 * @class Roo.data.SortTypes
8240 * Defines the default sorting (casting?) comparison functions used when sorting data.
8242 Roo.data.SortTypes = {
8244 * Default sort that does nothing
8245 * @param {Mixed} s The value being converted
8246 * @return {Mixed} The comparison value
8253 * The regular expression used to strip tags
8257 stripTagsRE : /<\/?[^>]+>/gi,
8260 * Strips all HTML tags to sort on text only
8261 * @param {Mixed} s The value being converted
8262 * @return {String} The comparison value
8264 asText : function(s){
8265 return String(s).replace(this.stripTagsRE, "");
8269 * Strips all HTML tags to sort on text only - Case insensitive
8270 * @param {Mixed} s The value being converted
8271 * @return {String} The comparison value
8273 asUCText : function(s){
8274 return String(s).toUpperCase().replace(this.stripTagsRE, "");
8278 * Case insensitive string
8279 * @param {Mixed} s The value being converted
8280 * @return {String} The comparison value
8282 asUCString : function(s) {
8283 return String(s).toUpperCase();
8288 * @param {Mixed} s The value being converted
8289 * @return {Number} The comparison value
8291 asDate : function(s) {
8295 if(s instanceof Date){
8298 return Date.parse(String(s));
8303 * @param {Mixed} s The value being converted
8304 * @return {Float} The comparison value
8306 asFloat : function(s) {
8307 var val = parseFloat(String(s).replace(/,/g, ""));
8308 if(isNaN(val)) val = 0;
8314 * @param {Mixed} s The value being converted
8315 * @return {Number} The comparison value
8317 asInt : function(s) {
8318 var val = parseInt(String(s).replace(/,/g, ""));
8319 if(isNaN(val)) val = 0;
8324 * Ext JS Library 1.1.1
8325 * Copyright(c) 2006-2007, Ext JS, LLC.
8327 * Originally Released Under LGPL - original licence link has changed is not relivant.
8330 * <script type="text/javascript">
8334 * @class Roo.data.Record
8335 * Instances of this class encapsulate both record <em>definition</em> information, and record
8336 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8337 * to access Records cached in an {@link Roo.data.Store} object.<br>
8339 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8340 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8343 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8345 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8346 * {@link #create}. The parameters are the same.
8347 * @param {Array} data An associative Array of data values keyed by the field name.
8348 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8349 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8350 * not specified an integer id is generated.
8352 Roo.data.Record = function(data, id){
8353 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8358 * Generate a constructor for a specific record layout.
8359 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8360 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8361 * Each field definition object may contain the following properties: <ul>
8362 * <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,
8363 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8364 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8365 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8366 * is being used, then this is a string containing the javascript expression to reference the data relative to
8367 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8368 * to the data item relative to the record element. If the mapping expression is the same as the field name,
8369 * this may be omitted.</p></li>
8370 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8371 * <ul><li>auto (Default, implies no conversion)</li>
8376 * <li>date</li></ul></p></li>
8377 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8378 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8379 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8380 * by the Reader into an object that will be stored in the Record. It is passed the
8381 * following parameters:<ul>
8382 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8384 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8386 * <br>usage:<br><pre><code>
8387 var TopicRecord = Roo.data.Record.create(
8388 {name: 'title', mapping: 'topic_title'},
8389 {name: 'author', mapping: 'username'},
8390 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8391 {name: 'lastPost', mapping: 'post_time', type: 'date'},
8392 {name: 'lastPoster', mapping: 'user2'},
8393 {name: 'excerpt', mapping: 'post_text'}
8396 var myNewRecord = new TopicRecord({
8397 title: 'Do my job please',
8400 lastPost: new Date(),
8401 lastPoster: 'Animal',
8402 excerpt: 'No way dude!'
8404 myStore.add(myNewRecord);
8409 Roo.data.Record.create = function(o){
8411 f.superclass.constructor.apply(this, arguments);
8413 Roo.extend(f, Roo.data.Record);
8414 var p = f.prototype;
8415 p.fields = new Roo.util.MixedCollection(false, function(field){
8418 for(var i = 0, len = o.length; i < len; i++){
8419 p.fields.add(new Roo.data.Field(o[i]));
8421 f.getField = function(name){
8422 return p.fields.get(name);
8427 Roo.data.Record.AUTO_ID = 1000;
8428 Roo.data.Record.EDIT = 'edit';
8429 Roo.data.Record.REJECT = 'reject';
8430 Roo.data.Record.COMMIT = 'commit';
8432 Roo.data.Record.prototype = {
8434 * Readonly flag - true if this record has been modified.
8443 join : function(store){
8448 * Set the named field to the specified value.
8449 * @param {String} name The name of the field to set.
8450 * @param {Object} value The value to set the field to.
8452 set : function(name, value){
8453 if(this.data[name] == value){
8460 if(typeof this.modified[name] == 'undefined'){
8461 this.modified[name] = this.data[name];
8463 this.data[name] = value;
8464 if(!this.editing && this.store){
8465 this.store.afterEdit(this);
8470 * Get the value of the named field.
8471 * @param {String} name The name of the field to get the value of.
8472 * @return {Object} The value of the field.
8474 get : function(name){
8475 return this.data[name];
8479 beginEdit : function(){
8480 this.editing = true;
8485 cancelEdit : function(){
8486 this.editing = false;
8487 delete this.modified;
8491 endEdit : function(){
8492 this.editing = false;
8493 if(this.dirty && this.store){
8494 this.store.afterEdit(this);
8499 * Usually called by the {@link Roo.data.Store} which owns the Record.
8500 * Rejects all changes made to the Record since either creation, or the last commit operation.
8501 * Modified fields are reverted to their original values.
8503 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8504 * of reject operations.
8506 reject : function(){
8507 var m = this.modified;
8509 if(typeof m[n] != "function"){
8510 this.data[n] = m[n];
8514 delete this.modified;
8515 this.editing = false;
8517 this.store.afterReject(this);
8522 * Usually called by the {@link Roo.data.Store} which owns the Record.
8523 * Commits all changes made to the Record since either creation, or the last commit operation.
8525 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8526 * of commit operations.
8528 commit : function(){
8530 delete this.modified;
8531 this.editing = false;
8533 this.store.afterCommit(this);
8538 hasError : function(){
8539 return this.error != null;
8543 clearError : function(){
8548 * Creates a copy of this record.
8549 * @param {String} id (optional) A new record id if you don't want to use this record's id
8552 copy : function(newId) {
8553 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8557 * Ext JS Library 1.1.1
8558 * Copyright(c) 2006-2007, Ext JS, LLC.
8560 * Originally Released Under LGPL - original licence link has changed is not relivant.
8563 * <script type="text/javascript">
8569 * @class Roo.data.Store
8570 * @extends Roo.util.Observable
8571 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8572 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8574 * 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
8575 * has no knowledge of the format of the data returned by the Proxy.<br>
8577 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8578 * instances from the data object. These records are cached and made available through accessor functions.
8580 * Creates a new Store.
8581 * @param {Object} config A config object containing the objects needed for the Store to access data,
8582 * and read the data into Records.
8584 Roo.data.Store = function(config){
8585 this.data = new Roo.util.MixedCollection(false);
8586 this.data.getKey = function(o){
8589 this.baseParams = {};
8596 "multisort" : "_multisort"
8599 if(config && config.data){
8600 this.inlineData = config.data;
8604 Roo.apply(this, config);
8606 if(this.reader){ // reader passed
8607 this.reader = Roo.factory(this.reader, Roo.data);
8608 this.reader.xmodule = this.xmodule || false;
8609 if(!this.recordType){
8610 this.recordType = this.reader.recordType;
8612 if(this.reader.onMetaChange){
8613 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8617 if(this.recordType){
8618 this.fields = this.recordType.prototype.fields;
8624 * @event datachanged
8625 * Fires when the data cache has changed, and a widget which is using this Store
8626 * as a Record cache should refresh its view.
8627 * @param {Store} this
8632 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8633 * @param {Store} this
8634 * @param {Object} meta The JSON metadata
8639 * Fires when Records have been added to the Store
8640 * @param {Store} this
8641 * @param {Roo.data.Record[]} records The array of Records added
8642 * @param {Number} index The index at which the record(s) were added
8647 * Fires when a Record has been removed from the Store
8648 * @param {Store} this
8649 * @param {Roo.data.Record} record The Record that was removed
8650 * @param {Number} index The index at which the record was removed
8655 * Fires when a Record has been updated
8656 * @param {Store} this
8657 * @param {Roo.data.Record} record The Record that was updated
8658 * @param {String} operation The update operation being performed. Value may be one of:
8660 Roo.data.Record.EDIT
8661 Roo.data.Record.REJECT
8662 Roo.data.Record.COMMIT
8668 * Fires when the data cache has been cleared.
8669 * @param {Store} this
8674 * Fires before a request is made for a new data object. If the beforeload handler returns false
8675 * the load action will be canceled.
8676 * @param {Store} this
8677 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8681 * @event beforeloadadd
8682 * Fires after a new set of Records has been loaded.
8683 * @param {Store} this
8684 * @param {Roo.data.Record[]} records The Records that were loaded
8685 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8687 beforeloadadd : true,
8690 * Fires after a new set of Records has been loaded, before they are added to the store.
8691 * @param {Store} this
8692 * @param {Roo.data.Record[]} records The Records that were loaded
8693 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8694 * @params {Object} return from reader
8698 * @event loadexception
8699 * Fires if an exception occurs in the Proxy during loading.
8700 * Called with the signature of the Proxy's "loadexception" event.
8701 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8704 * @param {Object} return from JsonData.reader() - success, totalRecords, records
8705 * @param {Object} load options
8706 * @param {Object} jsonData from your request (normally this contains the Exception)
8708 loadexception : true
8712 this.proxy = Roo.factory(this.proxy, Roo.data);
8713 this.proxy.xmodule = this.xmodule || false;
8714 this.relayEvents(this.proxy, ["loadexception"]);
8716 this.sortToggle = {};
8717 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8719 Roo.data.Store.superclass.constructor.call(this);
8721 if(this.inlineData){
8722 this.loadData(this.inlineData);
8723 delete this.inlineData;
8727 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8729 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
8730 * without a remote query - used by combo/forms at present.
8734 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8737 * @cfg {Array} data Inline data to be loaded when the store is initialized.
8740 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8741 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8744 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8745 * on any HTTP request
8748 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8751 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8755 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8756 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8761 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8762 * loaded or when a record is removed. (defaults to false).
8764 pruneModifiedRecords : false,
8770 * Add Records to the Store and fires the add event.
8771 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8773 add : function(records){
8774 records = [].concat(records);
8775 for(var i = 0, len = records.length; i < len; i++){
8776 records[i].join(this);
8778 var index = this.data.length;
8779 this.data.addAll(records);
8780 this.fireEvent("add", this, records, index);
8784 * Remove a Record from the Store and fires the remove event.
8785 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8787 remove : function(record){
8788 var index = this.data.indexOf(record);
8789 this.data.removeAt(index);
8790 if(this.pruneModifiedRecords){
8791 this.modified.remove(record);
8793 this.fireEvent("remove", this, record, index);
8797 * Remove all Records from the Store and fires the clear event.
8799 removeAll : function(){
8801 if(this.pruneModifiedRecords){
8804 this.fireEvent("clear", this);
8808 * Inserts Records to the Store at the given index and fires the add event.
8809 * @param {Number} index The start index at which to insert the passed Records.
8810 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8812 insert : function(index, records){
8813 records = [].concat(records);
8814 for(var i = 0, len = records.length; i < len; i++){
8815 this.data.insert(index, records[i]);
8816 records[i].join(this);
8818 this.fireEvent("add", this, records, index);
8822 * Get the index within the cache of the passed Record.
8823 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8824 * @return {Number} The index of the passed Record. Returns -1 if not found.
8826 indexOf : function(record){
8827 return this.data.indexOf(record);
8831 * Get the index within the cache of the Record with the passed id.
8832 * @param {String} id The id of the Record to find.
8833 * @return {Number} The index of the Record. Returns -1 if not found.
8835 indexOfId : function(id){
8836 return this.data.indexOfKey(id);
8840 * Get the Record with the specified id.
8841 * @param {String} id The id of the Record to find.
8842 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8844 getById : function(id){
8845 return this.data.key(id);
8849 * Get the Record at the specified index.
8850 * @param {Number} index The index of the Record to find.
8851 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8853 getAt : function(index){
8854 return this.data.itemAt(index);
8858 * Returns a range of Records between specified indices.
8859 * @param {Number} startIndex (optional) The starting index (defaults to 0)
8860 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8861 * @return {Roo.data.Record[]} An array of Records
8863 getRange : function(start, end){
8864 return this.data.getRange(start, end);
8868 storeOptions : function(o){
8869 o = Roo.apply({}, o);
8872 this.lastOptions = o;
8876 * Loads the Record cache from the configured Proxy using the configured Reader.
8878 * If using remote paging, then the first load call must specify the <em>start</em>
8879 * and <em>limit</em> properties in the options.params property to establish the initial
8880 * position within the dataset, and the number of Records to cache on each read from the Proxy.
8882 * <strong>It is important to note that for remote data sources, loading is asynchronous,
8883 * and this call will return before the new data has been loaded. Perform any post-processing
8884 * in a callback function, or in a "load" event handler.</strong>
8886 * @param {Object} options An object containing properties which control loading options:<ul>
8887 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8888 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8889 * passed the following arguments:<ul>
8890 * <li>r : Roo.data.Record[]</li>
8891 * <li>options: Options object from the load call</li>
8892 * <li>success: Boolean success indicator</li></ul></li>
8893 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8894 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8897 load : function(options){
8898 options = options || {};
8899 if(this.fireEvent("beforeload", this, options) !== false){
8900 this.storeOptions(options);
8901 var p = Roo.apply(options.params || {}, this.baseParams);
8902 // if meta was not loaded from remote source.. try requesting it.
8903 if (!this.reader.metaFromRemote) {
8906 if(this.sortInfo && this.remoteSort){
8907 var pn = this.paramNames;
8908 p[pn["sort"]] = this.sortInfo.field;
8909 p[pn["dir"]] = this.sortInfo.direction;
8911 if (this.multiSort) {
8912 var pn = this.paramNames;
8913 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8916 this.proxy.load(p, this.reader, this.loadRecords, this, options);
8921 * Reloads the Record cache from the configured Proxy using the configured Reader and
8922 * the options from the last load operation performed.
8923 * @param {Object} options (optional) An object containing properties which may override the options
8924 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8925 * the most recently used options are reused).
8927 reload : function(options){
8928 this.load(Roo.applyIf(options||{}, this.lastOptions));
8932 // Called as a callback by the Reader during a load operation.
8933 loadRecords : function(o, options, success){
8934 if(!o || success === false){
8935 if(success !== false){
8936 this.fireEvent("load", this, [], options, o);
8938 if(options.callback){
8939 options.callback.call(options.scope || this, [], options, false);
8943 // if data returned failure - throw an exception.
8944 if (o.success === false) {
8945 // show a message if no listener is registered.
8946 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8947 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8949 // loadmask wil be hooked into this..
8950 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8953 var r = o.records, t = o.totalRecords || r.length;
8955 this.fireEvent("beforeloadadd", this, r, options, o);
8957 if(!options || options.add !== true){
8958 if(this.pruneModifiedRecords){
8961 for(var i = 0, len = r.length; i < len; i++){
8965 this.data = this.snapshot;
8966 delete this.snapshot;
8969 this.data.addAll(r);
8970 this.totalLength = t;
8972 this.fireEvent("datachanged", this);
8974 this.totalLength = Math.max(t, this.data.length+r.length);
8977 this.fireEvent("load", this, r, options, o);
8978 if(options.callback){
8979 options.callback.call(options.scope || this, r, options, true);
8985 * Loads data from a passed data block. A Reader which understands the format of the data
8986 * must have been configured in the constructor.
8987 * @param {Object} data The data block from which to read the Records. The format of the data expected
8988 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8989 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8991 loadData : function(o, append){
8992 var r = this.reader.readRecords(o);
8993 this.loadRecords(r, {add: append}, true);
8997 * Gets the number of cached records.
8999 * <em>If using paging, this may not be the total size of the dataset. If the data object
9000 * used by the Reader contains the dataset size, then the getTotalCount() function returns
9001 * the data set size</em>
9003 getCount : function(){
9004 return this.data.length || 0;
9008 * Gets the total number of records in the dataset as returned by the server.
9010 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9011 * the dataset size</em>
9013 getTotalCount : function(){
9014 return this.totalLength || 0;
9018 * Returns the sort state of the Store as an object with two properties:
9020 field {String} The name of the field by which the Records are sorted
9021 direction {String} The sort order, "ASC" or "DESC"
9024 getSortState : function(){
9025 return this.sortInfo;
9029 applySort : function(){
9030 if(this.sortInfo && !this.remoteSort){
9031 var s = this.sortInfo, f = s.field;
9032 var st = this.fields.get(f).sortType;
9033 var fn = function(r1, r2){
9034 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9035 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9037 this.data.sort(s.direction, fn);
9038 if(this.snapshot && this.snapshot != this.data){
9039 this.snapshot.sort(s.direction, fn);
9045 * Sets the default sort column and order to be used by the next load operation.
9046 * @param {String} fieldName The name of the field to sort by.
9047 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9049 setDefaultSort : function(field, dir){
9050 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9055 * If remote sorting is used, the sort is performed on the server, and the cache is
9056 * reloaded. If local sorting is used, the cache is sorted internally.
9057 * @param {String} fieldName The name of the field to sort by.
9058 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9060 sort : function(fieldName, dir){
9061 var f = this.fields.get(fieldName);
9063 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9065 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9066 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9071 this.sortToggle[f.name] = dir;
9072 this.sortInfo = {field: f.name, direction: dir};
9073 if(!this.remoteSort){
9075 this.fireEvent("datachanged", this);
9077 this.load(this.lastOptions);
9082 * Calls the specified function for each of the Records in the cache.
9083 * @param {Function} fn The function to call. The Record is passed as the first parameter.
9084 * Returning <em>false</em> aborts and exits the iteration.
9085 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9087 each : function(fn, scope){
9088 this.data.each(fn, scope);
9092 * Gets all records modified since the last commit. Modified records are persisted across load operations
9093 * (e.g., during paging).
9094 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9096 getModifiedRecords : function(){
9097 return this.modified;
9101 createFilterFn : function(property, value, anyMatch){
9102 if(!value.exec){ // not a regex
9103 value = String(value);
9104 if(value.length == 0){
9107 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9110 return value.test(r.data[property]);
9115 * Sums the value of <i>property</i> for each record between start and end and returns the result.
9116 * @param {String} property A field on your records
9117 * @param {Number} start The record index to start at (defaults to 0)
9118 * @param {Number} end The last record index to include (defaults to length - 1)
9119 * @return {Number} The sum
9121 sum : function(property, start, end){
9122 var rs = this.data.items, v = 0;
9124 end = (end || end === 0) ? end : rs.length-1;
9126 for(var i = start; i <= end; i++){
9127 v += (rs[i].data[property] || 0);
9133 * Filter the records by a specified property.
9134 * @param {String} field A field on your records
9135 * @param {String/RegExp} value Either a string that the field
9136 * should start with or a RegExp to test against the field
9137 * @param {Boolean} anyMatch True to match any part not just the beginning
9139 filter : function(property, value, anyMatch){
9140 var fn = this.createFilterFn(property, value, anyMatch);
9141 return fn ? this.filterBy(fn) : this.clearFilter();
9145 * Filter by a function. The specified function will be called with each
9146 * record in this data source. If the function returns true the record is included,
9147 * otherwise it is filtered.
9148 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9149 * @param {Object} scope (optional) The scope of the function (defaults to this)
9151 filterBy : function(fn, scope){
9152 this.snapshot = this.snapshot || this.data;
9153 this.data = this.queryBy(fn, scope||this);
9154 this.fireEvent("datachanged", this);
9158 * Query the records by a specified property.
9159 * @param {String} field A field on your records
9160 * @param {String/RegExp} value Either a string that the field
9161 * should start with or a RegExp to test against the field
9162 * @param {Boolean} anyMatch True to match any part not just the beginning
9163 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9165 query : function(property, value, anyMatch){
9166 var fn = this.createFilterFn(property, value, anyMatch);
9167 return fn ? this.queryBy(fn) : this.data.clone();
9171 * Query by a function. The specified function will be called with each
9172 * record in this data source. If the function returns true the record is included
9174 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9175 * @param {Object} scope (optional) The scope of the function (defaults to this)
9176 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9178 queryBy : function(fn, scope){
9179 var data = this.snapshot || this.data;
9180 return data.filterBy(fn, scope||this);
9184 * Collects unique values for a particular dataIndex from this store.
9185 * @param {String} dataIndex The property to collect
9186 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9187 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9188 * @return {Array} An array of the unique values
9190 collect : function(dataIndex, allowNull, bypassFilter){
9191 var d = (bypassFilter === true && this.snapshot) ?
9192 this.snapshot.items : this.data.items;
9193 var v, sv, r = [], l = {};
9194 for(var i = 0, len = d.length; i < len; i++){
9195 v = d[i].data[dataIndex];
9197 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9206 * Revert to a view of the Record cache with no filtering applied.
9207 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9209 clearFilter : function(suppressEvent){
9210 if(this.snapshot && this.snapshot != this.data){
9211 this.data = this.snapshot;
9212 delete this.snapshot;
9213 if(suppressEvent !== true){
9214 this.fireEvent("datachanged", this);
9220 afterEdit : function(record){
9221 if(this.modified.indexOf(record) == -1){
9222 this.modified.push(record);
9224 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9228 afterReject : function(record){
9229 this.modified.remove(record);
9230 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9234 afterCommit : function(record){
9235 this.modified.remove(record);
9236 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9240 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9241 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9243 commitChanges : function(){
9244 var m = this.modified.slice(0);
9246 for(var i = 0, len = m.length; i < len; i++){
9252 * Cancel outstanding changes on all changed records.
9254 rejectChanges : function(){
9255 var m = this.modified.slice(0);
9257 for(var i = 0, len = m.length; i < len; i++){
9262 onMetaChange : function(meta, rtype, o){
9263 this.recordType = rtype;
9264 this.fields = rtype.prototype.fields;
9265 delete this.snapshot;
9266 this.sortInfo = meta.sortInfo || this.sortInfo;
9268 this.fireEvent('metachange', this, this.reader.meta);
9271 moveIndex : function(data, type)
9273 var index = this.indexOf(data);
9275 var newIndex = index + type;
9279 this.insert(newIndex, data);
9284 * Ext JS Library 1.1.1
9285 * Copyright(c) 2006-2007, Ext JS, LLC.
9287 * Originally Released Under LGPL - original licence link has changed is not relivant.
9290 * <script type="text/javascript">
9294 * @class Roo.data.SimpleStore
9295 * @extends Roo.data.Store
9296 * Small helper class to make creating Stores from Array data easier.
9297 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9298 * @cfg {Array} fields An array of field definition objects, or field name strings.
9299 * @cfg {Array} data The multi-dimensional array of data
9301 * @param {Object} config
9303 Roo.data.SimpleStore = function(config){
9304 Roo.data.SimpleStore.superclass.constructor.call(this, {
9306 reader: new Roo.data.ArrayReader({
9309 Roo.data.Record.create(config.fields)
9311 proxy : new Roo.data.MemoryProxy(config.data)
9315 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9317 * Ext JS Library 1.1.1
9318 * Copyright(c) 2006-2007, Ext JS, LLC.
9320 * Originally Released Under LGPL - original licence link has changed is not relivant.
9323 * <script type="text/javascript">
9328 * @extends Roo.data.Store
9329 * @class Roo.data.JsonStore
9330 * Small helper class to make creating Stores for JSON data easier. <br/>
9332 var store = new Roo.data.JsonStore({
9333 url: 'get-images.php',
9335 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9338 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9339 * JsonReader and HttpProxy (unless inline data is provided).</b>
9340 * @cfg {Array} fields An array of field definition objects, or field name strings.
9342 * @param {Object} config
9344 Roo.data.JsonStore = function(c){
9345 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9346 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9347 reader: new Roo.data.JsonReader(c, c.fields)
9350 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9352 * Ext JS Library 1.1.1
9353 * Copyright(c) 2006-2007, Ext JS, LLC.
9355 * Originally Released Under LGPL - original licence link has changed is not relivant.
9358 * <script type="text/javascript">
9362 Roo.data.Field = function(config){
9363 if(typeof config == "string"){
9364 config = {name: config};
9366 Roo.apply(this, config);
9372 var st = Roo.data.SortTypes;
9373 // named sortTypes are supported, here we look them up
9374 if(typeof this.sortType == "string"){
9375 this.sortType = st[this.sortType];
9378 // set default sortType for strings and dates
9382 this.sortType = st.asUCString;
9385 this.sortType = st.asDate;
9388 this.sortType = st.none;
9393 var stripRe = /[\$,%]/g;
9395 // prebuilt conversion function for this field, instead of
9396 // switching every time we're reading a value
9398 var cv, dateFormat = this.dateFormat;
9403 cv = function(v){ return v; };
9406 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9410 return v !== undefined && v !== null && v !== '' ?
9411 parseInt(String(v).replace(stripRe, ""), 10) : '';
9416 return v !== undefined && v !== null && v !== '' ?
9417 parseFloat(String(v).replace(stripRe, ""), 10) : '';
9422 cv = function(v){ return v === true || v === "true" || v == 1; };
9429 if(v instanceof Date){
9433 if(dateFormat == "timestamp"){
9434 return new Date(v*1000);
9436 return Date.parseDate(v, dateFormat);
9438 var parsed = Date.parse(v);
9439 return parsed ? new Date(parsed) : null;
9448 Roo.data.Field.prototype = {
9456 * Ext JS Library 1.1.1
9457 * Copyright(c) 2006-2007, Ext JS, LLC.
9459 * Originally Released Under LGPL - original licence link has changed is not relivant.
9462 * <script type="text/javascript">
9465 // Base class for reading structured data from a data source. This class is intended to be
9466 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9469 * @class Roo.data.DataReader
9470 * Base class for reading structured data from a data source. This class is intended to be
9471 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9474 Roo.data.DataReader = function(meta, recordType){
9478 this.recordType = recordType instanceof Array ?
9479 Roo.data.Record.create(recordType) : recordType;
9482 Roo.data.DataReader.prototype = {
9484 * Create an empty record
9485 * @param {Object} data (optional) - overlay some values
9486 * @return {Roo.data.Record} record created.
9488 newRow : function(d) {
9490 this.recordType.prototype.fields.each(function(c) {
9492 case 'int' : da[c.name] = 0; break;
9493 case 'date' : da[c.name] = new Date(); break;
9494 case 'float' : da[c.name] = 0.0; break;
9495 case 'boolean' : da[c.name] = false; break;
9496 default : da[c.name] = ""; break;
9500 return new this.recordType(Roo.apply(da, d));
9505 * Ext JS Library 1.1.1
9506 * Copyright(c) 2006-2007, Ext JS, LLC.
9508 * Originally Released Under LGPL - original licence link has changed is not relivant.
9511 * <script type="text/javascript">
9515 * @class Roo.data.DataProxy
9516 * @extends Roo.data.Observable
9517 * This class is an abstract base class for implementations which provide retrieval of
9518 * unformatted data objects.<br>
9520 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9521 * (of the appropriate type which knows how to parse the data object) to provide a block of
9522 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9524 * Custom implementations must implement the load method as described in
9525 * {@link Roo.data.HttpProxy#load}.
9527 Roo.data.DataProxy = function(){
9531 * Fires before a network request is made to retrieve a data object.
9532 * @param {Object} This DataProxy object.
9533 * @param {Object} params The params parameter to the load function.
9538 * Fires before the load method's callback is called.
9539 * @param {Object} This DataProxy object.
9540 * @param {Object} o The data object.
9541 * @param {Object} arg The callback argument object passed to the load function.
9545 * @event loadexception
9546 * Fires if an Exception occurs during data retrieval.
9547 * @param {Object} This DataProxy object.
9548 * @param {Object} o The data object.
9549 * @param {Object} arg The callback argument object passed to the load function.
9550 * @param {Object} e The Exception.
9552 loadexception : true
9554 Roo.data.DataProxy.superclass.constructor.call(this);
9557 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9560 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9564 * Ext JS Library 1.1.1
9565 * Copyright(c) 2006-2007, Ext JS, LLC.
9567 * Originally Released Under LGPL - original licence link has changed is not relivant.
9570 * <script type="text/javascript">
9573 * @class Roo.data.MemoryProxy
9574 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9575 * to the Reader when its load method is called.
9577 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9579 Roo.data.MemoryProxy = function(data){
9583 Roo.data.MemoryProxy.superclass.constructor.call(this);
9587 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9589 * Load data from the requested source (in this case an in-memory
9590 * data object passed to the constructor), read the data object into
9591 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9592 * process that block using the passed callback.
9593 * @param {Object} params This parameter is not used by the MemoryProxy class.
9594 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9595 * object into a block of Roo.data.Records.
9596 * @param {Function} callback The function into which to pass the block of Roo.data.records.
9597 * The function must be passed <ul>
9598 * <li>The Record block object</li>
9599 * <li>The "arg" argument from the load function</li>
9600 * <li>A boolean success indicator</li>
9602 * @param {Object} scope The scope in which to call the callback
9603 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9605 load : function(params, reader, callback, scope, arg){
9606 params = params || {};
9609 result = reader.readRecords(this.data);
9611 this.fireEvent("loadexception", this, arg, null, e);
9612 callback.call(scope, null, arg, false);
9615 callback.call(scope, result, arg, true);
9619 update : function(params, records){
9624 * Ext JS Library 1.1.1
9625 * Copyright(c) 2006-2007, Ext JS, LLC.
9627 * Originally Released Under LGPL - original licence link has changed is not relivant.
9630 * <script type="text/javascript">
9633 * @class Roo.data.HttpProxy
9634 * @extends Roo.data.DataProxy
9635 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9636 * configured to reference a certain URL.<br><br>
9638 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9639 * from which the running page was served.<br><br>
9641 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9643 * Be aware that to enable the browser to parse an XML document, the server must set
9644 * the Content-Type header in the HTTP response to "text/xml".
9646 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9647 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
9648 * will be used to make the request.
9650 Roo.data.HttpProxy = function(conn){
9651 Roo.data.HttpProxy.superclass.constructor.call(this);
9652 // is conn a conn config or a real conn?
9654 this.useAjax = !conn || !conn.events;
9658 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9659 // thse are take from connection...
9662 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9665 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9666 * extra parameters to each request made by this object. (defaults to undefined)
9669 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9670 * to each request made by this object. (defaults to undefined)
9673 * @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)
9676 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9679 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9685 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9689 * Return the {@link Roo.data.Connection} object being used by this Proxy.
9690 * @return {Connection} The Connection object. This object may be used to subscribe to events on
9691 * a finer-grained basis than the DataProxy events.
9693 getConnection : function(){
9694 return this.useAjax ? Roo.Ajax : this.conn;
9698 * Load data from the configured {@link Roo.data.Connection}, read the data object into
9699 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9700 * process that block using the passed callback.
9701 * @param {Object} params An object containing properties which are to be used as HTTP parameters
9702 * for the request to the remote server.
9703 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9704 * object into a block of Roo.data.Records.
9705 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9706 * The function must be passed <ul>
9707 * <li>The Record block object</li>
9708 * <li>The "arg" argument from the load function</li>
9709 * <li>A boolean success indicator</li>
9711 * @param {Object} scope The scope in which to call the callback
9712 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9714 load : function(params, reader, callback, scope, arg){
9715 if(this.fireEvent("beforeload", this, params) !== false){
9717 params : params || {},
9719 callback : callback,
9724 callback : this.loadResponse,
9728 Roo.applyIf(o, this.conn);
9729 if(this.activeRequest){
9730 Roo.Ajax.abort(this.activeRequest);
9732 this.activeRequest = Roo.Ajax.request(o);
9734 this.conn.request(o);
9737 callback.call(scope||this, null, arg, false);
9742 loadResponse : function(o, success, response){
9743 delete this.activeRequest;
9745 this.fireEvent("loadexception", this, o, response);
9746 o.request.callback.call(o.request.scope, null, o.request.arg, false);
9751 result = o.reader.read(response);
9753 this.fireEvent("loadexception", this, o, response, e);
9754 o.request.callback.call(o.request.scope, null, o.request.arg, false);
9758 this.fireEvent("load", this, o, o.request.arg);
9759 o.request.callback.call(o.request.scope, result, o.request.arg, true);
9763 update : function(dataSet){
9768 updateResponse : function(dataSet){
9773 * Ext JS Library 1.1.1
9774 * Copyright(c) 2006-2007, Ext JS, LLC.
9776 * Originally Released Under LGPL - original licence link has changed is not relivant.
9779 * <script type="text/javascript">
9783 * @class Roo.data.ScriptTagProxy
9784 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9785 * other than the originating domain of the running page.<br><br>
9787 * <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
9788 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9790 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9791 * source code that is used as the source inside a <script> tag.<br><br>
9793 * In order for the browser to process the returned data, the server must wrap the data object
9794 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9795 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9796 * depending on whether the callback name was passed:
9799 boolean scriptTag = false;
9800 String cb = request.getParameter("callback");
9803 response.setContentType("text/javascript");
9805 response.setContentType("application/x-json");
9807 Writer out = response.getWriter();
9809 out.write(cb + "(");
9811 out.print(dataBlock.toJsonString());
9818 * @param {Object} config A configuration object.
9820 Roo.data.ScriptTagProxy = function(config){
9821 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9822 Roo.apply(this, config);
9823 this.head = document.getElementsByTagName("head")[0];
9826 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9828 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9830 * @cfg {String} url The URL from which to request the data object.
9833 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9837 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9838 * the server the name of the callback function set up by the load call to process the returned data object.
9839 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9840 * javascript output which calls this named function passing the data object as its only parameter.
9842 callbackParam : "callback",
9844 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9845 * name to the request.
9850 * Load data from the configured URL, read the data object into
9851 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9852 * process that block using the passed callback.
9853 * @param {Object} params An object containing properties which are to be used as HTTP parameters
9854 * for the request to the remote server.
9855 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9856 * object into a block of Roo.data.Records.
9857 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9858 * The function must be passed <ul>
9859 * <li>The Record block object</li>
9860 * <li>The "arg" argument from the load function</li>
9861 * <li>A boolean success indicator</li>
9863 * @param {Object} scope The scope in which to call the callback
9864 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9866 load : function(params, reader, callback, scope, arg){
9867 if(this.fireEvent("beforeload", this, params) !== false){
9869 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9872 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9874 url += "&_dc=" + (new Date().getTime());
9876 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9879 cb : "stcCallback"+transId,
9880 scriptId : "stcScript"+transId,
9884 callback : callback,
9890 window[trans.cb] = function(o){
9891 conn.handleResponse(o, trans);
9894 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9896 if(this.autoAbort !== false){
9900 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9902 var script = document.createElement("script");
9903 script.setAttribute("src", url);
9904 script.setAttribute("type", "text/javascript");
9905 script.setAttribute("id", trans.scriptId);
9906 this.head.appendChild(script);
9910 callback.call(scope||this, null, arg, false);
9915 isLoading : function(){
9916 return this.trans ? true : false;
9920 * Abort the current server request.
9923 if(this.isLoading()){
9924 this.destroyTrans(this.trans);
9929 destroyTrans : function(trans, isLoaded){
9930 this.head.removeChild(document.getElementById(trans.scriptId));
9931 clearTimeout(trans.timeoutId);
9933 window[trans.cb] = undefined;
9935 delete window[trans.cb];
9938 // if hasn't been loaded, wait for load to remove it to prevent script error
9939 window[trans.cb] = function(){
9940 window[trans.cb] = undefined;
9942 delete window[trans.cb];
9949 handleResponse : function(o, trans){
9951 this.destroyTrans(trans, true);
9954 result = trans.reader.readRecords(o);
9956 this.fireEvent("loadexception", this, o, trans.arg, e);
9957 trans.callback.call(trans.scope||window, null, trans.arg, false);
9960 this.fireEvent("load", this, o, trans.arg);
9961 trans.callback.call(trans.scope||window, result, trans.arg, true);
9965 handleFailure : function(trans){
9967 this.destroyTrans(trans, false);
9968 this.fireEvent("loadexception", this, null, trans.arg);
9969 trans.callback.call(trans.scope||window, null, trans.arg, false);
9973 * Ext JS Library 1.1.1
9974 * Copyright(c) 2006-2007, Ext JS, LLC.
9976 * Originally Released Under LGPL - original licence link has changed is not relivant.
9979 * <script type="text/javascript">
9983 * @class Roo.data.JsonReader
9984 * @extends Roo.data.DataReader
9985 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9986 * based on mappings in a provided Roo.data.Record constructor.
9988 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9989 * in the reply previously.
9994 var RecordDef = Roo.data.Record.create([
9995 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
9996 {name: 'occupation'} // This field will use "occupation" as the mapping.
9998 var myReader = new Roo.data.JsonReader({
9999 totalProperty: "results", // The property which contains the total dataset size (optional)
10000 root: "rows", // The property which contains an Array of row objects
10001 id: "id" // The property within each row object that provides an ID for the record (optional)
10005 * This would consume a JSON file like this:
10007 { 'results': 2, 'rows': [
10008 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10009 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10012 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10013 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10014 * paged from the remote server.
10015 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10016 * @cfg {String} root name of the property which contains the Array of row objects.
10017 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10019 * Create a new JsonReader
10020 * @param {Object} meta Metadata configuration options
10021 * @param {Object} recordType Either an Array of field definition objects,
10022 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10024 Roo.data.JsonReader = function(meta, recordType){
10027 // set some defaults:
10028 Roo.applyIf(meta, {
10029 totalProperty: 'total',
10030 successProperty : 'success',
10035 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10037 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10040 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
10041 * Used by Store query builder to append _requestMeta to params.
10044 metaFromRemote : false,
10046 * This method is only used by a DataProxy which has retrieved data from a remote server.
10047 * @param {Object} response The XHR object which contains the JSON data in its responseText.
10048 * @return {Object} data A data block which is used by an Roo.data.Store object as
10049 * a cache of Roo.data.Records.
10051 read : function(response){
10052 var json = response.responseText;
10054 var o = /* eval:var:o */ eval("("+json+")");
10056 throw {message: "JsonReader.read: Json object not found"};
10062 this.metaFromRemote = true;
10063 this.meta = o.metaData;
10064 this.recordType = Roo.data.Record.create(o.metaData.fields);
10065 this.onMetaChange(this.meta, this.recordType, o);
10067 return this.readRecords(o);
10070 // private function a store will implement
10071 onMetaChange : function(meta, recordType, o){
10078 simpleAccess: function(obj, subsc) {
10085 getJsonAccessor: function(){
10087 return function(expr) {
10089 return(re.test(expr))
10090 ? new Function("obj", "return obj." + expr)
10095 return Roo.emptyFn;
10100 * Create a data block containing Roo.data.Records from an XML document.
10101 * @param {Object} o An object which contains an Array of row objects in the property specified
10102 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10103 * which contains the total size of the dataset.
10104 * @return {Object} data A data block which is used by an Roo.data.Store object as
10105 * a cache of Roo.data.Records.
10107 readRecords : function(o){
10109 * After any data loads, the raw JSON data is available for further custom processing.
10113 var s = this.meta, Record = this.recordType,
10114 f = Record.prototype.fields, fi = f.items, fl = f.length;
10116 // Generate extraction functions for the totalProperty, the root, the id, and for each field
10118 if(s.totalProperty) {
10119 this.getTotal = this.getJsonAccessor(s.totalProperty);
10121 if(s.successProperty) {
10122 this.getSuccess = this.getJsonAccessor(s.successProperty);
10124 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10126 var g = this.getJsonAccessor(s.id);
10127 this.getId = function(rec) {
10129 return (r === undefined || r === "") ? null : r;
10132 this.getId = function(){return null;};
10135 for(var jj = 0; jj < fl; jj++){
10137 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10138 this.ef[jj] = this.getJsonAccessor(map);
10142 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10143 if(s.totalProperty){
10144 var vt = parseInt(this.getTotal(o), 10);
10149 if(s.successProperty){
10150 var vs = this.getSuccess(o);
10151 if(vs === false || vs === 'false'){
10156 for(var i = 0; i < c; i++){
10159 var id = this.getId(n);
10160 for(var j = 0; j < fl; j++){
10162 var v = this.ef[j](n);
10164 Roo.log('missing convert for ' + f.name);
10168 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10170 var record = new Record(values, id);
10172 records[i] = record;
10178 totalRecords : totalRecords
10183 * Ext JS Library 1.1.1
10184 * Copyright(c) 2006-2007, Ext JS, LLC.
10186 * Originally Released Under LGPL - original licence link has changed is not relivant.
10189 * <script type="text/javascript">
10193 * @class Roo.data.ArrayReader
10194 * @extends Roo.data.DataReader
10195 * Data reader class to create an Array of Roo.data.Record objects from an Array.
10196 * Each element of that Array represents a row of data fields. The
10197 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10198 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10202 var RecordDef = Roo.data.Record.create([
10203 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
10204 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
10206 var myReader = new Roo.data.ArrayReader({
10207 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
10211 * This would consume an Array like this:
10213 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10215 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10217 * Create a new JsonReader
10218 * @param {Object} meta Metadata configuration options.
10219 * @param {Object} recordType Either an Array of field definition objects
10220 * as specified to {@link Roo.data.Record#create},
10221 * or an {@link Roo.data.Record} object
10222 * created using {@link Roo.data.Record#create}.
10224 Roo.data.ArrayReader = function(meta, recordType){
10225 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10228 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10230 * Create a data block containing Roo.data.Records from an XML document.
10231 * @param {Object} o An Array of row objects which represents the dataset.
10232 * @return {Object} data A data block which is used by an Roo.data.Store object as
10233 * a cache of Roo.data.Records.
10235 readRecords : function(o){
10236 var sid = this.meta ? this.meta.id : null;
10237 var recordType = this.recordType, fields = recordType.prototype.fields;
10240 for(var i = 0; i < root.length; i++){
10243 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10244 for(var j = 0, jlen = fields.length; j < jlen; j++){
10245 var f = fields.items[j];
10246 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10247 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10249 values[f.name] = v;
10251 var record = new recordType(values, id);
10253 records[records.length] = record;
10257 totalRecords : records.length
10266 * @class Roo.bootstrap.ComboBox
10267 * @extends Roo.bootstrap.TriggerField
10268 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10269 * @cfg {Boolean} append (true|false) default false
10270 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10271 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10272 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10273 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10274 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10276 * Create a new ComboBox.
10277 * @param {Object} config Configuration options
10279 Roo.bootstrap.ComboBox = function(config){
10280 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10284 * Fires when the dropdown list is expanded
10285 * @param {Roo.bootstrap.ComboBox} combo This combo box
10290 * Fires when the dropdown list is collapsed
10291 * @param {Roo.bootstrap.ComboBox} combo This combo box
10295 * @event beforeselect
10296 * Fires before a list item is selected. Return false to cancel the selection.
10297 * @param {Roo.bootstrap.ComboBox} combo This combo box
10298 * @param {Roo.data.Record} record The data record returned from the underlying store
10299 * @param {Number} index The index of the selected item in the dropdown list
10301 'beforeselect' : true,
10304 * Fires when a list item is selected
10305 * @param {Roo.bootstrap.ComboBox} combo This combo box
10306 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10307 * @param {Number} index The index of the selected item in the dropdown list
10311 * @event beforequery
10312 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10313 * The event object passed has these properties:
10314 * @param {Roo.bootstrap.ComboBox} combo This combo box
10315 * @param {String} query The query
10316 * @param {Boolean} forceAll true to force "all" query
10317 * @param {Boolean} cancel true to cancel the query
10318 * @param {Object} e The query event object
10320 'beforequery': true,
10323 * Fires when the 'add' icon is pressed (add a listener to enable add button)
10324 * @param {Roo.bootstrap.ComboBox} combo This combo box
10329 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10330 * @param {Roo.bootstrap.ComboBox} combo This combo box
10331 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10336 * Fires when the remove value from the combobox array
10337 * @param {Roo.bootstrap.ComboBox} combo This combo box
10344 this.tickItems = [];
10346 this.selectedIndex = -1;
10347 if(this.mode == 'local'){
10348 if(config.queryDelay === undefined){
10349 this.queryDelay = 10;
10351 if(config.minChars === undefined){
10357 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10360 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10361 * rendering into an Roo.Editor, defaults to false)
10364 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10365 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10368 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10371 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10372 * the dropdown list (defaults to undefined, with no header element)
10376 * @cfg {String/Roo.Template} tpl The template to use to render the output
10380 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10382 listWidth: undefined,
10384 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10385 * mode = 'remote' or 'text' if mode = 'local')
10387 displayField: undefined,
10389 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10390 * mode = 'remote' or 'value' if mode = 'local').
10391 * Note: use of a valueField requires the user make a selection
10392 * in order for a value to be mapped.
10394 valueField: undefined,
10398 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10399 * field's data value (defaults to the underlying DOM element's name)
10401 hiddenName: undefined,
10403 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10407 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10409 selectedClass: 'active',
10412 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10416 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10417 * anchor positions (defaults to 'tl-bl')
10419 listAlign: 'tl-bl?',
10421 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10425 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
10426 * query specified by the allQuery config option (defaults to 'query')
10428 triggerAction: 'query',
10430 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10431 * (defaults to 4, does not apply if editable = false)
10435 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10436 * delay (typeAheadDelay) if it matches a known value (defaults to false)
10440 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10441 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10445 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10446 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
10450 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
10451 * when editable = true (defaults to false)
10453 selectOnFocus:false,
10455 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10457 queryParam: 'query',
10459 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
10460 * when mode = 'remote' (defaults to 'Loading...')
10462 loadingText: 'Loading...',
10464 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10468 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10472 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10473 * traditional select (defaults to true)
10477 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10481 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10485 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10486 * listWidth has a higher value)
10490 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10491 * allow the user to set arbitrary text into the field (defaults to false)
10493 forceSelection:false,
10495 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10496 * if typeAhead = true (defaults to 250)
10498 typeAheadDelay : 250,
10500 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10501 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10503 valueNotFoundText : undefined,
10505 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10507 blockFocus : false,
10510 * @cfg {Boolean} disableClear Disable showing of clear button.
10512 disableClear : false,
10514 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
10516 alwaysQuery : false,
10519 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
10533 btnPosition : 'right',
10534 triggerList : true,
10535 showToggleBtn : true,
10536 // element that contains real text value.. (when hidden is used..)
10538 getAutoCreate : function()
10545 if(!this.tickable){
10546 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10551 * ComboBox with tickable selections
10554 var align = this.labelAlign || this.parentLabelAlign();
10557 cls : 'form-group roo-combobox-tickable' //input-group
10563 cls : 'tickable-buttons',
10568 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10575 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10582 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10589 Roo.each(buttons.cn, function(c){
10591 c.cls += ' btn-' + _this.size;
10594 if (_this.disabled) {
10605 cls: 'form-hidden-field'
10609 cls: 'select2-choices',
10613 cls: 'select2-search-field',
10625 cls: 'select2-container input-group select2-container-multi',
10630 // cls: 'typeahead typeahead-long dropdown-menu',
10631 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
10636 if (align ==='left' && this.fieldLabel.length) {
10638 Roo.log("left and has label");
10644 cls : 'control-label col-sm-' + this.labelWidth,
10645 html : this.fieldLabel
10649 cls : "col-sm-" + (12 - this.labelWidth),
10656 } else if ( this.fieldLabel.length) {
10662 //cls : 'input-group-addon',
10663 html : this.fieldLabel
10673 Roo.log(" no label && no align");
10680 ['xs','sm','md','lg'].map(function(size){
10681 if (settings[size]) {
10682 cfg.cls += ' col-' + size + '-' + settings[size];
10691 initEvents: function()
10695 throw "can not find store for combo";
10697 this.store = Roo.factory(this.store, Roo.data);
10700 this.initTickableEvents();
10704 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10706 if(this.hiddenName){
10708 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10710 this.hiddenField.dom.value =
10711 this.hiddenValue !== undefined ? this.hiddenValue :
10712 this.value !== undefined ? this.value : '';
10714 // prevent input submission
10715 this.el.dom.removeAttribute('name');
10716 this.hiddenField.dom.setAttribute('name', this.hiddenName);
10721 // this.el.dom.setAttribute('autocomplete', 'off');
10724 var cls = 'x-combo-list';
10726 //this.list = new Roo.Layer({
10727 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10733 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10734 _this.list.setWidth(lw);
10737 this.list.on('mouseover', this.onViewOver, this);
10738 this.list.on('mousemove', this.onViewMove, this);
10740 this.list.on('scroll', this.onViewScroll, this);
10743 this.list.swallowEvent('mousewheel');
10744 this.assetHeight = 0;
10747 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10748 this.assetHeight += this.header.getHeight();
10751 this.innerList = this.list.createChild({cls:cls+'-inner'});
10752 this.innerList.on('mouseover', this.onViewOver, this);
10753 this.innerList.on('mousemove', this.onViewMove, this);
10754 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10756 if(this.allowBlank && !this.pageSize && !this.disableClear){
10757 this.footer = this.list.createChild({cls:cls+'-ft'});
10758 this.pageTb = new Roo.Toolbar(this.footer);
10762 this.footer = this.list.createChild({cls:cls+'-ft'});
10763 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10764 {pageSize: this.pageSize});
10768 if (this.pageTb && this.allowBlank && !this.disableClear) {
10770 this.pageTb.add(new Roo.Toolbar.Fill(), {
10771 cls: 'x-btn-icon x-btn-clear',
10773 handler: function()
10776 _this.clearValue();
10777 _this.onSelect(false, -1);
10782 this.assetHeight += this.footer.getHeight();
10787 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10790 this.view = new Roo.View(this.list, this.tpl, {
10791 singleSelect:true, store: this.store, selectedClass: this.selectedClass
10793 //this.view.wrapEl.setDisplayed(false);
10794 this.view.on('click', this.onViewClick, this);
10798 this.store.on('beforeload', this.onBeforeLoad, this);
10799 this.store.on('load', this.onLoad, this);
10800 this.store.on('loadexception', this.onLoadException, this);
10802 if(this.resizable){
10803 this.resizer = new Roo.Resizable(this.list, {
10804 pinned:true, handles:'se'
10806 this.resizer.on('resize', function(r, w, h){
10807 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10808 this.listWidth = w;
10809 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10810 this.restrictHeight();
10812 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10815 if(!this.editable){
10816 this.editable = true;
10817 this.setEditable(false);
10822 if (typeof(this.events.add.listeners) != 'undefined') {
10824 this.addicon = this.wrap.createChild(
10825 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
10827 this.addicon.on('click', function(e) {
10828 this.fireEvent('add', this);
10831 if (typeof(this.events.edit.listeners) != 'undefined') {
10833 this.editicon = this.wrap.createChild(
10834 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
10835 if (this.addicon) {
10836 this.editicon.setStyle('margin-left', '40px');
10838 this.editicon.on('click', function(e) {
10840 // we fire even if inothing is selected..
10841 this.fireEvent('edit', this, this.lastData );
10847 this.keyNav = new Roo.KeyNav(this.inputEl(), {
10848 "up" : function(e){
10849 this.inKeyMode = true;
10853 "down" : function(e){
10854 if(!this.isExpanded()){
10855 this.onTriggerClick();
10857 this.inKeyMode = true;
10862 "enter" : function(e){
10863 // this.onViewClick();
10867 if(this.fireEvent("specialkey", this, e)){
10868 this.onViewClick(false);
10874 "esc" : function(e){
10878 "tab" : function(e){
10881 if(this.fireEvent("specialkey", this, e)){
10882 this.onViewClick(false);
10890 doRelay : function(foo, bar, hname){
10891 if(hname == 'down' || this.scope.isExpanded()){
10892 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10901 this.queryDelay = Math.max(this.queryDelay || 10,
10902 this.mode == 'local' ? 10 : 250);
10905 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10907 if(this.typeAhead){
10908 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10910 if(this.editable !== false){
10911 this.inputEl().on("keyup", this.onKeyUp, this);
10913 if(this.forceSelection){
10914 this.inputEl().on('blur', this.doForce, this);
10918 this.choices = this.el.select('ul.select2-choices', true).first();
10919 this.searchField = this.el.select('ul li.select2-search-field', true).first();
10923 initTickableEvents: function()
10927 if(this.hiddenName){
10929 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10931 this.hiddenField.dom.value =
10932 this.hiddenValue !== undefined ? this.hiddenValue :
10933 this.value !== undefined ? this.value : '';
10935 // prevent input submission
10936 this.el.dom.removeAttribute('name');
10937 this.hiddenField.dom.setAttribute('name', this.hiddenName);
10942 // this.list = this.el.select('ul.dropdown-menu',true).first();
10944 this.choices = this.el.select('ul.select2-choices', true).first();
10945 this.searchField = this.el.select('ul li.select2-search-field', true).first();
10946 if(this.triggerList){
10947 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10950 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10951 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10953 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10954 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10956 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10957 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10959 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10960 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10961 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10964 this.cancelBtn.hide();
10969 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10970 _this.list.setWidth(lw);
10973 this.list.on('mouseover', this.onViewOver, this);
10974 this.list.on('mousemove', this.onViewMove, this);
10976 this.list.on('scroll', this.onViewScroll, this);
10979 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>';
10982 this.view = new Roo.View(this.list, this.tpl, {
10983 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10986 //this.view.wrapEl.setDisplayed(false);
10987 this.view.on('click', this.onViewClick, this);
10991 this.store.on('beforeload', this.onBeforeLoad, this);
10992 this.store.on('load', this.onLoad, this);
10993 this.store.on('loadexception', this.onLoadException, this);
10995 // this.keyNav = new Roo.KeyNav(this.inputEl(), {
10996 // "up" : function(e){
10997 // this.inKeyMode = true;
10998 // this.selectPrev();
11001 // "down" : function(e){
11002 // if(!this.isExpanded()){
11003 // this.onTriggerClick();
11005 // this.inKeyMode = true;
11006 // this.selectNext();
11010 // "enter" : function(e){
11011 //// this.onViewClick();
11013 // this.collapse();
11015 // if(this.fireEvent("specialkey", this, e)){
11016 // this.onViewClick(false);
11022 // "esc" : function(e){
11023 // this.collapse();
11026 // "tab" : function(e){
11027 // this.collapse();
11029 // if(this.fireEvent("specialkey", this, e)){
11030 // this.onViewClick(false);
11038 // doRelay : function(foo, bar, hname){
11039 // if(hname == 'down' || this.scope.isExpanded()){
11040 // return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11045 // forceKeyDown: true
11049 this.queryDelay = Math.max(this.queryDelay || 10,
11050 this.mode == 'local' ? 10 : 250);
11053 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11055 if(this.typeAhead){
11056 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11060 onDestroy : function(){
11062 this.view.setStore(null);
11063 this.view.el.removeAllListeners();
11064 this.view.el.remove();
11065 this.view.purgeListeners();
11068 this.list.dom.innerHTML = '';
11072 this.store.un('beforeload', this.onBeforeLoad, this);
11073 this.store.un('load', this.onLoad, this);
11074 this.store.un('loadexception', this.onLoadException, this);
11076 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11080 fireKey : function(e){
11081 if(e.isNavKeyPress() && !this.list.isVisible()){
11082 this.fireEvent("specialkey", this, e);
11087 onResize: function(w, h){
11088 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11090 // if(typeof w != 'number'){
11091 // // we do not handle it!?!?
11094 // var tw = this.trigger.getWidth();
11095 // // tw += this.addicon ? this.addicon.getWidth() : 0;
11096 // // tw += this.editicon ? this.editicon.getWidth() : 0;
11098 // this.inputEl().setWidth( this.adjustWidth('input', x));
11100 // //this.trigger.setStyle('left', x+'px');
11102 // if(this.list && this.listWidth === undefined){
11103 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11104 // this.list.setWidth(lw);
11105 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11113 * Allow or prevent the user from directly editing the field text. If false is passed,
11114 * the user will only be able to select from the items defined in the dropdown list. This method
11115 * is the runtime equivalent of setting the 'editable' config option at config time.
11116 * @param {Boolean} value True to allow the user to directly edit the field text
11118 setEditable : function(value){
11119 if(value == this.editable){
11122 this.editable = value;
11124 this.inputEl().dom.setAttribute('readOnly', true);
11125 this.inputEl().on('mousedown', this.onTriggerClick, this);
11126 this.inputEl().addClass('x-combo-noedit');
11128 this.inputEl().dom.setAttribute('readOnly', false);
11129 this.inputEl().un('mousedown', this.onTriggerClick, this);
11130 this.inputEl().removeClass('x-combo-noedit');
11136 onBeforeLoad : function(combo,opts){
11137 if(!this.hasFocus){
11141 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11143 this.restrictHeight();
11144 this.selectedIndex = -1;
11148 onLoad : function(){
11150 this.hasQuery = false;
11152 if(!this.hasFocus){
11156 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11157 this.loading.hide();
11160 if(this.store.getCount() > 0){
11162 // this.restrictHeight();
11163 if(this.lastQuery == this.allQuery){
11164 if(this.editable && !this.tickable){
11165 this.inputEl().dom.select();
11169 !this.selectByValue(this.value, true) &&
11170 this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' ||
11171 this.store.lastOptions.add != true)
11173 this.select(0, true);
11176 if(this.autoFocus){
11179 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11180 this.taTask.delay(this.typeAheadDelay);
11184 this.onEmptyResults();
11190 onLoadException : function()
11192 this.hasQuery = false;
11194 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11195 this.loading.hide();
11199 Roo.log(this.store.reader.jsonData);
11200 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11202 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11208 onTypeAhead : function(){
11209 if(this.store.getCount() > 0){
11210 var r = this.store.getAt(0);
11211 var newValue = r.data[this.displayField];
11212 var len = newValue.length;
11213 var selStart = this.getRawValue().length;
11215 if(selStart != len){
11216 this.setRawValue(newValue);
11217 this.selectText(selStart, newValue.length);
11223 onSelect : function(record, index){
11225 if(this.fireEvent('beforeselect', this, record, index) !== false){
11227 this.setFromData(index > -1 ? record.data : false);
11230 this.fireEvent('select', this, record, index);
11235 * Returns the currently selected field value or empty string if no value is set.
11236 * @return {String} value The selected value
11238 getValue : function(){
11241 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11244 if(this.valueField){
11245 return typeof this.value != 'undefined' ? this.value : '';
11247 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11252 * Clears any text/value currently set in the field
11254 clearValue : function(){
11255 if(this.hiddenField){
11256 this.hiddenField.dom.value = '';
11259 this.setRawValue('');
11260 this.lastSelectionText = '';
11265 * Sets the specified value into the field. If the value finds a match, the corresponding record text
11266 * will be displayed in the field. If the value does not match the data value of an existing item,
11267 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11268 * Otherwise the field will be blank (although the value will still be set).
11269 * @param {String} value The value to match
11271 setValue : function(v){
11278 if(this.valueField){
11279 var r = this.findRecord(this.valueField, v);
11281 text = r.data[this.displayField];
11282 }else if(this.valueNotFoundText !== undefined){
11283 text = this.valueNotFoundText;
11286 this.lastSelectionText = text;
11287 if(this.hiddenField){
11288 this.hiddenField.dom.value = v;
11290 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11294 * @property {Object} the last set data for the element
11299 * Sets the value of the field based on a object which is related to the record format for the store.
11300 * @param {Object} value the value to set as. or false on reset?
11302 setFromData : function(o){
11305 if(typeof o.display_name !== 'string'){
11306 for(var i=0;i<o.display_name.length;i++){
11307 this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
11315 var dv = ''; // display value
11316 var vv = ''; // value value..
11318 if (this.displayField) {
11319 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11321 // this is an error condition!!!
11322 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
11325 if(this.valueField){
11326 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11329 if(this.hiddenField){
11330 this.hiddenField.dom.value = vv;
11332 this.lastSelectionText = dv;
11333 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11337 // no hidden field.. - we store the value in 'value', but still display
11338 // display field!!!!
11339 this.lastSelectionText = dv;
11340 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11346 reset : function(){
11347 // overridden so that last data is reset..
11348 this.setValue(this.originalValue);
11349 this.clearInvalid();
11350 this.lastData = false;
11352 this.view.clearSelections();
11356 findRecord : function(prop, value){
11358 if(this.store.getCount() > 0){
11359 this.store.each(function(r){
11360 if(r.data[prop] == value){
11370 getName: function()
11372 // returns hidden if it's set..
11373 if (!this.rendered) {return ''};
11374 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
11378 onViewMove : function(e, t){
11379 this.inKeyMode = false;
11383 onViewOver : function(e, t){
11384 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11387 var item = this.view.findItemFromChild(t);
11390 var index = this.view.indexOf(item);
11391 this.select(index, false);
11396 onViewClick : function(view, doFocus, el, e)
11398 var index = this.view.getSelectedIndexes()[0];
11400 var r = this.store.getAt(index);
11404 if(e.getTarget().nodeName.toLowerCase() != 'input'){
11411 Roo.each(this.tickItems, function(v,k){
11413 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11414 _this.tickItems.splice(k, 1);
11424 this.tickItems.push(r.data);
11429 this.onSelect(r, index);
11431 if(doFocus !== false && !this.blockFocus){
11432 this.inputEl().focus();
11437 restrictHeight : function(){
11438 //this.innerList.dom.style.height = '';
11439 //var inner = this.innerList.dom;
11440 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11441 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11442 //this.list.beginUpdate();
11443 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11444 this.list.alignTo(this.inputEl(), this.listAlign);
11445 this.list.alignTo(this.inputEl(), this.listAlign);
11446 //this.list.endUpdate();
11450 onEmptyResults : function(){
11455 * Returns true if the dropdown list is expanded, else false.
11457 isExpanded : function(){
11458 return this.list.isVisible();
11462 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11463 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11464 * @param {String} value The data value of the item to select
11465 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11466 * selected item if it is not currently in view (defaults to true)
11467 * @return {Boolean} True if the value matched an item in the list, else false
11469 selectByValue : function(v, scrollIntoView){
11470 if(v !== undefined && v !== null){
11471 var r = this.findRecord(this.valueField || this.displayField, v);
11473 this.select(this.store.indexOf(r), scrollIntoView);
11481 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11482 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11483 * @param {Number} index The zero-based index of the list item to select
11484 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11485 * selected item if it is not currently in view (defaults to true)
11487 select : function(index, scrollIntoView){
11488 this.selectedIndex = index;
11489 this.view.select(index);
11490 if(scrollIntoView !== false){
11491 var el = this.view.getNode(index);
11492 if(el && !this.multiple && !this.tickable){
11493 this.list.scrollChildIntoView(el, false);
11499 selectNext : function(){
11500 var ct = this.store.getCount();
11502 if(this.selectedIndex == -1){
11504 }else if(this.selectedIndex < ct-1){
11505 this.select(this.selectedIndex+1);
11511 selectPrev : function(){
11512 var ct = this.store.getCount();
11514 if(this.selectedIndex == -1){
11516 }else if(this.selectedIndex != 0){
11517 this.select(this.selectedIndex-1);
11523 onKeyUp : function(e){
11524 if(this.editable !== false && !e.isSpecialKey()){
11525 this.lastKey = e.getKey();
11526 this.dqTask.delay(this.queryDelay);
11531 validateBlur : function(){
11532 return !this.list || !this.list.isVisible();
11536 initQuery : function(){
11537 this.doQuery(this.getRawValue());
11541 doForce : function(){
11542 if(this.inputEl().dom.value.length > 0){
11543 this.inputEl().dom.value =
11544 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11550 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
11551 * query allowing the query action to be canceled if needed.
11552 * @param {String} query The SQL query to execute
11553 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11554 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
11555 * saved in the current store (defaults to false)
11557 doQuery : function(q, forceAll){
11559 if(q === undefined || q === null){
11564 forceAll: forceAll,
11568 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11573 forceAll = qe.forceAll;
11574 if(forceAll === true || (q.length >= this.minChars)){
11576 this.hasQuery = true;
11578 if(this.lastQuery != q || this.alwaysQuery){
11579 this.lastQuery = q;
11580 if(this.mode == 'local'){
11581 this.selectedIndex = -1;
11583 this.store.clearFilter();
11585 this.store.filter(this.displayField, q);
11589 this.store.baseParams[this.queryParam] = q;
11591 var options = {params : this.getParams(q)};
11594 options.add = true;
11595 options.params.start = this.page * this.pageSize;
11598 this.store.load(options);
11600 * this code will make the page width larger, at the beginning, the list not align correctly,
11601 * we should expand the list on onLoad
11602 * so command out it
11607 this.selectedIndex = -1;
11612 this.loadNext = false;
11616 getParams : function(q){
11618 //p[this.queryParam] = q;
11622 p.limit = this.pageSize;
11628 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11630 collapse : function(){
11631 if(!this.isExpanded()){
11639 this.cancelBtn.hide();
11640 this.trigger.show();
11643 Roo.get(document).un('mousedown', this.collapseIf, this);
11644 Roo.get(document).un('mousewheel', this.collapseIf, this);
11645 if (!this.editable) {
11646 Roo.get(document).un('keydown', this.listKeyPress, this);
11648 this.fireEvent('collapse', this);
11652 collapseIf : function(e){
11653 var in_combo = e.within(this.el);
11654 var in_list = e.within(this.list);
11655 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11657 if (in_combo || in_list || is_list) {
11658 //e.stopPropagation();
11663 this.onTickableFooterButtonClick(e, false, false);
11671 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11673 expand : function(){
11675 if(this.isExpanded() || !this.hasFocus){
11679 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11680 this.list.setWidth(lw);
11687 this.restrictHeight();
11691 this.tickItems = Roo.apply([], this.item);
11694 this.cancelBtn.show();
11695 this.trigger.hide();
11699 Roo.get(document).on('mousedown', this.collapseIf, this);
11700 Roo.get(document).on('mousewheel', this.collapseIf, this);
11701 if (!this.editable) {
11702 Roo.get(document).on('keydown', this.listKeyPress, this);
11705 this.fireEvent('expand', this);
11709 // Implements the default empty TriggerField.onTriggerClick function
11710 onTriggerClick : function(e)
11712 Roo.log('trigger click');
11714 if(this.disabled || !this.triggerList){
11719 this.loadNext = false;
11721 if(this.isExpanded()){
11723 if (!this.blockFocus) {
11724 this.inputEl().focus();
11728 this.hasFocus = true;
11729 if(this.triggerAction == 'all') {
11730 this.doQuery(this.allQuery, true);
11732 this.doQuery(this.getRawValue());
11734 if (!this.blockFocus) {
11735 this.inputEl().focus();
11740 onTickableTriggerClick : function(e)
11747 this.loadNext = false;
11748 this.hasFocus = true;
11750 if(this.triggerAction == 'all') {
11751 this.doQuery(this.allQuery, true);
11753 this.doQuery(this.getRawValue());
11757 onSearchFieldClick : function(e)
11759 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11764 this.loadNext = false;
11765 this.hasFocus = true;
11767 if(this.triggerAction == 'all') {
11768 this.doQuery(this.allQuery, true);
11770 this.doQuery(this.getRawValue());
11774 listKeyPress : function(e)
11776 //Roo.log('listkeypress');
11777 // scroll to first matching element based on key pres..
11778 if (e.isSpecialKey()) {
11781 var k = String.fromCharCode(e.getKey()).toUpperCase();
11784 var csel = this.view.getSelectedNodes();
11785 var cselitem = false;
11787 var ix = this.view.indexOf(csel[0]);
11788 cselitem = this.store.getAt(ix);
11789 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11795 this.store.each(function(v) {
11797 // start at existing selection.
11798 if (cselitem.id == v.id) {
11804 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11805 match = this.store.indexOf(v);
11811 if (match === false) {
11812 return true; // no more action?
11815 this.view.select(match);
11816 var sn = Roo.get(this.view.getSelectedNodes()[0])
11817 //sn.scrollIntoView(sn.dom.parentNode, false);
11820 onViewScroll : function(e, t){
11822 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){
11826 this.hasQuery = true;
11828 this.loading = this.list.select('.loading', true).first();
11830 if(this.loading === null){
11831 this.list.createChild({
11833 cls: 'loading select2-more-results select2-active',
11834 html: 'Loading more results...'
11837 this.loading = this.list.select('.loading', true).first();
11839 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11841 this.loading.hide();
11844 this.loading.show();
11849 this.loadNext = true;
11851 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11856 addItem : function(o)
11858 var dv = ''; // display value
11860 if (this.displayField) {
11861 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11863 // this is an error condition!!!
11864 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
11871 var choice = this.choices.createChild({
11873 cls: 'select2-search-choice',
11882 cls: 'select2-search-choice-close',
11887 }, this.searchField);
11889 var close = choice.select('a.select2-search-choice-close', true).first()
11891 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11899 this.inputEl().dom.value = '';
11903 onRemoveItem : function(e, _self, o)
11905 e.preventDefault();
11906 var index = this.item.indexOf(o.data) * 1;
11909 Roo.log('not this item?!');
11913 this.item.splice(index, 1);
11918 this.fireEvent('remove', this, e);
11922 syncValue : function()
11924 if(!this.item.length){
11931 Roo.each(this.item, function(i){
11932 if(_this.valueField){
11933 value.push(i[_this.valueField]);
11940 this.value = value.join(',');
11942 if(this.hiddenField){
11943 this.hiddenField.dom.value = this.value;
11947 clearItem : function()
11949 if(!this.multiple){
11955 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11962 inputEl: function ()
11965 return this.searchField;
11967 return this.el.select('input.form-control',true).first();
11971 onTickableFooterButtonClick : function(e, btn, el)
11973 e.preventDefault();
11975 if(btn && btn.name == 'cancel'){
11976 this.tickItems = Roo.apply([], this.item);
11985 Roo.each(this.tickItems, function(o){
11996 * @cfg {Boolean} grow
12000 * @cfg {Number} growMin
12004 * @cfg {Number} growMax
12014 * Ext JS Library 1.1.1
12015 * Copyright(c) 2006-2007, Ext JS, LLC.
12017 * Originally Released Under LGPL - original licence link has changed is not relivant.
12020 * <script type="text/javascript">
12025 * @extends Roo.util.Observable
12026 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
12027 * This class also supports single and multi selection modes. <br>
12028 * Create a data model bound view:
12030 var store = new Roo.data.Store(...);
12032 var view = new Roo.View({
12034 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
12036 singleSelect: true,
12037 selectedClass: "ydataview-selected",
12041 // listen for node click?
12042 view.on("click", function(vw, index, node, e){
12043 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12047 dataModel.load("foobar.xml");
12049 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12051 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12052 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12054 * Note: old style constructor is still suported (container, template, config)
12057 * Create a new View
12058 * @param {Object} config The config object
12061 Roo.View = function(config, depreciated_tpl, depreciated_config){
12063 this.parent = false;
12065 if (typeof(depreciated_tpl) == 'undefined') {
12066 // new way.. - universal constructor.
12067 Roo.apply(this, config);
12068 this.el = Roo.get(this.el);
12071 this.el = Roo.get(config);
12072 this.tpl = depreciated_tpl;
12073 Roo.apply(this, depreciated_config);
12075 this.wrapEl = this.el.wrap().wrap();
12076 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12079 if(typeof(this.tpl) == "string"){
12080 this.tpl = new Roo.Template(this.tpl);
12082 // support xtype ctors..
12083 this.tpl = new Roo.factory(this.tpl, Roo);
12087 this.tpl.compile();
12092 * @event beforeclick
12093 * Fires before a click is processed. Returns false to cancel the default action.
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
12099 "beforeclick" : true,
12102 * Fires when a template node is 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
12111 * Fires when a template node is double 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
12119 * @event contextmenu
12120 * Fires when a template node is right clicked.
12121 * @param {Roo.View} this
12122 * @param {Number} index The index of the target node
12123 * @param {HTMLElement} node The target node
12124 * @param {Roo.EventObject} e The raw event object
12126 "contextmenu" : true,
12128 * @event selectionchange
12129 * Fires when the selected nodes change.
12130 * @param {Roo.View} this
12131 * @param {Array} selections Array of the selected nodes
12133 "selectionchange" : true,
12136 * @event beforeselect
12137 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12138 * @param {Roo.View} this
12139 * @param {HTMLElement} node The node to be selected
12140 * @param {Array} selections Array of currently selected nodes
12142 "beforeselect" : true,
12144 * @event preparedata
12145 * Fires on every row to render, to allow you to change the data.
12146 * @param {Roo.View} this
12147 * @param {Object} data to be rendered (change this)
12149 "preparedata" : true
12157 "click": this.onClick,
12158 "dblclick": this.onDblClick,
12159 "contextmenu": this.onContextMenu,
12163 this.selections = [];
12165 this.cmp = new Roo.CompositeElementLite([]);
12167 this.store = Roo.factory(this.store, Roo.data);
12168 this.setStore(this.store, true);
12171 if ( this.footer && this.footer.xtype) {
12173 var fctr = this.wrapEl.appendChild(document.createElement("div"));
12175 this.footer.dataSource = this.store
12176 this.footer.container = fctr;
12177 this.footer = Roo.factory(this.footer, Roo);
12178 fctr.insertFirst(this.el);
12180 // this is a bit insane - as the paging toolbar seems to detach the el..
12181 // dom.parentNode.parentNode.parentNode
12182 // they get detached?
12186 Roo.View.superclass.constructor.call(this);
12191 Roo.extend(Roo.View, Roo.util.Observable, {
12194 * @cfg {Roo.data.Store} store Data store to load data from.
12199 * @cfg {String|Roo.Element} el The container element.
12204 * @cfg {String|Roo.Template} tpl The template used by this View
12208 * @cfg {String} dataName the named area of the template to use as the data area
12209 * Works with domtemplates roo-name="name"
12213 * @cfg {String} selectedClass The css class to add to selected nodes
12215 selectedClass : "x-view-selected",
12217 * @cfg {String} emptyText The empty text to show when nothing is loaded.
12222 * @cfg {String} text to display on mask (default Loading)
12226 * @cfg {Boolean} multiSelect Allow multiple selection
12228 multiSelect : false,
12230 * @cfg {Boolean} singleSelect Allow single selection
12232 singleSelect: false,
12235 * @cfg {Boolean} toggleSelect - selecting
12237 toggleSelect : false,
12240 * @cfg {Boolean} tickable - selecting
12245 * Returns the element this view is bound to.
12246 * @return {Roo.Element}
12248 getEl : function(){
12249 return this.wrapEl;
12255 * Refreshes the view. - called by datachanged on the store. - do not call directly.
12257 refresh : function(){
12258 Roo.log('refresh');
12261 // if we are using something like 'domtemplate', then
12262 // the what gets used is:
12263 // t.applySubtemplate(NAME, data, wrapping data..)
12264 // the outer template then get' applied with
12265 // the store 'extra data'
12266 // and the body get's added to the
12267 // roo-name="data" node?
12268 // <span class='roo-tpl-{name}'></span> ?????
12272 this.clearSelections();
12273 this.el.update("");
12275 var records = this.store.getRange();
12276 if(records.length < 1) {
12278 // is this valid?? = should it render a template??
12280 this.el.update(this.emptyText);
12284 if (this.dataName) {
12285 this.el.update(t.apply(this.store.meta)); //????
12286 el = this.el.child('.roo-tpl-' + this.dataName);
12289 for(var i = 0, len = records.length; i < len; i++){
12290 var data = this.prepareData(records[i].data, i, records[i]);
12291 this.fireEvent("preparedata", this, data, i, records[i]);
12293 var d = Roo.apply({}, data);
12296 Roo.apply(d, {'roo-id' : Roo.id()});
12300 Roo.each(this.parent.item, function(item){
12301 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12304 Roo.apply(d, {'roo-data-checked' : 'checked'});
12308 html[html.length] = Roo.util.Format.trim(
12310 t.applySubtemplate(this.dataName, d, this.store.meta) :
12317 el.update(html.join(""));
12318 this.nodes = el.dom.childNodes;
12319 this.updateIndexes(0);
12324 * Function to override to reformat the data that is sent to
12325 * the template for each node.
12326 * DEPRICATED - use the preparedata event handler.
12327 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12328 * a JSON object for an UpdateManager bound view).
12330 prepareData : function(data, index, record)
12332 this.fireEvent("preparedata", this, data, index, record);
12336 onUpdate : function(ds, record){
12337 Roo.log('on update');
12338 this.clearSelections();
12339 var index = this.store.indexOf(record);
12340 var n = this.nodes[index];
12341 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12342 n.parentNode.removeChild(n);
12343 this.updateIndexes(index, index);
12349 onAdd : function(ds, records, index)
12351 Roo.log(['on Add', ds, records, index] );
12352 this.clearSelections();
12353 if(this.nodes.length == 0){
12357 var n = this.nodes[index];
12358 for(var i = 0, len = records.length; i < len; i++){
12359 var d = this.prepareData(records[i].data, i, records[i]);
12361 this.tpl.insertBefore(n, d);
12364 this.tpl.append(this.el, d);
12367 this.updateIndexes(index);
12370 onRemove : function(ds, record, index){
12371 Roo.log('onRemove');
12372 this.clearSelections();
12373 var el = this.dataName ?
12374 this.el.child('.roo-tpl-' + this.dataName) :
12377 el.dom.removeChild(this.nodes[index]);
12378 this.updateIndexes(index);
12382 * Refresh an individual node.
12383 * @param {Number} index
12385 refreshNode : function(index){
12386 this.onUpdate(this.store, this.store.getAt(index));
12389 updateIndexes : function(startIndex, endIndex){
12390 var ns = this.nodes;
12391 startIndex = startIndex || 0;
12392 endIndex = endIndex || ns.length - 1;
12393 for(var i = startIndex; i <= endIndex; i++){
12394 ns[i].nodeIndex = i;
12399 * Changes the data store this view uses and refresh the view.
12400 * @param {Store} store
12402 setStore : function(store, initial){
12403 if(!initial && this.store){
12404 this.store.un("datachanged", this.refresh);
12405 this.store.un("add", this.onAdd);
12406 this.store.un("remove", this.onRemove);
12407 this.store.un("update", this.onUpdate);
12408 this.store.un("clear", this.refresh);
12409 this.store.un("beforeload", this.onBeforeLoad);
12410 this.store.un("load", this.onLoad);
12411 this.store.un("loadexception", this.onLoad);
12415 store.on("datachanged", this.refresh, this);
12416 store.on("add", this.onAdd, this);
12417 store.on("remove", this.onRemove, this);
12418 store.on("update", this.onUpdate, this);
12419 store.on("clear", this.refresh, this);
12420 store.on("beforeload", this.onBeforeLoad, this);
12421 store.on("load", this.onLoad, this);
12422 store.on("loadexception", this.onLoad, this);
12430 * onbeforeLoad - masks the loading area.
12433 onBeforeLoad : function(store,opts)
12435 Roo.log('onBeforeLoad');
12437 this.el.update("");
12439 this.el.mask(this.mask ? this.mask : "Loading" );
12441 onLoad : function ()
12448 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12449 * @param {HTMLElement} node
12450 * @return {HTMLElement} The template node
12452 findItemFromChild : function(node){
12453 var el = this.dataName ?
12454 this.el.child('.roo-tpl-' + this.dataName,true) :
12457 if(!node || node.parentNode == el){
12460 var p = node.parentNode;
12461 while(p && p != el){
12462 if(p.parentNode == el){
12471 onClick : function(e){
12472 var item = this.findItemFromChild(e.getTarget());
12474 var index = this.indexOf(item);
12475 if(this.onItemClick(item, index, e) !== false){
12476 this.fireEvent("click", this, index, item, e);
12479 this.clearSelections();
12484 onContextMenu : function(e){
12485 var item = this.findItemFromChild(e.getTarget());
12487 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12492 onDblClick : function(e){
12493 var item = this.findItemFromChild(e.getTarget());
12495 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12499 onItemClick : function(item, index, e)
12501 if(this.fireEvent("beforeclick", this, index, item, e) === false){
12504 if (this.toggleSelect) {
12505 var m = this.isSelected(item) ? 'unselect' : 'select';
12508 _t[m](item, true, false);
12511 if(this.multiSelect || this.singleSelect){
12512 if(this.multiSelect && e.shiftKey && this.lastSelection){
12513 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12515 this.select(item, this.multiSelect && e.ctrlKey);
12516 this.lastSelection = item;
12519 if(!this.tickable){
12520 e.preventDefault();
12528 * Get the number of selected nodes.
12531 getSelectionCount : function(){
12532 return this.selections.length;
12536 * Get the currently selected nodes.
12537 * @return {Array} An array of HTMLElements
12539 getSelectedNodes : function(){
12540 return this.selections;
12544 * Get the indexes of the selected nodes.
12547 getSelectedIndexes : function(){
12548 var indexes = [], s = this.selections;
12549 for(var i = 0, len = s.length; i < len; i++){
12550 indexes.push(s[i].nodeIndex);
12556 * Clear all selections
12557 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12559 clearSelections : function(suppressEvent){
12560 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12561 this.cmp.elements = this.selections;
12562 this.cmp.removeClass(this.selectedClass);
12563 this.selections = [];
12564 if(!suppressEvent){
12565 this.fireEvent("selectionchange", this, this.selections);
12571 * Returns true if the passed node is selected
12572 * @param {HTMLElement/Number} node The node or node index
12573 * @return {Boolean}
12575 isSelected : function(node){
12576 var s = this.selections;
12580 node = this.getNode(node);
12581 return s.indexOf(node) !== -1;
12586 * @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
12587 * @param {Boolean} keepExisting (optional) true to keep existing selections
12588 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12590 select : function(nodeInfo, keepExisting, suppressEvent){
12591 if(nodeInfo instanceof Array){
12593 this.clearSelections(true);
12595 for(var i = 0, len = nodeInfo.length; i < len; i++){
12596 this.select(nodeInfo[i], true, true);
12600 var node = this.getNode(nodeInfo);
12601 if(!node || this.isSelected(node)){
12602 return; // already selected.
12605 this.clearSelections(true);
12607 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12608 Roo.fly(node).addClass(this.selectedClass);
12609 this.selections.push(node);
12610 if(!suppressEvent){
12611 this.fireEvent("selectionchange", this, this.selections);
12619 * @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
12620 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12621 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12623 unselect : function(nodeInfo, keepExisting, suppressEvent)
12625 if(nodeInfo instanceof Array){
12626 Roo.each(this.selections, function(s) {
12627 this.unselect(s, nodeInfo);
12631 var node = this.getNode(nodeInfo);
12632 if(!node || !this.isSelected(node)){
12633 Roo.log("not selected");
12634 return; // not selected.
12638 Roo.each(this.selections, function(s) {
12640 Roo.fly(node).removeClass(this.selectedClass);
12647 this.selections= ns;
12648 this.fireEvent("selectionchange", this, this.selections);
12652 * Gets a template node.
12653 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12654 * @return {HTMLElement} The node or null if it wasn't found
12656 getNode : function(nodeInfo){
12657 if(typeof nodeInfo == "string"){
12658 return document.getElementById(nodeInfo);
12659 }else if(typeof nodeInfo == "number"){
12660 return this.nodes[nodeInfo];
12666 * Gets a range template nodes.
12667 * @param {Number} startIndex
12668 * @param {Number} endIndex
12669 * @return {Array} An array of nodes
12671 getNodes : function(start, end){
12672 var ns = this.nodes;
12673 start = start || 0;
12674 end = typeof end == "undefined" ? ns.length - 1 : end;
12677 for(var i = start; i <= end; i++){
12681 for(var i = start; i >= end; i--){
12689 * Finds the index of the passed node
12690 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12691 * @return {Number} The index of the node or -1
12693 indexOf : function(node){
12694 node = this.getNode(node);
12695 if(typeof node.nodeIndex == "number"){
12696 return node.nodeIndex;
12698 var ns = this.nodes;
12699 for(var i = 0, len = ns.length; i < len; i++){
12710 * based on jquery fullcalendar
12714 Roo.bootstrap = Roo.bootstrap || {};
12716 * @class Roo.bootstrap.Calendar
12717 * @extends Roo.bootstrap.Component
12718 * Bootstrap Calendar class
12719 * @cfg {Boolean} loadMask (true|false) default false
12720 * @cfg {Object} header generate the user specific header of the calendar, default false
12723 * Create a new Container
12724 * @param {Object} config The config object
12729 Roo.bootstrap.Calendar = function(config){
12730 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12734 * Fires when a date is selected
12735 * @param {DatePicker} this
12736 * @param {Date} date The selected date
12740 * @event monthchange
12741 * Fires when the displayed month changes
12742 * @param {DatePicker} this
12743 * @param {Date} date The selected month
12745 'monthchange': true,
12747 * @event evententer
12748 * Fires when mouse over an event
12749 * @param {Calendar} this
12750 * @param {event} Event
12752 'evententer': true,
12754 * @event eventleave
12755 * Fires when the mouse leaves an
12756 * @param {Calendar} this
12759 'eventleave': true,
12761 * @event eventclick
12762 * Fires when the mouse click an
12763 * @param {Calendar} this
12772 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
12775 * @cfg {Number} startDay
12776 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12784 getAutoCreate : function(){
12787 var fc_button = function(name, corner, style, content ) {
12788 return Roo.apply({},{
12790 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
12792 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12795 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12806 style : 'width:100%',
12813 cls : 'fc-header-left',
12815 fc_button('prev', 'left', 'arrow', '‹' ),
12816 fc_button('next', 'right', 'arrow', '›' ),
12817 { tag: 'span', cls: 'fc-header-space' },
12818 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
12826 cls : 'fc-header-center',
12830 cls: 'fc-header-title',
12833 html : 'month / year'
12841 cls : 'fc-header-right',
12843 /* fc_button('month', 'left', '', 'month' ),
12844 fc_button('week', '', '', 'week' ),
12845 fc_button('day', 'right', '', 'day' )
12857 header = this.header;
12860 var cal_heads = function() {
12862 // fixme - handle this.
12864 for (var i =0; i < Date.dayNames.length; i++) {
12865 var d = Date.dayNames[i];
12868 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12869 html : d.substring(0,3)
12873 ret[0].cls += ' fc-first';
12874 ret[6].cls += ' fc-last';
12877 var cal_cell = function(n) {
12880 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12885 cls: 'fc-day-number',
12889 cls: 'fc-day-content',
12893 style: 'position: relative;' // height: 17px;
12905 var cal_rows = function() {
12908 for (var r = 0; r < 6; r++) {
12915 for (var i =0; i < Date.dayNames.length; i++) {
12916 var d = Date.dayNames[i];
12917 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12920 row.cn[0].cls+=' fc-first';
12921 row.cn[0].cn[0].style = 'min-height:90px';
12922 row.cn[6].cls+=' fc-last';
12926 ret[0].cls += ' fc-first';
12927 ret[4].cls += ' fc-prev-last';
12928 ret[5].cls += ' fc-last';
12935 cls: 'fc-border-separate',
12936 style : 'width:100%',
12944 cls : 'fc-first fc-last',
12962 cls : 'fc-content',
12963 style : "position: relative;",
12966 cls : 'fc-view fc-view-month fc-grid',
12967 style : 'position: relative',
12968 unselectable : 'on',
12971 cls : 'fc-event-container',
12972 style : 'position:absolute;z-index:8;top:0;left:0;'
12990 initEvents : function()
12993 throw "can not find store for calendar";
12999 style: "text-align:center",
13003 style: "background-color:white;width:50%;margin:250 auto",
13007 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
13018 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13020 var size = this.el.select('.fc-content', true).first().getSize();
13021 this.maskEl.setSize(size.width, size.height);
13022 this.maskEl.enableDisplayMode("block");
13023 if(!this.loadMask){
13024 this.maskEl.hide();
13027 this.store = Roo.factory(this.store, Roo.data);
13028 this.store.on('load', this.onLoad, this);
13029 this.store.on('beforeload', this.onBeforeLoad, this);
13033 this.cells = this.el.select('.fc-day',true);
13034 //Roo.log(this.cells);
13035 this.textNodes = this.el.query('.fc-day-number');
13036 this.cells.addClassOnOver('fc-state-hover');
13038 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13039 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13040 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13041 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13043 this.on('monthchange', this.onMonthChange, this);
13045 this.update(new Date().clearTime());
13048 resize : function() {
13049 var sz = this.el.getSize();
13051 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13052 this.el.select('.fc-day-content div',true).setHeight(34);
13057 showPrevMonth : function(e){
13058 this.update(this.activeDate.add("mo", -1));
13060 showToday : function(e){
13061 this.update(new Date().clearTime());
13064 showNextMonth : function(e){
13065 this.update(this.activeDate.add("mo", 1));
13069 showPrevYear : function(){
13070 this.update(this.activeDate.add("y", -1));
13074 showNextYear : function(){
13075 this.update(this.activeDate.add("y", 1));
13080 update : function(date)
13082 var vd = this.activeDate;
13083 this.activeDate = date;
13084 // if(vd && this.el){
13085 // var t = date.getTime();
13086 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13087 // Roo.log('using add remove');
13089 // this.fireEvent('monthchange', this, date);
13091 // this.cells.removeClass("fc-state-highlight");
13092 // this.cells.each(function(c){
13093 // if(c.dateValue == t){
13094 // c.addClass("fc-state-highlight");
13095 // setTimeout(function(){
13096 // try{c.dom.firstChild.focus();}catch(e){}
13106 var days = date.getDaysInMonth();
13108 var firstOfMonth = date.getFirstDateOfMonth();
13109 var startingPos = firstOfMonth.getDay()-this.startDay;
13111 if(startingPos < this.startDay){
13115 var pm = date.add(Date.MONTH, -1);
13116 var prevStart = pm.getDaysInMonth()-startingPos;
13118 this.cells = this.el.select('.fc-day',true);
13119 this.textNodes = this.el.query('.fc-day-number');
13120 this.cells.addClassOnOver('fc-state-hover');
13122 var cells = this.cells.elements;
13123 var textEls = this.textNodes;
13125 Roo.each(cells, function(cell){
13126 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13129 days += startingPos;
13131 // convert everything to numbers so it's fast
13132 var day = 86400000;
13133 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13136 //Roo.log(prevStart);
13138 var today = new Date().clearTime().getTime();
13139 var sel = date.clearTime().getTime();
13140 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13141 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13142 var ddMatch = this.disabledDatesRE;
13143 var ddText = this.disabledDatesText;
13144 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13145 var ddaysText = this.disabledDaysText;
13146 var format = this.format;
13148 var setCellClass = function(cal, cell){
13152 //Roo.log('set Cell Class');
13154 var t = d.getTime();
13158 cell.dateValue = t;
13160 cell.className += " fc-today";
13161 cell.className += " fc-state-highlight";
13162 cell.title = cal.todayText;
13165 // disable highlight in other month..
13166 //cell.className += " fc-state-highlight";
13171 cell.className = " fc-state-disabled";
13172 cell.title = cal.minText;
13176 cell.className = " fc-state-disabled";
13177 cell.title = cal.maxText;
13181 if(ddays.indexOf(d.getDay()) != -1){
13182 cell.title = ddaysText;
13183 cell.className = " fc-state-disabled";
13186 if(ddMatch && format){
13187 var fvalue = d.dateFormat(format);
13188 if(ddMatch.test(fvalue)){
13189 cell.title = ddText.replace("%0", fvalue);
13190 cell.className = " fc-state-disabled";
13194 if (!cell.initialClassName) {
13195 cell.initialClassName = cell.dom.className;
13198 cell.dom.className = cell.initialClassName + ' ' + cell.className;
13203 for(; i < startingPos; i++) {
13204 textEls[i].innerHTML = (++prevStart);
13205 d.setDate(d.getDate()+1);
13207 cells[i].className = "fc-past fc-other-month";
13208 setCellClass(this, cells[i]);
13213 for(; i < days; i++){
13214 intDay = i - startingPos + 1;
13215 textEls[i].innerHTML = (intDay);
13216 d.setDate(d.getDate()+1);
13218 cells[i].className = ''; // "x-date-active";
13219 setCellClass(this, cells[i]);
13223 for(; i < 42; i++) {
13224 textEls[i].innerHTML = (++extraDays);
13225 d.setDate(d.getDate()+1);
13227 cells[i].className = "fc-future fc-other-month";
13228 setCellClass(this, cells[i]);
13231 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13233 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13235 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13236 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13238 if(totalRows != 6){
13239 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13240 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13243 this.fireEvent('monthchange', this, date);
13247 if(!this.internalRender){
13248 var main = this.el.dom.firstChild;
13249 var w = main.offsetWidth;
13250 this.el.setWidth(w + this.el.getBorderWidth("lr"));
13251 Roo.fly(main).setWidth(w);
13252 this.internalRender = true;
13253 // opera does not respect the auto grow header center column
13254 // then, after it gets a width opera refuses to recalculate
13255 // without a second pass
13256 if(Roo.isOpera && !this.secondPass){
13257 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13258 this.secondPass = true;
13259 this.update.defer(10, this, [date]);
13266 findCell : function(dt) {
13267 dt = dt.clearTime().getTime();
13269 this.cells.each(function(c){
13270 //Roo.log("check " +c.dateValue + '?=' + dt);
13271 if(c.dateValue == dt){
13281 findCells : function(ev) {
13282 var s = ev.start.clone().clearTime().getTime();
13284 var e= ev.end.clone().clearTime().getTime();
13287 this.cells.each(function(c){
13288 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13290 if(c.dateValue > e){
13293 if(c.dateValue < s){
13302 // findBestRow: function(cells)
13306 // for (var i =0 ; i < cells.length;i++) {
13307 // ret = Math.max(cells[i].rows || 0,ret);
13314 addItem : function(ev)
13316 // look for vertical location slot in
13317 var cells = this.findCells(ev);
13319 // ev.row = this.findBestRow(cells);
13321 // work out the location.
13325 for(var i =0; i < cells.length; i++) {
13327 cells[i].row = cells[0].row;
13330 cells[i].row = cells[i].row + 1;
13340 if (crow.start.getY() == cells[i].getY()) {
13342 crow.end = cells[i];
13359 cells[0].events.push(ev);
13361 this.calevents.push(ev);
13364 clearEvents: function() {
13366 if(!this.calevents){
13370 Roo.each(this.cells.elements, function(c){
13376 Roo.each(this.calevents, function(e) {
13377 Roo.each(e.els, function(el) {
13378 el.un('mouseenter' ,this.onEventEnter, this);
13379 el.un('mouseleave' ,this.onEventLeave, this);
13384 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13390 renderEvents: function()
13394 this.cells.each(function(c) {
13403 if(c.row != c.events.length){
13404 r = 4 - (4 - (c.row - c.events.length));
13407 c.events = ev.slice(0, r);
13408 c.more = ev.slice(r);
13410 if(c.more.length && c.more.length == 1){
13411 c.events.push(c.more.pop());
13414 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13418 this.cells.each(function(c) {
13420 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13423 for (var e = 0; e < c.events.length; e++){
13424 var ev = c.events[e];
13425 var rows = ev.rows;
13427 for(var i = 0; i < rows.length; i++) {
13429 // how many rows should it span..
13432 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13433 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13435 unselectable : "on",
13438 cls: 'fc-event-inner',
13442 // cls: 'fc-event-time',
13443 // html : cells.length > 1 ? '' : ev.time
13447 cls: 'fc-event-title',
13448 html : String.format('{0}', ev.title)
13455 cls: 'ui-resizable-handle ui-resizable-e',
13456 html : '  '
13463 cfg.cls += ' fc-event-start';
13465 if ((i+1) == rows.length) {
13466 cfg.cls += ' fc-event-end';
13469 var ctr = _this.el.select('.fc-event-container',true).first();
13470 var cg = ctr.createChild(cfg);
13472 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13473 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13475 var r = (c.more.length) ? 1 : 0;
13476 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
13477 cg.setWidth(ebox.right - sbox.x -2);
13479 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13480 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13481 cg.on('click', _this.onEventClick, _this, ev);
13492 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13493 style : 'position: absolute',
13494 unselectable : "on",
13497 cls: 'fc-event-inner',
13501 cls: 'fc-event-title',
13509 cls: 'ui-resizable-handle ui-resizable-e',
13510 html : '  '
13516 var ctr = _this.el.select('.fc-event-container',true).first();
13517 var cg = ctr.createChild(cfg);
13519 var sbox = c.select('.fc-day-content',true).first().getBox();
13520 var ebox = c.select('.fc-day-content',true).first().getBox();
13522 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
13523 cg.setWidth(ebox.right - sbox.x -2);
13525 cg.on('click', _this.onMoreEventClick, _this, c.more);
13535 onEventEnter: function (e, el,event,d) {
13536 this.fireEvent('evententer', this, el, event);
13539 onEventLeave: function (e, el,event,d) {
13540 this.fireEvent('eventleave', this, el, event);
13543 onEventClick: function (e, el,event,d) {
13544 this.fireEvent('eventclick', this, el, event);
13547 onMonthChange: function () {
13551 onMoreEventClick: function(e, el, more)
13555 this.calpopover.placement = 'right';
13556 this.calpopover.setTitle('More');
13558 this.calpopover.setContent('');
13560 var ctr = this.calpopover.el.select('.popover-content', true).first();
13562 Roo.each(more, function(m){
13564 cls : 'fc-event-hori fc-event-draggable',
13567 var cg = ctr.createChild(cfg);
13569 cg.on('click', _this.onEventClick, _this, m);
13572 this.calpopover.show(el);
13577 onLoad: function ()
13579 this.calevents = [];
13582 if(this.store.getCount() > 0){
13583 this.store.data.each(function(d){
13586 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13587 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13588 time : d.data.start_time,
13589 title : d.data.title,
13590 description : d.data.description,
13591 venue : d.data.venue
13596 this.renderEvents();
13598 if(this.calevents.length && this.loadMask){
13599 this.maskEl.hide();
13603 onBeforeLoad: function()
13605 this.clearEvents();
13607 this.maskEl.show();
13621 * @class Roo.bootstrap.Popover
13622 * @extends Roo.bootstrap.Component
13623 * Bootstrap Popover class
13624 * @cfg {String} html contents of the popover (or false to use children..)
13625 * @cfg {String} title of popover (or false to hide)
13626 * @cfg {String} placement how it is placed
13627 * @cfg {String} trigger click || hover (or false to trigger manually)
13628 * @cfg {String} over what (parent or false to trigger manually.)
13631 * Create a new Popover
13632 * @param {Object} config The config object
13635 Roo.bootstrap.Popover = function(config){
13636 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13639 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
13641 title: 'Fill in a title',
13644 placement : 'right',
13645 trigger : 'hover', // hover
13649 can_build_overlaid : false,
13651 getChildContainer : function()
13653 return this.el.select('.popover-content',true).first();
13656 getAutoCreate : function(){
13657 Roo.log('make popover?');
13659 cls : 'popover roo-dynamic',
13660 style: 'display:block',
13666 cls : 'popover-inner',
13670 cls: 'popover-title',
13674 cls : 'popover-content',
13685 setTitle: function(str)
13687 this.el.select('.popover-title',true).first().dom.innerHTML = str;
13689 setContent: function(str)
13691 this.el.select('.popover-content',true).first().dom.innerHTML = str;
13693 // as it get's added to the bottom of the page.
13694 onRender : function(ct, position)
13696 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13698 var cfg = Roo.apply({}, this.getAutoCreate());
13702 cfg.cls += ' ' + this.cls;
13705 cfg.style = this.style;
13707 Roo.log("adding to ")
13708 this.el = Roo.get(document.body).createChild(cfg, position);
13714 initEvents : function()
13716 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13717 this.el.enableDisplayMode('block');
13719 if (this.over === false) {
13722 if (this.triggers === false) {
13725 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13726 var triggers = this.trigger ? this.trigger.split(' ') : [];
13727 Roo.each(triggers, function(trigger) {
13729 if (trigger == 'click') {
13730 on_el.on('click', this.toggle, this);
13731 } else if (trigger != 'manual') {
13732 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
13733 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13735 on_el.on(eventIn ,this.enter, this);
13736 on_el.on(eventOut, this.leave, this);
13747 toggle : function () {
13748 this.hoverState == 'in' ? this.leave() : this.enter();
13751 enter : function () {
13754 clearTimeout(this.timeout);
13756 this.hoverState = 'in'
13758 if (!this.delay || !this.delay.show) {
13763 this.timeout = setTimeout(function () {
13764 if (_t.hoverState == 'in') {
13767 }, this.delay.show)
13769 leave : function() {
13770 clearTimeout(this.timeout);
13772 this.hoverState = 'out'
13774 if (!this.delay || !this.delay.hide) {
13779 this.timeout = setTimeout(function () {
13780 if (_t.hoverState == 'out') {
13783 }, this.delay.hide)
13786 show : function (on_el)
13789 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13792 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13793 if (this.html !== false) {
13794 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13796 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13797 if (!this.title.length) {
13798 this.el.select('.popover-title',true).hide();
13801 var placement = typeof this.placement == 'function' ?
13802 this.placement.call(this, this.el, on_el) :
13805 var autoToken = /\s?auto?\s?/i;
13806 var autoPlace = autoToken.test(placement);
13808 placement = placement.replace(autoToken, '') || 'top';
13812 //this.el.setXY([0,0]);
13814 this.el.dom.style.display='block';
13815 this.el.addClass(placement);
13817 //this.el.appendTo(on_el);
13819 var p = this.getPosition();
13820 var box = this.el.getBox();
13825 var align = Roo.bootstrap.Popover.alignment[placement]
13826 this.el.alignTo(on_el, align[0],align[1]);
13827 //var arrow = this.el.select('.arrow',true).first();
13828 //arrow.set(align[2],
13830 this.el.addClass('in');
13831 this.hoverState = null;
13833 if (this.el.hasClass('fade')) {
13840 this.el.setXY([0,0]);
13841 this.el.removeClass('in');
13848 Roo.bootstrap.Popover.alignment = {
13849 'left' : ['r-l', [-10,0], 'right'],
13850 'right' : ['l-r', [10,0], 'left'],
13851 'bottom' : ['t-b', [0,10], 'top'],
13852 'top' : [ 'b-t', [0,-10], 'bottom']
13863 * @class Roo.bootstrap.Progress
13864 * @extends Roo.bootstrap.Component
13865 * Bootstrap Progress class
13866 * @cfg {Boolean} striped striped of the progress bar
13867 * @cfg {Boolean} active animated of the progress bar
13871 * Create a new Progress
13872 * @param {Object} config The config object
13875 Roo.bootstrap.Progress = function(config){
13876 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13879 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
13884 getAutoCreate : function(){
13892 cfg.cls += ' progress-striped';
13896 cfg.cls += ' active';
13915 * @class Roo.bootstrap.ProgressBar
13916 * @extends Roo.bootstrap.Component
13917 * Bootstrap ProgressBar class
13918 * @cfg {Number} aria_valuenow aria-value now
13919 * @cfg {Number} aria_valuemin aria-value min
13920 * @cfg {Number} aria_valuemax aria-value max
13921 * @cfg {String} label label for the progress bar
13922 * @cfg {String} panel (success | info | warning | danger )
13923 * @cfg {String} role role of the progress bar
13924 * @cfg {String} sr_only text
13928 * Create a new ProgressBar
13929 * @param {Object} config The config object
13932 Roo.bootstrap.ProgressBar = function(config){
13933 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13936 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
13940 aria_valuemax : 100,
13946 getAutoCreate : function()
13951 cls: 'progress-bar',
13952 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13964 cfg.role = this.role;
13967 if(this.aria_valuenow){
13968 cfg['aria-valuenow'] = this.aria_valuenow;
13971 if(this.aria_valuemin){
13972 cfg['aria-valuemin'] = this.aria_valuemin;
13975 if(this.aria_valuemax){
13976 cfg['aria-valuemax'] = this.aria_valuemax;
13979 if(this.label && !this.sr_only){
13980 cfg.html = this.label;
13984 cfg.cls += ' progress-bar-' + this.panel;
13990 update : function(aria_valuenow)
13992 this.aria_valuenow = aria_valuenow;
13994 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14009 * @class Roo.bootstrap.TabGroup
14010 * @extends Roo.bootstrap.Column
14011 * Bootstrap Column class
14012 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14013 * @cfg {Boolean} carousel true to make the group behave like a carousel
14016 * Create a new TabGroup
14017 * @param {Object} config The config object
14020 Roo.bootstrap.TabGroup = function(config){
14021 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14023 this.navId = Roo.id();
14026 Roo.bootstrap.TabGroup.register(this);
14030 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
14033 transition : false,
14035 getAutoCreate : function()
14037 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14039 cfg.cls += ' tab-content';
14041 if (this.carousel) {
14042 cfg.cls += ' carousel slide';
14044 cls : 'carousel-inner'
14051 getChildContainer : function()
14053 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14057 * register a Navigation item
14058 * @param {Roo.bootstrap.NavItem} the navitem to add
14060 register : function(item)
14062 this.tabs.push( item);
14063 item.navId = this.navId; // not really needed..
14067 getActivePanel : function()
14070 Roo.each(this.tabs, function(t) {
14080 getPanelByName : function(n)
14083 Roo.each(this.tabs, function(t) {
14084 if (t.tabId == n) {
14092 indexOfPanel : function(p)
14095 Roo.each(this.tabs, function(t,i) {
14096 if (t.tabId == p.tabId) {
14105 * show a specific panel
14106 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14107 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14109 showPanel : function (pan)
14112 if (typeof(pan) == 'number') {
14113 pan = this.tabs[pan];
14115 if (typeof(pan) == 'string') {
14116 pan = this.getPanelByName(pan);
14118 if (pan.tabId == this.getActivePanel().tabId) {
14121 var cur = this.getActivePanel();
14123 if (false === cur.fireEvent('beforedeactivate')) {
14127 if (this.carousel) {
14128 this.transition = true;
14129 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
14130 var lr = dir == 'next' ? 'left' : 'right';
14131 pan.el.addClass(dir); // or prev
14132 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14133 cur.el.addClass(lr); // or right
14134 pan.el.addClass(lr);
14137 cur.el.on('transitionend', function() {
14138 Roo.log("trans end?");
14140 pan.el.removeClass([lr,dir]);
14141 pan.setActive(true);
14143 cur.el.removeClass([lr]);
14144 cur.setActive(false);
14146 _this.transition = false;
14148 }, this, { single: true } );
14152 cur.setActive(false);
14153 pan.setActive(true);
14157 showPanelNext : function()
14159 var i = this.indexOfPanel(this.getActivePanel());
14160 if (i > this.tabs.length) {
14163 this.showPanel(this.tabs[i+1]);
14165 showPanelPrev : function()
14167 var i = this.indexOfPanel(this.getActivePanel());
14171 this.showPanel(this.tabs[i-1]);
14182 Roo.apply(Roo.bootstrap.TabGroup, {
14186 * register a Navigation Group
14187 * @param {Roo.bootstrap.NavGroup} the navgroup to add
14189 register : function(navgrp)
14191 this.groups[navgrp.navId] = navgrp;
14195 * fetch a Navigation Group based on the navigation ID
14196 * if one does not exist , it will get created.
14197 * @param {string} the navgroup to add
14198 * @returns {Roo.bootstrap.NavGroup} the navgroup
14200 get: function(navId) {
14201 if (typeof(this.groups[navId]) == 'undefined') {
14202 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14204 return this.groups[navId] ;
14219 * @class Roo.bootstrap.TabPanel
14220 * @extends Roo.bootstrap.Component
14221 * Bootstrap TabPanel class
14222 * @cfg {Boolean} active panel active
14223 * @cfg {String} html panel content
14224 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14225 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14229 * Create a new TabPanel
14230 * @param {Object} config The config object
14233 Roo.bootstrap.TabPanel = function(config){
14234 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14238 * Fires when the active status changes
14239 * @param {Roo.bootstrap.TabPanel} this
14240 * @param {Boolean} state the new state
14245 * @event beforedeactivate
14246 * Fires before a tab is de-activated - can be used to do validation on a form.
14247 * @param {Roo.bootstrap.TabPanel} this
14248 * @return {Boolean} false if there is an error
14251 'beforedeactivate': true
14254 this.tabId = this.tabId || Roo.id();
14258 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
14265 getAutoCreate : function(){
14268 // item is needed for carousel - not sure if it has any effect otherwise
14269 cls: 'tab-pane item',
14270 html: this.html || ''
14274 cfg.cls += ' active';
14278 cfg.tabId = this.tabId;
14285 initEvents: function()
14287 Roo.log('-------- init events on tab panel ---------');
14289 var p = this.parent();
14290 this.navId = this.navId || p.navId;
14292 if (typeof(this.navId) != 'undefined') {
14293 // not really needed.. but just in case.. parent should be a NavGroup.
14294 var tg = Roo.bootstrap.TabGroup.get(this.navId);
14295 Roo.log(['register', tg, this]);
14301 onRender : function(ct, position)
14303 // Roo.log("Call onRender: " + this.xtype);
14305 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14313 setActive: function(state)
14315 Roo.log("panel - set active " + this.tabId + "=" + state);
14317 this.active = state;
14319 this.el.removeClass('active');
14321 } else if (!this.el.hasClass('active')) {
14322 this.el.addClass('active');
14324 this.fireEvent('changed', this, state);
14341 * @class Roo.bootstrap.DateField
14342 * @extends Roo.bootstrap.Input
14343 * Bootstrap DateField class
14344 * @cfg {Number} weekStart default 0
14345 * @cfg {Number} weekStart default 0
14346 * @cfg {Number} viewMode default empty, (months|years)
14347 * @cfg {Number} minViewMode default empty, (months|years)
14348 * @cfg {Number} startDate default -Infinity
14349 * @cfg {Number} endDate default Infinity
14350 * @cfg {Boolean} todayHighlight default false
14351 * @cfg {Boolean} todayBtn default false
14352 * @cfg {Boolean} calendarWeeks default false
14353 * @cfg {Object} daysOfWeekDisabled default empty
14355 * @cfg {Boolean} keyboardNavigation default true
14356 * @cfg {String} language default en
14359 * Create a new DateField
14360 * @param {Object} config The config object
14363 Roo.bootstrap.DateField = function(config){
14364 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14368 * Fires when this field show.
14369 * @param {Roo.bootstrap.DateField} this
14370 * @param {Mixed} date The date value
14375 * Fires when this field hide.
14376 * @param {Roo.bootstrap.DateField} this
14377 * @param {Mixed} date The date value
14382 * Fires when select a date.
14383 * @param {Roo.bootstrap.DateField} this
14384 * @param {Mixed} date The date value
14390 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
14393 * @cfg {String} format
14394 * The default date format string which can be overriden for localization support. The format must be
14395 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14399 * @cfg {String} altFormats
14400 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14401 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14403 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14411 todayHighlight : false,
14417 keyboardNavigation: true,
14419 calendarWeeks: false,
14421 startDate: -Infinity,
14425 daysOfWeekDisabled: [],
14429 UTCDate: function()
14431 return new Date(Date.UTC.apply(Date, arguments));
14434 UTCToday: function()
14436 var today = new Date();
14437 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14440 getDate: function() {
14441 var d = this.getUTCDate();
14442 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14445 getUTCDate: function() {
14449 setDate: function(d) {
14450 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14453 setUTCDate: function(d) {
14455 this.setValue(this.formatDate(this.date));
14458 onRender: function(ct, position)
14461 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14463 this.language = this.language || 'en';
14464 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14465 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14467 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14468 this.format = this.format || 'm/d/y';
14469 this.isInline = false;
14470 this.isInput = true;
14471 this.component = this.el.select('.add-on', true).first() || false;
14472 this.component = (this.component && this.component.length === 0) ? false : this.component;
14473 this.hasInput = this.component && this.inputEL().length;
14475 if (typeof(this.minViewMode === 'string')) {
14476 switch (this.minViewMode) {
14478 this.minViewMode = 1;
14481 this.minViewMode = 2;
14484 this.minViewMode = 0;
14489 if (typeof(this.viewMode === 'string')) {
14490 switch (this.viewMode) {
14503 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14505 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14507 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14509 this.picker().on('mousedown', this.onMousedown, this);
14510 this.picker().on('click', this.onClick, this);
14512 this.picker().addClass('datepicker-dropdown');
14514 this.startViewMode = this.viewMode;
14517 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14518 if(!this.calendarWeeks){
14523 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14524 v.attr('colspan', function(i, val){
14525 return parseInt(val) + 1;
14530 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14532 this.setStartDate(this.startDate);
14533 this.setEndDate(this.endDate);
14535 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14542 if(this.isInline) {
14547 picker : function()
14549 return this.pickerEl;
14550 // return this.el.select('.datepicker', true).first();
14553 fillDow: function()
14555 var dowCnt = this.weekStart;
14564 if(this.calendarWeeks){
14572 while (dowCnt < this.weekStart + 7) {
14576 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14580 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14583 fillMonths: function()
14586 var months = this.picker().select('>.datepicker-months td', true).first();
14588 months.dom.innerHTML = '';
14594 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14597 months.createChild(month);
14604 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;
14606 if (this.date < this.startDate) {
14607 this.viewDate = new Date(this.startDate);
14608 } else if (this.date > this.endDate) {
14609 this.viewDate = new Date(this.endDate);
14611 this.viewDate = new Date(this.date);
14619 var d = new Date(this.viewDate),
14620 year = d.getUTCFullYear(),
14621 month = d.getUTCMonth(),
14622 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14623 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14624 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14625 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14626 currentDate = this.date && this.date.valueOf(),
14627 today = this.UTCToday();
14629 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14631 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14633 // this.picker.select('>tfoot th.today').
14634 // .text(dates[this.language].today)
14635 // .toggle(this.todayBtn !== false);
14637 this.updateNavArrows();
14640 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14642 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14644 prevMonth.setUTCDate(day);
14646 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14648 var nextMonth = new Date(prevMonth);
14650 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14652 nextMonth = nextMonth.valueOf();
14654 var fillMonths = false;
14656 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14658 while(prevMonth.valueOf() < nextMonth) {
14661 if (prevMonth.getUTCDay() === this.weekStart) {
14663 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14671 if(this.calendarWeeks){
14672 // ISO 8601: First week contains first thursday.
14673 // ISO also states week starts on Monday, but we can be more abstract here.
14675 // Start of current week: based on weekstart/current date
14676 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14677 // Thursday of this week
14678 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14679 // First Thursday of year, year from thursday
14680 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14681 // Calendar week: ms between thursdays, div ms per day, div 7 days
14682 calWeek = (th - yth) / 864e5 / 7 + 1;
14684 fillMonths.cn.push({
14692 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14694 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14697 if (this.todayHighlight &&
14698 prevMonth.getUTCFullYear() == today.getFullYear() &&
14699 prevMonth.getUTCMonth() == today.getMonth() &&
14700 prevMonth.getUTCDate() == today.getDate()) {
14701 clsName += ' today';
14704 if (currentDate && prevMonth.valueOf() === currentDate) {
14705 clsName += ' active';
14708 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14709 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14710 clsName += ' disabled';
14713 fillMonths.cn.push({
14715 cls: 'day ' + clsName,
14716 html: prevMonth.getDate()
14719 prevMonth.setDate(prevMonth.getDate()+1);
14722 var currentYear = this.date && this.date.getUTCFullYear();
14723 var currentMonth = this.date && this.date.getUTCMonth();
14725 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14727 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14728 v.removeClass('active');
14730 if(currentYear === year && k === currentMonth){
14731 v.addClass('active');
14734 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14735 v.addClass('disabled');
14741 year = parseInt(year/10, 10) * 10;
14743 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14745 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14748 for (var i = -1; i < 11; i++) {
14749 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14751 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14759 showMode: function(dir)
14762 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14764 Roo.each(this.picker().select('>div',true).elements, function(v){
14765 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14768 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14773 if(this.isInline) return;
14775 this.picker().removeClass(['bottom', 'top']);
14777 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14779 * place to the top of element!
14783 this.picker().addClass('top');
14784 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14789 this.picker().addClass('bottom');
14791 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14794 parseDate : function(value)
14796 if(!value || value instanceof Date){
14799 var v = Date.parseDate(value, this.format);
14800 if (!v && this.useIso) {
14801 v = Date.parseDate(value, 'Y-m-d');
14803 if(!v && this.altFormats){
14804 if(!this.altFormatsArray){
14805 this.altFormatsArray = this.altFormats.split("|");
14807 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14808 v = Date.parseDate(value, this.altFormatsArray[i]);
14814 formatDate : function(date, fmt)
14816 return (!date || !(date instanceof Date)) ?
14817 date : date.dateFormat(fmt || this.format);
14820 onFocus : function()
14822 Roo.bootstrap.DateField.superclass.onFocus.call(this);
14826 onBlur : function()
14828 Roo.bootstrap.DateField.superclass.onBlur.call(this);
14830 var d = this.inputEl().getValue();
14839 this.picker().show();
14843 this.fireEvent('show', this, this.date);
14848 if(this.isInline) return;
14849 this.picker().hide();
14850 this.viewMode = this.startViewMode;
14853 this.fireEvent('hide', this, this.date);
14857 onMousedown: function(e)
14859 e.stopPropagation();
14860 e.preventDefault();
14865 Roo.bootstrap.DateField.superclass.keyup.call(this);
14869 setValue: function(v)
14871 var d = new Date(v).clearTime();
14873 if(isNaN(d.getTime())){
14874 this.date = this.viewDate = '';
14875 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14879 v = this.formatDate(d);
14881 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14883 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14887 this.fireEvent('select', this, this.date);
14891 getValue: function()
14893 return this.formatDate(this.date);
14896 fireKey: function(e)
14898 if (!this.picker().isVisible()){
14899 if (e.keyCode == 27) // allow escape to hide and re-show picker
14904 var dateChanged = false,
14906 newDate, newViewDate;
14911 e.preventDefault();
14915 if (!this.keyboardNavigation) break;
14916 dir = e.keyCode == 37 ? -1 : 1;
14919 newDate = this.moveYear(this.date, dir);
14920 newViewDate = this.moveYear(this.viewDate, dir);
14921 } else if (e.shiftKey){
14922 newDate = this.moveMonth(this.date, dir);
14923 newViewDate = this.moveMonth(this.viewDate, dir);
14925 newDate = new Date(this.date);
14926 newDate.setUTCDate(this.date.getUTCDate() + dir);
14927 newViewDate = new Date(this.viewDate);
14928 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14930 if (this.dateWithinRange(newDate)){
14931 this.date = newDate;
14932 this.viewDate = newViewDate;
14933 this.setValue(this.formatDate(this.date));
14935 e.preventDefault();
14936 dateChanged = true;
14941 if (!this.keyboardNavigation) break;
14942 dir = e.keyCode == 38 ? -1 : 1;
14944 newDate = this.moveYear(this.date, dir);
14945 newViewDate = this.moveYear(this.viewDate, dir);
14946 } else if (e.shiftKey){
14947 newDate = this.moveMonth(this.date, dir);
14948 newViewDate = this.moveMonth(this.viewDate, dir);
14950 newDate = new Date(this.date);
14951 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14952 newViewDate = new Date(this.viewDate);
14953 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14955 if (this.dateWithinRange(newDate)){
14956 this.date = newDate;
14957 this.viewDate = newViewDate;
14958 this.setValue(this.formatDate(this.date));
14960 e.preventDefault();
14961 dateChanged = true;
14965 this.setValue(this.formatDate(this.date));
14967 e.preventDefault();
14970 this.setValue(this.formatDate(this.date));
14984 onClick: function(e)
14986 e.stopPropagation();
14987 e.preventDefault();
14989 var target = e.getTarget();
14991 if(target.nodeName.toLowerCase() === 'i'){
14992 target = Roo.get(target).dom.parentNode;
14995 var nodeName = target.nodeName;
14996 var className = target.className;
14997 var html = target.innerHTML;
14999 switch(nodeName.toLowerCase()) {
15001 switch(className) {
15007 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15008 switch(this.viewMode){
15010 this.viewDate = this.moveMonth(this.viewDate, dir);
15014 this.viewDate = this.moveYear(this.viewDate, dir);
15020 var date = new Date();
15021 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15023 this.setValue(this.formatDate(this.date));
15030 if (className.indexOf('disabled') === -1) {
15031 this.viewDate.setUTCDate(1);
15032 if (className.indexOf('month') !== -1) {
15033 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15035 var year = parseInt(html, 10) || 0;
15036 this.viewDate.setUTCFullYear(year);
15045 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
15046 var day = parseInt(html, 10) || 1;
15047 var year = this.viewDate.getUTCFullYear(),
15048 month = this.viewDate.getUTCMonth();
15050 if (className.indexOf('old') !== -1) {
15057 } else if (className.indexOf('new') !== -1) {
15065 this.date = this.UTCDate(year, month, day,0,0,0,0);
15066 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15068 this.setValue(this.formatDate(this.date));
15075 setStartDate: function(startDate)
15077 this.startDate = startDate || -Infinity;
15078 if (this.startDate !== -Infinity) {
15079 this.startDate = this.parseDate(this.startDate);
15082 this.updateNavArrows();
15085 setEndDate: function(endDate)
15087 this.endDate = endDate || Infinity;
15088 if (this.endDate !== Infinity) {
15089 this.endDate = this.parseDate(this.endDate);
15092 this.updateNavArrows();
15095 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15097 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15098 if (typeof(this.daysOfWeekDisabled) !== 'object') {
15099 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15101 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15102 return parseInt(d, 10);
15105 this.updateNavArrows();
15108 updateNavArrows: function()
15110 var d = new Date(this.viewDate),
15111 year = d.getUTCFullYear(),
15112 month = d.getUTCMonth();
15114 Roo.each(this.picker().select('.prev', true).elements, function(v){
15116 switch (this.viewMode) {
15119 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15125 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15132 Roo.each(this.picker().select('.next', true).elements, function(v){
15134 switch (this.viewMode) {
15137 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15143 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15151 moveMonth: function(date, dir)
15153 if (!dir) return date;
15154 var new_date = new Date(date.valueOf()),
15155 day = new_date.getUTCDate(),
15156 month = new_date.getUTCMonth(),
15157 mag = Math.abs(dir),
15159 dir = dir > 0 ? 1 : -1;
15162 // If going back one month, make sure month is not current month
15163 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15165 return new_date.getUTCMonth() == month;
15167 // If going forward one month, make sure month is as expected
15168 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15170 return new_date.getUTCMonth() != new_month;
15172 new_month = month + dir;
15173 new_date.setUTCMonth(new_month);
15174 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15175 if (new_month < 0 || new_month > 11)
15176 new_month = (new_month + 12) % 12;
15178 // For magnitudes >1, move one month at a time...
15179 for (var i=0; i<mag; i++)
15180 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15181 new_date = this.moveMonth(new_date, dir);
15182 // ...then reset the day, keeping it in the new month
15183 new_month = new_date.getUTCMonth();
15184 new_date.setUTCDate(day);
15186 return new_month != new_date.getUTCMonth();
15189 // Common date-resetting loop -- if date is beyond end of month, make it
15192 new_date.setUTCDate(--day);
15193 new_date.setUTCMonth(new_month);
15198 moveYear: function(date, dir)
15200 return this.moveMonth(date, dir*12);
15203 dateWithinRange: function(date)
15205 return date >= this.startDate && date <= this.endDate;
15211 this.picker().remove();
15216 Roo.apply(Roo.bootstrap.DateField, {
15227 html: '<i class="fa fa-arrow-left"/>'
15237 html: '<i class="fa fa-arrow-right"/>'
15279 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15280 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15281 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15282 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15283 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15296 navFnc: 'FullYear',
15301 navFnc: 'FullYear',
15306 Roo.apply(Roo.bootstrap.DateField, {
15310 cls: 'datepicker dropdown-menu',
15314 cls: 'datepicker-days',
15318 cls: 'table-condensed',
15320 Roo.bootstrap.DateField.head,
15324 Roo.bootstrap.DateField.footer
15331 cls: 'datepicker-months',
15335 cls: 'table-condensed',
15337 Roo.bootstrap.DateField.head,
15338 Roo.bootstrap.DateField.content,
15339 Roo.bootstrap.DateField.footer
15346 cls: 'datepicker-years',
15350 cls: 'table-condensed',
15352 Roo.bootstrap.DateField.head,
15353 Roo.bootstrap.DateField.content,
15354 Roo.bootstrap.DateField.footer
15373 * @class Roo.bootstrap.TimeField
15374 * @extends Roo.bootstrap.Input
15375 * Bootstrap DateField class
15379 * Create a new TimeField
15380 * @param {Object} config The config object
15383 Roo.bootstrap.TimeField = function(config){
15384 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15388 * Fires when this field show.
15389 * @param {Roo.bootstrap.DateField} this
15390 * @param {Mixed} date The date value
15395 * Fires when this field hide.
15396 * @param {Roo.bootstrap.DateField} this
15397 * @param {Mixed} date The date value
15402 * Fires when select a date.
15403 * @param {Roo.bootstrap.DateField} this
15404 * @param {Mixed} date The date value
15410 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
15413 * @cfg {String} format
15414 * The default time format string which can be overriden for localization support. The format must be
15415 * valid according to {@link Date#parseDate} (defaults to 'H:i').
15419 onRender: function(ct, position)
15422 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15424 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15426 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15428 this.pop = this.picker().select('>.datepicker-time',true).first();
15429 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
15431 this.picker().on('mousedown', this.onMousedown, this);
15432 this.picker().on('click', this.onClick, this);
15434 this.picker().addClass('datepicker-dropdown');
15439 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15440 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15441 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15442 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15443 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15444 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15448 fireKey: function(e){
15449 if (!this.picker().isVisible()){
15450 if (e.keyCode == 27) // allow escape to hide and re-show picker
15455 e.preventDefault();
15463 this.onTogglePeriod();
15466 this.onIncrementMinutes();
15469 this.onDecrementMinutes();
15478 onClick: function(e) {
15479 e.stopPropagation();
15480 e.preventDefault();
15483 picker : function()
15485 return this.el.select('.datepicker', true).first();
15488 fillTime: function()
15490 var time = this.pop.select('tbody', true).first();
15492 time.dom.innerHTML = '';
15507 cls: 'hours-up glyphicon glyphicon-chevron-up'
15527 cls: 'minutes-up glyphicon glyphicon-chevron-up'
15548 cls: 'timepicker-hour',
15563 cls: 'timepicker-minute',
15578 cls: 'btn btn-primary period',
15600 cls: 'hours-down glyphicon glyphicon-chevron-down'
15620 cls: 'minutes-down glyphicon glyphicon-chevron-down'
15638 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15645 var hours = this.time.getHours();
15646 var minutes = this.time.getMinutes();
15659 hours = hours - 12;
15663 hours = '0' + hours;
15667 minutes = '0' + minutes;
15670 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15671 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15672 this.pop.select('button', true).first().dom.innerHTML = period;
15678 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15680 var cls = ['bottom'];
15682 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15689 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15694 this.picker().addClass(cls.join('-'));
15698 Roo.each(cls, function(c){
15700 _this.picker().setTop(_this.inputEl().getHeight());
15704 _this.picker().setTop(0 - _this.picker().getHeight());
15709 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15713 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15720 onFocus : function()
15722 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15726 onBlur : function()
15728 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15734 this.picker().show();
15739 this.fireEvent('show', this, this.date);
15744 this.picker().hide();
15747 this.fireEvent('hide', this, this.date);
15750 setTime : function()
15753 this.setValue(this.time.format(this.format));
15755 this.fireEvent('select', this, this.date);
15760 onMousedown: function(e){
15761 e.stopPropagation();
15762 e.preventDefault();
15765 onIncrementHours: function()
15767 Roo.log('onIncrementHours');
15768 this.time = this.time.add(Date.HOUR, 1);
15773 onDecrementHours: function()
15775 Roo.log('onDecrementHours');
15776 this.time = this.time.add(Date.HOUR, -1);
15780 onIncrementMinutes: function()
15782 Roo.log('onIncrementMinutes');
15783 this.time = this.time.add(Date.MINUTE, 1);
15787 onDecrementMinutes: function()
15789 Roo.log('onDecrementMinutes');
15790 this.time = this.time.add(Date.MINUTE, -1);
15794 onTogglePeriod: function()
15796 Roo.log('onTogglePeriod');
15797 this.time = this.time.add(Date.HOUR, 12);
15804 Roo.apply(Roo.bootstrap.TimeField, {
15834 cls: 'btn btn-info ok',
15846 Roo.apply(Roo.bootstrap.TimeField, {
15850 cls: 'datepicker dropdown-menu',
15854 cls: 'datepicker-time',
15858 cls: 'table-condensed',
15860 Roo.bootstrap.TimeField.content,
15861 Roo.bootstrap.TimeField.footer
15880 * @class Roo.bootstrap.CheckBox
15881 * @extends Roo.bootstrap.Input
15882 * Bootstrap CheckBox class
15884 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15885 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15886 * @cfg {String} boxLabel The text that appears beside the checkbox
15887 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15888 * @cfg {Boolean} checked initnal the element
15892 * Create a new CheckBox
15893 * @param {Object} config The config object
15896 Roo.bootstrap.CheckBox = function(config){
15897 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15902 * Fires when the element is checked or unchecked.
15903 * @param {Roo.bootstrap.CheckBox} this This input
15904 * @param {Boolean} checked The new checked value
15910 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
15912 inputType: 'checkbox',
15919 getAutoCreate : function()
15921 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15927 cfg.cls = 'form-group checkbox' //input-group
15935 type : this.inputType,
15936 value : (!this.checked) ? this.valueOff : this.inputValue,
15937 cls : 'roo-checkbox', //'form-box',
15938 placeholder : this.placeholder || ''
15942 if (this.weight) { // Validity check?
15943 cfg.cls += " checkbox-" + this.weight;
15946 if (this.disabled) {
15947 input.disabled=true;
15951 input.checked = this.checked;
15955 input.name = this.name;
15959 input.cls += ' input-' + this.size;
15963 ['xs','sm','md','lg'].map(function(size){
15964 if (settings[size]) {
15965 cfg.cls += ' col-' + size + '-' + settings[size];
15971 var inputblock = input;
15976 if (this.before || this.after) {
15979 cls : 'input-group',
15983 inputblock.cn.push({
15985 cls : 'input-group-addon',
15989 inputblock.cn.push(input);
15991 inputblock.cn.push({
15993 cls : 'input-group-addon',
16000 if (align ==='left' && this.fieldLabel.length) {
16001 Roo.log("left and has label");
16007 cls : 'control-label col-md-' + this.labelWidth,
16008 html : this.fieldLabel
16012 cls : "col-md-" + (12 - this.labelWidth),
16019 } else if ( this.fieldLabel.length) {
16024 tag: this.boxLabel ? 'span' : 'label',
16026 cls: 'control-label box-input-label',
16027 //cls : 'input-group-addon',
16028 html : this.fieldLabel
16038 Roo.log(" no label && no align");
16039 cfg.cn = [ inputblock ] ;
16048 html: this.boxLabel
16060 * return the real input element.
16062 inputEl: function ()
16064 return this.el.select('input.roo-checkbox',true).first();
16069 return this.el.select('label.control-label',true).first();
16072 initEvents : function()
16074 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16076 this.inputEl().on('click', this.onClick, this);
16080 onClick : function()
16082 this.setChecked(!this.checked);
16085 setChecked : function(state,suppressEvent)
16087 this.checked = state;
16089 this.inputEl().dom.checked = state;
16091 if(suppressEvent !== true){
16092 this.fireEvent('check', this, state);
16095 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16099 setValue : function(v,suppressEvent)
16101 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16115 * @class Roo.bootstrap.Radio
16116 * @extends Roo.bootstrap.CheckBox
16117 * Bootstrap Radio class
16120 * Create a new Radio
16121 * @param {Object} config The config object
16124 Roo.bootstrap.Radio = function(config){
16125 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16129 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
16131 inputType: 'radio',
16135 getAutoCreate : function()
16137 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16143 cfg.cls = 'form-group radio' //input-group
16148 type : this.inputType,
16149 value : (!this.checked) ? this.valueOff : this.inputValue,
16151 placeholder : this.placeholder || ''
16154 if (this.weight) { // Validity check?
16155 cfg.cls += " radio-" + this.weight;
16157 if (this.disabled) {
16158 input.disabled=true;
16162 input.checked = this.checked;
16166 input.name = this.name;
16170 input.cls += ' input-' + this.size;
16174 ['xs','sm','md','lg'].map(function(size){
16175 if (settings[size]) {
16176 cfg.cls += ' col-' + size + '-' + settings[size];
16180 var inputblock = input;
16182 if (this.before || this.after) {
16185 cls : 'input-group',
16189 inputblock.cn.push({
16191 cls : 'input-group-addon',
16195 inputblock.cn.push(input);
16197 inputblock.cn.push({
16199 cls : 'input-group-addon',
16206 if (align ==='left' && this.fieldLabel.length) {
16207 Roo.log("left and has label");
16213 cls : 'control-label col-md-' + this.labelWidth,
16214 html : this.fieldLabel
16218 cls : "col-md-" + (12 - this.labelWidth),
16225 } else if ( this.fieldLabel.length) {
16232 cls: 'control-label box-input-label',
16233 //cls : 'input-group-addon',
16234 html : this.fieldLabel
16244 Roo.log(" no label && no align");
16259 html: this.boxLabel
16266 inputEl: function ()
16268 return this.el.select('input.roo-radio',true).first();
16270 onClick : function()
16272 this.setChecked(true);
16275 setChecked : function(state,suppressEvent)
16278 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16279 v.dom.checked = false;
16283 this.checked = state;
16284 this.inputEl().dom.checked = state;
16286 if(suppressEvent !== true){
16287 this.fireEvent('check', this, state);
16290 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16294 getGroupValue : function()
16297 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16298 if(v.dom.checked == true){
16299 value = v.dom.value;
16307 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
16308 * @return {Mixed} value The field value
16310 getValue : function(){
16311 return this.getGroupValue();
16317 //<script type="text/javascript">
16320 * Based Ext JS Library 1.1.1
16321 * Copyright(c) 2006-2007, Ext JS, LLC.
16327 * @class Roo.HtmlEditorCore
16328 * @extends Roo.Component
16329 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16331 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16334 Roo.HtmlEditorCore = function(config){
16337 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16342 * @event initialize
16343 * Fires when the editor is fully initialized (including the iframe)
16344 * @param {Roo.HtmlEditorCore} this
16349 * Fires when the editor is first receives the focus. Any insertion must wait
16350 * until after this event.
16351 * @param {Roo.HtmlEditorCore} this
16355 * @event beforesync
16356 * Fires before the textarea is updated with content from the editor iframe. Return false
16357 * to cancel the sync.
16358 * @param {Roo.HtmlEditorCore} this
16359 * @param {String} html
16363 * @event beforepush
16364 * Fires before the iframe editor is updated with content from the textarea. Return false
16365 * to cancel the push.
16366 * @param {Roo.HtmlEditorCore} this
16367 * @param {String} html
16372 * Fires when the textarea is updated with content from the editor iframe.
16373 * @param {Roo.HtmlEditorCore} this
16374 * @param {String} html
16379 * Fires when the iframe editor is updated with content from the textarea.
16380 * @param {Roo.HtmlEditorCore} this
16381 * @param {String} html
16386 * @event editorevent
16387 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16388 * @param {Roo.HtmlEditorCore} this
16393 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16395 // defaults : white / black...
16396 this.applyBlacklists();
16403 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
16407 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
16413 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
16418 * @cfg {Number} height (in pixels)
16422 * @cfg {Number} width (in pixels)
16427 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16430 stylesheets: false,
16435 // private properties
16436 validationEvent : false,
16438 initialized : false,
16440 sourceEditMode : false,
16441 onFocus : Roo.emptyFn,
16443 hideMode:'offsets',
16447 // blacklist + whitelisted elements..
16454 * Protected method that will not generally be called directly. It
16455 * is called when the editor initializes the iframe with HTML contents. Override this method if you
16456 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16458 getDocMarkup : function(){
16461 Roo.log(this.stylesheets);
16463 // inherit styels from page...??
16464 if (this.stylesheets === false) {
16466 Roo.get(document.head).select('style').each(function(node) {
16467 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16470 Roo.get(document.head).select('link').each(function(node) {
16471 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16474 } else if (!this.stylesheets.length) {
16476 st = '<style type="text/css">' +
16477 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16480 Roo.each(this.stylesheets, function(s) {
16481 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16486 st += '<style type="text/css">' +
16487 'IMG { cursor: pointer } ' +
16491 return '<html><head>' + st +
16492 //<style type="text/css">' +
16493 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16495 ' </head><body class="roo-htmleditor-body"></body></html>';
16499 onRender : function(ct, position)
16502 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16503 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16506 this.el.dom.style.border = '0 none';
16507 this.el.dom.setAttribute('tabIndex', -1);
16508 this.el.addClass('x-hidden hide');
16512 if(Roo.isIE){ // fix IE 1px bogus margin
16513 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16517 this.frameId = Roo.id();
16521 var iframe = this.owner.wrap.createChild({
16523 cls: 'form-control', // bootstrap..
16525 name: this.frameId,
16526 frameBorder : 'no',
16527 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
16532 this.iframe = iframe.dom;
16534 this.assignDocWin();
16536 this.doc.designMode = 'on';
16539 this.doc.write(this.getDocMarkup());
16543 var task = { // must defer to wait for browser to be ready
16545 //console.log("run task?" + this.doc.readyState);
16546 this.assignDocWin();
16547 if(this.doc.body || this.doc.readyState == 'complete'){
16549 this.doc.designMode="on";
16553 Roo.TaskMgr.stop(task);
16554 this.initEditor.defer(10, this);
16561 Roo.TaskMgr.start(task);
16568 onResize : function(w, h)
16570 Roo.log('resize: ' +w + ',' + h );
16571 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16575 if(typeof w == 'number'){
16577 this.iframe.style.width = w + 'px';
16579 if(typeof h == 'number'){
16581 this.iframe.style.height = h + 'px';
16583 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16590 * Toggles the editor between standard and source edit mode.
16591 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16593 toggleSourceEdit : function(sourceEditMode){
16595 this.sourceEditMode = sourceEditMode === true;
16597 if(this.sourceEditMode){
16599 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
16602 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16603 //this.iframe.className = '';
16606 //this.setSize(this.owner.wrap.getSize());
16607 //this.fireEvent('editmodechange', this, this.sourceEditMode);
16614 * Protected method that will not generally be called directly. If you need/want
16615 * custom HTML cleanup, this is the method you should override.
16616 * @param {String} html The HTML to be cleaned
16617 * return {String} The cleaned HTML
16619 cleanHtml : function(html){
16620 html = String(html);
16621 if(html.length > 5){
16622 if(Roo.isSafari){ // strip safari nonsense
16623 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16626 if(html == ' '){
16633 * HTML Editor -> Textarea
16634 * Protected method that will not generally be called directly. Syncs the contents
16635 * of the editor iframe with the textarea.
16637 syncValue : function(){
16638 if(this.initialized){
16639 var bd = (this.doc.body || this.doc.documentElement);
16640 //this.cleanUpPaste(); -- this is done else where and causes havoc..
16641 var html = bd.innerHTML;
16643 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16644 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16646 html = '<div style="'+m[0]+'">' + html + '</div>';
16649 html = this.cleanHtml(html);
16650 // fix up the special chars.. normaly like back quotes in word...
16651 // however we do not want to do this with chinese..
16652 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16653 var cc = b.charCodeAt();
16655 (cc >= 0x4E00 && cc < 0xA000 ) ||
16656 (cc >= 0x3400 && cc < 0x4E00 ) ||
16657 (cc >= 0xf900 && cc < 0xfb00 )
16663 if(this.owner.fireEvent('beforesync', this, html) !== false){
16664 this.el.dom.value = html;
16665 this.owner.fireEvent('sync', this, html);
16671 * Protected method that will not generally be called directly. Pushes the value of the textarea
16672 * into the iframe editor.
16674 pushValue : function(){
16675 if(this.initialized){
16676 var v = this.el.dom.value.trim();
16678 // if(v.length < 1){
16682 if(this.owner.fireEvent('beforepush', this, v) !== false){
16683 var d = (this.doc.body || this.doc.documentElement);
16685 this.cleanUpPaste();
16686 this.el.dom.value = d.innerHTML;
16687 this.owner.fireEvent('push', this, v);
16693 deferFocus : function(){
16694 this.focus.defer(10, this);
16698 focus : function(){
16699 if(this.win && !this.sourceEditMode){
16706 assignDocWin: function()
16708 var iframe = this.iframe;
16711 this.doc = iframe.contentWindow.document;
16712 this.win = iframe.contentWindow;
16714 // if (!Roo.get(this.frameId)) {
16717 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16718 // this.win = Roo.get(this.frameId).dom.contentWindow;
16720 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16724 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16725 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16730 initEditor : function(){
16731 //console.log("INIT EDITOR");
16732 this.assignDocWin();
16736 this.doc.designMode="on";
16738 this.doc.write(this.getDocMarkup());
16741 var dbody = (this.doc.body || this.doc.documentElement);
16742 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16743 // this copies styles from the containing element into thsi one..
16744 // not sure why we need all of this..
16745 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16747 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16748 //ss['background-attachment'] = 'fixed'; // w3c
16749 dbody.bgProperties = 'fixed'; // ie
16750 //Roo.DomHelper.applyStyles(dbody, ss);
16751 Roo.EventManager.on(this.doc, {
16752 //'mousedown': this.onEditorEvent,
16753 'mouseup': this.onEditorEvent,
16754 'dblclick': this.onEditorEvent,
16755 'click': this.onEditorEvent,
16756 'keyup': this.onEditorEvent,
16761 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16763 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16764 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16766 this.initialized = true;
16768 this.owner.fireEvent('initialize', this);
16773 onDestroy : function(){
16779 //for (var i =0; i < this.toolbars.length;i++) {
16780 // // fixme - ask toolbars for heights?
16781 // this.toolbars[i].onDestroy();
16784 //this.wrap.dom.innerHTML = '';
16785 //this.wrap.remove();
16790 onFirstFocus : function(){
16792 this.assignDocWin();
16795 this.activated = true;
16798 if(Roo.isGecko){ // prevent silly gecko errors
16800 var s = this.win.getSelection();
16801 if(!s.focusNode || s.focusNode.nodeType != 3){
16802 var r = s.getRangeAt(0);
16803 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16808 this.execCmd('useCSS', true);
16809 this.execCmd('styleWithCSS', false);
16812 this.owner.fireEvent('activate', this);
16816 adjustFont: function(btn){
16817 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16818 //if(Roo.isSafari){ // safari
16821 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16822 if(Roo.isSafari){ // safari
16823 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16824 v = (v < 10) ? 10 : v;
16825 v = (v > 48) ? 48 : v;
16826 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16831 v = Math.max(1, v+adjust);
16833 this.execCmd('FontSize', v );
16836 onEditorEvent : function(e){
16837 this.owner.fireEvent('editorevent', this, e);
16838 // this.updateToolbar();
16839 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16842 insertTag : function(tg)
16844 // could be a bit smarter... -> wrap the current selected tRoo..
16845 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16847 range = this.createRange(this.getSelection());
16848 var wrappingNode = this.doc.createElement(tg.toLowerCase());
16849 wrappingNode.appendChild(range.extractContents());
16850 range.insertNode(wrappingNode);
16857 this.execCmd("formatblock", tg);
16861 insertText : function(txt)
16865 var range = this.createRange();
16866 range.deleteContents();
16867 //alert(Sender.getAttribute('label'));
16869 range.insertNode(this.doc.createTextNode(txt));
16875 * Executes a Midas editor command on the editor document and performs necessary focus and
16876 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16877 * @param {String} cmd The Midas command
16878 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16880 relayCmd : function(cmd, value){
16882 this.execCmd(cmd, value);
16883 this.owner.fireEvent('editorevent', this);
16884 //this.updateToolbar();
16885 this.owner.deferFocus();
16889 * Executes a Midas editor command directly on the editor document.
16890 * For visual commands, you should use {@link #relayCmd} instead.
16891 * <b>This should only be called after the editor is initialized.</b>
16892 * @param {String} cmd The Midas command
16893 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16895 execCmd : function(cmd, value){
16896 this.doc.execCommand(cmd, false, value === undefined ? null : value);
16903 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16905 * @param {String} text | dom node..
16907 insertAtCursor : function(text)
16912 if(!this.activated){
16918 var r = this.doc.selection.createRange();
16929 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16933 // from jquery ui (MIT licenced)
16935 var win = this.win;
16937 if (win.getSelection && win.getSelection().getRangeAt) {
16938 range = win.getSelection().getRangeAt(0);
16939 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16940 range.insertNode(node);
16941 } else if (win.document.selection && win.document.selection.createRange) {
16942 // no firefox support
16943 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16944 win.document.selection.createRange().pasteHTML(txt);
16946 // no firefox support
16947 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16948 this.execCmd('InsertHTML', txt);
16957 mozKeyPress : function(e){
16959 var c = e.getCharCode(), cmd;
16962 c = String.fromCharCode(c).toLowerCase();
16976 this.cleanUpPaste.defer(100, this);
16984 e.preventDefault();
16992 fixKeys : function(){ // load time branching for fastest keydown performance
16994 return function(e){
16995 var k = e.getKey(), r;
16998 r = this.doc.selection.createRange();
17001 r.pasteHTML('    ');
17008 r = this.doc.selection.createRange();
17010 var target = r.parentElement();
17011 if(!target || target.tagName.toLowerCase() != 'li'){
17013 r.pasteHTML('<br />');
17019 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17020 this.cleanUpPaste.defer(100, this);
17026 }else if(Roo.isOpera){
17027 return function(e){
17028 var k = e.getKey();
17032 this.execCmd('InsertHTML','    ');
17035 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17036 this.cleanUpPaste.defer(100, this);
17041 }else if(Roo.isSafari){
17042 return function(e){
17043 var k = e.getKey();
17047 this.execCmd('InsertText','\t');
17051 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17052 this.cleanUpPaste.defer(100, this);
17060 getAllAncestors: function()
17062 var p = this.getSelectedNode();
17065 a.push(p); // push blank onto stack..
17066 p = this.getParentElement();
17070 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17074 a.push(this.doc.body);
17078 lastSelNode : false,
17081 getSelection : function()
17083 this.assignDocWin();
17084 return Roo.isIE ? this.doc.selection : this.win.getSelection();
17087 getSelectedNode: function()
17089 // this may only work on Gecko!!!
17091 // should we cache this!!!!
17096 var range = this.createRange(this.getSelection()).cloneRange();
17099 var parent = range.parentElement();
17101 var testRange = range.duplicate();
17102 testRange.moveToElementText(parent);
17103 if (testRange.inRange(range)) {
17106 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17109 parent = parent.parentElement;
17114 // is ancestor a text element.
17115 var ac = range.commonAncestorContainer;
17116 if (ac.nodeType == 3) {
17117 ac = ac.parentNode;
17120 var ar = ac.childNodes;
17123 var other_nodes = [];
17124 var has_other_nodes = false;
17125 for (var i=0;i<ar.length;i++) {
17126 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
17129 // fullly contained node.
17131 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17136 // probably selected..
17137 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17138 other_nodes.push(ar[i]);
17142 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
17147 has_other_nodes = true;
17149 if (!nodes.length && other_nodes.length) {
17150 nodes= other_nodes;
17152 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17158 createRange: function(sel)
17160 // this has strange effects when using with
17161 // top toolbar - not sure if it's a great idea.
17162 //this.editor.contentWindow.focus();
17163 if (typeof sel != "undefined") {
17165 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17167 return this.doc.createRange();
17170 return this.doc.createRange();
17173 getParentElement: function()
17176 this.assignDocWin();
17177 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17179 var range = this.createRange(sel);
17182 var p = range.commonAncestorContainer;
17183 while (p.nodeType == 3) { // text node
17194 * Range intersection.. the hard stuff...
17198 * [ -- selected range --- ]
17202 * if end is before start or hits it. fail.
17203 * if start is after end or hits it fail.
17205 * if either hits (but other is outside. - then it's not
17211 // @see http://www.thismuchiknow.co.uk/?p=64.
17212 rangeIntersectsNode : function(range, node)
17214 var nodeRange = node.ownerDocument.createRange();
17216 nodeRange.selectNode(node);
17218 nodeRange.selectNodeContents(node);
17221 var rangeStartRange = range.cloneRange();
17222 rangeStartRange.collapse(true);
17224 var rangeEndRange = range.cloneRange();
17225 rangeEndRange.collapse(false);
17227 var nodeStartRange = nodeRange.cloneRange();
17228 nodeStartRange.collapse(true);
17230 var nodeEndRange = nodeRange.cloneRange();
17231 nodeEndRange.collapse(false);
17233 return rangeStartRange.compareBoundaryPoints(
17234 Range.START_TO_START, nodeEndRange) == -1 &&
17235 rangeEndRange.compareBoundaryPoints(
17236 Range.START_TO_START, nodeStartRange) == 1;
17240 rangeCompareNode : function(range, node)
17242 var nodeRange = node.ownerDocument.createRange();
17244 nodeRange.selectNode(node);
17246 nodeRange.selectNodeContents(node);
17250 range.collapse(true);
17252 nodeRange.collapse(true);
17254 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17255 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
17257 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17259 var nodeIsBefore = ss == 1;
17260 var nodeIsAfter = ee == -1;
17262 if (nodeIsBefore && nodeIsAfter)
17264 if (!nodeIsBefore && nodeIsAfter)
17265 return 1; //right trailed.
17267 if (nodeIsBefore && !nodeIsAfter)
17268 return 2; // left trailed.
17273 // private? - in a new class?
17274 cleanUpPaste : function()
17276 // cleans up the whole document..
17277 Roo.log('cleanuppaste');
17279 this.cleanUpChildren(this.doc.body);
17280 var clean = this.cleanWordChars(this.doc.body.innerHTML);
17281 if (clean != this.doc.body.innerHTML) {
17282 this.doc.body.innerHTML = clean;
17287 cleanWordChars : function(input) {// change the chars to hex code
17288 var he = Roo.HtmlEditorCore;
17290 var output = input;
17291 Roo.each(he.swapCodes, function(sw) {
17292 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17294 output = output.replace(swapper, sw[1]);
17301 cleanUpChildren : function (n)
17303 if (!n.childNodes.length) {
17306 for (var i = n.childNodes.length-1; i > -1 ; i--) {
17307 this.cleanUpChild(n.childNodes[i]);
17314 cleanUpChild : function (node)
17317 //console.log(node);
17318 if (node.nodeName == "#text") {
17319 // clean up silly Windows -- stuff?
17322 if (node.nodeName == "#comment") {
17323 node.parentNode.removeChild(node);
17324 // clean up silly Windows -- stuff?
17327 var lcname = node.tagName.toLowerCase();
17328 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17329 // whitelist of tags..
17331 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17333 node.parentNode.removeChild(node);
17338 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17340 // remove <a name=....> as rendering on yahoo mailer is borked with this.
17341 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17343 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17344 // remove_keep_children = true;
17347 if (remove_keep_children) {
17348 this.cleanUpChildren(node);
17349 // inserts everything just before this node...
17350 while (node.childNodes.length) {
17351 var cn = node.childNodes[0];
17352 node.removeChild(cn);
17353 node.parentNode.insertBefore(cn, node);
17355 node.parentNode.removeChild(node);
17359 if (!node.attributes || !node.attributes.length) {
17360 this.cleanUpChildren(node);
17364 function cleanAttr(n,v)
17367 if (v.match(/^\./) || v.match(/^\//)) {
17370 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17373 if (v.match(/^#/)) {
17376 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17377 node.removeAttribute(n);
17381 var cwhite = this.cwhite;
17382 var cblack = this.cblack;
17384 function cleanStyle(n,v)
17386 if (v.match(/expression/)) { //XSS?? should we even bother..
17387 node.removeAttribute(n);
17391 var parts = v.split(/;/);
17394 Roo.each(parts, function(p) {
17395 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17399 var l = p.split(':').shift().replace(/\s+/g,'');
17400 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17402 if ( cwhite.length && cblack.indexOf(l) > -1) {
17403 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17404 //node.removeAttribute(n);
17408 // only allow 'c whitelisted system attributes'
17409 if ( cwhite.length && cwhite.indexOf(l) < 0) {
17410 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17411 //node.removeAttribute(n);
17421 if (clean.length) {
17422 node.setAttribute(n, clean.join(';'));
17424 node.removeAttribute(n);
17430 for (var i = node.attributes.length-1; i > -1 ; i--) {
17431 var a = node.attributes[i];
17434 if (a.name.toLowerCase().substr(0,2)=='on') {
17435 node.removeAttribute(a.name);
17438 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17439 node.removeAttribute(a.name);
17442 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17443 cleanAttr(a.name,a.value); // fixme..
17446 if (a.name == 'style') {
17447 cleanStyle(a.name,a.value);
17450 /// clean up MS crap..
17451 // tecnically this should be a list of valid class'es..
17454 if (a.name == 'class') {
17455 if (a.value.match(/^Mso/)) {
17456 node.className = '';
17459 if (a.value.match(/body/)) {
17460 node.className = '';
17471 this.cleanUpChildren(node);
17476 * Clean up MS wordisms...
17478 cleanWord : function(node)
17481 var cleanWordChildren = function()
17483 if (!node.childNodes.length) {
17486 for (var i = node.childNodes.length-1; i > -1 ; i--) {
17487 _t.cleanWord(node.childNodes[i]);
17493 this.cleanWord(this.doc.body);
17496 if (node.nodeName == "#text") {
17497 // clean up silly Windows -- stuff?
17500 if (node.nodeName == "#comment") {
17501 node.parentNode.removeChild(node);
17502 // clean up silly Windows -- stuff?
17506 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17507 node.parentNode.removeChild(node);
17511 // remove - but keep children..
17512 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17513 while (node.childNodes.length) {
17514 var cn = node.childNodes[0];
17515 node.removeChild(cn);
17516 node.parentNode.insertBefore(cn, node);
17518 node.parentNode.removeChild(node);
17519 cleanWordChildren();
17523 if (node.className.length) {
17525 var cn = node.className.split(/\W+/);
17527 Roo.each(cn, function(cls) {
17528 if (cls.match(/Mso[a-zA-Z]+/)) {
17533 node.className = cna.length ? cna.join(' ') : '';
17535 node.removeAttribute("class");
17539 if (node.hasAttribute("lang")) {
17540 node.removeAttribute("lang");
17543 if (node.hasAttribute("style")) {
17545 var styles = node.getAttribute("style").split(";");
17547 Roo.each(styles, function(s) {
17548 if (!s.match(/:/)) {
17551 var kv = s.split(":");
17552 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17555 // what ever is left... we allow.
17558 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17559 if (!nstyle.length) {
17560 node.removeAttribute('style');
17564 cleanWordChildren();
17568 domToHTML : function(currentElement, depth, nopadtext) {
17570 depth = depth || 0;
17571 nopadtext = nopadtext || false;
17573 if (!currentElement) {
17574 return this.domToHTML(this.doc.body);
17577 //Roo.log(currentElement);
17579 var allText = false;
17580 var nodeName = currentElement.nodeName;
17581 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17583 if (nodeName == '#text') {
17584 return currentElement.nodeValue;
17589 if (nodeName != 'BODY') {
17592 // Prints the node tagName, such as <A>, <IMG>, etc
17595 for(i = 0; i < currentElement.attributes.length;i++) {
17597 var aname = currentElement.attributes.item(i).name;
17598 if (!currentElement.attributes.item(i).value.length) {
17601 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17604 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17613 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17616 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17621 // Traverse the tree
17623 var currentElementChild = currentElement.childNodes.item(i);
17624 var allText = true;
17625 var innerHTML = '';
17627 while (currentElementChild) {
17628 // Formatting code (indent the tree so it looks nice on the screen)
17629 var nopad = nopadtext;
17630 if (lastnode == 'SPAN') {
17634 if (currentElementChild.nodeName == '#text') {
17635 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17636 if (!nopad && toadd.length > 80) {
17637 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
17639 innerHTML += toadd;
17642 currentElementChild = currentElement.childNodes.item(i);
17648 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
17650 // Recursively traverse the tree structure of the child node
17651 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
17652 lastnode = currentElementChild.nodeName;
17654 currentElementChild=currentElement.childNodes.item(i);
17660 // The remaining code is mostly for formatting the tree
17661 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
17666 ret+= "</"+tagName+">";
17672 applyBlacklists : function()
17674 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
17675 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
17679 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17680 if (b.indexOf(tag) > -1) {
17683 this.white.push(tag);
17687 Roo.each(w, function(tag) {
17688 if (b.indexOf(tag) > -1) {
17691 if (this.white.indexOf(tag) > -1) {
17694 this.white.push(tag);
17699 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17700 if (w.indexOf(tag) > -1) {
17703 this.black.push(tag);
17707 Roo.each(b, function(tag) {
17708 if (w.indexOf(tag) > -1) {
17711 if (this.black.indexOf(tag) > -1) {
17714 this.black.push(tag);
17719 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
17720 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
17724 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17725 if (b.indexOf(tag) > -1) {
17728 this.cwhite.push(tag);
17732 Roo.each(w, function(tag) {
17733 if (b.indexOf(tag) > -1) {
17736 if (this.cwhite.indexOf(tag) > -1) {
17739 this.cwhite.push(tag);
17744 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17745 if (w.indexOf(tag) > -1) {
17748 this.cblack.push(tag);
17752 Roo.each(b, function(tag) {
17753 if (w.indexOf(tag) > -1) {
17756 if (this.cblack.indexOf(tag) > -1) {
17759 this.cblack.push(tag);
17764 // hide stuff that is not compatible
17778 * @event specialkey
17782 * @cfg {String} fieldClass @hide
17785 * @cfg {String} focusClass @hide
17788 * @cfg {String} autoCreate @hide
17791 * @cfg {String} inputType @hide
17794 * @cfg {String} invalidClass @hide
17797 * @cfg {String} invalidText @hide
17800 * @cfg {String} msgFx @hide
17803 * @cfg {String} validateOnBlur @hide
17807 Roo.HtmlEditorCore.white = [
17808 'area', 'br', 'img', 'input', 'hr', 'wbr',
17810 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
17811 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
17812 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
17813 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
17814 'table', 'ul', 'xmp',
17816 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
17819 'dir', 'menu', 'ol', 'ul', 'dl',
17825 Roo.HtmlEditorCore.black = [
17826 // 'embed', 'object', // enable - backend responsiblity to clean thiese
17828 'base', 'basefont', 'bgsound', 'blink', 'body',
17829 'frame', 'frameset', 'head', 'html', 'ilayer',
17830 'iframe', 'layer', 'link', 'meta', 'object',
17831 'script', 'style' ,'title', 'xml' // clean later..
17833 Roo.HtmlEditorCore.clean = [
17834 'script', 'style', 'title', 'xml'
17836 Roo.HtmlEditorCore.remove = [
17841 Roo.HtmlEditorCore.ablack = [
17845 Roo.HtmlEditorCore.aclean = [
17846 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
17850 Roo.HtmlEditorCore.pwhite= [
17851 'http', 'https', 'mailto'
17854 // white listed style attributes.
17855 Roo.HtmlEditorCore.cwhite= [
17856 // 'text-align', /// default is to allow most things..
17862 // black listed style attributes.
17863 Roo.HtmlEditorCore.cblack= [
17864 // 'font-size' -- this can be set by the project
17868 Roo.HtmlEditorCore.swapCodes =[
17887 * @class Roo.bootstrap.HtmlEditor
17888 * @extends Roo.bootstrap.TextArea
17889 * Bootstrap HtmlEditor class
17892 * Create a new HtmlEditor
17893 * @param {Object} config The config object
17896 Roo.bootstrap.HtmlEditor = function(config){
17897 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17898 if (!this.toolbars) {
17899 this.toolbars = [];
17901 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17904 * @event initialize
17905 * Fires when the editor is fully initialized (including the iframe)
17906 * @param {HtmlEditor} this
17911 * Fires when the editor is first receives the focus. Any insertion must wait
17912 * until after this event.
17913 * @param {HtmlEditor} this
17917 * @event beforesync
17918 * Fires before the textarea is updated with content from the editor iframe. Return false
17919 * to cancel the sync.
17920 * @param {HtmlEditor} this
17921 * @param {String} html
17925 * @event beforepush
17926 * Fires before the iframe editor is updated with content from the textarea. Return false
17927 * to cancel the push.
17928 * @param {HtmlEditor} this
17929 * @param {String} html
17934 * Fires when the textarea is updated with content from the editor iframe.
17935 * @param {HtmlEditor} this
17936 * @param {String} html
17941 * Fires when the iframe editor is updated with content from the textarea.
17942 * @param {HtmlEditor} this
17943 * @param {String} html
17947 * @event editmodechange
17948 * Fires when the editor switches edit modes
17949 * @param {HtmlEditor} this
17950 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17952 editmodechange: true,
17954 * @event editorevent
17955 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17956 * @param {HtmlEditor} this
17960 * @event firstfocus
17961 * Fires when on first focus - needed by toolbars..
17962 * @param {HtmlEditor} this
17967 * Auto save the htmlEditor value as a file into Events
17968 * @param {HtmlEditor} this
17972 * @event savedpreview
17973 * preview the saved version of htmlEditor
17974 * @param {HtmlEditor} this
17981 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
17985 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17990 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
17995 * @cfg {Number} height (in pixels)
17999 * @cfg {Number} width (in pixels)
18004 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18007 stylesheets: false,
18012 // private properties
18013 validationEvent : false,
18015 initialized : false,
18018 onFocus : Roo.emptyFn,
18020 hideMode:'offsets',
18023 tbContainer : false,
18025 toolbarContainer :function() {
18026 return this.wrap.select('.x-html-editor-tb',true).first();
18030 * Protected method that will not generally be called directly. It
18031 * is called when the editor creates its toolbar. Override this method if you need to
18032 * add custom toolbar buttons.
18033 * @param {HtmlEditor} editor
18035 createToolbar : function(){
18037 Roo.log("create toolbars");
18039 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18040 this.toolbars[0].render(this.toolbarContainer());
18044 // if (!editor.toolbars || !editor.toolbars.length) {
18045 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18048 // for (var i =0 ; i < editor.toolbars.length;i++) {
18049 // editor.toolbars[i] = Roo.factory(
18050 // typeof(editor.toolbars[i]) == 'string' ?
18051 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
18052 // Roo.bootstrap.HtmlEditor);
18053 // editor.toolbars[i].init(editor);
18059 onRender : function(ct, position)
18061 // Roo.log("Call onRender: " + this.xtype);
18063 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18065 this.wrap = this.inputEl().wrap({
18066 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18069 this.editorcore.onRender(ct, position);
18071 if (this.resizable) {
18072 this.resizeEl = new Roo.Resizable(this.wrap, {
18076 minHeight : this.height,
18077 height: this.height,
18078 handles : this.resizable,
18081 resize : function(r, w, h) {
18082 _t.onResize(w,h); // -something
18088 this.createToolbar(this);
18091 if(!this.width && this.resizable){
18092 this.setSize(this.wrap.getSize());
18094 if (this.resizeEl) {
18095 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18096 // should trigger onReize..
18102 onResize : function(w, h)
18104 Roo.log('resize: ' +w + ',' + h );
18105 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18109 if(this.inputEl() ){
18110 if(typeof w == 'number'){
18111 var aw = w - this.wrap.getFrameWidth('lr');
18112 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18115 if(typeof h == 'number'){
18116 var tbh = -11; // fixme it needs to tool bar size!
18117 for (var i =0; i < this.toolbars.length;i++) {
18118 // fixme - ask toolbars for heights?
18119 tbh += this.toolbars[i].el.getHeight();
18120 //if (this.toolbars[i].footer) {
18121 // tbh += this.toolbars[i].footer.el.getHeight();
18129 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18130 ah -= 5; // knock a few pixes off for look..
18131 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18135 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18136 this.editorcore.onResize(ew,eh);
18141 * Toggles the editor between standard and source edit mode.
18142 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18144 toggleSourceEdit : function(sourceEditMode)
18146 this.editorcore.toggleSourceEdit(sourceEditMode);
18148 if(this.editorcore.sourceEditMode){
18149 Roo.log('editor - showing textarea');
18152 // Roo.log(this.syncValue());
18154 this.inputEl().removeClass(['hide', 'x-hidden']);
18155 this.inputEl().dom.removeAttribute('tabIndex');
18156 this.inputEl().focus();
18158 Roo.log('editor - hiding textarea');
18160 // Roo.log(this.pushValue());
18163 this.inputEl().addClass(['hide', 'x-hidden']);
18164 this.inputEl().dom.setAttribute('tabIndex', -1);
18165 //this.deferFocus();
18168 if(this.resizable){
18169 this.setSize(this.wrap.getSize());
18172 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18175 // private (for BoxComponent)
18176 adjustSize : Roo.BoxComponent.prototype.adjustSize,
18178 // private (for BoxComponent)
18179 getResizeEl : function(){
18183 // private (for BoxComponent)
18184 getPositionEl : function(){
18189 initEvents : function(){
18190 this.originalValue = this.getValue();
18194 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18197 // markInvalid : Roo.emptyFn,
18199 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18202 // clearInvalid : Roo.emptyFn,
18204 setValue : function(v){
18205 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18206 this.editorcore.pushValue();
18211 deferFocus : function(){
18212 this.focus.defer(10, this);
18216 focus : function(){
18217 this.editorcore.focus();
18223 onDestroy : function(){
18229 for (var i =0; i < this.toolbars.length;i++) {
18230 // fixme - ask toolbars for heights?
18231 this.toolbars[i].onDestroy();
18234 this.wrap.dom.innerHTML = '';
18235 this.wrap.remove();
18240 onFirstFocus : function(){
18241 //Roo.log("onFirstFocus");
18242 this.editorcore.onFirstFocus();
18243 for (var i =0; i < this.toolbars.length;i++) {
18244 this.toolbars[i].onFirstFocus();
18250 syncValue : function()
18252 this.editorcore.syncValue();
18255 pushValue : function()
18257 this.editorcore.pushValue();
18261 // hide stuff that is not compatible
18275 * @event specialkey
18279 * @cfg {String} fieldClass @hide
18282 * @cfg {String} focusClass @hide
18285 * @cfg {String} autoCreate @hide
18288 * @cfg {String} inputType @hide
18291 * @cfg {String} invalidClass @hide
18294 * @cfg {String} invalidText @hide
18297 * @cfg {String} msgFx @hide
18300 * @cfg {String} validateOnBlur @hide
18309 Roo.namespace('Roo.bootstrap.htmleditor');
18311 * @class Roo.bootstrap.HtmlEditorToolbar1
18316 new Roo.bootstrap.HtmlEditor({
18319 new Roo.bootstrap.HtmlEditorToolbar1({
18320 disable : { fonts: 1 , format: 1, ..., ... , ...],
18326 * @cfg {Object} disable List of elements to disable..
18327 * @cfg {Array} btns List of additional buttons.
18331 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18334 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18337 Roo.apply(this, config);
18339 // default disabled, based on 'good practice'..
18340 this.disable = this.disable || {};
18341 Roo.applyIf(this.disable, {
18344 specialElements : true
18346 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18348 this.editor = config.editor;
18349 this.editorcore = config.editor.editorcore;
18351 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18353 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18354 // dont call parent... till later.
18356 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
18361 editorcore : false,
18366 "h1","h2","h3","h4","h5","h6",
18368 "abbr", "acronym", "address", "cite", "samp", "var",
18372 onRender : function(ct, position)
18374 // Roo.log("Call onRender: " + this.xtype);
18376 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18378 this.el.dom.style.marginBottom = '0';
18380 var editorcore = this.editorcore;
18381 var editor= this.editor;
18384 var btn = function(id,cmd , toggle, handler){
18386 var event = toggle ? 'toggle' : 'click';
18391 xns: Roo.bootstrap,
18394 enableToggle:toggle !== false,
18396 pressed : toggle ? false : null,
18399 a.listeners[toggle ? 'toggle' : 'click'] = function() {
18400 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
18409 xns: Roo.bootstrap,
18410 glyphicon : 'font',
18414 xns: Roo.bootstrap,
18418 Roo.each(this.formats, function(f) {
18419 style.menu.items.push({
18421 xns: Roo.bootstrap,
18422 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18427 editorcore.insertTag(this.tagname);
18434 children.push(style);
18437 btn('bold',false,true);
18438 btn('italic',false,true);
18439 btn('align-left', 'justifyleft',true);
18440 btn('align-center', 'justifycenter',true);
18441 btn('align-right' , 'justifyright',true);
18442 btn('link', false, false, function(btn) {
18443 //Roo.log("create link?");
18444 var url = prompt(this.createLinkText, this.defaultLinkValue);
18445 if(url && url != 'http:/'+'/'){
18446 this.editorcore.relayCmd('createlink', url);
18449 btn('list','insertunorderedlist',true);
18450 btn('pencil', false,true, function(btn){
18453 this.toggleSourceEdit(btn.pressed);
18459 xns: Roo.bootstrap,
18464 xns: Roo.bootstrap,
18469 cog.menu.items.push({
18471 xns: Roo.bootstrap,
18472 html : Clean styles,
18477 editorcore.insertTag(this.tagname);
18486 this.xtype = 'NavSimplebar';
18488 for(var i=0;i< children.length;i++) {
18490 this.buttons.add(this.addxtypeChild(children[i]));
18494 editor.on('editorevent', this.updateToolbar, this);
18496 onBtnClick : function(id)
18498 this.editorcore.relayCmd(id);
18499 this.editorcore.focus();
18503 * Protected method that will not generally be called directly. It triggers
18504 * a toolbar update by reading the markup state of the current selection in the editor.
18506 updateToolbar: function(){
18508 if(!this.editorcore.activated){
18509 this.editor.onFirstFocus(); // is this neeed?
18513 var btns = this.buttons;
18514 var doc = this.editorcore.doc;
18515 btns.get('bold').setActive(doc.queryCommandState('bold'));
18516 btns.get('italic').setActive(doc.queryCommandState('italic'));
18517 //btns.get('underline').setActive(doc.queryCommandState('underline'));
18519 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18520 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18521 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18523 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18524 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18527 var ans = this.editorcore.getAllAncestors();
18528 if (this.formatCombo) {
18531 var store = this.formatCombo.store;
18532 this.formatCombo.setValue("");
18533 for (var i =0; i < ans.length;i++) {
18534 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18536 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18544 // hides menus... - so this cant be on a menu...
18545 Roo.bootstrap.MenuMgr.hideAll();
18547 Roo.bootstrap.MenuMgr.hideAll();
18548 //this.editorsyncValue();
18550 onFirstFocus: function() {
18551 this.buttons.each(function(item){
18555 toggleSourceEdit : function(sourceEditMode){
18558 if(sourceEditMode){
18559 Roo.log("disabling buttons");
18560 this.buttons.each( function(item){
18561 if(item.cmd != 'pencil'){
18567 Roo.log("enabling buttons");
18568 if(this.editorcore.initialized){
18569 this.buttons.each( function(item){
18575 Roo.log("calling toggole on editor");
18576 // tell the editor that it's been pressed..
18577 this.editor.toggleSourceEdit(sourceEditMode);
18587 * @class Roo.bootstrap.Table.AbstractSelectionModel
18588 * @extends Roo.util.Observable
18589 * Abstract base class for grid SelectionModels. It provides the interface that should be
18590 * implemented by descendant classes. This class should not be directly instantiated.
18593 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18594 this.locked = false;
18595 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18599 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
18600 /** @ignore Called by the grid automatically. Do not call directly. */
18601 init : function(grid){
18607 * Locks the selections.
18610 this.locked = true;
18614 * Unlocks the selections.
18616 unlock : function(){
18617 this.locked = false;
18621 * Returns true if the selections are locked.
18622 * @return {Boolean}
18624 isLocked : function(){
18625 return this.locked;
18629 * @extends Roo.bootstrap.Table.AbstractSelectionModel
18630 * @class Roo.bootstrap.Table.RowSelectionModel
18631 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18632 * It supports multiple selections and keyboard selection/navigation.
18634 * @param {Object} config
18637 Roo.bootstrap.Table.RowSelectionModel = function(config){
18638 Roo.apply(this, config);
18639 this.selections = new Roo.util.MixedCollection(false, function(o){
18644 this.lastActive = false;
18648 * @event selectionchange
18649 * Fires when the selection changes
18650 * @param {SelectionModel} this
18652 "selectionchange" : true,
18654 * @event afterselectionchange
18655 * Fires after the selection changes (eg. by key press or clicking)
18656 * @param {SelectionModel} this
18658 "afterselectionchange" : true,
18660 * @event beforerowselect
18661 * Fires when a row is selected being selected, return false to cancel.
18662 * @param {SelectionModel} this
18663 * @param {Number} rowIndex The selected index
18664 * @param {Boolean} keepExisting False if other selections will be cleared
18666 "beforerowselect" : true,
18669 * Fires when a row is selected.
18670 * @param {SelectionModel} this
18671 * @param {Number} rowIndex The selected index
18672 * @param {Roo.data.Record} r The record
18674 "rowselect" : true,
18676 * @event rowdeselect
18677 * Fires when a row is deselected.
18678 * @param {SelectionModel} this
18679 * @param {Number} rowIndex The selected index
18681 "rowdeselect" : true
18683 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18684 this.locked = false;
18687 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
18689 * @cfg {Boolean} singleSelect
18690 * True to allow selection of only one row at a time (defaults to false)
18692 singleSelect : false,
18695 initEvents : function(){
18697 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18698 this.grid.on("mousedown", this.handleMouseDown, this);
18699 }else{ // allow click to work like normal
18700 this.grid.on("rowclick", this.handleDragableRowClick, this);
18703 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18704 "up" : function(e){
18706 this.selectPrevious(e.shiftKey);
18707 }else if(this.last !== false && this.lastActive !== false){
18708 var last = this.last;
18709 this.selectRange(this.last, this.lastActive-1);
18710 this.grid.getView().focusRow(this.lastActive);
18711 if(last !== false){
18715 this.selectFirstRow();
18717 this.fireEvent("afterselectionchange", this);
18719 "down" : function(e){
18721 this.selectNext(e.shiftKey);
18722 }else if(this.last !== false && this.lastActive !== false){
18723 var last = this.last;
18724 this.selectRange(this.last, this.lastActive+1);
18725 this.grid.getView().focusRow(this.lastActive);
18726 if(last !== false){
18730 this.selectFirstRow();
18732 this.fireEvent("afterselectionchange", this);
18737 var view = this.grid.view;
18738 view.on("refresh", this.onRefresh, this);
18739 view.on("rowupdated", this.onRowUpdated, this);
18740 view.on("rowremoved", this.onRemove, this);
18744 onRefresh : function(){
18745 var ds = this.grid.dataSource, i, v = this.grid.view;
18746 var s = this.selections;
18747 s.each(function(r){
18748 if((i = ds.indexOfId(r.id)) != -1){
18757 onRemove : function(v, index, r){
18758 this.selections.remove(r);
18762 onRowUpdated : function(v, index, r){
18763 if(this.isSelected(r)){
18764 v.onRowSelect(index);
18770 * @param {Array} records The records to select
18771 * @param {Boolean} keepExisting (optional) True to keep existing selections
18773 selectRecords : function(records, keepExisting){
18775 this.clearSelections();
18777 var ds = this.grid.dataSource;
18778 for(var i = 0, len = records.length; i < len; i++){
18779 this.selectRow(ds.indexOf(records[i]), true);
18784 * Gets the number of selected rows.
18787 getCount : function(){
18788 return this.selections.length;
18792 * Selects the first row in the grid.
18794 selectFirstRow : function(){
18799 * Select the last row.
18800 * @param {Boolean} keepExisting (optional) True to keep existing selections
18802 selectLastRow : function(keepExisting){
18803 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18807 * Selects the row immediately following the last selected row.
18808 * @param {Boolean} keepExisting (optional) True to keep existing selections
18810 selectNext : function(keepExisting){
18811 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18812 this.selectRow(this.last+1, keepExisting);
18813 this.grid.getView().focusRow(this.last);
18818 * Selects the row that precedes the last selected row.
18819 * @param {Boolean} keepExisting (optional) True to keep existing selections
18821 selectPrevious : function(keepExisting){
18823 this.selectRow(this.last-1, keepExisting);
18824 this.grid.getView().focusRow(this.last);
18829 * Returns the selected records
18830 * @return {Array} Array of selected records
18832 getSelections : function(){
18833 return [].concat(this.selections.items);
18837 * Returns the first selected record.
18840 getSelected : function(){
18841 return this.selections.itemAt(0);
18846 * Clears all selections.
18848 clearSelections : function(fast){
18849 if(this.locked) return;
18851 var ds = this.grid.dataSource;
18852 var s = this.selections;
18853 s.each(function(r){
18854 this.deselectRow(ds.indexOfId(r.id));
18858 this.selections.clear();
18865 * Selects all rows.
18867 selectAll : function(){
18868 if(this.locked) return;
18869 this.selections.clear();
18870 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18871 this.selectRow(i, true);
18876 * Returns True if there is a selection.
18877 * @return {Boolean}
18879 hasSelection : function(){
18880 return this.selections.length > 0;
18884 * Returns True if the specified row is selected.
18885 * @param {Number/Record} record The record or index of the record to check
18886 * @return {Boolean}
18888 isSelected : function(index){
18889 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18890 return (r && this.selections.key(r.id) ? true : false);
18894 * Returns True if the specified record id is selected.
18895 * @param {String} id The id of record to check
18896 * @return {Boolean}
18898 isIdSelected : function(id){
18899 return (this.selections.key(id) ? true : false);
18903 handleMouseDown : function(e, t){
18904 var view = this.grid.getView(), rowIndex;
18905 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18908 if(e.shiftKey && this.last !== false){
18909 var last = this.last;
18910 this.selectRange(last, rowIndex, e.ctrlKey);
18911 this.last = last; // reset the last
18912 view.focusRow(rowIndex);
18914 var isSelected = this.isSelected(rowIndex);
18915 if(e.button !== 0 && isSelected){
18916 view.focusRow(rowIndex);
18917 }else if(e.ctrlKey && isSelected){
18918 this.deselectRow(rowIndex);
18919 }else if(!isSelected){
18920 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18921 view.focusRow(rowIndex);
18924 this.fireEvent("afterselectionchange", this);
18927 handleDragableRowClick : function(grid, rowIndex, e)
18929 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18930 this.selectRow(rowIndex, false);
18931 grid.view.focusRow(rowIndex);
18932 this.fireEvent("afterselectionchange", this);
18937 * Selects multiple rows.
18938 * @param {Array} rows Array of the indexes of the row to select
18939 * @param {Boolean} keepExisting (optional) True to keep existing selections
18941 selectRows : function(rows, keepExisting){
18943 this.clearSelections();
18945 for(var i = 0, len = rows.length; i < len; i++){
18946 this.selectRow(rows[i], true);
18951 * Selects a range of rows. All rows in between startRow and endRow are also selected.
18952 * @param {Number} startRow The index of the first row in the range
18953 * @param {Number} endRow The index of the last row in the range
18954 * @param {Boolean} keepExisting (optional) True to retain existing selections
18956 selectRange : function(startRow, endRow, keepExisting){
18957 if(this.locked) return;
18959 this.clearSelections();
18961 if(startRow <= endRow){
18962 for(var i = startRow; i <= endRow; i++){
18963 this.selectRow(i, true);
18966 for(var i = startRow; i >= endRow; i--){
18967 this.selectRow(i, true);
18973 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18974 * @param {Number} startRow The index of the first row in the range
18975 * @param {Number} endRow The index of the last row in the range
18977 deselectRange : function(startRow, endRow, preventViewNotify){
18978 if(this.locked) return;
18979 for(var i = startRow; i <= endRow; i++){
18980 this.deselectRow(i, preventViewNotify);
18986 * @param {Number} row The index of the row to select
18987 * @param {Boolean} keepExisting (optional) True to keep existing selections
18989 selectRow : function(index, keepExisting, preventViewNotify){
18990 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18991 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18992 if(!keepExisting || this.singleSelect){
18993 this.clearSelections();
18995 var r = this.grid.dataSource.getAt(index);
18996 this.selections.add(r);
18997 this.last = this.lastActive = index;
18998 if(!preventViewNotify){
18999 this.grid.getView().onRowSelect(index);
19001 this.fireEvent("rowselect", this, index, r);
19002 this.fireEvent("selectionchange", this);
19008 * @param {Number} row The index of the row to deselect
19010 deselectRow : function(index, preventViewNotify){
19011 if(this.locked) return;
19012 if(this.last == index){
19015 if(this.lastActive == index){
19016 this.lastActive = false;
19018 var r = this.grid.dataSource.getAt(index);
19019 this.selections.remove(r);
19020 if(!preventViewNotify){
19021 this.grid.getView().onRowDeselect(index);
19023 this.fireEvent("rowdeselect", this, index);
19024 this.fireEvent("selectionchange", this);
19028 restoreLast : function(){
19030 this.last = this._last;
19035 acceptsNav : function(row, col, cm){
19036 return !cm.isHidden(col) && cm.isCellEditable(col, row);
19040 onEditorKey : function(field, e){
19041 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19046 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19048 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19050 }else if(k == e.ENTER && !e.ctrlKey){
19054 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19056 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19058 }else if(k == e.ESC){
19062 g.startEditing(newCell[0], newCell[1]);
19067 * Ext JS Library 1.1.1
19068 * Copyright(c) 2006-2007, Ext JS, LLC.
19070 * Originally Released Under LGPL - original licence link has changed is not relivant.
19073 * <script type="text/javascript">
19077 * @class Roo.bootstrap.PagingToolbar
19079 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19081 * Create a new PagingToolbar
19082 * @param {Object} config The config object
19084 Roo.bootstrap.PagingToolbar = function(config)
19086 // old args format still supported... - xtype is prefered..
19087 // created from xtype...
19088 var ds = config.dataSource;
19089 this.toolbarItems = [];
19090 if (config.items) {
19091 this.toolbarItems = config.items;
19092 // config.items = [];
19095 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19102 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19106 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19108 * @cfg {Roo.data.Store} dataSource
19109 * The underlying data store providing the paged data
19112 * @cfg {String/HTMLElement/Element} container
19113 * container The id or element that will contain the toolbar
19116 * @cfg {Boolean} displayInfo
19117 * True to display the displayMsg (defaults to false)
19120 * @cfg {Number} pageSize
19121 * The number of records to display per page (defaults to 20)
19125 * @cfg {String} displayMsg
19126 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19128 displayMsg : 'Displaying {0} - {1} of {2}',
19130 * @cfg {String} emptyMsg
19131 * The message to display when no records are found (defaults to "No data to display")
19133 emptyMsg : 'No data to display',
19135 * Customizable piece of the default paging text (defaults to "Page")
19138 beforePageText : "Page",
19140 * Customizable piece of the default paging text (defaults to "of %0")
19143 afterPageText : "of {0}",
19145 * Customizable piece of the default paging text (defaults to "First Page")
19148 firstText : "First Page",
19150 * Customizable piece of the default paging text (defaults to "Previous Page")
19153 prevText : "Previous Page",
19155 * Customizable piece of the default paging text (defaults to "Next Page")
19158 nextText : "Next Page",
19160 * Customizable piece of the default paging text (defaults to "Last Page")
19163 lastText : "Last Page",
19165 * Customizable piece of the default paging text (defaults to "Refresh")
19168 refreshText : "Refresh",
19172 onRender : function(ct, position)
19174 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19175 this.navgroup.parentId = this.id;
19176 this.navgroup.onRender(this.el, null);
19177 // add the buttons to the navgroup
19179 if(this.displayInfo){
19180 Roo.log(this.el.select('ul.navbar-nav',true).first());
19181 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19182 this.displayEl = this.el.select('.x-paging-info', true).first();
19183 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19184 // this.displayEl = navel.el.select('span',true).first();
19190 Roo.each(_this.buttons, function(e){
19191 Roo.factory(e).onRender(_this.el, null);
19195 Roo.each(_this.toolbarItems, function(e) {
19196 _this.navgroup.addItem(e);
19199 this.first = this.navgroup.addItem({
19200 tooltip: this.firstText,
19202 icon : 'fa fa-backward',
19204 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19207 this.prev = this.navgroup.addItem({
19208 tooltip: this.prevText,
19210 icon : 'fa fa-step-backward',
19212 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
19214 //this.addSeparator();
19217 var field = this.navgroup.addItem( {
19219 cls : 'x-paging-position',
19221 html : this.beforePageText +
19222 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19223 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
19226 this.field = field.el.select('input', true).first();
19227 this.field.on("keydown", this.onPagingKeydown, this);
19228 this.field.on("focus", function(){this.dom.select();});
19231 this.afterTextEl = field.el.select('.x-paging-after',true).first();
19232 //this.field.setHeight(18);
19233 //this.addSeparator();
19234 this.next = this.navgroup.addItem({
19235 tooltip: this.nextText,
19237 html : ' <i class="fa fa-step-forward">',
19239 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
19241 this.last = this.navgroup.addItem({
19242 tooltip: this.lastText,
19243 icon : 'fa fa-forward',
19246 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
19248 //this.addSeparator();
19249 this.loading = this.navgroup.addItem({
19250 tooltip: this.refreshText,
19251 icon: 'fa fa-refresh',
19253 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19259 updateInfo : function(){
19260 if(this.displayEl){
19261 var count = this.ds.getCount();
19262 var msg = count == 0 ?
19266 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
19268 this.displayEl.update(msg);
19273 onLoad : function(ds, r, o){
19274 this.cursor = o.params ? o.params.start : 0;
19275 var d = this.getPageData(),
19279 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19280 this.field.dom.value = ap;
19281 this.first.setDisabled(ap == 1);
19282 this.prev.setDisabled(ap == 1);
19283 this.next.setDisabled(ap == ps);
19284 this.last.setDisabled(ap == ps);
19285 this.loading.enable();
19290 getPageData : function(){
19291 var total = this.ds.getTotalCount();
19294 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19295 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19300 onLoadError : function(){
19301 this.loading.enable();
19305 onPagingKeydown : function(e){
19306 var k = e.getKey();
19307 var d = this.getPageData();
19309 var v = this.field.dom.value, pageNum;
19310 if(!v || isNaN(pageNum = parseInt(v, 10))){
19311 this.field.dom.value = d.activePage;
19314 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19315 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19318 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))
19320 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19321 this.field.dom.value = pageNum;
19322 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19325 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19327 var v = this.field.dom.value, pageNum;
19328 var increment = (e.shiftKey) ? 10 : 1;
19329 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19331 if(!v || isNaN(pageNum = parseInt(v, 10))) {
19332 this.field.dom.value = d.activePage;
19335 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19337 this.field.dom.value = parseInt(v, 10) + increment;
19338 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19339 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19346 beforeLoad : function(){
19348 this.loading.disable();
19353 onClick : function(which){
19360 ds.load({params:{start: 0, limit: this.pageSize}});
19363 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19366 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19369 var total = ds.getTotalCount();
19370 var extra = total % this.pageSize;
19371 var lastStart = extra ? (total - extra) : total-this.pageSize;
19372 ds.load({params:{start: lastStart, limit: this.pageSize}});
19375 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19381 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19382 * @param {Roo.data.Store} store The data store to unbind
19384 unbind : function(ds){
19385 ds.un("beforeload", this.beforeLoad, this);
19386 ds.un("load", this.onLoad, this);
19387 ds.un("loadexception", this.onLoadError, this);
19388 ds.un("remove", this.updateInfo, this);
19389 ds.un("add", this.updateInfo, this);
19390 this.ds = undefined;
19394 * Binds the paging toolbar to the specified {@link Roo.data.Store}
19395 * @param {Roo.data.Store} store The data store to bind
19397 bind : function(ds){
19398 ds.on("beforeload", this.beforeLoad, this);
19399 ds.on("load", this.onLoad, this);
19400 ds.on("loadexception", this.onLoadError, this);
19401 ds.on("remove", this.updateInfo, this);
19402 ds.on("add", this.updateInfo, this);
19413 * @class Roo.bootstrap.MessageBar
19414 * @extends Roo.bootstrap.Component
19415 * Bootstrap MessageBar class
19416 * @cfg {String} html contents of the MessageBar
19417 * @cfg {String} weight (info | success | warning | danger) default info
19418 * @cfg {String} beforeClass insert the bar before the given class
19419 * @cfg {Boolean} closable (true | false) default false
19420 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19423 * Create a new Element
19424 * @param {Object} config The config object
19427 Roo.bootstrap.MessageBar = function(config){
19428 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19431 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
19437 beforeClass: 'bootstrap-sticky-wrap',
19439 getAutoCreate : function(){
19443 cls: 'alert alert-dismissable alert-' + this.weight,
19448 html: this.html || ''
19454 cfg.cls += ' alert-messages-fixed';
19468 onRender : function(ct, position)
19470 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19473 var cfg = Roo.apply({}, this.getAutoCreate());
19477 cfg.cls += ' ' + this.cls;
19480 cfg.style = this.style;
19482 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19484 this.el.setVisibilityMode(Roo.Element.DISPLAY);
19487 this.el.select('>button.close').on('click', this.hide, this);
19493 if (!this.rendered) {
19499 this.fireEvent('show', this);
19505 if (!this.rendered) {
19511 this.fireEvent('hide', this);
19514 update : function()
19516 // var e = this.el.dom.firstChild;
19518 // if(this.closable){
19519 // e = e.nextSibling;
19522 // e.data = this.html || '';
19524 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19540 * @class Roo.bootstrap.Graph
19541 * @extends Roo.bootstrap.Component
19542 * Bootstrap Graph class
19546 @cfg {String} graphtype bar | vbar | pie
19547 @cfg {number} g_x coodinator | centre x (pie)
19548 @cfg {number} g_y coodinator | centre y (pie)
19549 @cfg {number} g_r radius (pie)
19550 @cfg {number} g_height height of the chart (respected by all elements in the set)
19551 @cfg {number} g_width width of the chart (respected by all elements in the set)
19552 @cfg {Object} title The title of the chart
19555 -opts (object) options for the chart
19557 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19558 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19560 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.
19561 o stacked (boolean) whether or not to tread values as in a stacked bar chart
19563 o stretch (boolean)
19565 -opts (object) options for the pie
19568 o startAngle (number)
19569 o endAngle (number)
19573 * Create a new Input
19574 * @param {Object} config The config object
19577 Roo.bootstrap.Graph = function(config){
19578 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19584 * The img click event for the img.
19585 * @param {Roo.EventObject} e
19591 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
19602 //g_colors: this.colors,
19609 getAutoCreate : function(){
19620 onRender : function(ct,position){
19621 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19622 this.raphael = Raphael(this.el.dom);
19624 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19625 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19626 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19627 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19629 r.text(160, 10, "Single Series Chart").attr(txtattr);
19630 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19631 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19632 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19634 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19635 r.barchart(330, 10, 300, 220, data1);
19636 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19637 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19640 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19641 // r.barchart(30, 30, 560, 250, xdata, {
19642 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19643 // axis : "0 0 1 1",
19644 // axisxlabels : xdata
19645 // //yvalues : cols,
19648 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19650 // this.load(null,xdata,{
19651 // axis : "0 0 1 1",
19652 // axisxlabels : xdata
19657 load : function(graphtype,xdata,opts){
19658 this.raphael.clear();
19660 graphtype = this.graphtype;
19665 var r = this.raphael,
19666 fin = function () {
19667 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19669 fout = function () {
19670 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19672 pfin = function() {
19673 this.sector.stop();
19674 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19677 this.label[0].stop();
19678 this.label[0].attr({ r: 7.5 });
19679 this.label[1].attr({ "font-weight": 800 });
19682 pfout = function() {
19683 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19686 this.label[0].animate({ r: 5 }, 500, "bounce");
19687 this.label[1].attr({ "font-weight": 400 });
19693 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19696 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19699 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
19700 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19702 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19709 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19714 setTitle: function(o)
19719 initEvents: function() {
19722 this.el.on('click', this.onClick, this);
19726 onClick : function(e)
19728 Roo.log('img onclick');
19729 this.fireEvent('click', this, e);
19741 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19744 * @class Roo.bootstrap.dash.NumberBox
19745 * @extends Roo.bootstrap.Component
19746 * Bootstrap NumberBox class
19747 * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19748 * @cfg {String} headline Box headline
19749 * @cfg {String} content Box content
19750 * @cfg {String} icon Box icon
19751 * @cfg {String} footer Footer text
19752 * @cfg {String} fhref Footer href
19755 * Create a new NumberBox
19756 * @param {Object} config The config object
19760 Roo.bootstrap.dash.NumberBox = function(config){
19761 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19765 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
19775 getAutoCreate : function(){
19779 cls : 'small-box bg-' + this.bgcolor,
19787 cls : 'roo-headline',
19788 html : this.headline
19792 cls : 'roo-content',
19793 html : this.content
19807 cls : 'ion ' + this.icon
19816 cls : 'small-box-footer',
19817 href : this.fhref || '#',
19821 cfg.cn.push(footer);
19828 onRender : function(ct,position){
19829 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19836 setHeadline: function (value)
19838 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19841 setFooter: function (value, href)
19843 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19846 this.el.select('a.small-box-footer',true).first().attr('href', href);
19851 setContent: function (value)
19853 this.el.select('.roo-content',true).first().dom.innerHTML = value;
19856 initEvents: function()
19870 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19873 * @class Roo.bootstrap.dash.TabBox
19874 * @extends Roo.bootstrap.Component
19875 * Bootstrap TabBox class
19876 * @cfg {String} title Title of the TabBox
19877 * @cfg {String} icon Icon of the TabBox
19878 * @cfg {Boolean} showtabs (true|false) show the tabs default true
19881 * Create a new TabBox
19882 * @param {Object} config The config object
19886 Roo.bootstrap.dash.TabBox = function(config){
19887 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19892 * When a pane is added
19893 * @param {Roo.bootstrap.dash.TabPane} pane
19900 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
19906 getChildContainer : function()
19908 return this.el.select('.tab-content', true).first();
19911 getAutoCreate : function(){
19915 cls: 'pull-left header',
19923 cls: 'fa ' + this.icon
19930 cls: 'nav-tabs-custom',
19934 cls: 'nav nav-tabs pull-right',
19941 cls: 'tab-content no-padding',
19949 initEvents : function()
19951 //Roo.log('add add pane handler');
19952 this.on('addpane', this.onAddPane, this);
19955 * Updates the box title
19956 * @param {String} html to set the title to.
19958 setTitle : function(value)
19960 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19962 onAddPane : function(pane)
19964 //Roo.log('addpane');
19966 // tabs are rendere left to right..
19967 if(!this.showtabs){
19971 var ctr = this.el.select('.nav-tabs', true).first();
19974 var existing = ctr.select('.nav-tab',true);
19975 var qty = existing.getCount();;
19978 var tab = ctr.createChild({
19980 cls : 'nav-tab' + (qty ? '' : ' active'),
19988 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19991 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19993 pane.el.addClass('active');
19998 onTabClick : function(ev,un,ob,pane)
20000 //Roo.log('tab - prev default');
20001 ev.preventDefault();
20004 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20005 pane.tab.addClass('active');
20006 //Roo.log(pane.title);
20007 this.getChildContainer().select('.tab-pane',true).removeClass('active');
20008 // technically we should have a deactivate event.. but maybe add later.
20009 // and it should not de-activate the selected tab...
20011 pane.el.addClass('active');
20012 pane.fireEvent('activate');
20027 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20029 * @class Roo.bootstrap.TabPane
20030 * @extends Roo.bootstrap.Component
20031 * Bootstrap TabPane class
20032 * @cfg {Boolean} active (false | true) Default false
20033 * @cfg {String} title title of panel
20037 * Create a new TabPane
20038 * @param {Object} config The config object
20041 Roo.bootstrap.dash.TabPane = function(config){
20042 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20048 * When a pane is activated
20049 * @param {Roo.bootstrap.dash.TabPane} pane
20056 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
20061 // the tabBox that this is attached to.
20064 getAutoCreate : function()
20072 cfg.cls += ' active';
20077 initEvents : function()
20079 //Roo.log('trigger add pane handler');
20080 this.parent().fireEvent('addpane', this)
20084 * Updates the tab title
20085 * @param {String} html to set the title to.
20087 setTitle: function(str)
20093 this.tab.select('a', true).first().dom.innerHTML = str;
20110 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20113 * @class Roo.bootstrap.menu.Menu
20114 * @extends Roo.bootstrap.Component
20115 * Bootstrap Menu class - container for Menu
20116 * @cfg {String} html Text of the menu
20117 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20118 * @cfg {String} icon Font awesome icon
20119 * @cfg {String} pos Menu align to (top | bottom) default bottom
20123 * Create a new Menu
20124 * @param {Object} config The config object
20128 Roo.bootstrap.menu.Menu = function(config){
20129 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20133 * @event beforeshow
20134 * Fires before this menu is displayed
20135 * @param {Roo.bootstrap.menu.Menu} this
20139 * @event beforehide
20140 * Fires before this menu is hidden
20141 * @param {Roo.bootstrap.menu.Menu} this
20146 * Fires after this menu is displayed
20147 * @param {Roo.bootstrap.menu.Menu} this
20152 * Fires after this menu is hidden
20153 * @param {Roo.bootstrap.menu.Menu} this
20158 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20159 * @param {Roo.bootstrap.menu.Menu} this
20160 * @param {Roo.EventObject} e
20167 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
20171 weight : 'default',
20176 getChildContainer : function() {
20177 if(this.isSubMenu){
20181 return this.el.select('ul.dropdown-menu', true).first();
20184 getAutoCreate : function()
20189 cls : 'roo-menu-text',
20197 cls : 'fa ' + this.icon
20208 cls : 'dropdown-button btn btn-' + this.weight,
20213 cls : 'dropdown-toggle btn btn-' + this.weight,
20223 cls : 'dropdown-menu'
20229 if(this.pos == 'top'){
20230 cfg.cls += ' dropup';
20233 if(this.isSubMenu){
20236 cls : 'dropdown-menu'
20243 onRender : function(ct, position)
20245 this.isSubMenu = ct.hasClass('dropdown-submenu');
20247 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20250 initEvents : function()
20252 if(this.isSubMenu){
20256 this.hidden = true;
20258 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20259 this.triggerEl.on('click', this.onTriggerPress, this);
20261 this.buttonEl = this.el.select('button.dropdown-button', true).first();
20262 this.buttonEl.on('click', this.onClick, this);
20268 if(this.isSubMenu){
20272 return this.el.select('ul.dropdown-menu', true).first();
20275 onClick : function(e)
20277 this.fireEvent("click", this, e);
20280 onTriggerPress : function(e)
20282 if (this.isVisible()) {
20289 isVisible : function(){
20290 return !this.hidden;
20295 this.fireEvent("beforeshow", this);
20297 this.hidden = false;
20298 this.el.addClass('open');
20300 Roo.get(document).on("mouseup", this.onMouseUp, this);
20302 this.fireEvent("show", this);
20309 this.fireEvent("beforehide", this);
20311 this.hidden = true;
20312 this.el.removeClass('open');
20314 Roo.get(document).un("mouseup", this.onMouseUp);
20316 this.fireEvent("hide", this);
20319 onMouseUp : function()
20333 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20336 * @class Roo.bootstrap.menu.Item
20337 * @extends Roo.bootstrap.Component
20338 * Bootstrap MenuItem class
20339 * @cfg {Boolean} submenu (true | false) default false
20340 * @cfg {String} html text of the item
20341 * @cfg {String} href the link
20342 * @cfg {Boolean} disable (true | false) default false
20343 * @cfg {Boolean} preventDefault (true | false) default true
20344 * @cfg {String} icon Font awesome icon
20345 * @cfg {String} pos Submenu align to (left | right) default right
20349 * Create a new Item
20350 * @param {Object} config The config object
20354 Roo.bootstrap.menu.Item = function(config){
20355 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20359 * Fires when the mouse is hovering over this menu
20360 * @param {Roo.bootstrap.menu.Item} this
20361 * @param {Roo.EventObject} e
20366 * Fires when the mouse exits this menu
20367 * @param {Roo.bootstrap.menu.Item} this
20368 * @param {Roo.EventObject} e
20374 * The raw click event for the entire grid.
20375 * @param {Roo.EventObject} e
20381 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
20386 preventDefault: true,
20391 getAutoCreate : function()
20396 cls : 'roo-menu-item-text',
20404 cls : 'fa ' + this.icon
20413 href : this.href || '#',
20420 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20424 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20426 if(this.pos == 'left'){
20427 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20434 initEvents : function()
20436 this.el.on('mouseover', this.onMouseOver, this);
20437 this.el.on('mouseout', this.onMouseOut, this);
20439 this.el.select('a', true).first().on('click', this.onClick, this);
20443 onClick : function(e)
20445 if(this.preventDefault){
20446 e.preventDefault();
20449 this.fireEvent("click", this, e);
20452 onMouseOver : function(e)
20454 if(this.submenu && this.pos == 'left'){
20455 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20458 this.fireEvent("mouseover", this, e);
20461 onMouseOut : function(e)
20463 this.fireEvent("mouseout", this, e);
20475 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20478 * @class Roo.bootstrap.menu.Separator
20479 * @extends Roo.bootstrap.Component
20480 * Bootstrap Separator class
20483 * Create a new Separator
20484 * @param {Object} config The config object
20488 Roo.bootstrap.menu.Separator = function(config){
20489 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20492 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
20494 getAutoCreate : function(){