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);
1718 Roo.log('pass click event');
1722 this.fireEvent("click", this, t, e);
1726 onMouseOver : function(e){
1727 var t = this.findTargetItem(e);
1730 // if(t.canActivate && !t.disabled){
1731 // this.setActiveItem(t, true);
1735 this.fireEvent("mouseover", this, e, t);
1737 isVisible : function(){
1738 return !this.hidden;
1740 onMouseOut : function(e){
1741 var t = this.findTargetItem(e);
1744 // if(t == this.activeItem && t.shouldDeactivate(e)){
1745 // this.activeItem.deactivate();
1746 // delete this.activeItem;
1749 this.fireEvent("mouseout", this, e, t);
1754 * Displays this menu relative to another element
1755 * @param {String/HTMLElement/Roo.Element} element The element to align to
1756 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1757 * the element (defaults to this.defaultAlign)
1758 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1760 show : function(el, pos, parentMenu){
1761 this.parentMenu = parentMenu;
1765 this.fireEvent("beforeshow", this);
1766 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1769 * Displays this menu at a specific xy position
1770 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1771 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1773 showAt : function(xy, parentMenu, /* private: */_e){
1774 this.parentMenu = parentMenu;
1779 this.fireEvent("beforeshow", this);
1781 //xy = this.el.adjustForConstraints(xy);
1783 //this.el.setXY(xy);
1785 this.hideMenuItems();
1786 this.hidden = false;
1787 this.triggerEl.addClass('open');
1789 this.fireEvent("show", this);
1795 this.doFocus.defer(50, this);
1799 doFocus : function(){
1801 this.focusEl.focus();
1806 * Hides this menu and optionally all parent menus
1807 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1809 hide : function(deep){
1811 this.hideMenuItems();
1812 if(this.el && this.isVisible()){
1813 this.fireEvent("beforehide", this);
1814 if(this.activeItem){
1815 this.activeItem.deactivate();
1816 this.activeItem = null;
1818 this.triggerEl.removeClass('open');;
1820 this.fireEvent("hide", this);
1822 if(deep === true && this.parentMenu){
1823 this.parentMenu.hide(true);
1827 onTriggerPress : function(e)
1830 Roo.log('trigger press');
1831 //Roo.log(e.getTarget());
1832 // Roo.log(this.triggerEl.dom);
1833 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1836 if (this.isVisible()) {
1840 this.show(this.triggerEl, false, false);
1849 hideMenuItems : function()
1851 //$(backdrop).remove()
1852 Roo.select('.open',true).each(function(aa) {
1854 aa.removeClass('open');
1855 //var parent = getParent($(this))
1856 //var relatedTarget = { relatedTarget: this }
1858 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1859 //if (e.isDefaultPrevented()) return
1860 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1863 addxtypeChild : function (tree, cntr) {
1864 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1866 this.menuitems.add(comp);
1887 * @class Roo.bootstrap.MenuItem
1888 * @extends Roo.bootstrap.Component
1889 * Bootstrap MenuItem class
1890 * @cfg {String} html the menu label
1891 * @cfg {String} href the link
1892 * @cfg {Boolean} preventDefault (true | false) default true
1893 * @cfg {Boolean} isContainer (true | false) default false
1897 * Create a new MenuItem
1898 * @param {Object} config The config object
1902 Roo.bootstrap.MenuItem = function(config){
1903 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1908 * The raw click event for the entire grid.
1909 * @param {Roo.EventObject} e
1915 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
1919 preventDefault: true,
1920 isContainer : false,
1922 getAutoCreate : function(){
1924 if(this.isContainer){
1927 cls: 'dropdown-menu-item'
1933 cls: 'dropdown-menu-item',
1942 if (this.parent().type == 'treeview') {
1943 cfg.cls = 'treeview-menu';
1946 cfg.cn[0].href = this.href || cfg.cn[0].href ;
1947 cfg.cn[0].html = this.html || cfg.cn[0].html ;
1951 initEvents: function() {
1953 //this.el.select('a').on('click', this.onClick, this);
1956 onClick : function(e)
1958 Roo.log('item on click ');
1959 //if(this.preventDefault){
1960 // e.preventDefault();
1962 //this.parent().hideMenuItems();
1964 this.fireEvent('click', this, e);
1983 * @class Roo.bootstrap.MenuSeparator
1984 * @extends Roo.bootstrap.Component
1985 * Bootstrap MenuSeparator class
1988 * Create a new MenuItem
1989 * @param {Object} config The config object
1993 Roo.bootstrap.MenuSeparator = function(config){
1994 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1997 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
1999 getAutoCreate : function(){
2014 <div class="modal fade">
2015 <div class="modal-dialog">
2016 <div class="modal-content">
2017 <div class="modal-header">
2018 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
2019 <h4 class="modal-title">Modal title</h4>
2021 <div class="modal-body">
2022 <p>One fine body…</p>
2024 <div class="modal-footer">
2025 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2026 <button type="button" class="btn btn-primary">Save changes</button>
2028 </div><!-- /.modal-content -->
2029 </div><!-- /.modal-dialog -->
2030 </div><!-- /.modal -->
2040 * @class Roo.bootstrap.Modal
2041 * @extends Roo.bootstrap.Component
2042 * Bootstrap Modal class
2043 * @cfg {String} title Title of dialog
2044 * @cfg {Boolean} specificTitle (true|false) default false
2045 * @cfg {Array} buttons Array of buttons or standard button set..
2046 * @cfg {String} buttonPosition (left|right|center) default right
2047 * @cfg {Boolean} animate (true | false) default true
2050 * Create a new Modal Dialog
2051 * @param {Object} config The config object
2054 Roo.bootstrap.Modal = function(config){
2055 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2060 * The raw btnclick event for the button
2061 * @param {Roo.EventObject} e
2065 this.buttons = this.buttons || [];
2068 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2070 title : 'test dialog',
2077 specificTitle: false,
2079 buttonPosition: 'right',
2083 onRender : function(ct, position)
2085 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2088 var cfg = Roo.apply({}, this.getAutoCreate());
2091 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2093 //if (!cfg.name.length) {
2097 cfg.cls += ' ' + this.cls;
2100 cfg.style = this.style;
2102 this.el = Roo.get(document.body).createChild(cfg, position);
2104 //var type = this.el.dom.type;
2106 if(this.tabIndex !== undefined){
2107 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2112 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2113 this.maskEl.enableDisplayMode("block");
2115 //this.el.addClass("x-dlg-modal");
2117 if (this.buttons.length) {
2118 Roo.each(this.buttons, function(bb) {
2119 b = Roo.apply({}, bb);
2120 b.xns = b.xns || Roo.bootstrap;
2121 b.xtype = b.xtype || 'Button';
2122 if (typeof(b.listeners) == 'undefined') {
2123 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2126 var btn = Roo.factory(b);
2128 btn.onRender(this.el.select('.modal-footer div').first());
2132 // render the children.
2135 if(typeof(this.items) != 'undefined'){
2136 var items = this.items;
2139 for(var i =0;i < items.length;i++) {
2140 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2144 this.items = nitems;
2146 this.body = this.el.select('.modal-body',true).first();
2147 this.close = this.el.select('.modal-header .close', true).first();
2148 this.footer = this.el.select('.modal-footer',true).first();
2150 //this.el.addClass([this.fieldClass, this.cls]);
2153 getAutoCreate : function(){
2158 html : this.html || ''
2163 cls : 'modal-title',
2167 if(this.specificTitle){
2173 style : 'display: none',
2176 cls: "modal-dialog",
2179 cls : "modal-content",
2182 cls : 'modal-header',
2194 cls : 'modal-footer',
2198 cls: 'btn-' + this.buttonPosition
2215 modal.cls += ' fade';
2221 getChildContainer : function() {
2223 return this.el.select('.modal-body',true).first();
2226 getButtonContainer : function() {
2227 return this.el.select('.modal-footer div',true).first();
2230 initEvents : function()
2232 this.el.select('.modal-header .close').on('click', this.hide, this);
2234 // this.addxtype(this);
2238 if (!this.rendered) {
2242 this.el.setStyle('display', 'block');
2246 (function(){ _this.el.addClass('in'); }).defer(50);
2248 this.el.addClass('in');
2251 Roo.get(document.body).addClass("x-body-masked");
2252 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2254 this.el.setStyle('zIndex', '10001');
2255 this.fireEvent('show', this);
2262 Roo.get(document.body).removeClass("x-body-masked");
2263 this.el.removeClass('in');
2267 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2269 this.el.setStyle('display', 'none');
2272 this.fireEvent('hide', this);
2275 addButton : function(str, cb)
2279 var b = Roo.apply({}, { html : str } );
2280 b.xns = b.xns || Roo.bootstrap;
2281 b.xtype = b.xtype || 'Button';
2282 if (typeof(b.listeners) == 'undefined') {
2283 b.listeners = { click : cb.createDelegate(this) };
2286 var btn = Roo.factory(b);
2288 btn.onRender(this.el.select('.modal-footer div').first());
2294 setDefaultButton : function(btn)
2296 //this.el.select('.modal-footer').()
2298 resizeTo: function(w,h)
2302 setContentSize : function(w, h)
2306 onButtonClick: function(btn,e)
2309 this.fireEvent('btnclick', btn.name, e);
2311 setTitle: function(str) {
2312 this.el.select('.modal-title',true).first().dom.innerHTML = str;
2318 Roo.apply(Roo.bootstrap.Modal, {
2320 * Button config that displays a single OK button
2329 * Button config that displays Yes and No buttons
2345 * Button config that displays OK and Cancel buttons
2360 * Button config that displays Yes, No and Cancel buttons
2382 * messagebox - can be used as a replace
2386 * @class Roo.MessageBox
2387 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2391 Roo.Msg.alert('Status', 'Changes saved successfully.');
2393 // Prompt for user data:
2394 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2396 // process text value...
2400 // Show a dialog using config options:
2402 title:'Save Changes?',
2403 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2404 buttons: Roo.Msg.YESNOCANCEL,
2411 Roo.bootstrap.MessageBox = function(){
2412 var dlg, opt, mask, waitTimer;
2413 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2414 var buttons, activeTextEl, bwidth;
2418 var handleButton = function(button){
2420 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2424 var handleHide = function(){
2426 dlg.el.removeClass(opt.cls);
2429 // Roo.TaskMgr.stop(waitTimer);
2430 // waitTimer = null;
2435 var updateButtons = function(b){
2438 buttons["ok"].hide();
2439 buttons["cancel"].hide();
2440 buttons["yes"].hide();
2441 buttons["no"].hide();
2442 //dlg.footer.dom.style.display = 'none';
2445 dlg.footer.dom.style.display = '';
2446 for(var k in buttons){
2447 if(typeof buttons[k] != "function"){
2450 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2451 width += buttons[k].el.getWidth()+15;
2461 var handleEsc = function(d, k, e){
2462 if(opt && opt.closable !== false){
2472 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2473 * @return {Roo.BasicDialog} The BasicDialog element
2475 getDialog : function(){
2477 dlg = new Roo.bootstrap.Modal( {
2480 //constraintoviewport:false,
2482 //collapsible : false,
2487 //buttonAlign:"center",
2488 closeClick : function(){
2489 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2492 handleButton("cancel");
2497 dlg.on("hide", handleHide);
2499 //dlg.addKeyListener(27, handleEsc);
2501 this.buttons = buttons;
2502 var bt = this.buttonText;
2503 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2504 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2505 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2506 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2508 bodyEl = dlg.body.createChild({
2510 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2511 '<textarea class="roo-mb-textarea"></textarea>' +
2512 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2514 msgEl = bodyEl.dom.firstChild;
2515 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2516 textboxEl.enableDisplayMode();
2517 textboxEl.addKeyListener([10,13], function(){
2518 if(dlg.isVisible() && opt && opt.buttons){
2521 }else if(opt.buttons.yes){
2522 handleButton("yes");
2526 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2527 textareaEl.enableDisplayMode();
2528 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2529 progressEl.enableDisplayMode();
2530 var pf = progressEl.dom.firstChild;
2532 pp = Roo.get(pf.firstChild);
2533 pp.setHeight(pf.offsetHeight);
2541 * Updates the message box body text
2542 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2543 * the XHTML-compliant non-breaking space character '&#160;')
2544 * @return {Roo.MessageBox} This message box
2546 updateText : function(text){
2547 if(!dlg.isVisible() && !opt.width){
2548 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2550 msgEl.innerHTML = text || ' ';
2552 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2553 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2555 Math.min(opt.width || cw , this.maxWidth),
2556 Math.max(opt.minWidth || this.minWidth, bwidth)
2559 activeTextEl.setWidth(w);
2561 if(dlg.isVisible()){
2562 dlg.fixedcenter = false;
2564 // to big, make it scroll. = But as usual stupid IE does not support
2567 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2568 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2569 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2571 bodyEl.dom.style.height = '';
2572 bodyEl.dom.style.overflowY = '';
2575 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2577 bodyEl.dom.style.overflowX = '';
2580 dlg.setContentSize(w, bodyEl.getHeight());
2581 if(dlg.isVisible()){
2582 dlg.fixedcenter = true;
2588 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2589 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2590 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2591 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2592 * @return {Roo.MessageBox} This message box
2594 updateProgress : function(value, text){
2596 this.updateText(text);
2598 if (pp) { // weird bug on my firefox - for some reason this is not defined
2599 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2605 * Returns true if the message box is currently displayed
2606 * @return {Boolean} True if the message box is visible, else false
2608 isVisible : function(){
2609 return dlg && dlg.isVisible();
2613 * Hides the message box if it is displayed
2616 if(this.isVisible()){
2622 * Displays a new message box, or reinitializes an existing message box, based on the config options
2623 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2624 * The following config object properties are supported:
2626 Property Type Description
2627 ---------- --------------- ------------------------------------------------------------------------------------
2628 animEl String/Element An id or Element from which the message box should animate as it opens and
2629 closes (defaults to undefined)
2630 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2631 cancel:'Bar'}), or false to not show any buttons (defaults to false)
2632 closable Boolean False to hide the top-right close button (defaults to true). Note that
2633 progress and wait dialogs will ignore this property and always hide the
2634 close button as they can only be closed programmatically.
2635 cls String A custom CSS class to apply to the message box element
2636 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
2637 displayed (defaults to 75)
2638 fn Function A callback function to execute after closing the dialog. The arguments to the
2639 function will be btn (the name of the button that was clicked, if applicable,
2640 e.g. "ok"), and text (the value of the active text field, if applicable).
2641 Progress and wait dialogs will ignore this option since they do not respond to
2642 user actions and can only be closed programmatically, so any required function
2643 should be called by the same code after it closes the dialog.
2644 icon String A CSS class that provides a background image to be used as an icon for
2645 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2646 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
2647 minWidth Number The minimum width in pixels of the message box (defaults to 100)
2648 modal Boolean False to allow user interaction with the page while the message box is
2649 displayed (defaults to true)
2650 msg String A string that will replace the existing message box body text (defaults
2651 to the XHTML-compliant non-breaking space character ' ')
2652 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
2653 progress Boolean True to display a progress bar (defaults to false)
2654 progressText String The text to display inside the progress bar if progress = true (defaults to '')
2655 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
2656 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
2657 title String The title text
2658 value String The string value to set into the active textbox element if displayed
2659 wait Boolean True to display a progress bar (defaults to false)
2660 width Number The width of the dialog in pixels
2667 msg: 'Please enter your address:',
2669 buttons: Roo.MessageBox.OKCANCEL,
2672 animEl: 'addAddressBtn'
2675 * @param {Object} config Configuration options
2676 * @return {Roo.MessageBox} This message box
2678 show : function(options)
2681 // this causes nightmares if you show one dialog after another
2682 // especially on callbacks..
2684 if(this.isVisible()){
2687 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2688 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
2689 Roo.log("New Dialog Message:" + options.msg )
2690 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2691 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2694 var d = this.getDialog();
2696 d.setTitle(opt.title || " ");
2697 d.close.setDisplayed(opt.closable !== false);
2698 activeTextEl = textboxEl;
2699 opt.prompt = opt.prompt || (opt.multiline ? true : false);
2704 textareaEl.setHeight(typeof opt.multiline == "number" ?
2705 opt.multiline : this.defaultTextHeight);
2706 activeTextEl = textareaEl;
2715 progressEl.setDisplayed(opt.progress === true);
2716 this.updateProgress(0);
2717 activeTextEl.dom.value = opt.value || "";
2719 dlg.setDefaultButton(activeTextEl);
2721 var bs = opt.buttons;
2725 }else if(bs && bs.yes){
2726 db = buttons["yes"];
2728 dlg.setDefaultButton(db);
2730 bwidth = updateButtons(opt.buttons);
2731 this.updateText(opt.msg);
2733 d.el.addClass(opt.cls);
2735 d.proxyDrag = opt.proxyDrag === true;
2736 d.modal = opt.modal !== false;
2737 d.mask = opt.modal !== false ? mask : false;
2739 // force it to the end of the z-index stack so it gets a cursor in FF
2740 document.body.appendChild(dlg.el.dom);
2741 d.animateTarget = null;
2742 d.show(options.animEl);
2748 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
2749 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2750 * and closing the message box when the process is complete.
2751 * @param {String} title The title bar text
2752 * @param {String} msg The message box body text
2753 * @return {Roo.MessageBox} This message box
2755 progress : function(title, msg){
2762 minWidth: this.minProgressWidth,
2769 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2770 * If a callback function is passed it will be called after the user clicks the button, and the
2771 * id of the button that was clicked will be passed as the only parameter to the callback
2772 * (could also be the top-right close button).
2773 * @param {String} title The title bar text
2774 * @param {String} msg The message box body text
2775 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2776 * @param {Object} scope (optional) The scope of the callback function
2777 * @return {Roo.MessageBox} This message box
2779 alert : function(title, msg, fn, scope){
2792 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
2793 * interaction while waiting for a long-running process to complete that does not have defined intervals.
2794 * You are responsible for closing the message box when the process is complete.
2795 * @param {String} msg The message box body text
2796 * @param {String} title (optional) The title bar text
2797 * @return {Roo.MessageBox} This message box
2799 wait : function(msg, title){
2810 waitTimer = Roo.TaskMgr.start({
2812 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2820 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2821 * If a callback function is passed it will be called after the user clicks either button, and the id of the
2822 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2823 * @param {String} title The title bar text
2824 * @param {String} msg The message box body text
2825 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2826 * @param {Object} scope (optional) The scope of the callback function
2827 * @return {Roo.MessageBox} This message box
2829 confirm : function(title, msg, fn, scope){
2833 buttons: this.YESNO,
2842 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2843 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
2844 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2845 * (could also be the top-right close button) and the text that was entered will be passed as the two
2846 * parameters to the callback.
2847 * @param {String} title The title bar text
2848 * @param {String} msg The message box body text
2849 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2850 * @param {Object} scope (optional) The scope of the callback function
2851 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2852 * property, or the height in pixels to create the textbox (defaults to false / single-line)
2853 * @return {Roo.MessageBox} This message box
2855 prompt : function(title, msg, fn, scope, multiline){
2859 buttons: this.OKCANCEL,
2864 multiline: multiline,
2871 * Button config that displays a single OK button
2876 * Button config that displays Yes and No buttons
2879 YESNO : {yes:true, no:true},
2881 * Button config that displays OK and Cancel buttons
2884 OKCANCEL : {ok:true, cancel:true},
2886 * Button config that displays Yes, No and Cancel buttons
2889 YESNOCANCEL : {yes:true, no:true, cancel:true},
2892 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2895 defaultTextHeight : 75,
2897 * The maximum width in pixels of the message box (defaults to 600)
2902 * The minimum width in pixels of the message box (defaults to 100)
2907 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
2908 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2911 minProgressWidth : 250,
2913 * An object containing the default button text strings that can be overriden for localized language support.
2914 * Supported properties are: ok, cancel, yes and no.
2915 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2928 * Shorthand for {@link Roo.MessageBox}
2930 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox
2931 Roo.Msg = Roo.Msg || Roo.MessageBox;
2940 * @class Roo.bootstrap.Navbar
2941 * @extends Roo.bootstrap.Component
2942 * Bootstrap Navbar class
2945 * Create a new Navbar
2946 * @param {Object} config The config object
2950 Roo.bootstrap.Navbar = function(config){
2951 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2955 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
2964 getAutoCreate : function(){
2967 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2971 initEvents :function ()
2973 //Roo.log(this.el.select('.navbar-toggle',true));
2974 this.el.select('.navbar-toggle',true).on('click', function() {
2975 // Roo.log('click');
2976 this.el.select('.navbar-collapse',true).toggleClass('in');
2984 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2986 var size = this.el.getSize();
2987 this.maskEl.setSize(size.width, size.height);
2988 this.maskEl.enableDisplayMode("block");
2997 getChildContainer : function()
2999 if (this.el.select('.collapse').getCount()) {
3000 return this.el.select('.collapse',true).first();
3033 * @class Roo.bootstrap.NavSimplebar
3034 * @extends Roo.bootstrap.Navbar
3035 * Bootstrap Sidebar class
3037 * @cfg {Boolean} inverse is inverted color
3039 * @cfg {String} type (nav | pills | tabs)
3040 * @cfg {Boolean} arrangement stacked | justified
3041 * @cfg {String} align (left | right) alignment
3043 * @cfg {Boolean} main (true|false) main nav bar? default false
3044 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3046 * @cfg {String} tag (header|footer|nav|div) default is nav
3052 * Create a new Sidebar
3053 * @param {Object} config The config object
3057 Roo.bootstrap.NavSimplebar = function(config){
3058 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3061 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3077 getAutoCreate : function(){
3081 tag : this.tag || 'div',
3094 this.type = this.type || 'nav';
3095 if (['tabs','pills'].indexOf(this.type)!==-1) {
3096 cfg.cn[0].cls += ' nav-' + this.type
3100 if (this.type!=='nav') {
3101 Roo.log('nav type must be nav/tabs/pills')
3103 cfg.cn[0].cls += ' navbar-nav'
3109 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3110 cfg.cn[0].cls += ' nav-' + this.arrangement;
3114 if (this.align === 'right') {
3115 cfg.cn[0].cls += ' navbar-right';
3119 cfg.cls += ' navbar-inverse';
3146 * @class Roo.bootstrap.NavHeaderbar
3147 * @extends Roo.bootstrap.NavSimplebar
3148 * Bootstrap Sidebar class
3150 * @cfg {String} brand what is brand
3151 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3152 * @cfg {String} brand_href href of the brand
3153 * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3154 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3157 * Create a new Sidebar
3158 * @param {Object} config The config object
3162 Roo.bootstrap.NavHeaderbar = function(config){
3163 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3166 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3174 getAutoCreate : function(){
3177 tag: this.nav || 'nav',
3186 cls: 'navbar-header',
3191 cls: 'navbar-toggle',
3192 'data-toggle': 'collapse',
3197 html: 'Toggle navigation'
3219 cls: 'collapse navbar-collapse',
3223 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3225 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3226 cfg.cls += ' navbar-' + this.position;
3228 // tag can override this..
3230 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3233 if (this.brand !== '') {
3236 href: this.brand_href ? this.brand_href : '#',
3237 cls: 'navbar-brand',
3245 cfg.cls += ' main-nav';
3253 initEvents : function()
3255 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3257 if (this.autohide) {
3262 Roo.get(document).on('scroll',function(e) {
3263 var ns = Roo.get(document).getScroll().top;
3264 var os = prevScroll;
3268 ft.removeClass('slideDown');
3269 ft.addClass('slideUp');
3272 ft.removeClass('slideUp');
3273 ft.addClass('slideDown');
3297 * @class Roo.bootstrap.NavSidebar
3298 * @extends Roo.bootstrap.Navbar
3299 * Bootstrap Sidebar class
3302 * Create a new Sidebar
3303 * @param {Object} config The config object
3307 Roo.bootstrap.NavSidebar = function(config){
3308 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3311 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3313 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3315 getAutoCreate : function(){
3320 cls: 'sidebar sidebar-nav'
3342 * @class Roo.bootstrap.NavGroup
3343 * @extends Roo.bootstrap.Component
3344 * Bootstrap NavGroup class
3345 * @cfg {String} align left | right
3346 * @cfg {Boolean} inverse false | true
3347 * @cfg {String} type (nav|pills|tab) default nav
3348 * @cfg {String} navId - reference Id for navbar.
3352 * Create a new nav group
3353 * @param {Object} config The config object
3356 Roo.bootstrap.NavGroup = function(config){
3357 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3360 Roo.bootstrap.NavGroup.register(this);
3364 * Fires when the active item changes
3365 * @param {Roo.bootstrap.NavGroup} this
3366 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3367 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3374 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3385 getAutoCreate : function()
3387 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3394 if (['tabs','pills'].indexOf(this.type)!==-1) {
3395 cfg.cls += ' nav-' + this.type
3397 if (this.type!=='nav') {
3398 Roo.log('nav type must be nav/tabs/pills')
3400 cfg.cls += ' navbar-nav'
3403 if (this.parent().sidebar) {
3406 cls: 'dashboard-menu sidebar-menu'
3412 if (this.form === true) {
3418 if (this.align === 'right') {
3419 cfg.cls += ' navbar-right';
3421 cfg.cls += ' navbar-left';
3425 if (this.align === 'right') {
3426 cfg.cls += ' navbar-right';
3430 cfg.cls += ' navbar-inverse';
3438 * sets the active Navigation item
3439 * @param {Roo.bootstrap.NavItem} the new current navitem
3441 setActiveItem : function(item)
3444 Roo.each(this.navItems, function(v){
3449 v.setActive(false, true);
3456 item.setActive(true, true);
3457 this.fireEvent('changed', this, item, prev);
3462 * gets the active Navigation item
3463 * @return {Roo.bootstrap.NavItem} the current navitem
3465 getActive : function()
3469 Roo.each(this.navItems, function(v){
3480 indexOfNav : function()
3484 Roo.each(this.navItems, function(v,i){
3495 * adds a Navigation item
3496 * @param {Roo.bootstrap.NavItem} the navitem to add
3498 addItem : function(cfg)
3500 var cn = new Roo.bootstrap.NavItem(cfg);
3502 cn.parentId = this.id;
3503 cn.onRender(this.el, null);
3507 * register a Navigation item
3508 * @param {Roo.bootstrap.NavItem} the navitem to add
3510 register : function(item)
3512 this.navItems.push( item);
3513 item.navId = this.navId;
3518 * clear all the Navigation item
3521 clearAll : function()
3524 this.el.dom.innerHTML = '';
3527 getNavItem: function(tabId)
3530 Roo.each(this.navItems, function(e) {
3531 if (e.tabId == tabId) {
3541 setActiveNext : function()
3543 var i = this.indexOfNav(this.getActive());
3544 if (i > this.navItems.length) {
3547 this.setActiveItem(this.navItems[i+1]);
3549 setActivePrev : function()
3551 var i = this.indexOfNav(this.getActive());
3555 this.setActiveItem(this.navItems[i-1]);
3557 clearWasActive : function(except) {
3558 Roo.each(this.navItems, function(e) {
3559 if (e.tabId != except.tabId && e.was_active) {
3560 e.was_active = false;
3567 getWasActive : function ()
3570 Roo.each(this.navItems, function(e) {
3585 Roo.apply(Roo.bootstrap.NavGroup, {
3589 * register a Navigation Group
3590 * @param {Roo.bootstrap.NavGroup} the navgroup to add
3592 register : function(navgrp)
3594 this.groups[navgrp.navId] = navgrp;
3598 * fetch a Navigation Group based on the navigation ID
3599 * @param {string} the navgroup to add
3600 * @returns {Roo.bootstrap.NavGroup} the navgroup
3602 get: function(navId) {
3603 if (typeof(this.groups[navId]) == 'undefined') {
3605 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3607 return this.groups[navId] ;
3622 * @class Roo.bootstrap.NavItem
3623 * @extends Roo.bootstrap.Component
3624 * Bootstrap Navbar.NavItem class
3625 * @cfg {String} href link to
3626 * @cfg {String} html content of button
3627 * @cfg {String} badge text inside badge
3628 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3629 * @cfg {String} glyphicon name of glyphicon
3630 * @cfg {String} icon name of font awesome icon
3631 * @cfg {Boolean} active Is item active
3632 * @cfg {Boolean} disabled Is item disabled
3634 * @cfg {Boolean} preventDefault (true | false) default false
3635 * @cfg {String} tabId the tab that this item activates.
3636 * @cfg {String} tagtype (a|span) render as a href or span?
3639 * Create a new Navbar Item
3640 * @param {Object} config The config object
3642 Roo.bootstrap.NavItem = function(config){
3643 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3648 * The raw click event for the entire grid.
3649 * @param {Roo.EventObject} e
3654 * Fires when the active item active state changes
3655 * @param {Roo.bootstrap.NavItem} this
3656 * @param {boolean} state the new state
3664 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
3672 preventDefault : false,
3679 getAutoCreate : function(){
3687 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3689 if (this.disabled) {
3690 cfg.cls += ' disabled';
3693 if (this.href || this.html || this.glyphicon || this.icon) {
3697 href : this.href || "#",
3698 html: this.html || ''
3703 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3706 if(this.glyphicon) {
3707 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
3712 cfg.cn[0].html += " <span class='caret'></span>";
3716 if (this.badge !== '') {
3718 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3726 initEvents: function() {
3727 // Roo.log('init events?');
3728 // Roo.log(this.el.dom);
3729 if (typeof (this.menu) != 'undefined') {
3730 this.menu.parentType = this.xtype;
3731 this.menu.triggerEl = this.el;
3732 this.addxtype(Roo.apply({}, this.menu));
3736 this.el.select('a',true).on('click', this.onClick, this);
3737 // at this point parent should be available..
3738 this.parent().register(this);
3741 onClick : function(e)
3744 if(this.preventDefault){
3747 if (this.disabled) {
3751 var tg = Roo.bootstrap.TabGroup.get(this.navId);
3752 if (tg && tg.transition) {
3753 Roo.log("waiting for the transitionend");
3757 Roo.log("fire event clicked");
3758 if(this.fireEvent('click', this, e) === false){
3761 var p = this.parent();
3762 if (['tabs','pills'].indexOf(p.type)!==-1) {
3763 if (typeof(p.setActiveItem) !== 'undefined') {
3764 p.setActiveItem(this);
3767 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3768 if (p.parentType == 'NavHeaderbar' && !this.menu) {
3769 // remove the collapsed menu expand...
3770 p.parent().el.select('.navbar-collapse',true).removeClass('in');
3775 isActive: function () {
3778 setActive : function(state, fire, is_was_active)
3780 if (this.active && !state & this.navId) {
3781 this.was_active = true;
3782 var nv = Roo.bootstrap.NavGroup.get(this.navId);
3784 nv.clearWasActive(this);
3788 this.active = state;
3791 this.el.removeClass('active');
3792 } else if (!this.el.hasClass('active')) {
3793 this.el.addClass('active');
3796 this.fireEvent('changed', this, state);
3799 // show a panel if it's registered and related..
3801 if (!this.navId || !this.tabId || !state || is_was_active) {
3805 var tg = Roo.bootstrap.TabGroup.get(this.navId);
3809 var pan = tg.getPanelByName(this.tabId);
3813 // if we can not flip to new panel - go back to old nav highlight..
3814 if (false == tg.showPanel(pan)) {
3815 var nv = Roo.bootstrap.NavGroup.get(this.navId);
3817 var onav = nv.getWasActive();
3819 onav.setActive(true, false, true);
3828 // this should not be here...
3829 setDisabled : function(state)
3831 this.disabled = state;
3833 this.el.removeClass('disabled');
3834 } else if (!this.el.hasClass('disabled')) {
3835 this.el.addClass('disabled');
3848 * <span> icon </span>
3849 * <span> text </span>
3850 * <span>badge </span>
3854 * @class Roo.bootstrap.NavSidebarItem
3855 * @extends Roo.bootstrap.NavItem
3856 * Bootstrap Navbar.NavSidebarItem class
3858 * Create a new Navbar Button
3859 * @param {Object} config The config object
3861 Roo.bootstrap.NavSidebarItem = function(config){
3862 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3867 * The raw click event for the entire grid.
3868 * @param {Roo.EventObject} e
3873 * Fires when the active item active state changes
3874 * @param {Roo.bootstrap.NavSidebarItem} this
3875 * @param {boolean} state the new state
3883 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
3886 getAutoCreate : function(){
3891 href : this.href || '#',
3903 html : this.html || ''
3908 cfg.cls += ' active';
3912 if (this.glyphicon || this.icon) {
3913 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
3914 a.cn.push({ tag : 'i', cls : c }) ;
3919 if (this.badge !== '') {
3920 a.cn.push({ tag: 'span', cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge });
3924 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3925 a.cls += 'dropdown-toggle treeview' ;
3949 * @class Roo.bootstrap.Row
3950 * @extends Roo.bootstrap.Component
3951 * Bootstrap Row class (contains columns...)
3955 * @param {Object} config The config object
3958 Roo.bootstrap.Row = function(config){
3959 Roo.bootstrap.Row.superclass.constructor.call(this, config);
3962 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
3964 getAutoCreate : function(){
3983 * @class Roo.bootstrap.Element
3984 * @extends Roo.bootstrap.Component
3985 * Bootstrap Element class
3986 * @cfg {String} html contents of the element
3987 * @cfg {String} tag tag of the element
3988 * @cfg {String} cls class of the element
3991 * Create a new Element
3992 * @param {Object} config The config object
3995 Roo.bootstrap.Element = function(config){
3996 Roo.bootstrap.Element.superclass.constructor.call(this, config);
3999 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4006 getAutoCreate : function(){
4031 * @class Roo.bootstrap.Pagination
4032 * @extends Roo.bootstrap.Component
4033 * Bootstrap Pagination class
4034 * @cfg {String} size xs | sm | md | lg
4035 * @cfg {Boolean} inverse false | true
4038 * Create a new Pagination
4039 * @param {Object} config The config object
4042 Roo.bootstrap.Pagination = function(config){
4043 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4046 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4052 getAutoCreate : function(){
4058 cfg.cls += ' inverse';
4064 cfg.cls += " " + this.cls;
4082 * @class Roo.bootstrap.PaginationItem
4083 * @extends Roo.bootstrap.Component
4084 * Bootstrap PaginationItem class
4085 * @cfg {String} html text
4086 * @cfg {String} href the link
4087 * @cfg {Boolean} preventDefault (true | false) default true
4088 * @cfg {Boolean} active (true | false) default false
4092 * Create a new PaginationItem
4093 * @param {Object} config The config object
4097 Roo.bootstrap.PaginationItem = function(config){
4098 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4103 * The raw click event for the entire grid.
4104 * @param {Roo.EventObject} e
4110 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4114 preventDefault: true,
4118 getAutoCreate : function(){
4124 href : this.href ? this.href : '#',
4125 html : this.html ? this.html : ''
4135 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4141 initEvents: function() {
4143 this.el.on('click', this.onClick, this);
4146 onClick : function(e)
4148 Roo.log('PaginationItem on click ');
4149 if(this.preventDefault){
4153 this.fireEvent('click', this, e);
4169 * @class Roo.bootstrap.Slider
4170 * @extends Roo.bootstrap.Component
4171 * Bootstrap Slider class
4174 * Create a new Slider
4175 * @param {Object} config The config object
4178 Roo.bootstrap.Slider = function(config){
4179 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4182 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4184 getAutoCreate : function(){
4188 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4192 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4204 * Ext JS Library 1.1.1
4205 * Copyright(c) 2006-2007, Ext JS, LLC.
4207 * Originally Released Under LGPL - original licence link has changed is not relivant.
4210 * <script type="text/javascript">
4215 * @class Roo.grid.ColumnModel
4216 * @extends Roo.util.Observable
4217 * This is the default implementation of a ColumnModel used by the Grid. It defines
4218 * the columns in the grid.
4221 var colModel = new Roo.grid.ColumnModel([
4222 {header: "Ticker", width: 60, sortable: true, locked: true},
4223 {header: "Company Name", width: 150, sortable: true},
4224 {header: "Market Cap.", width: 100, sortable: true},
4225 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4226 {header: "Employees", width: 100, sortable: true, resizable: false}
4231 * The config options listed for this class are options which may appear in each
4232 * individual column definition.
4233 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4235 * @param {Object} config An Array of column config objects. See this class's
4236 * config objects for details.
4238 Roo.grid.ColumnModel = function(config){
4240 * The config passed into the constructor
4242 this.config = config;
4245 // if no id, create one
4246 // if the column does not have a dataIndex mapping,
4247 // map it to the order it is in the config
4248 for(var i = 0, len = config.length; i < len; i++){
4250 if(typeof c.dataIndex == "undefined"){
4253 if(typeof c.renderer == "string"){
4254 c.renderer = Roo.util.Format[c.renderer];
4256 if(typeof c.id == "undefined"){
4259 if(c.editor && c.editor.xtype){
4260 c.editor = Roo.factory(c.editor, Roo.grid);
4262 if(c.editor && c.editor.isFormField){
4263 c.editor = new Roo.grid.GridEditor(c.editor);
4265 this.lookup[c.id] = c;
4269 * The width of columns which have no width specified (defaults to 100)
4272 this.defaultWidth = 100;
4275 * Default sortable of columns which have no sortable specified (defaults to false)
4278 this.defaultSortable = false;
4282 * @event widthchange
4283 * Fires when the width of a column changes.
4284 * @param {ColumnModel} this
4285 * @param {Number} columnIndex The column index
4286 * @param {Number} newWidth The new width
4288 "widthchange": true,
4290 * @event headerchange
4291 * Fires when the text of a header changes.
4292 * @param {ColumnModel} this
4293 * @param {Number} columnIndex The column index
4294 * @param {Number} newText The new header text
4296 "headerchange": true,
4298 * @event hiddenchange
4299 * Fires when a column is hidden or "unhidden".
4300 * @param {ColumnModel} this
4301 * @param {Number} columnIndex The column index
4302 * @param {Boolean} hidden true if hidden, false otherwise
4304 "hiddenchange": true,
4306 * @event columnmoved
4307 * Fires when a column is moved.
4308 * @param {ColumnModel} this
4309 * @param {Number} oldIndex
4310 * @param {Number} newIndex
4312 "columnmoved" : true,
4314 * @event columlockchange
4315 * Fires when a column's locked state is changed
4316 * @param {ColumnModel} this
4317 * @param {Number} colIndex
4318 * @param {Boolean} locked true if locked
4320 "columnlockchange" : true
4322 Roo.grid.ColumnModel.superclass.constructor.call(this);
4324 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4326 * @cfg {String} header The header text to display in the Grid view.
4329 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4330 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4331 * specified, the column's index is used as an index into the Record's data Array.
4334 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4335 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4338 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4339 * Defaults to the value of the {@link #defaultSortable} property.
4340 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4343 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4346 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4349 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4352 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4355 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4356 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4357 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4358 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4361 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4364 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4368 * Returns the id of the column at the specified index.
4369 * @param {Number} index The column index
4370 * @return {String} the id
4372 getColumnId : function(index){
4373 return this.config[index].id;
4377 * Returns the column for a specified id.
4378 * @param {String} id The column id
4379 * @return {Object} the column
4381 getColumnById : function(id){
4382 return this.lookup[id];
4387 * Returns the column for a specified dataIndex.
4388 * @param {String} dataIndex The column dataIndex
4389 * @return {Object|Boolean} the column or false if not found
4391 getColumnByDataIndex: function(dataIndex){
4392 var index = this.findColumnIndex(dataIndex);
4393 return index > -1 ? this.config[index] : false;
4397 * Returns the index for a specified column id.
4398 * @param {String} id The column id
4399 * @return {Number} the index, or -1 if not found
4401 getIndexById : function(id){
4402 for(var i = 0, len = this.config.length; i < len; i++){
4403 if(this.config[i].id == id){
4411 * Returns the index for a specified column dataIndex.
4412 * @param {String} dataIndex The column dataIndex
4413 * @return {Number} the index, or -1 if not found
4416 findColumnIndex : function(dataIndex){
4417 for(var i = 0, len = this.config.length; i < len; i++){
4418 if(this.config[i].dataIndex == dataIndex){
4426 moveColumn : function(oldIndex, newIndex){
4427 var c = this.config[oldIndex];
4428 this.config.splice(oldIndex, 1);
4429 this.config.splice(newIndex, 0, c);
4430 this.dataMap = null;
4431 this.fireEvent("columnmoved", this, oldIndex, newIndex);
4434 isLocked : function(colIndex){
4435 return this.config[colIndex].locked === true;
4438 setLocked : function(colIndex, value, suppressEvent){
4439 if(this.isLocked(colIndex) == value){
4442 this.config[colIndex].locked = value;
4444 this.fireEvent("columnlockchange", this, colIndex, value);
4448 getTotalLockedWidth : function(){
4450 for(var i = 0; i < this.config.length; i++){
4451 if(this.isLocked(i) && !this.isHidden(i)){
4452 this.totalWidth += this.getColumnWidth(i);
4458 getLockedCount : function(){
4459 for(var i = 0, len = this.config.length; i < len; i++){
4460 if(!this.isLocked(i)){
4467 * Returns the number of columns.
4470 getColumnCount : function(visibleOnly){
4471 if(visibleOnly === true){
4473 for(var i = 0, len = this.config.length; i < len; i++){
4474 if(!this.isHidden(i)){
4480 return this.config.length;
4484 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4485 * @param {Function} fn
4486 * @param {Object} scope (optional)
4487 * @return {Array} result
4489 getColumnsBy : function(fn, scope){
4491 for(var i = 0, len = this.config.length; i < len; i++){
4492 var c = this.config[i];
4493 if(fn.call(scope||this, c, i) === true){
4501 * Returns true if the specified column is sortable.
4502 * @param {Number} col The column index
4505 isSortable : function(col){
4506 if(typeof this.config[col].sortable == "undefined"){
4507 return this.defaultSortable;
4509 return this.config[col].sortable;
4513 * Returns the rendering (formatting) function defined for the column.
4514 * @param {Number} col The column index.
4515 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4517 getRenderer : function(col){
4518 if(!this.config[col].renderer){
4519 return Roo.grid.ColumnModel.defaultRenderer;
4521 return this.config[col].renderer;
4525 * Sets the rendering (formatting) function for a column.
4526 * @param {Number} col The column index
4527 * @param {Function} fn The function to use to process the cell's raw data
4528 * to return HTML markup for the grid view. The render function is called with
4529 * the following parameters:<ul>
4530 * <li>Data value.</li>
4531 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4532 * <li>css A CSS style string to apply to the table cell.</li>
4533 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4534 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4535 * <li>Row index</li>
4536 * <li>Column index</li>
4537 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4539 setRenderer : function(col, fn){
4540 this.config[col].renderer = fn;
4544 * Returns the width for the specified column.
4545 * @param {Number} col The column index
4548 getColumnWidth : function(col){
4549 return this.config[col].width * 1 || this.defaultWidth;
4553 * Sets the width for a column.
4554 * @param {Number} col The column index
4555 * @param {Number} width The new width
4557 setColumnWidth : function(col, width, suppressEvent){
4558 this.config[col].width = width;
4559 this.totalWidth = null;
4561 this.fireEvent("widthchange", this, col, width);
4566 * Returns the total width of all columns.
4567 * @param {Boolean} includeHidden True to include hidden column widths
4570 getTotalWidth : function(includeHidden){
4571 if(!this.totalWidth){
4572 this.totalWidth = 0;
4573 for(var i = 0, len = this.config.length; i < len; i++){
4574 if(includeHidden || !this.isHidden(i)){
4575 this.totalWidth += this.getColumnWidth(i);
4579 return this.totalWidth;
4583 * Returns the header for the specified column.
4584 * @param {Number} col The column index
4587 getColumnHeader : function(col){
4588 return this.config[col].header;
4592 * Sets the header for a column.
4593 * @param {Number} col The column index
4594 * @param {String} header The new header
4596 setColumnHeader : function(col, header){
4597 this.config[col].header = header;
4598 this.fireEvent("headerchange", this, col, header);
4602 * Returns the tooltip for the specified column.
4603 * @param {Number} col The column index
4606 getColumnTooltip : function(col){
4607 return this.config[col].tooltip;
4610 * Sets the tooltip for a column.
4611 * @param {Number} col The column index
4612 * @param {String} tooltip The new tooltip
4614 setColumnTooltip : function(col, tooltip){
4615 this.config[col].tooltip = tooltip;
4619 * Returns the dataIndex for the specified column.
4620 * @param {Number} col The column index
4623 getDataIndex : function(col){
4624 return this.config[col].dataIndex;
4628 * Sets the dataIndex for a column.
4629 * @param {Number} col The column index
4630 * @param {Number} dataIndex The new dataIndex
4632 setDataIndex : function(col, dataIndex){
4633 this.config[col].dataIndex = dataIndex;
4639 * Returns true if the cell is editable.
4640 * @param {Number} colIndex The column index
4641 * @param {Number} rowIndex The row index
4644 isCellEditable : function(colIndex, rowIndex){
4645 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4649 * Returns the editor defined for the cell/column.
4650 * return false or null to disable editing.
4651 * @param {Number} colIndex The column index
4652 * @param {Number} rowIndex The row index
4655 getCellEditor : function(colIndex, rowIndex){
4656 return this.config[colIndex].editor;
4660 * Sets if a column is editable.
4661 * @param {Number} col The column index
4662 * @param {Boolean} editable True if the column is editable
4664 setEditable : function(col, editable){
4665 this.config[col].editable = editable;
4670 * Returns true if the column is hidden.
4671 * @param {Number} colIndex The column index
4674 isHidden : function(colIndex){
4675 return this.config[colIndex].hidden;
4680 * Returns true if the column width cannot be changed
4682 isFixed : function(colIndex){
4683 return this.config[colIndex].fixed;
4687 * Returns true if the column can be resized
4690 isResizable : function(colIndex){
4691 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4694 * Sets if a column is hidden.
4695 * @param {Number} colIndex The column index
4696 * @param {Boolean} hidden True if the column is hidden
4698 setHidden : function(colIndex, hidden){
4699 this.config[colIndex].hidden = hidden;
4700 this.totalWidth = null;
4701 this.fireEvent("hiddenchange", this, colIndex, hidden);
4705 * Sets the editor for a column.
4706 * @param {Number} col The column index
4707 * @param {Object} editor The editor object
4709 setEditor : function(col, editor){
4710 this.config[col].editor = editor;
4714 Roo.grid.ColumnModel.defaultRenderer = function(value){
4715 if(typeof value == "string" && value.length < 1){
4721 // Alias for backwards compatibility
4722 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4725 * Ext JS Library 1.1.1
4726 * Copyright(c) 2006-2007, Ext JS, LLC.
4728 * Originally Released Under LGPL - original licence link has changed is not relivant.
4731 * <script type="text/javascript">
4735 * @class Roo.LoadMask
4736 * A simple utility class for generically masking elements while loading data. If the element being masked has
4737 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4738 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
4739 * element's UpdateManager load indicator and will be destroyed after the initial load.
4741 * Create a new LoadMask
4742 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4743 * @param {Object} config The config object
4745 Roo.LoadMask = function(el, config){
4746 this.el = Roo.get(el);
4747 Roo.apply(this, config);
4749 this.store.on('beforeload', this.onBeforeLoad, this);
4750 this.store.on('load', this.onLoad, this);
4751 this.store.on('loadexception', this.onLoadException, this);
4752 this.removeMask = false;
4754 var um = this.el.getUpdateManager();
4755 um.showLoadIndicator = false; // disable the default indicator
4756 um.on('beforeupdate', this.onBeforeLoad, this);
4757 um.on('update', this.onLoad, this);
4758 um.on('failure', this.onLoad, this);
4759 this.removeMask = true;
4763 Roo.LoadMask.prototype = {
4765 * @cfg {Boolean} removeMask
4766 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4767 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
4771 * The text to display in a centered loading message box (defaults to 'Loading...')
4775 * @cfg {String} msgCls
4776 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4778 msgCls : 'x-mask-loading',
4781 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4787 * Disables the mask to prevent it from being displayed
4789 disable : function(){
4790 this.disabled = true;
4794 * Enables the mask so that it can be displayed
4796 enable : function(){
4797 this.disabled = false;
4800 onLoadException : function()
4804 if (typeof(arguments[3]) != 'undefined') {
4805 Roo.MessageBox.alert("Error loading",arguments[3]);
4809 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4810 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4819 this.el.unmask(this.removeMask);
4824 this.el.unmask(this.removeMask);
4828 onBeforeLoad : function(){
4830 this.el.mask(this.msg, this.msgCls);
4835 destroy : function(){
4837 this.store.un('beforeload', this.onBeforeLoad, this);
4838 this.store.un('load', this.onLoad, this);
4839 this.store.un('loadexception', this.onLoadException, this);
4841 var um = this.el.getUpdateManager();
4842 um.un('beforeupdate', this.onBeforeLoad, this);
4843 um.un('update', this.onLoad, this);
4844 um.un('failure', this.onLoad, this);
4855 * @class Roo.bootstrap.Table
4856 * @extends Roo.bootstrap.Component
4857 * Bootstrap Table class
4858 * @cfg {String} cls table class
4859 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4860 * @cfg {String} bgcolor Specifies the background color for a table
4861 * @cfg {Number} border Specifies whether the table cells should have borders or not
4862 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4863 * @cfg {Number} cellspacing Specifies the space between cells
4864 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4865 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4866 * @cfg {String} sortable Specifies that the table should be sortable
4867 * @cfg {String} summary Specifies a summary of the content of a table
4868 * @cfg {Number} width Specifies the width of a table
4869 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4871 * @cfg {boolean} striped Should the rows be alternative striped
4872 * @cfg {boolean} bordered Add borders to the table
4873 * @cfg {boolean} hover Add hover highlighting
4874 * @cfg {boolean} condensed Format condensed
4875 * @cfg {boolean} responsive Format condensed
4876 * @cfg {Boolean} loadMask (true|false) default false
4877 * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4878 * @cfg {Boolean} thead (true|false) generate thead, default true
4879 * @cfg {Boolean} RowSelection (true|false) default false
4880 * @cfg {Boolean} CellSelection (true|false) default false
4882 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
4886 * Create a new Table
4887 * @param {Object} config The config object
4890 Roo.bootstrap.Table = function(config){
4891 Roo.bootstrap.Table.superclass.constructor.call(this, config);
4894 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4895 this.sm = this.selModel;
4896 this.sm.xmodule = this.xmodule || false;
4898 if (this.cm && typeof(this.cm.config) == 'undefined') {
4899 this.colModel = new Roo.grid.ColumnModel(this.cm);
4900 this.cm = this.colModel;
4901 this.cm.xmodule = this.xmodule || false;
4904 this.store= Roo.factory(this.store, Roo.data);
4905 this.ds = this.store;
4906 this.ds.xmodule = this.xmodule || false;
4909 if (this.footer && this.store) {
4910 this.footer.dataSource = this.ds;
4911 this.footer = Roo.factory(this.footer);
4918 * Fires when a cell is clicked
4919 * @param {Roo.bootstrap.Table} this
4920 * @param {Roo.Element} el
4921 * @param {Number} rowIndex
4922 * @param {Number} columnIndex
4923 * @param {Roo.EventObject} e
4927 * @event celldblclick
4928 * Fires when a cell is double clicked
4929 * @param {Roo.bootstrap.Table} this
4930 * @param {Roo.Element} el
4931 * @param {Number} rowIndex
4932 * @param {Number} columnIndex
4933 * @param {Roo.EventObject} e
4935 "celldblclick" : true,
4938 * Fires when a row is clicked
4939 * @param {Roo.bootstrap.Table} this
4940 * @param {Roo.Element} el
4941 * @param {Number} rowIndex
4942 * @param {Roo.EventObject} e
4946 * @event rowdblclick
4947 * Fires when a row is double clicked
4948 * @param {Roo.bootstrap.Table} this
4949 * @param {Roo.Element} el
4950 * @param {Number} rowIndex
4951 * @param {Roo.EventObject} e
4953 "rowdblclick" : true,
4956 * Fires when a mouseover occur
4957 * @param {Roo.bootstrap.Table} this
4958 * @param {Roo.Element} el
4959 * @param {Number} rowIndex
4960 * @param {Number} columnIndex
4961 * @param {Roo.EventObject} e
4966 * Fires when a mouseout occur
4967 * @param {Roo.bootstrap.Table} this
4968 * @param {Roo.Element} el
4969 * @param {Number} rowIndex
4970 * @param {Number} columnIndex
4971 * @param {Roo.EventObject} e
4976 * Fires when a row is rendered, so you can change add a style to it.
4977 * @param {Roo.bootstrap.Table} this
4978 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
4985 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5009 RowSelection : false,
5010 CellSelection : false,
5013 // Roo.Element - the tbody
5016 getAutoCreate : function(){
5017 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5026 cfg.cls += ' table-striped';
5030 cfg.cls += ' table-hover';
5032 if (this.bordered) {
5033 cfg.cls += ' table-bordered';
5035 if (this.condensed) {
5036 cfg.cls += ' table-condensed';
5038 if (this.responsive) {
5039 cfg.cls += ' table-responsive';
5043 cfg.cls+= ' ' +this.cls;
5046 // this lot should be simplifed...
5049 cfg.align=this.align;
5052 cfg.bgcolor=this.bgcolor;
5055 cfg.border=this.border;
5057 if (this.cellpadding) {
5058 cfg.cellpadding=this.cellpadding;
5060 if (this.cellspacing) {
5061 cfg.cellspacing=this.cellspacing;
5064 cfg.frame=this.frame;
5067 cfg.rules=this.rules;
5069 if (this.sortable) {
5070 cfg.sortable=this.sortable;
5073 cfg.summary=this.summary;
5076 cfg.width=this.width;
5079 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5082 if(this.store || this.cm){
5084 cfg.cn.push(this.renderHeader());
5087 cfg.cn.push(this.renderBody());
5090 cfg.cn.push(this.renderFooter());
5093 cfg.cls+= ' TableGrid';
5096 return { cn : [ cfg ] };
5099 initEvents : function()
5101 if(!this.store || !this.cm){
5105 //Roo.log('initEvents with ds!!!!');
5107 this.mainBody = this.el.select('tbody', true).first();
5112 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5113 e.on('click', _this.sort, _this);
5116 this.el.on("click", this.onClick, this);
5117 this.el.on("dblclick", this.onDblClick, this);
5119 this.parent().el.setStyle('position', 'relative');
5121 this.footer.parentId = this.id;
5122 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5125 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5127 this.store.on('load', this.onLoad, this);
5128 this.store.on('beforeload', this.onBeforeLoad, this);
5129 this.store.on('update', this.onUpdate, this);
5133 onMouseover : function(e, el)
5135 var cell = Roo.get(el);
5141 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5142 cell = cell.findParent('td', false, true);
5145 var row = cell.findParent('tr', false, true);
5146 var cellIndex = cell.dom.cellIndex;
5147 var rowIndex = row.dom.rowIndex - 1; // start from 0
5149 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5153 onMouseout : function(e, el)
5155 var cell = Roo.get(el);
5161 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5162 cell = cell.findParent('td', false, true);
5165 var row = cell.findParent('tr', false, true);
5166 var cellIndex = cell.dom.cellIndex;
5167 var rowIndex = row.dom.rowIndex - 1; // start from 0
5169 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5173 onClick : function(e, el)
5175 var cell = Roo.get(el);
5177 if(!cell || (!this.CellSelection && !this.RowSelection)){
5182 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5183 cell = cell.findParent('td', false, true);
5186 var row = cell.findParent('tr', false, true);
5187 var cellIndex = cell.dom.cellIndex;
5188 var rowIndex = row.dom.rowIndex - 1;
5190 if(this.CellSelection){
5191 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5194 if(this.RowSelection){
5195 this.fireEvent('rowclick', this, row, rowIndex, e);
5201 onDblClick : function(e,el)
5203 var cell = Roo.get(el);
5205 if(!cell || (!this.CellSelection && !this.RowSelection)){
5209 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5210 cell = cell.findParent('td', false, true);
5213 var row = cell.findParent('tr', false, true);
5214 var cellIndex = cell.dom.cellIndex;
5215 var rowIndex = row.dom.rowIndex - 1;
5217 if(this.CellSelection){
5218 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5221 if(this.RowSelection){
5222 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5226 sort : function(e,el)
5228 var col = Roo.get(el)
5230 if(!col.hasClass('sortable')){
5234 var sort = col.attr('sort');
5237 if(col.hasClass('glyphicon-arrow-up')){
5241 this.store.sortInfo = {field : sort, direction : dir};
5244 Roo.log("calling footer first");
5245 this.footer.onClick('first');
5248 this.store.load({ params : { start : 0 } });
5252 renderHeader : function()
5261 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5263 var config = cm.config[i];
5268 html: cm.getColumnHeader(i)
5271 if(typeof(config.hidden) != 'undefined' && config.hidden){
5272 c.style += ' display:none;';
5275 if(typeof(config.dataIndex) != 'undefined'){
5276 c.sort = config.dataIndex;
5279 if(typeof(config.sortable) != 'undefined' && config.sortable){
5283 if(typeof(config.align) != 'undefined' && config.align.length){
5284 c.style += ' text-align:' + config.align + ';';
5287 if(typeof(config.width) != 'undefined'){
5288 c.style += ' width:' + config.width + 'px;';
5297 renderBody : function()
5307 colspan : this.cm.getColumnCount()
5317 renderFooter : function()
5327 colspan : this.cm.getColumnCount()
5341 Roo.log('ds onload');
5346 var ds = this.store;
5348 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5349 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5351 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5352 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5355 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5356 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5360 var tbody = this.mainBody;
5362 if(ds.getCount() > 0){
5363 ds.data.each(function(d,rowIndex){
5364 var row = this.renderRow(cm, ds, rowIndex);
5366 tbody.createChild(row);
5370 if(row.cellObjects.length){
5371 Roo.each(row.cellObjects, function(r){
5372 _this.renderCellObject(r);
5379 Roo.each(this.el.select('tbody td', true).elements, function(e){
5380 e.on('mouseover', _this.onMouseover, _this);
5383 Roo.each(this.el.select('tbody td', true).elements, function(e){
5384 e.on('mouseout', _this.onMouseout, _this);
5387 //if(this.loadMask){
5388 // this.maskEl.hide();
5393 onUpdate : function(ds,record)
5395 this.refreshRow(record);
5397 onRemove : function(ds, record, index, isUpdate){
5398 if(isUpdate !== true){
5399 this.fireEvent("beforerowremoved", this, index, record);
5401 var bt = this.mainBody.dom;
5403 bt.removeChild(bt.rows[index]);
5406 if(isUpdate !== true){
5407 //this.stripeRows(index);
5408 //this.syncRowHeights(index, index);
5410 this.fireEvent("rowremoved", this, index, record);
5415 refreshRow : function(record){
5416 var ds = this.store, index;
5417 if(typeof record == 'number'){
5419 record = ds.getAt(index);
5421 index = ds.indexOf(record);
5423 this.insertRow(ds, index, true);
5424 this.onRemove(ds, record, index+1, true);
5425 //this.syncRowHeights(index, index);
5427 this.fireEvent("rowupdated", this, index, record);
5430 insertRow : function(dm, rowIndex, isUpdate){
5433 this.fireEvent("beforerowsinserted", this, rowIndex);
5435 //var s = this.getScrollState();
5436 var row = this.renderRow(this.cm, this.store, rowIndex);
5437 // insert before rowIndex..
5438 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5442 if(row.cellObjects.length){
5443 Roo.each(row.cellObjects, function(r){
5444 _this.renderCellObject(r);
5449 this.fireEvent("rowsinserted", this, rowIndex);
5450 //this.syncRowHeights(firstRow, lastRow);
5451 //this.stripeRows(firstRow);
5458 getRowDom : function(rowIndex)
5460 // not sure if I need to check this.. but let's do it anyway..
5461 return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5462 this.mainBody.dom.rows[rowIndex] : false
5464 // returns the object tree for a tr..
5467 renderRow : function(cm, ds, rowIndex) {
5469 var d = ds.getAt(rowIndex);
5476 var cellObjects = [];
5478 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5479 var config = cm.config[i];
5481 var renderer = cm.getRenderer(i);
5485 if(typeof(renderer) !== 'undefined'){
5486 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5488 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5489 // and are rendered into the cells after the row is rendered - using the id for the element.
5491 if(typeof(value) === 'object'){
5501 rowIndex : rowIndex,
5506 this.fireEvent('rowclass', this, rowcfg);
5510 cls : rowcfg.rowClass,
5512 html: (typeof(value) === 'object') ? '' : value
5519 if(typeof(config.hidden) != 'undefined' && config.hidden){
5520 td.style += ' display:none;';
5523 if(typeof(config.align) != 'undefined' && config.align.length){
5524 td.style += ' text-align:' + config.align + ';';
5527 if(typeof(config.width) != 'undefined'){
5528 td.style += ' width:' + config.width + 'px;';
5535 row.cellObjects = cellObjects;
5543 onBeforeLoad : function()
5545 //Roo.log('ds onBeforeLoad');
5549 //if(this.loadMask){
5550 // this.maskEl.show();
5556 this.el.select('tbody', true).first().dom.innerHTML = '';
5559 getSelectionModel : function(){
5561 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5563 return this.selModel;
5566 * Render the Roo.bootstrap object from renderder
5568 renderCellObject : function(r)
5572 var t = r.cfg.render(r.container);
5575 Roo.each(r.cfg.cn, function(c){
5577 container: t.getChildContainer(),
5580 _this.renderCellObject(child);
5597 * @class Roo.bootstrap.TableCell
5598 * @extends Roo.bootstrap.Component
5599 * Bootstrap TableCell class
5600 * @cfg {String} html cell contain text
5601 * @cfg {String} cls cell class
5602 * @cfg {String} tag cell tag (td|th) default td
5603 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5604 * @cfg {String} align Aligns the content in a cell
5605 * @cfg {String} axis Categorizes cells
5606 * @cfg {String} bgcolor Specifies the background color of a cell
5607 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5608 * @cfg {Number} colspan Specifies the number of columns a cell should span
5609 * @cfg {String} headers Specifies one or more header cells a cell is related to
5610 * @cfg {Number} height Sets the height of a cell
5611 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5612 * @cfg {Number} rowspan Sets the number of rows a cell should span
5613 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5614 * @cfg {String} valign Vertical aligns the content in a cell
5615 * @cfg {Number} width Specifies the width of a cell
5618 * Create a new TableCell
5619 * @param {Object} config The config object
5622 Roo.bootstrap.TableCell = function(config){
5623 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5626 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
5646 getAutoCreate : function(){
5647 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5667 cfg.align=this.align
5673 cfg.bgcolor=this.bgcolor
5676 cfg.charoff=this.charoff
5679 cfg.colspan=this.colspan
5682 cfg.headers=this.headers
5685 cfg.height=this.height
5688 cfg.nowrap=this.nowrap
5691 cfg.rowspan=this.rowspan
5694 cfg.scope=this.scope
5697 cfg.valign=this.valign
5700 cfg.width=this.width
5719 * @class Roo.bootstrap.TableRow
5720 * @extends Roo.bootstrap.Component
5721 * Bootstrap TableRow class
5722 * @cfg {String} cls row class
5723 * @cfg {String} align Aligns the content in a table row
5724 * @cfg {String} bgcolor Specifies a background color for a table row
5725 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5726 * @cfg {String} valign Vertical aligns the content in a table row
5729 * Create a new TableRow
5730 * @param {Object} config The config object
5733 Roo.bootstrap.TableRow = function(config){
5734 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5737 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
5745 getAutoCreate : function(){
5746 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5756 cfg.align = this.align;
5759 cfg.bgcolor = this.bgcolor;
5762 cfg.charoff = this.charoff;
5765 cfg.valign = this.valign;
5783 * @class Roo.bootstrap.TableBody
5784 * @extends Roo.bootstrap.Component
5785 * Bootstrap TableBody class
5786 * @cfg {String} cls element class
5787 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5788 * @cfg {String} align Aligns the content inside the element
5789 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5790 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5793 * Create a new TableBody
5794 * @param {Object} config The config object
5797 Roo.bootstrap.TableBody = function(config){
5798 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5801 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
5809 getAutoCreate : function(){
5810 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5824 cfg.align = this.align;
5827 cfg.charoff = this.charoff;
5830 cfg.valign = this.valign;
5837 // initEvents : function()
5844 // this.store = Roo.factory(this.store, Roo.data);
5845 // this.store.on('load', this.onLoad, this);
5847 // this.store.load();
5851 // onLoad: function ()
5853 // this.fireEvent('load', this);
5863 * Ext JS Library 1.1.1
5864 * Copyright(c) 2006-2007, Ext JS, LLC.
5866 * Originally Released Under LGPL - original licence link has changed is not relivant.
5869 * <script type="text/javascript">
5872 // as we use this in bootstrap.
5873 Roo.namespace('Roo.form');
5875 * @class Roo.form.Action
5876 * Internal Class used to handle form actions
5878 * @param {Roo.form.BasicForm} el The form element or its id
5879 * @param {Object} config Configuration options
5884 // define the action interface
5885 Roo.form.Action = function(form, options){
5887 this.options = options || {};
5890 * Client Validation Failed
5893 Roo.form.Action.CLIENT_INVALID = 'client';
5895 * Server Validation Failed
5898 Roo.form.Action.SERVER_INVALID = 'server';
5900 * Connect to Server Failed
5903 Roo.form.Action.CONNECT_FAILURE = 'connect';
5905 * Reading Data from Server Failed
5908 Roo.form.Action.LOAD_FAILURE = 'load';
5910 Roo.form.Action.prototype = {
5912 failureType : undefined,
5913 response : undefined,
5917 run : function(options){
5922 success : function(response){
5927 handleResponse : function(response){
5931 // default connection failure
5932 failure : function(response){
5934 this.response = response;
5935 this.failureType = Roo.form.Action.CONNECT_FAILURE;
5936 this.form.afterAction(this, false);
5939 processResponse : function(response){
5940 this.response = response;
5941 if(!response.responseText){
5944 this.result = this.handleResponse(response);
5948 // utility functions used internally
5949 getUrl : function(appendParams){
5950 var url = this.options.url || this.form.url || this.form.el.dom.action;
5952 var p = this.getParams();
5954 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5960 getMethod : function(){
5961 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5964 getParams : function(){
5965 var bp = this.form.baseParams;
5966 var p = this.options.params;
5968 if(typeof p == "object"){
5969 p = Roo.urlEncode(Roo.applyIf(p, bp));
5970 }else if(typeof p == 'string' && bp){
5971 p += '&' + Roo.urlEncode(bp);
5974 p = Roo.urlEncode(bp);
5979 createCallback : function(){
5981 success: this.success,
5982 failure: this.failure,
5984 timeout: (this.form.timeout*1000),
5985 upload: this.form.fileUpload ? this.success : undefined
5990 Roo.form.Action.Submit = function(form, options){
5991 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5994 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5997 haveProgress : false,
5998 uploadComplete : false,
6000 // uploadProgress indicator.
6001 uploadProgress : function()
6003 if (!this.form.progressUrl) {
6007 if (!this.haveProgress) {
6008 Roo.MessageBox.progress("Uploading", "Uploading");
6010 if (this.uploadComplete) {
6011 Roo.MessageBox.hide();
6015 this.haveProgress = true;
6017 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6019 var c = new Roo.data.Connection();
6021 url : this.form.progressUrl,
6026 success : function(req){
6027 //console.log(data);
6031 rdata = Roo.decode(req.responseText)
6033 Roo.log("Invalid data from server..");
6037 if (!rdata || !rdata.success) {
6039 Roo.MessageBox.alert(Roo.encode(rdata));
6042 var data = rdata.data;
6044 if (this.uploadComplete) {
6045 Roo.MessageBox.hide();
6050 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6051 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6054 this.uploadProgress.defer(2000,this);
6057 failure: function(data) {
6058 Roo.log('progress url failed ');
6069 // run get Values on the form, so it syncs any secondary forms.
6070 this.form.getValues();
6072 var o = this.options;
6073 var method = this.getMethod();
6074 var isPost = method == 'POST';
6075 if(o.clientValidation === false || this.form.isValid()){
6077 if (this.form.progressUrl) {
6078 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6079 (new Date() * 1) + '' + Math.random());
6084 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6085 form:this.form.el.dom,
6086 url:this.getUrl(!isPost),
6088 params:isPost ? this.getParams() : null,
6089 isUpload: this.form.fileUpload
6092 this.uploadProgress();
6094 }else if (o.clientValidation !== false){ // client validation failed
6095 this.failureType = Roo.form.Action.CLIENT_INVALID;
6096 this.form.afterAction(this, false);
6100 success : function(response)
6102 this.uploadComplete= true;
6103 if (this.haveProgress) {
6104 Roo.MessageBox.hide();
6108 var result = this.processResponse(response);
6109 if(result === true || result.success){
6110 this.form.afterAction(this, true);
6114 this.form.markInvalid(result.errors);
6115 this.failureType = Roo.form.Action.SERVER_INVALID;
6117 this.form.afterAction(this, false);
6119 failure : function(response)
6121 this.uploadComplete= true;
6122 if (this.haveProgress) {
6123 Roo.MessageBox.hide();
6126 this.response = response;
6127 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6128 this.form.afterAction(this, false);
6131 handleResponse : function(response){
6132 if(this.form.errorReader){
6133 var rs = this.form.errorReader.read(response);
6136 for(var i = 0, len = rs.records.length; i < len; i++) {
6137 var r = rs.records[i];
6141 if(errors.length < 1){
6145 success : rs.success,
6151 ret = Roo.decode(response.responseText);
6155 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6165 Roo.form.Action.Load = function(form, options){
6166 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6167 this.reader = this.form.reader;
6170 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6175 Roo.Ajax.request(Roo.apply(
6176 this.createCallback(), {
6177 method:this.getMethod(),
6178 url:this.getUrl(false),
6179 params:this.getParams()
6183 success : function(response){
6185 var result = this.processResponse(response);
6186 if(result === true || !result.success || !result.data){
6187 this.failureType = Roo.form.Action.LOAD_FAILURE;
6188 this.form.afterAction(this, false);
6191 this.form.clearInvalid();
6192 this.form.setValues(result.data);
6193 this.form.afterAction(this, true);
6196 handleResponse : function(response){
6197 if(this.form.reader){
6198 var rs = this.form.reader.read(response);
6199 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6201 success : rs.success,
6205 return Roo.decode(response.responseText);
6209 Roo.form.Action.ACTION_TYPES = {
6210 'load' : Roo.form.Action.Load,
6211 'submit' : Roo.form.Action.Submit
6220 * @class Roo.bootstrap.Form
6221 * @extends Roo.bootstrap.Component
6222 * Bootstrap Form class
6223 * @cfg {String} method GET | POST (default POST)
6224 * @cfg {String} labelAlign top | left (default top)
6225 * @cfg {String} align left | right - for navbars
6226 * @cfg {Boolean} loadMask load mask when submit (default true)
6231 * @param {Object} config The config object
6235 Roo.bootstrap.Form = function(config){
6236 Roo.bootstrap.Form.superclass.constructor.call(this, config);
6239 * @event clientvalidation
6240 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6241 * @param {Form} this
6242 * @param {Boolean} valid true if the form has passed client-side validation
6244 clientvalidation: true,
6246 * @event beforeaction
6247 * Fires before any action is performed. Return false to cancel the action.
6248 * @param {Form} this
6249 * @param {Action} action The action to be performed
6253 * @event actionfailed
6254 * Fires when an action fails.
6255 * @param {Form} this
6256 * @param {Action} action The action that failed
6258 actionfailed : true,
6260 * @event actioncomplete
6261 * Fires when an action is completed.
6262 * @param {Form} this
6263 * @param {Action} action The action that completed
6265 actioncomplete : true
6270 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
6273 * @cfg {String} method
6274 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6279 * The URL to use for form actions if one isn't supplied in the action options.
6282 * @cfg {Boolean} fileUpload
6283 * Set to true if this form is a file upload.
6287 * @cfg {Object} baseParams
6288 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6292 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6296 * @cfg {Sting} align (left|right) for navbar forms
6301 activeAction : null,
6304 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6305 * element by passing it or its id or mask the form itself by passing in true.
6308 waitMsgTarget : false,
6312 getAutoCreate : function(){
6316 method : this.method || 'POST',
6317 id : this.id || Roo.id(),
6320 if (this.parent().xtype.match(/^Nav/)) {
6321 cfg.cls = 'navbar-form navbar-' + this.align;
6325 if (this.labelAlign == 'left' ) {
6326 cfg.cls += ' form-horizontal';
6332 initEvents : function()
6334 this.el.on('submit', this.onSubmit, this);
6335 // this was added as random key presses on the form where triggering form submit.
6336 this.el.on('keypress', function(e) {
6337 if (e.getCharCode() != 13) {
6340 // we might need to allow it for textareas.. and some other items.
6341 // check e.getTarget().
6343 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6347 Roo.log("keypress blocked");
6355 onSubmit : function(e){
6360 * Returns true if client-side validation on the form is successful.
6363 isValid : function(){
6364 var items = this.getItems();
6366 items.each(function(f){
6375 * Returns true if any fields in this form have changed since their original load.
6378 isDirty : function(){
6380 var items = this.getItems();
6381 items.each(function(f){
6391 * Performs a predefined action (submit or load) or custom actions you define on this form.
6392 * @param {String} actionName The name of the action type
6393 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
6394 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6395 * accept other config options):
6397 Property Type Description
6398 ---------------- --------------- ----------------------------------------------------------------------------------
6399 url String The url for the action (defaults to the form's url)
6400 method String The form method to use (defaults to the form's method, or POST if not defined)
6401 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
6402 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
6403 validate the form on the client (defaults to false)
6405 * @return {BasicForm} this
6407 doAction : function(action, options){
6408 if(typeof action == 'string'){
6409 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6411 if(this.fireEvent('beforeaction', this, action) !== false){
6412 this.beforeAction(action);
6413 action.run.defer(100, action);
6419 beforeAction : function(action){
6420 var o = action.options;
6423 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6425 // not really supported yet.. ??
6427 //if(this.waitMsgTarget === true){
6428 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6429 //}else if(this.waitMsgTarget){
6430 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6431 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6433 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6439 afterAction : function(action, success){
6440 this.activeAction = null;
6441 var o = action.options;
6443 //if(this.waitMsgTarget === true){
6445 //}else if(this.waitMsgTarget){
6446 // this.waitMsgTarget.unmask();
6448 // Roo.MessageBox.updateProgress(1);
6449 // Roo.MessageBox.hide();
6456 Roo.callback(o.success, o.scope, [this, action]);
6457 this.fireEvent('actioncomplete', this, action);
6461 // failure condition..
6462 // we have a scenario where updates need confirming.
6463 // eg. if a locking scenario exists..
6464 // we look for { errors : { needs_confirm : true }} in the response.
6466 (typeof(action.result) != 'undefined') &&
6467 (typeof(action.result.errors) != 'undefined') &&
6468 (typeof(action.result.errors.needs_confirm) != 'undefined')
6471 Roo.log("not supported yet");
6474 Roo.MessageBox.confirm(
6475 "Change requires confirmation",
6476 action.result.errorMsg,
6481 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
6491 Roo.callback(o.failure, o.scope, [this, action]);
6492 // show an error message if no failed handler is set..
6493 if (!this.hasListener('actionfailed')) {
6494 Roo.log("need to add dialog support");
6496 Roo.MessageBox.alert("Error",
6497 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6498 action.result.errorMsg :
6499 "Saving Failed, please check your entries or try again"
6504 this.fireEvent('actionfailed', this, action);
6509 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6510 * @param {String} id The value to search for
6513 findField : function(id){
6514 var items = this.getItems();
6515 var field = items.get(id);
6517 items.each(function(f){
6518 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6525 return field || null;
6528 * Mark fields in this form invalid in bulk.
6529 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6530 * @return {BasicForm} this
6532 markInvalid : function(errors){
6533 if(errors instanceof Array){
6534 for(var i = 0, len = errors.length; i < len; i++){
6535 var fieldError = errors[i];
6536 var f = this.findField(fieldError.id);
6538 f.markInvalid(fieldError.msg);
6544 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6545 field.markInvalid(errors[id]);
6549 //Roo.each(this.childForms || [], function (f) {
6550 // f.markInvalid(errors);
6557 * Set values for fields in this form in bulk.
6558 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6559 * @return {BasicForm} this
6561 setValues : function(values){
6562 if(values instanceof Array){ // array of objects
6563 for(var i = 0, len = values.length; i < len; i++){
6565 var f = this.findField(v.id);
6567 f.setValue(v.value);
6568 if(this.trackResetOnLoad){
6569 f.originalValue = f.getValue();
6573 }else{ // object hash
6576 if(typeof values[id] != 'function' && (field = this.findField(id))){
6578 if (field.setFromData &&
6580 field.displayField &&
6581 // combos' with local stores can
6582 // be queried via setValue()
6583 // to set their value..
6584 (field.store && !field.store.isLocal)
6588 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6589 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6590 field.setFromData(sd);
6593 field.setValue(values[id]);
6597 if(this.trackResetOnLoad){
6598 field.originalValue = field.getValue();
6604 //Roo.each(this.childForms || [], function (f) {
6605 // f.setValues(values);
6612 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6613 * they are returned as an array.
6614 * @param {Boolean} asString
6617 getValues : function(asString){
6618 //if (this.childForms) {
6619 // copy values from the child forms
6620 // Roo.each(this.childForms, function (f) {
6621 // this.setValues(f.getValues());
6627 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6628 if(asString === true){
6631 return Roo.urlDecode(fs);
6635 * Returns the fields in this form as an object with key/value pairs.
6636 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6639 getFieldValues : function(with_hidden)
6641 var items = this.getItems();
6643 items.each(function(f){
6647 var v = f.getValue();
6648 if (f.inputType =='radio') {
6649 if (typeof(ret[f.getName()]) == 'undefined') {
6650 ret[f.getName()] = ''; // empty..
6653 if (!f.el.dom.checked) {
6661 // not sure if this supported any more..
6662 if ((typeof(v) == 'object') && f.getRawValue) {
6663 v = f.getRawValue() ; // dates..
6665 // combo boxes where name != hiddenName...
6666 if (f.name != f.getName()) {
6667 ret[f.name] = f.getRawValue();
6669 ret[f.getName()] = v;
6676 * Clears all invalid messages in this form.
6677 * @return {BasicForm} this
6679 clearInvalid : function(){
6680 var items = this.getItems();
6682 items.each(function(f){
6693 * @return {BasicForm} this
6696 var items = this.getItems();
6697 items.each(function(f){
6701 Roo.each(this.childForms || [], function (f) {
6708 getItems : function()
6710 var r=new Roo.util.MixedCollection(false, function(o){
6711 return o.id || (o.id = Roo.id());
6713 var iter = function(el) {
6720 Roo.each(el.items,function(e) {
6739 * Ext JS Library 1.1.1
6740 * Copyright(c) 2006-2007, Ext JS, LLC.
6742 * Originally Released Under LGPL - original licence link has changed is not relivant.
6745 * <script type="text/javascript">
6748 * @class Roo.form.VTypes
6749 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6752 Roo.form.VTypes = function(){
6753 // closure these in so they are only created once.
6754 var alpha = /^[a-zA-Z_]+$/;
6755 var alphanum = /^[a-zA-Z0-9_]+$/;
6756 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6757 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6759 // All these messages and functions are configurable
6762 * The function used to validate email addresses
6763 * @param {String} value The email address
6765 'email' : function(v){
6766 return email.test(v);
6769 * The error text to display when the email validation function returns false
6772 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6774 * The keystroke filter mask to be applied on email input
6777 'emailMask' : /[a-z0-9_\.\-@]/i,
6780 * The function used to validate URLs
6781 * @param {String} value The URL
6783 'url' : function(v){
6787 * The error text to display when the url validation function returns false
6790 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6793 * The function used to validate alpha values
6794 * @param {String} value The value
6796 'alpha' : function(v){
6797 return alpha.test(v);
6800 * The error text to display when the alpha validation function returns false
6803 'alphaText' : 'This field should only contain letters and _',
6805 * The keystroke filter mask to be applied on alpha input
6808 'alphaMask' : /[a-z_]/i,
6811 * The function used to validate alphanumeric values
6812 * @param {String} value The value
6814 'alphanum' : function(v){
6815 return alphanum.test(v);
6818 * The error text to display when the alphanumeric validation function returns false
6821 'alphanumText' : 'This field should only contain letters, numbers and _',
6823 * The keystroke filter mask to be applied on alphanumeric input
6826 'alphanumMask' : /[a-z0-9_]/i
6836 * @class Roo.bootstrap.Input
6837 * @extends Roo.bootstrap.Component
6838 * Bootstrap Input class
6839 * @cfg {Boolean} disabled is it disabled
6840 * @cfg {String} fieldLabel - the label associated
6841 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6842 * @cfg {String} name name of the input
6843 * @cfg {string} fieldLabel - the label associated
6844 * @cfg {string} inputType - input / file submit ...
6845 * @cfg {string} placeholder - placeholder to put in text.
6846 * @cfg {string} before - input group add on before
6847 * @cfg {string} after - input group add on after
6848 * @cfg {string} size - (lg|sm) or leave empty..
6849 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6850 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6851 * @cfg {Number} md colspan out of 12 for computer-sized screens
6852 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6853 * @cfg {string} value default value of the input
6854 * @cfg {Number} labelWidth set the width of label (0-12)
6855 * @cfg {String} labelAlign (top|left)
6856 * @cfg {Boolean} readOnly Specifies that the field should be read-only
6857 * @cfg {String} align (left|center|right) Default left
6861 * Create a new Input
6862 * @param {Object} config The config object
6865 Roo.bootstrap.Input = function(config){
6866 Roo.bootstrap.Input.superclass.constructor.call(this, config);
6871 * Fires when this field receives input focus.
6872 * @param {Roo.form.Field} this
6877 * Fires when this field loses input focus.
6878 * @param {Roo.form.Field} this
6883 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
6884 * {@link Roo.EventObject#getKey} to determine which key was pressed.
6885 * @param {Roo.form.Field} this
6886 * @param {Roo.EventObject} e The event object
6891 * Fires just before the field blurs if the field value has changed.
6892 * @param {Roo.form.Field} this
6893 * @param {Mixed} newValue The new value
6894 * @param {Mixed} oldValue The original value
6899 * Fires after the field has been marked as invalid.
6900 * @param {Roo.form.Field} this
6901 * @param {String} msg The validation message
6906 * Fires after the field has been validated with no errors.
6907 * @param {Roo.form.Field} this
6912 * Fires after the key up
6913 * @param {Roo.form.Field} this
6914 * @param {Roo.EventObject} e The event Object
6920 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
6922 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6923 automatic validation (defaults to "keyup").
6925 validationEvent : "keyup",
6927 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6929 validateOnBlur : true,
6931 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6933 validationDelay : 250,
6935 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6937 focusClass : "x-form-focus", // not needed???
6941 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6943 invalidClass : "has-error",
6946 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6948 selectOnFocus : false,
6951 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6955 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6960 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6962 disableKeyFilter : false,
6965 * @cfg {Boolean} disabled True to disable the field (defaults to false).
6969 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6973 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6975 blankText : "This field is required",
6978 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6982 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6984 maxLength : Number.MAX_VALUE,
6986 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6988 minLengthText : "The minimum length for this field is {0}",
6990 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6992 maxLengthText : "The maximum length for this field is {0}",
6996 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6997 * If available, this function will be called only after the basic validators all return true, and will be passed the
6998 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7002 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7003 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7004 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7008 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7031 formatedValue : false,
7033 parentLabelAlign : function()
7036 while (parent.parent()) {
7037 parent = parent.parent();
7038 if (typeof(parent.labelAlign) !='undefined') {
7039 return parent.labelAlign;
7046 getAutoCreate : function(){
7048 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7054 if(this.inputType != 'hidden'){
7055 cfg.cls = 'form-group' //input-group
7061 type : this.inputType,
7063 cls : 'form-control',
7064 placeholder : this.placeholder || ''
7069 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7072 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7073 input.maxLength = this.maxLength;
7076 if (this.disabled) {
7077 input.disabled=true;
7080 if (this.readOnly) {
7081 input.readonly=true;
7085 input.name = this.name;
7088 input.cls += ' input-' + this.size;
7091 ['xs','sm','md','lg'].map(function(size){
7092 if (settings[size]) {
7093 cfg.cls += ' col-' + size + '-' + settings[size];
7097 var inputblock = input;
7099 if (this.before || this.after) {
7102 cls : 'input-group',
7105 if (this.before && typeof(this.before) == 'string') {
7107 inputblock.cn.push({
7109 cls : 'roo-input-before input-group-addon',
7113 if (this.before && typeof(this.before) == 'object') {
7114 this.before = Roo.factory(this.before);
7115 Roo.log(this.before);
7116 inputblock.cn.push({
7118 cls : 'roo-input-before input-group-' +
7119 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7123 inputblock.cn.push(input);
7125 if (this.after && typeof(this.after) == 'string') {
7126 inputblock.cn.push({
7128 cls : 'roo-input-after input-group-addon',
7132 if (this.after && typeof(this.after) == 'object') {
7133 this.after = Roo.factory(this.after);
7134 Roo.log(this.after);
7135 inputblock.cn.push({
7137 cls : 'roo-input-after input-group-' +
7138 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7143 if (align ==='left' && this.fieldLabel.length) {
7144 Roo.log("left and has label");
7150 cls : 'control-label col-sm-' + this.labelWidth,
7151 html : this.fieldLabel
7155 cls : "col-sm-" + (12 - this.labelWidth),
7162 } else if ( this.fieldLabel.length) {
7168 //cls : 'input-group-addon',
7169 html : this.fieldLabel
7179 Roo.log(" no label && no align");
7188 Roo.log('input-parentType: ' + this.parentType);
7190 if (this.parentType === 'Navbar' && this.parent().bar) {
7191 cfg.cls += ' navbar-form';
7199 * return the real input element.
7201 inputEl: function ()
7203 return this.el.select('input.form-control',true).first();
7205 setDisabled : function(v)
7207 var i = this.inputEl().dom;
7209 i.removeAttribute('disabled');
7213 i.setAttribute('disabled','true');
7215 initEvents : function()
7218 this.inputEl().on("keydown" , this.fireKey, this);
7219 this.inputEl().on("focus", this.onFocus, this);
7220 this.inputEl().on("blur", this.onBlur, this);
7222 this.inputEl().relayEvent('keyup', this);
7224 // reference to original value for reset
7225 this.originalValue = this.getValue();
7226 //Roo.form.TextField.superclass.initEvents.call(this);
7227 if(this.validationEvent == 'keyup'){
7228 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7229 this.inputEl().on('keyup', this.filterValidation, this);
7231 else if(this.validationEvent !== false){
7232 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7235 if(this.selectOnFocus){
7236 this.on("focus", this.preFocus, this);
7239 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7240 this.inputEl().on("keypress", this.filterKeys, this);
7243 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
7244 this.el.on("click", this.autoSize, this);
7247 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7248 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7251 if (typeof(this.before) == 'object') {
7252 this.before.render(this.el.select('.roo-input-before',true).first());
7254 if (typeof(this.after) == 'object') {
7255 this.after.render(this.el.select('.roo-input-after',true).first());
7260 filterValidation : function(e){
7261 if(!e.isNavKeyPress()){
7262 this.validationTask.delay(this.validationDelay);
7266 * Validates the field value
7267 * @return {Boolean} True if the value is valid, else false
7269 validate : function(){
7270 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7271 if(this.disabled || this.validateValue(this.getRawValue())){
7272 this.clearInvalid();
7280 * Validates a value according to the field's validation rules and marks the field as invalid
7281 * if the validation fails
7282 * @param {Mixed} value The value to validate
7283 * @return {Boolean} True if the value is valid, else false
7285 validateValue : function(value){
7286 if(value.length < 1) { // if it's blank
7287 if(this.allowBlank){
7288 this.clearInvalid();
7291 this.markInvalid(this.blankText);
7295 if(value.length < this.minLength){
7296 this.markInvalid(String.format(this.minLengthText, this.minLength));
7299 if(value.length > this.maxLength){
7300 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7304 var vt = Roo.form.VTypes;
7305 if(!vt[this.vtype](value, this)){
7306 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7310 if(typeof this.validator == "function"){
7311 var msg = this.validator(value);
7313 this.markInvalid(msg);
7317 if(this.regex && !this.regex.test(value)){
7318 this.markInvalid(this.regexText);
7327 fireKey : function(e){
7328 //Roo.log('field ' + e.getKey());
7329 if(e.isNavKeyPress()){
7330 this.fireEvent("specialkey", this, e);
7333 focus : function (selectText){
7335 this.inputEl().focus();
7336 if(selectText === true){
7337 this.inputEl().dom.select();
7343 onFocus : function(){
7344 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7345 // this.el.addClass(this.focusClass);
7348 this.hasFocus = true;
7349 this.startValue = this.getValue();
7350 this.fireEvent("focus", this);
7354 beforeBlur : Roo.emptyFn,
7358 onBlur : function(){
7360 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7361 //this.el.removeClass(this.focusClass);
7363 this.hasFocus = false;
7364 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7367 var v = this.getValue();
7368 if(String(v) !== String(this.startValue)){
7369 this.fireEvent('change', this, v, this.startValue);
7371 this.fireEvent("blur", this);
7375 * Resets the current field value to the originally loaded value and clears any validation messages
7378 this.setValue(this.originalValue);
7379 this.clearInvalid();
7382 * Returns the name of the field
7383 * @return {Mixed} name The name field
7385 getName: function(){
7389 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
7390 * @return {Mixed} value The field value
7392 getValue : function(){
7394 var v = this.inputEl().getValue();
7399 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
7400 * @return {Mixed} value The field value
7402 getRawValue : function(){
7403 var v = this.inputEl().getValue();
7409 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
7410 * @param {Mixed} value The value to set
7412 setRawValue : function(v){
7413 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7416 selectText : function(start, end){
7417 var v = this.getRawValue();
7419 start = start === undefined ? 0 : start;
7420 end = end === undefined ? v.length : end;
7421 var d = this.inputEl().dom;
7422 if(d.setSelectionRange){
7423 d.setSelectionRange(start, end);
7424 }else if(d.createTextRange){
7425 var range = d.createTextRange();
7426 range.moveStart("character", start);
7427 range.moveEnd("character", v.length-end);
7434 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
7435 * @param {Mixed} value The value to set
7437 setValue : function(v){
7440 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7446 processValue : function(value){
7447 if(this.stripCharsRe){
7448 var newValue = value.replace(this.stripCharsRe, '');
7449 if(newValue !== value){
7450 this.setRawValue(newValue);
7457 preFocus : function(){
7459 if(this.selectOnFocus){
7460 this.inputEl().dom.select();
7463 filterKeys : function(e){
7465 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7468 var c = e.getCharCode(), cc = String.fromCharCode(c);
7469 if(Roo.isIE && (e.isSpecialKey() || !cc)){
7472 if(!this.maskRe.test(cc)){
7477 * Clear any invalid styles/messages for this field
7479 clearInvalid : function(){
7481 if(!this.el || this.preventMark){ // not rendered
7484 this.el.removeClass(this.invalidClass);
7486 switch(this.msgTarget){
7488 this.el.dom.qtip = '';
7491 this.el.dom.title = '';
7495 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7500 this.errorIcon.dom.qtip = '';
7501 this.errorIcon.hide();
7502 this.un('resize', this.alignErrorIcon, this);
7506 var t = Roo.getDom(this.msgTarget);
7508 t.style.display = 'none';
7512 this.fireEvent('valid', this);
7515 * Mark this field as invalid
7516 * @param {String} msg The validation message
7518 markInvalid : function(msg){
7519 if(!this.el || this.preventMark){ // not rendered
7522 this.el.addClass(this.invalidClass);
7524 msg = msg || this.invalidText;
7525 switch(this.msgTarget){
7527 this.el.dom.qtip = msg;
7528 this.el.dom.qclass = 'x-form-invalid-tip';
7529 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7530 Roo.QuickTips.enable();
7534 this.el.dom.title = msg;
7538 var elp = this.el.findParent('.x-form-element', 5, true);
7539 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7540 this.errorEl.setWidth(elp.getWidth(true)-20);
7542 this.errorEl.update(msg);
7543 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7546 if(!this.errorIcon){
7547 var elp = this.el.findParent('.x-form-element', 5, true);
7548 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7550 this.alignErrorIcon();
7551 this.errorIcon.dom.qtip = msg;
7552 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7553 this.errorIcon.show();
7554 this.on('resize', this.alignErrorIcon, this);
7557 var t = Roo.getDom(this.msgTarget);
7559 t.style.display = this.msgDisplay;
7563 this.fireEvent('invalid', this, msg);
7566 SafariOnKeyDown : function(event)
7568 // this is a workaround for a password hang bug on chrome/ webkit.
7570 var isSelectAll = false;
7572 if(this.inputEl().dom.selectionEnd > 0){
7573 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7575 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7576 event.preventDefault();
7581 if(isSelectAll){ // backspace and delete key
7583 event.preventDefault();
7584 // this is very hacky as keydown always get's upper case.
7586 var cc = String.fromCharCode(event.getCharCode());
7587 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
7591 adjustWidth : function(tag, w){
7592 tag = tag.toLowerCase();
7593 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7594 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7598 if(tag == 'textarea'){
7601 }else if(Roo.isOpera){
7605 if(tag == 'textarea'){
7624 * @class Roo.bootstrap.TextArea
7625 * @extends Roo.bootstrap.Input
7626 * Bootstrap TextArea class
7627 * @cfg {Number} cols Specifies the visible width of a text area
7628 * @cfg {Number} rows Specifies the visible number of lines in a text area
7629 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7630 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7631 * @cfg {string} html text
7634 * Create a new TextArea
7635 * @param {Object} config The config object
7638 Roo.bootstrap.TextArea = function(config){
7639 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7643 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
7653 getAutoCreate : function(){
7655 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7666 value : this.value || '',
7667 html: this.html || '',
7668 cls : 'form-control',
7669 placeholder : this.placeholder || ''
7673 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7674 input.maxLength = this.maxLength;
7678 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7682 input.cols = this.cols;
7685 if (this.readOnly) {
7686 input.readonly = true;
7690 input.name = this.name;
7694 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7698 ['xs','sm','md','lg'].map(function(size){
7699 if (settings[size]) {
7700 cfg.cls += ' col-' + size + '-' + settings[size];
7704 var inputblock = input;
7706 if (this.before || this.after) {
7709 cls : 'input-group',
7713 inputblock.cn.push({
7715 cls : 'input-group-addon',
7719 inputblock.cn.push(input);
7721 inputblock.cn.push({
7723 cls : 'input-group-addon',
7730 if (align ==='left' && this.fieldLabel.length) {
7731 Roo.log("left and has label");
7737 cls : 'control-label col-sm-' + this.labelWidth,
7738 html : this.fieldLabel
7742 cls : "col-sm-" + (12 - this.labelWidth),
7749 } else if ( this.fieldLabel.length) {
7755 //cls : 'input-group-addon',
7756 html : this.fieldLabel
7766 Roo.log(" no label && no align");
7776 if (this.disabled) {
7777 input.disabled=true;
7784 * return the real textarea element.
7786 inputEl: function ()
7788 return this.el.select('textarea.form-control',true).first();
7796 * trigger field - base class for combo..
7801 * @class Roo.bootstrap.TriggerField
7802 * @extends Roo.bootstrap.Input
7803 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7804 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7805 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7806 * for which you can provide a custom implementation. For example:
7808 var trigger = new Roo.bootstrap.TriggerField();
7809 trigger.onTriggerClick = myTriggerFn;
7810 trigger.applyTo('my-field');
7813 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7814 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7815 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
7816 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7818 * Create a new TriggerField.
7819 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7820 * to the base TextField)
7822 Roo.bootstrap.TriggerField = function(config){
7823 this.mimicing = false;
7824 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7827 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
7829 * @cfg {String} triggerClass A CSS class to apply to the trigger
7832 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7836 /** @cfg {Boolean} grow @hide */
7837 /** @cfg {Number} growMin @hide */
7838 /** @cfg {Number} growMax @hide */
7844 autoSize: Roo.emptyFn,
7851 actionMode : 'wrap',
7855 getAutoCreate : function(){
7857 var align = this.labelAlign || this.parentLabelAlign();
7862 cls: 'form-group' //input-group
7869 type : this.inputType,
7870 cls : 'form-control',
7871 autocomplete: 'off',
7872 placeholder : this.placeholder || ''
7876 input.name = this.name;
7879 input.cls += ' input-' + this.size;
7882 if (this.disabled) {
7883 input.disabled=true;
7886 var inputblock = input;
7888 if (this.before || this.after) {
7891 cls : 'input-group',
7895 inputblock.cn.push({
7897 cls : 'input-group-addon',
7901 inputblock.cn.push(input);
7903 inputblock.cn.push({
7905 cls : 'input-group-addon',
7918 cls: 'form-hidden-field'
7926 Roo.log('multiple');
7934 cls: 'form-hidden-field'
7938 cls: 'select2-choices',
7942 cls: 'select2-search-field',
7955 cls: 'select2-container input-group',
7960 // cls: 'typeahead typeahead-long dropdown-menu',
7961 // style: 'display:none'
7966 if(!this.multiple && this.showToggleBtn){
7969 cls : 'input-group-addon btn dropdown-toggle',
7977 cls: 'combobox-clear',
7991 combobox.cls += ' select2-container-multi';
7994 if (align ==='left' && this.fieldLabel.length) {
7996 Roo.log("left and has label");
8002 cls : 'control-label col-sm-' + this.labelWidth,
8003 html : this.fieldLabel
8007 cls : "col-sm-" + (12 - this.labelWidth),
8014 } else if ( this.fieldLabel.length) {
8020 //cls : 'input-group-addon',
8021 html : this.fieldLabel
8031 Roo.log(" no label && no align");
8038 ['xs','sm','md','lg'].map(function(size){
8039 if (settings[size]) {
8040 cfg.cls += ' col-' + size + '-' + settings[size];
8051 onResize : function(w, h){
8052 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8053 // if(typeof w == 'number'){
8054 // var x = w - this.trigger.getWidth();
8055 // this.inputEl().setWidth(this.adjustWidth('input', x));
8056 // this.trigger.setStyle('left', x+'px');
8061 adjustSize : Roo.BoxComponent.prototype.adjustSize,
8064 getResizeEl : function(){
8065 return this.inputEl();
8069 getPositionEl : function(){
8070 return this.inputEl();
8074 alignErrorIcon : function(){
8075 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8079 initEvents : function(){
8083 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8084 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8085 if(!this.multiple && this.showToggleBtn){
8086 this.trigger = this.el.select('span.dropdown-toggle',true).first();
8087 if(this.hideTrigger){
8088 this.trigger.setDisplayed(false);
8090 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8094 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8097 //this.trigger.addClassOnOver('x-form-trigger-over');
8098 //this.trigger.addClassOnClick('x-form-trigger-click');
8101 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8105 createList : function()
8107 this.list = Roo.get(document.body).createChild({
8109 cls: 'typeahead typeahead-long dropdown-menu',
8110 style: 'display:none'
8113 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8118 initTrigger : function(){
8123 onDestroy : function(){
8125 this.trigger.removeAllListeners();
8126 // this.trigger.remove();
8129 // this.wrap.remove();
8131 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8135 onFocus : function(){
8136 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8139 this.wrap.addClass('x-trigger-wrap-focus');
8140 this.mimicing = true;
8141 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8142 if(this.monitorTab){
8143 this.el.on("keydown", this.checkTab, this);
8150 checkTab : function(e){
8151 if(e.getKey() == e.TAB){
8157 onBlur : function(){
8162 mimicBlur : function(e, t){
8164 if(!this.wrap.contains(t) && this.validateBlur()){
8171 triggerBlur : function(){
8172 this.mimicing = false;
8173 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8174 if(this.monitorTab){
8175 this.el.un("keydown", this.checkTab, this);
8177 //this.wrap.removeClass('x-trigger-wrap-focus');
8178 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8182 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8183 validateBlur : function(e, t){
8188 onDisable : function(){
8189 this.inputEl().dom.disabled = true;
8190 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8192 // this.wrap.addClass('x-item-disabled');
8197 onEnable : function(){
8198 this.inputEl().dom.disabled = false;
8199 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8201 // this.el.removeClass('x-item-disabled');
8206 onShow : function(){
8207 var ae = this.getActionEl();
8210 ae.dom.style.display = '';
8211 ae.dom.style.visibility = 'visible';
8217 onHide : function(){
8218 var ae = this.getActionEl();
8219 ae.dom.style.display = 'none';
8223 * The function that should handle the trigger's click event. This method does nothing by default until overridden
8224 * by an implementing function.
8226 * @param {EventObject} e
8228 onTriggerClick : Roo.emptyFn
8232 * Ext JS Library 1.1.1
8233 * Copyright(c) 2006-2007, Ext JS, LLC.
8235 * Originally Released Under LGPL - original licence link has changed is not relivant.
8238 * <script type="text/javascript">
8243 * @class Roo.data.SortTypes
8245 * Defines the default sorting (casting?) comparison functions used when sorting data.
8247 Roo.data.SortTypes = {
8249 * Default sort that does nothing
8250 * @param {Mixed} s The value being converted
8251 * @return {Mixed} The comparison value
8258 * The regular expression used to strip tags
8262 stripTagsRE : /<\/?[^>]+>/gi,
8265 * Strips all HTML tags to sort on text only
8266 * @param {Mixed} s The value being converted
8267 * @return {String} The comparison value
8269 asText : function(s){
8270 return String(s).replace(this.stripTagsRE, "");
8274 * Strips all HTML tags to sort on text only - Case insensitive
8275 * @param {Mixed} s The value being converted
8276 * @return {String} The comparison value
8278 asUCText : function(s){
8279 return String(s).toUpperCase().replace(this.stripTagsRE, "");
8283 * Case insensitive string
8284 * @param {Mixed} s The value being converted
8285 * @return {String} The comparison value
8287 asUCString : function(s) {
8288 return String(s).toUpperCase();
8293 * @param {Mixed} s The value being converted
8294 * @return {Number} The comparison value
8296 asDate : function(s) {
8300 if(s instanceof Date){
8303 return Date.parse(String(s));
8308 * @param {Mixed} s The value being converted
8309 * @return {Float} The comparison value
8311 asFloat : function(s) {
8312 var val = parseFloat(String(s).replace(/,/g, ""));
8313 if(isNaN(val)) val = 0;
8319 * @param {Mixed} s The value being converted
8320 * @return {Number} The comparison value
8322 asInt : function(s) {
8323 var val = parseInt(String(s).replace(/,/g, ""));
8324 if(isNaN(val)) val = 0;
8329 * Ext JS Library 1.1.1
8330 * Copyright(c) 2006-2007, Ext JS, LLC.
8332 * Originally Released Under LGPL - original licence link has changed is not relivant.
8335 * <script type="text/javascript">
8339 * @class Roo.data.Record
8340 * Instances of this class encapsulate both record <em>definition</em> information, and record
8341 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8342 * to access Records cached in an {@link Roo.data.Store} object.<br>
8344 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8345 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8348 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8350 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8351 * {@link #create}. The parameters are the same.
8352 * @param {Array} data An associative Array of data values keyed by the field name.
8353 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8354 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8355 * not specified an integer id is generated.
8357 Roo.data.Record = function(data, id){
8358 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8363 * Generate a constructor for a specific record layout.
8364 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8365 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8366 * Each field definition object may contain the following properties: <ul>
8367 * <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,
8368 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8369 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8370 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8371 * is being used, then this is a string containing the javascript expression to reference the data relative to
8372 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8373 * to the data item relative to the record element. If the mapping expression is the same as the field name,
8374 * this may be omitted.</p></li>
8375 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8376 * <ul><li>auto (Default, implies no conversion)</li>
8381 * <li>date</li></ul></p></li>
8382 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8383 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8384 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8385 * by the Reader into an object that will be stored in the Record. It is passed the
8386 * following parameters:<ul>
8387 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8389 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8391 * <br>usage:<br><pre><code>
8392 var TopicRecord = Roo.data.Record.create(
8393 {name: 'title', mapping: 'topic_title'},
8394 {name: 'author', mapping: 'username'},
8395 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8396 {name: 'lastPost', mapping: 'post_time', type: 'date'},
8397 {name: 'lastPoster', mapping: 'user2'},
8398 {name: 'excerpt', mapping: 'post_text'}
8401 var myNewRecord = new TopicRecord({
8402 title: 'Do my job please',
8405 lastPost: new Date(),
8406 lastPoster: 'Animal',
8407 excerpt: 'No way dude!'
8409 myStore.add(myNewRecord);
8414 Roo.data.Record.create = function(o){
8416 f.superclass.constructor.apply(this, arguments);
8418 Roo.extend(f, Roo.data.Record);
8419 var p = f.prototype;
8420 p.fields = new Roo.util.MixedCollection(false, function(field){
8423 for(var i = 0, len = o.length; i < len; i++){
8424 p.fields.add(new Roo.data.Field(o[i]));
8426 f.getField = function(name){
8427 return p.fields.get(name);
8432 Roo.data.Record.AUTO_ID = 1000;
8433 Roo.data.Record.EDIT = 'edit';
8434 Roo.data.Record.REJECT = 'reject';
8435 Roo.data.Record.COMMIT = 'commit';
8437 Roo.data.Record.prototype = {
8439 * Readonly flag - true if this record has been modified.
8448 join : function(store){
8453 * Set the named field to the specified value.
8454 * @param {String} name The name of the field to set.
8455 * @param {Object} value The value to set the field to.
8457 set : function(name, value){
8458 if(this.data[name] == value){
8465 if(typeof this.modified[name] == 'undefined'){
8466 this.modified[name] = this.data[name];
8468 this.data[name] = value;
8469 if(!this.editing && this.store){
8470 this.store.afterEdit(this);
8475 * Get the value of the named field.
8476 * @param {String} name The name of the field to get the value of.
8477 * @return {Object} The value of the field.
8479 get : function(name){
8480 return this.data[name];
8484 beginEdit : function(){
8485 this.editing = true;
8490 cancelEdit : function(){
8491 this.editing = false;
8492 delete this.modified;
8496 endEdit : function(){
8497 this.editing = false;
8498 if(this.dirty && this.store){
8499 this.store.afterEdit(this);
8504 * Usually called by the {@link Roo.data.Store} which owns the Record.
8505 * Rejects all changes made to the Record since either creation, or the last commit operation.
8506 * Modified fields are reverted to their original values.
8508 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8509 * of reject operations.
8511 reject : function(){
8512 var m = this.modified;
8514 if(typeof m[n] != "function"){
8515 this.data[n] = m[n];
8519 delete this.modified;
8520 this.editing = false;
8522 this.store.afterReject(this);
8527 * Usually called by the {@link Roo.data.Store} which owns the Record.
8528 * Commits all changes made to the Record since either creation, or the last commit operation.
8530 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8531 * of commit operations.
8533 commit : function(){
8535 delete this.modified;
8536 this.editing = false;
8538 this.store.afterCommit(this);
8543 hasError : function(){
8544 return this.error != null;
8548 clearError : function(){
8553 * Creates a copy of this record.
8554 * @param {String} id (optional) A new record id if you don't want to use this record's id
8557 copy : function(newId) {
8558 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8562 * Ext JS Library 1.1.1
8563 * Copyright(c) 2006-2007, Ext JS, LLC.
8565 * Originally Released Under LGPL - original licence link has changed is not relivant.
8568 * <script type="text/javascript">
8574 * @class Roo.data.Store
8575 * @extends Roo.util.Observable
8576 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8577 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8579 * 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
8580 * has no knowledge of the format of the data returned by the Proxy.<br>
8582 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8583 * instances from the data object. These records are cached and made available through accessor functions.
8585 * Creates a new Store.
8586 * @param {Object} config A config object containing the objects needed for the Store to access data,
8587 * and read the data into Records.
8589 Roo.data.Store = function(config){
8590 this.data = new Roo.util.MixedCollection(false);
8591 this.data.getKey = function(o){
8594 this.baseParams = {};
8601 "multisort" : "_multisort"
8604 if(config && config.data){
8605 this.inlineData = config.data;
8609 Roo.apply(this, config);
8611 if(this.reader){ // reader passed
8612 this.reader = Roo.factory(this.reader, Roo.data);
8613 this.reader.xmodule = this.xmodule || false;
8614 if(!this.recordType){
8615 this.recordType = this.reader.recordType;
8617 if(this.reader.onMetaChange){
8618 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8622 if(this.recordType){
8623 this.fields = this.recordType.prototype.fields;
8629 * @event datachanged
8630 * Fires when the data cache has changed, and a widget which is using this Store
8631 * as a Record cache should refresh its view.
8632 * @param {Store} this
8637 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8638 * @param {Store} this
8639 * @param {Object} meta The JSON metadata
8644 * Fires when Records have been added to the Store
8645 * @param {Store} this
8646 * @param {Roo.data.Record[]} records The array of Records added
8647 * @param {Number} index The index at which the record(s) were added
8652 * Fires when a Record has been removed from the Store
8653 * @param {Store} this
8654 * @param {Roo.data.Record} record The Record that was removed
8655 * @param {Number} index The index at which the record was removed
8660 * Fires when a Record has been updated
8661 * @param {Store} this
8662 * @param {Roo.data.Record} record The Record that was updated
8663 * @param {String} operation The update operation being performed. Value may be one of:
8665 Roo.data.Record.EDIT
8666 Roo.data.Record.REJECT
8667 Roo.data.Record.COMMIT
8673 * Fires when the data cache has been cleared.
8674 * @param {Store} this
8679 * Fires before a request is made for a new data object. If the beforeload handler returns false
8680 * the load action will be canceled.
8681 * @param {Store} this
8682 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8686 * @event beforeloadadd
8687 * Fires after a new set of Records has been loaded.
8688 * @param {Store} this
8689 * @param {Roo.data.Record[]} records The Records that were loaded
8690 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8692 beforeloadadd : true,
8695 * Fires after a new set of Records has been loaded, before they are added to the store.
8696 * @param {Store} this
8697 * @param {Roo.data.Record[]} records The Records that were loaded
8698 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8699 * @params {Object} return from reader
8703 * @event loadexception
8704 * Fires if an exception occurs in the Proxy during loading.
8705 * Called with the signature of the Proxy's "loadexception" event.
8706 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8709 * @param {Object} return from JsonData.reader() - success, totalRecords, records
8710 * @param {Object} load options
8711 * @param {Object} jsonData from your request (normally this contains the Exception)
8713 loadexception : true
8717 this.proxy = Roo.factory(this.proxy, Roo.data);
8718 this.proxy.xmodule = this.xmodule || false;
8719 this.relayEvents(this.proxy, ["loadexception"]);
8721 this.sortToggle = {};
8722 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8724 Roo.data.Store.superclass.constructor.call(this);
8726 if(this.inlineData){
8727 this.loadData(this.inlineData);
8728 delete this.inlineData;
8732 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8734 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
8735 * without a remote query - used by combo/forms at present.
8739 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8742 * @cfg {Array} data Inline data to be loaded when the store is initialized.
8745 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8746 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8749 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8750 * on any HTTP request
8753 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8756 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8760 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8761 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8766 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8767 * loaded or when a record is removed. (defaults to false).
8769 pruneModifiedRecords : false,
8775 * Add Records to the Store and fires the add event.
8776 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8778 add : function(records){
8779 records = [].concat(records);
8780 for(var i = 0, len = records.length; i < len; i++){
8781 records[i].join(this);
8783 var index = this.data.length;
8784 this.data.addAll(records);
8785 this.fireEvent("add", this, records, index);
8789 * Remove a Record from the Store and fires the remove event.
8790 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8792 remove : function(record){
8793 var index = this.data.indexOf(record);
8794 this.data.removeAt(index);
8795 if(this.pruneModifiedRecords){
8796 this.modified.remove(record);
8798 this.fireEvent("remove", this, record, index);
8802 * Remove all Records from the Store and fires the clear event.
8804 removeAll : function(){
8806 if(this.pruneModifiedRecords){
8809 this.fireEvent("clear", this);
8813 * Inserts Records to the Store at the given index and fires the add event.
8814 * @param {Number} index The start index at which to insert the passed Records.
8815 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8817 insert : function(index, records){
8818 records = [].concat(records);
8819 for(var i = 0, len = records.length; i < len; i++){
8820 this.data.insert(index, records[i]);
8821 records[i].join(this);
8823 this.fireEvent("add", this, records, index);
8827 * Get the index within the cache of the passed Record.
8828 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8829 * @return {Number} The index of the passed Record. Returns -1 if not found.
8831 indexOf : function(record){
8832 return this.data.indexOf(record);
8836 * Get the index within the cache of the Record with the passed id.
8837 * @param {String} id The id of the Record to find.
8838 * @return {Number} The index of the Record. Returns -1 if not found.
8840 indexOfId : function(id){
8841 return this.data.indexOfKey(id);
8845 * Get the Record with the specified id.
8846 * @param {String} id The id of the Record to find.
8847 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8849 getById : function(id){
8850 return this.data.key(id);
8854 * Get the Record at the specified index.
8855 * @param {Number} index The index of the Record to find.
8856 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8858 getAt : function(index){
8859 return this.data.itemAt(index);
8863 * Returns a range of Records between specified indices.
8864 * @param {Number} startIndex (optional) The starting index (defaults to 0)
8865 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8866 * @return {Roo.data.Record[]} An array of Records
8868 getRange : function(start, end){
8869 return this.data.getRange(start, end);
8873 storeOptions : function(o){
8874 o = Roo.apply({}, o);
8877 this.lastOptions = o;
8881 * Loads the Record cache from the configured Proxy using the configured Reader.
8883 * If using remote paging, then the first load call must specify the <em>start</em>
8884 * and <em>limit</em> properties in the options.params property to establish the initial
8885 * position within the dataset, and the number of Records to cache on each read from the Proxy.
8887 * <strong>It is important to note that for remote data sources, loading is asynchronous,
8888 * and this call will return before the new data has been loaded. Perform any post-processing
8889 * in a callback function, or in a "load" event handler.</strong>
8891 * @param {Object} options An object containing properties which control loading options:<ul>
8892 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8893 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8894 * passed the following arguments:<ul>
8895 * <li>r : Roo.data.Record[]</li>
8896 * <li>options: Options object from the load call</li>
8897 * <li>success: Boolean success indicator</li></ul></li>
8898 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8899 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8902 load : function(options){
8903 options = options || {};
8904 if(this.fireEvent("beforeload", this, options) !== false){
8905 this.storeOptions(options);
8906 var p = Roo.apply(options.params || {}, this.baseParams);
8907 // if meta was not loaded from remote source.. try requesting it.
8908 if (!this.reader.metaFromRemote) {
8911 if(this.sortInfo && this.remoteSort){
8912 var pn = this.paramNames;
8913 p[pn["sort"]] = this.sortInfo.field;
8914 p[pn["dir"]] = this.sortInfo.direction;
8916 if (this.multiSort) {
8917 var pn = this.paramNames;
8918 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8921 this.proxy.load(p, this.reader, this.loadRecords, this, options);
8926 * Reloads the Record cache from the configured Proxy using the configured Reader and
8927 * the options from the last load operation performed.
8928 * @param {Object} options (optional) An object containing properties which may override the options
8929 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8930 * the most recently used options are reused).
8932 reload : function(options){
8933 this.load(Roo.applyIf(options||{}, this.lastOptions));
8937 // Called as a callback by the Reader during a load operation.
8938 loadRecords : function(o, options, success){
8939 if(!o || success === false){
8940 if(success !== false){
8941 this.fireEvent("load", this, [], options, o);
8943 if(options.callback){
8944 options.callback.call(options.scope || this, [], options, false);
8948 // if data returned failure - throw an exception.
8949 if (o.success === false) {
8950 // show a message if no listener is registered.
8951 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8952 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8954 // loadmask wil be hooked into this..
8955 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8958 var r = o.records, t = o.totalRecords || r.length;
8960 this.fireEvent("beforeloadadd", this, r, options, o);
8962 if(!options || options.add !== true){
8963 if(this.pruneModifiedRecords){
8966 for(var i = 0, len = r.length; i < len; i++){
8970 this.data = this.snapshot;
8971 delete this.snapshot;
8974 this.data.addAll(r);
8975 this.totalLength = t;
8977 this.fireEvent("datachanged", this);
8979 this.totalLength = Math.max(t, this.data.length+r.length);
8982 this.fireEvent("load", this, r, options, o);
8983 if(options.callback){
8984 options.callback.call(options.scope || this, r, options, true);
8990 * Loads data from a passed data block. A Reader which understands the format of the data
8991 * must have been configured in the constructor.
8992 * @param {Object} data The data block from which to read the Records. The format of the data expected
8993 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8994 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8996 loadData : function(o, append){
8997 var r = this.reader.readRecords(o);
8998 this.loadRecords(r, {add: append}, true);
9002 * Gets the number of cached records.
9004 * <em>If using paging, this may not be the total size of the dataset. If the data object
9005 * used by the Reader contains the dataset size, then the getTotalCount() function returns
9006 * the data set size</em>
9008 getCount : function(){
9009 return this.data.length || 0;
9013 * Gets the total number of records in the dataset as returned by the server.
9015 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9016 * the dataset size</em>
9018 getTotalCount : function(){
9019 return this.totalLength || 0;
9023 * Returns the sort state of the Store as an object with two properties:
9025 field {String} The name of the field by which the Records are sorted
9026 direction {String} The sort order, "ASC" or "DESC"
9029 getSortState : function(){
9030 return this.sortInfo;
9034 applySort : function(){
9035 if(this.sortInfo && !this.remoteSort){
9036 var s = this.sortInfo, f = s.field;
9037 var st = this.fields.get(f).sortType;
9038 var fn = function(r1, r2){
9039 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9040 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9042 this.data.sort(s.direction, fn);
9043 if(this.snapshot && this.snapshot != this.data){
9044 this.snapshot.sort(s.direction, fn);
9050 * Sets the default sort column and order to be used by the next load operation.
9051 * @param {String} fieldName The name of the field to sort by.
9052 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9054 setDefaultSort : function(field, dir){
9055 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9060 * If remote sorting is used, the sort is performed on the server, and the cache is
9061 * reloaded. If local sorting is used, the cache is sorted internally.
9062 * @param {String} fieldName The name of the field to sort by.
9063 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9065 sort : function(fieldName, dir){
9066 var f = this.fields.get(fieldName);
9068 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9070 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9071 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9076 this.sortToggle[f.name] = dir;
9077 this.sortInfo = {field: f.name, direction: dir};
9078 if(!this.remoteSort){
9080 this.fireEvent("datachanged", this);
9082 this.load(this.lastOptions);
9087 * Calls the specified function for each of the Records in the cache.
9088 * @param {Function} fn The function to call. The Record is passed as the first parameter.
9089 * Returning <em>false</em> aborts and exits the iteration.
9090 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9092 each : function(fn, scope){
9093 this.data.each(fn, scope);
9097 * Gets all records modified since the last commit. Modified records are persisted across load operations
9098 * (e.g., during paging).
9099 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9101 getModifiedRecords : function(){
9102 return this.modified;
9106 createFilterFn : function(property, value, anyMatch){
9107 if(!value.exec){ // not a regex
9108 value = String(value);
9109 if(value.length == 0){
9112 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9115 return value.test(r.data[property]);
9120 * Sums the value of <i>property</i> for each record between start and end and returns the result.
9121 * @param {String} property A field on your records
9122 * @param {Number} start The record index to start at (defaults to 0)
9123 * @param {Number} end The last record index to include (defaults to length - 1)
9124 * @return {Number} The sum
9126 sum : function(property, start, end){
9127 var rs = this.data.items, v = 0;
9129 end = (end || end === 0) ? end : rs.length-1;
9131 for(var i = start; i <= end; i++){
9132 v += (rs[i].data[property] || 0);
9138 * Filter the records by a specified property.
9139 * @param {String} field A field on your records
9140 * @param {String/RegExp} value Either a string that the field
9141 * should start with or a RegExp to test against the field
9142 * @param {Boolean} anyMatch True to match any part not just the beginning
9144 filter : function(property, value, anyMatch){
9145 var fn = this.createFilterFn(property, value, anyMatch);
9146 return fn ? this.filterBy(fn) : this.clearFilter();
9150 * Filter by a function. The specified function will be called with each
9151 * record in this data source. If the function returns true the record is included,
9152 * otherwise it is filtered.
9153 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9154 * @param {Object} scope (optional) The scope of the function (defaults to this)
9156 filterBy : function(fn, scope){
9157 this.snapshot = this.snapshot || this.data;
9158 this.data = this.queryBy(fn, scope||this);
9159 this.fireEvent("datachanged", this);
9163 * Query the records by a specified property.
9164 * @param {String} field A field on your records
9165 * @param {String/RegExp} value Either a string that the field
9166 * should start with or a RegExp to test against the field
9167 * @param {Boolean} anyMatch True to match any part not just the beginning
9168 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9170 query : function(property, value, anyMatch){
9171 var fn = this.createFilterFn(property, value, anyMatch);
9172 return fn ? this.queryBy(fn) : this.data.clone();
9176 * Query by a function. The specified function will be called with each
9177 * record in this data source. If the function returns true the record is included
9179 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9180 * @param {Object} scope (optional) The scope of the function (defaults to this)
9181 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9183 queryBy : function(fn, scope){
9184 var data = this.snapshot || this.data;
9185 return data.filterBy(fn, scope||this);
9189 * Collects unique values for a particular dataIndex from this store.
9190 * @param {String} dataIndex The property to collect
9191 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9192 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9193 * @return {Array} An array of the unique values
9195 collect : function(dataIndex, allowNull, bypassFilter){
9196 var d = (bypassFilter === true && this.snapshot) ?
9197 this.snapshot.items : this.data.items;
9198 var v, sv, r = [], l = {};
9199 for(var i = 0, len = d.length; i < len; i++){
9200 v = d[i].data[dataIndex];
9202 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9211 * Revert to a view of the Record cache with no filtering applied.
9212 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9214 clearFilter : function(suppressEvent){
9215 if(this.snapshot && this.snapshot != this.data){
9216 this.data = this.snapshot;
9217 delete this.snapshot;
9218 if(suppressEvent !== true){
9219 this.fireEvent("datachanged", this);
9225 afterEdit : function(record){
9226 if(this.modified.indexOf(record) == -1){
9227 this.modified.push(record);
9229 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9233 afterReject : function(record){
9234 this.modified.remove(record);
9235 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9239 afterCommit : function(record){
9240 this.modified.remove(record);
9241 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9245 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9246 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9248 commitChanges : function(){
9249 var m = this.modified.slice(0);
9251 for(var i = 0, len = m.length; i < len; i++){
9257 * Cancel outstanding changes on all changed records.
9259 rejectChanges : function(){
9260 var m = this.modified.slice(0);
9262 for(var i = 0, len = m.length; i < len; i++){
9267 onMetaChange : function(meta, rtype, o){
9268 this.recordType = rtype;
9269 this.fields = rtype.prototype.fields;
9270 delete this.snapshot;
9271 this.sortInfo = meta.sortInfo || this.sortInfo;
9273 this.fireEvent('metachange', this, this.reader.meta);
9276 moveIndex : function(data, type)
9278 var index = this.indexOf(data);
9280 var newIndex = index + type;
9284 this.insert(newIndex, data);
9289 * Ext JS Library 1.1.1
9290 * Copyright(c) 2006-2007, Ext JS, LLC.
9292 * Originally Released Under LGPL - original licence link has changed is not relivant.
9295 * <script type="text/javascript">
9299 * @class Roo.data.SimpleStore
9300 * @extends Roo.data.Store
9301 * Small helper class to make creating Stores from Array data easier.
9302 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9303 * @cfg {Array} fields An array of field definition objects, or field name strings.
9304 * @cfg {Array} data The multi-dimensional array of data
9306 * @param {Object} config
9308 Roo.data.SimpleStore = function(config){
9309 Roo.data.SimpleStore.superclass.constructor.call(this, {
9311 reader: new Roo.data.ArrayReader({
9314 Roo.data.Record.create(config.fields)
9316 proxy : new Roo.data.MemoryProxy(config.data)
9320 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9322 * Ext JS Library 1.1.1
9323 * Copyright(c) 2006-2007, Ext JS, LLC.
9325 * Originally Released Under LGPL - original licence link has changed is not relivant.
9328 * <script type="text/javascript">
9333 * @extends Roo.data.Store
9334 * @class Roo.data.JsonStore
9335 * Small helper class to make creating Stores for JSON data easier. <br/>
9337 var store = new Roo.data.JsonStore({
9338 url: 'get-images.php',
9340 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9343 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9344 * JsonReader and HttpProxy (unless inline data is provided).</b>
9345 * @cfg {Array} fields An array of field definition objects, or field name strings.
9347 * @param {Object} config
9349 Roo.data.JsonStore = function(c){
9350 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9351 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9352 reader: new Roo.data.JsonReader(c, c.fields)
9355 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9357 * Ext JS Library 1.1.1
9358 * Copyright(c) 2006-2007, Ext JS, LLC.
9360 * Originally Released Under LGPL - original licence link has changed is not relivant.
9363 * <script type="text/javascript">
9367 Roo.data.Field = function(config){
9368 if(typeof config == "string"){
9369 config = {name: config};
9371 Roo.apply(this, config);
9377 var st = Roo.data.SortTypes;
9378 // named sortTypes are supported, here we look them up
9379 if(typeof this.sortType == "string"){
9380 this.sortType = st[this.sortType];
9383 // set default sortType for strings and dates
9387 this.sortType = st.asUCString;
9390 this.sortType = st.asDate;
9393 this.sortType = st.none;
9398 var stripRe = /[\$,%]/g;
9400 // prebuilt conversion function for this field, instead of
9401 // switching every time we're reading a value
9403 var cv, dateFormat = this.dateFormat;
9408 cv = function(v){ return v; };
9411 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9415 return v !== undefined && v !== null && v !== '' ?
9416 parseInt(String(v).replace(stripRe, ""), 10) : '';
9421 return v !== undefined && v !== null && v !== '' ?
9422 parseFloat(String(v).replace(stripRe, ""), 10) : '';
9427 cv = function(v){ return v === true || v === "true" || v == 1; };
9434 if(v instanceof Date){
9438 if(dateFormat == "timestamp"){
9439 return new Date(v*1000);
9441 return Date.parseDate(v, dateFormat);
9443 var parsed = Date.parse(v);
9444 return parsed ? new Date(parsed) : null;
9453 Roo.data.Field.prototype = {
9461 * Ext JS Library 1.1.1
9462 * Copyright(c) 2006-2007, Ext JS, LLC.
9464 * Originally Released Under LGPL - original licence link has changed is not relivant.
9467 * <script type="text/javascript">
9470 // Base class for reading structured data from a data source. This class is intended to be
9471 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9474 * @class Roo.data.DataReader
9475 * Base class for reading structured data from a data source. This class is intended to be
9476 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9479 Roo.data.DataReader = function(meta, recordType){
9483 this.recordType = recordType instanceof Array ?
9484 Roo.data.Record.create(recordType) : recordType;
9487 Roo.data.DataReader.prototype = {
9489 * Create an empty record
9490 * @param {Object} data (optional) - overlay some values
9491 * @return {Roo.data.Record} record created.
9493 newRow : function(d) {
9495 this.recordType.prototype.fields.each(function(c) {
9497 case 'int' : da[c.name] = 0; break;
9498 case 'date' : da[c.name] = new Date(); break;
9499 case 'float' : da[c.name] = 0.0; break;
9500 case 'boolean' : da[c.name] = false; break;
9501 default : da[c.name] = ""; break;
9505 return new this.recordType(Roo.apply(da, d));
9510 * Ext JS Library 1.1.1
9511 * Copyright(c) 2006-2007, Ext JS, LLC.
9513 * Originally Released Under LGPL - original licence link has changed is not relivant.
9516 * <script type="text/javascript">
9520 * @class Roo.data.DataProxy
9521 * @extends Roo.data.Observable
9522 * This class is an abstract base class for implementations which provide retrieval of
9523 * unformatted data objects.<br>
9525 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9526 * (of the appropriate type which knows how to parse the data object) to provide a block of
9527 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9529 * Custom implementations must implement the load method as described in
9530 * {@link Roo.data.HttpProxy#load}.
9532 Roo.data.DataProxy = function(){
9536 * Fires before a network request is made to retrieve a data object.
9537 * @param {Object} This DataProxy object.
9538 * @param {Object} params The params parameter to the load function.
9543 * Fires before the load method's callback is called.
9544 * @param {Object} This DataProxy object.
9545 * @param {Object} o The data object.
9546 * @param {Object} arg The callback argument object passed to the load function.
9550 * @event loadexception
9551 * Fires if an Exception occurs during data retrieval.
9552 * @param {Object} This DataProxy object.
9553 * @param {Object} o The data object.
9554 * @param {Object} arg The callback argument object passed to the load function.
9555 * @param {Object} e The Exception.
9557 loadexception : true
9559 Roo.data.DataProxy.superclass.constructor.call(this);
9562 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9565 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9569 * Ext JS Library 1.1.1
9570 * Copyright(c) 2006-2007, Ext JS, LLC.
9572 * Originally Released Under LGPL - original licence link has changed is not relivant.
9575 * <script type="text/javascript">
9578 * @class Roo.data.MemoryProxy
9579 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9580 * to the Reader when its load method is called.
9582 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9584 Roo.data.MemoryProxy = function(data){
9588 Roo.data.MemoryProxy.superclass.constructor.call(this);
9592 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9594 * Load data from the requested source (in this case an in-memory
9595 * data object passed to the constructor), read the data object into
9596 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9597 * process that block using the passed callback.
9598 * @param {Object} params This parameter is not used by the MemoryProxy class.
9599 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9600 * object into a block of Roo.data.Records.
9601 * @param {Function} callback The function into which to pass the block of Roo.data.records.
9602 * The function must be passed <ul>
9603 * <li>The Record block object</li>
9604 * <li>The "arg" argument from the load function</li>
9605 * <li>A boolean success indicator</li>
9607 * @param {Object} scope The scope in which to call the callback
9608 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9610 load : function(params, reader, callback, scope, arg){
9611 params = params || {};
9614 result = reader.readRecords(this.data);
9616 this.fireEvent("loadexception", this, arg, null, e);
9617 callback.call(scope, null, arg, false);
9620 callback.call(scope, result, arg, true);
9624 update : function(params, records){
9629 * Ext JS Library 1.1.1
9630 * Copyright(c) 2006-2007, Ext JS, LLC.
9632 * Originally Released Under LGPL - original licence link has changed is not relivant.
9635 * <script type="text/javascript">
9638 * @class Roo.data.HttpProxy
9639 * @extends Roo.data.DataProxy
9640 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9641 * configured to reference a certain URL.<br><br>
9643 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9644 * from which the running page was served.<br><br>
9646 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9648 * Be aware that to enable the browser to parse an XML document, the server must set
9649 * the Content-Type header in the HTTP response to "text/xml".
9651 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9652 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
9653 * will be used to make the request.
9655 Roo.data.HttpProxy = function(conn){
9656 Roo.data.HttpProxy.superclass.constructor.call(this);
9657 // is conn a conn config or a real conn?
9659 this.useAjax = !conn || !conn.events;
9663 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9664 // thse are take from connection...
9667 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9670 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9671 * extra parameters to each request made by this object. (defaults to undefined)
9674 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9675 * to each request made by this object. (defaults to undefined)
9678 * @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)
9681 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9684 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9690 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9694 * Return the {@link Roo.data.Connection} object being used by this Proxy.
9695 * @return {Connection} The Connection object. This object may be used to subscribe to events on
9696 * a finer-grained basis than the DataProxy events.
9698 getConnection : function(){
9699 return this.useAjax ? Roo.Ajax : this.conn;
9703 * Load data from the configured {@link Roo.data.Connection}, read the data object into
9704 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9705 * process that block using the passed callback.
9706 * @param {Object} params An object containing properties which are to be used as HTTP parameters
9707 * for the request to the remote server.
9708 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9709 * object into a block of Roo.data.Records.
9710 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9711 * The function must be passed <ul>
9712 * <li>The Record block object</li>
9713 * <li>The "arg" argument from the load function</li>
9714 * <li>A boolean success indicator</li>
9716 * @param {Object} scope The scope in which to call the callback
9717 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9719 load : function(params, reader, callback, scope, arg){
9720 if(this.fireEvent("beforeload", this, params) !== false){
9722 params : params || {},
9724 callback : callback,
9729 callback : this.loadResponse,
9733 Roo.applyIf(o, this.conn);
9734 if(this.activeRequest){
9735 Roo.Ajax.abort(this.activeRequest);
9737 this.activeRequest = Roo.Ajax.request(o);
9739 this.conn.request(o);
9742 callback.call(scope||this, null, arg, false);
9747 loadResponse : function(o, success, response){
9748 delete this.activeRequest;
9750 this.fireEvent("loadexception", this, o, response);
9751 o.request.callback.call(o.request.scope, null, o.request.arg, false);
9756 result = o.reader.read(response);
9758 this.fireEvent("loadexception", this, o, response, e);
9759 o.request.callback.call(o.request.scope, null, o.request.arg, false);
9763 this.fireEvent("load", this, o, o.request.arg);
9764 o.request.callback.call(o.request.scope, result, o.request.arg, true);
9768 update : function(dataSet){
9773 updateResponse : function(dataSet){
9778 * Ext JS Library 1.1.1
9779 * Copyright(c) 2006-2007, Ext JS, LLC.
9781 * Originally Released Under LGPL - original licence link has changed is not relivant.
9784 * <script type="text/javascript">
9788 * @class Roo.data.ScriptTagProxy
9789 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9790 * other than the originating domain of the running page.<br><br>
9792 * <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
9793 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9795 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9796 * source code that is used as the source inside a <script> tag.<br><br>
9798 * In order for the browser to process the returned data, the server must wrap the data object
9799 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9800 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9801 * depending on whether the callback name was passed:
9804 boolean scriptTag = false;
9805 String cb = request.getParameter("callback");
9808 response.setContentType("text/javascript");
9810 response.setContentType("application/x-json");
9812 Writer out = response.getWriter();
9814 out.write(cb + "(");
9816 out.print(dataBlock.toJsonString());
9823 * @param {Object} config A configuration object.
9825 Roo.data.ScriptTagProxy = function(config){
9826 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9827 Roo.apply(this, config);
9828 this.head = document.getElementsByTagName("head")[0];
9831 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9833 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9835 * @cfg {String} url The URL from which to request the data object.
9838 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9842 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9843 * the server the name of the callback function set up by the load call to process the returned data object.
9844 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9845 * javascript output which calls this named function passing the data object as its only parameter.
9847 callbackParam : "callback",
9849 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9850 * name to the request.
9855 * Load data from the configured URL, read the data object into
9856 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9857 * process that block using the passed callback.
9858 * @param {Object} params An object containing properties which are to be used as HTTP parameters
9859 * for the request to the remote server.
9860 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9861 * object into a block of Roo.data.Records.
9862 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9863 * The function must be passed <ul>
9864 * <li>The Record block object</li>
9865 * <li>The "arg" argument from the load function</li>
9866 * <li>A boolean success indicator</li>
9868 * @param {Object} scope The scope in which to call the callback
9869 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9871 load : function(params, reader, callback, scope, arg){
9872 if(this.fireEvent("beforeload", this, params) !== false){
9874 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9877 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9879 url += "&_dc=" + (new Date().getTime());
9881 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9884 cb : "stcCallback"+transId,
9885 scriptId : "stcScript"+transId,
9889 callback : callback,
9895 window[trans.cb] = function(o){
9896 conn.handleResponse(o, trans);
9899 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9901 if(this.autoAbort !== false){
9905 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9907 var script = document.createElement("script");
9908 script.setAttribute("src", url);
9909 script.setAttribute("type", "text/javascript");
9910 script.setAttribute("id", trans.scriptId);
9911 this.head.appendChild(script);
9915 callback.call(scope||this, null, arg, false);
9920 isLoading : function(){
9921 return this.trans ? true : false;
9925 * Abort the current server request.
9928 if(this.isLoading()){
9929 this.destroyTrans(this.trans);
9934 destroyTrans : function(trans, isLoaded){
9935 this.head.removeChild(document.getElementById(trans.scriptId));
9936 clearTimeout(trans.timeoutId);
9938 window[trans.cb] = undefined;
9940 delete window[trans.cb];
9943 // if hasn't been loaded, wait for load to remove it to prevent script error
9944 window[trans.cb] = function(){
9945 window[trans.cb] = undefined;
9947 delete window[trans.cb];
9954 handleResponse : function(o, trans){
9956 this.destroyTrans(trans, true);
9959 result = trans.reader.readRecords(o);
9961 this.fireEvent("loadexception", this, o, trans.arg, e);
9962 trans.callback.call(trans.scope||window, null, trans.arg, false);
9965 this.fireEvent("load", this, o, trans.arg);
9966 trans.callback.call(trans.scope||window, result, trans.arg, true);
9970 handleFailure : function(trans){
9972 this.destroyTrans(trans, false);
9973 this.fireEvent("loadexception", this, null, trans.arg);
9974 trans.callback.call(trans.scope||window, null, trans.arg, false);
9978 * Ext JS Library 1.1.1
9979 * Copyright(c) 2006-2007, Ext JS, LLC.
9981 * Originally Released Under LGPL - original licence link has changed is not relivant.
9984 * <script type="text/javascript">
9988 * @class Roo.data.JsonReader
9989 * @extends Roo.data.DataReader
9990 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9991 * based on mappings in a provided Roo.data.Record constructor.
9993 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9994 * in the reply previously.
9999 var RecordDef = Roo.data.Record.create([
10000 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
10001 {name: 'occupation'} // This field will use "occupation" as the mapping.
10003 var myReader = new Roo.data.JsonReader({
10004 totalProperty: "results", // The property which contains the total dataset size (optional)
10005 root: "rows", // The property which contains an Array of row objects
10006 id: "id" // The property within each row object that provides an ID for the record (optional)
10010 * This would consume a JSON file like this:
10012 { 'results': 2, 'rows': [
10013 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10014 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10017 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10018 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10019 * paged from the remote server.
10020 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10021 * @cfg {String} root name of the property which contains the Array of row objects.
10022 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10024 * Create a new JsonReader
10025 * @param {Object} meta Metadata configuration options
10026 * @param {Object} recordType Either an Array of field definition objects,
10027 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10029 Roo.data.JsonReader = function(meta, recordType){
10032 // set some defaults:
10033 Roo.applyIf(meta, {
10034 totalProperty: 'total',
10035 successProperty : 'success',
10040 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10042 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10045 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
10046 * Used by Store query builder to append _requestMeta to params.
10049 metaFromRemote : false,
10051 * This method is only used by a DataProxy which has retrieved data from a remote server.
10052 * @param {Object} response The XHR object which contains the JSON data in its responseText.
10053 * @return {Object} data A data block which is used by an Roo.data.Store object as
10054 * a cache of Roo.data.Records.
10056 read : function(response){
10057 var json = response.responseText;
10059 var o = /* eval:var:o */ eval("("+json+")");
10061 throw {message: "JsonReader.read: Json object not found"};
10067 this.metaFromRemote = true;
10068 this.meta = o.metaData;
10069 this.recordType = Roo.data.Record.create(o.metaData.fields);
10070 this.onMetaChange(this.meta, this.recordType, o);
10072 return this.readRecords(o);
10075 // private function a store will implement
10076 onMetaChange : function(meta, recordType, o){
10083 simpleAccess: function(obj, subsc) {
10090 getJsonAccessor: function(){
10092 return function(expr) {
10094 return(re.test(expr))
10095 ? new Function("obj", "return obj." + expr)
10100 return Roo.emptyFn;
10105 * Create a data block containing Roo.data.Records from an XML document.
10106 * @param {Object} o An object which contains an Array of row objects in the property specified
10107 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10108 * which contains the total size of the dataset.
10109 * @return {Object} data A data block which is used by an Roo.data.Store object as
10110 * a cache of Roo.data.Records.
10112 readRecords : function(o){
10114 * After any data loads, the raw JSON data is available for further custom processing.
10118 var s = this.meta, Record = this.recordType,
10119 f = Record.prototype.fields, fi = f.items, fl = f.length;
10121 // Generate extraction functions for the totalProperty, the root, the id, and for each field
10123 if(s.totalProperty) {
10124 this.getTotal = this.getJsonAccessor(s.totalProperty);
10126 if(s.successProperty) {
10127 this.getSuccess = this.getJsonAccessor(s.successProperty);
10129 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10131 var g = this.getJsonAccessor(s.id);
10132 this.getId = function(rec) {
10134 return (r === undefined || r === "") ? null : r;
10137 this.getId = function(){return null;};
10140 for(var jj = 0; jj < fl; jj++){
10142 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10143 this.ef[jj] = this.getJsonAccessor(map);
10147 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10148 if(s.totalProperty){
10149 var vt = parseInt(this.getTotal(o), 10);
10154 if(s.successProperty){
10155 var vs = this.getSuccess(o);
10156 if(vs === false || vs === 'false'){
10161 for(var i = 0; i < c; i++){
10164 var id = this.getId(n);
10165 for(var j = 0; j < fl; j++){
10167 var v = this.ef[j](n);
10169 Roo.log('missing convert for ' + f.name);
10173 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10175 var record = new Record(values, id);
10177 records[i] = record;
10183 totalRecords : totalRecords
10188 * Ext JS Library 1.1.1
10189 * Copyright(c) 2006-2007, Ext JS, LLC.
10191 * Originally Released Under LGPL - original licence link has changed is not relivant.
10194 * <script type="text/javascript">
10198 * @class Roo.data.ArrayReader
10199 * @extends Roo.data.DataReader
10200 * Data reader class to create an Array of Roo.data.Record objects from an Array.
10201 * Each element of that Array represents a row of data fields. The
10202 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10203 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10207 var RecordDef = Roo.data.Record.create([
10208 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
10209 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
10211 var myReader = new Roo.data.ArrayReader({
10212 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
10216 * This would consume an Array like this:
10218 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10220 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10222 * Create a new JsonReader
10223 * @param {Object} meta Metadata configuration options.
10224 * @param {Object} recordType Either an Array of field definition objects
10225 * as specified to {@link Roo.data.Record#create},
10226 * or an {@link Roo.data.Record} object
10227 * created using {@link Roo.data.Record#create}.
10229 Roo.data.ArrayReader = function(meta, recordType){
10230 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10233 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10235 * Create a data block containing Roo.data.Records from an XML document.
10236 * @param {Object} o An Array of row objects which represents the dataset.
10237 * @return {Object} data A data block which is used by an Roo.data.Store object as
10238 * a cache of Roo.data.Records.
10240 readRecords : function(o){
10241 var sid = this.meta ? this.meta.id : null;
10242 var recordType = this.recordType, fields = recordType.prototype.fields;
10245 for(var i = 0; i < root.length; i++){
10248 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10249 for(var j = 0, jlen = fields.length; j < jlen; j++){
10250 var f = fields.items[j];
10251 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10252 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10254 values[f.name] = v;
10256 var record = new recordType(values, id);
10258 records[records.length] = record;
10262 totalRecords : records.length
10271 * @class Roo.bootstrap.ComboBox
10272 * @extends Roo.bootstrap.TriggerField
10273 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10274 * @cfg {Boolean} append (true|false) default false
10275 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10276 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10277 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10278 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10279 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10281 * Create a new ComboBox.
10282 * @param {Object} config Configuration options
10284 Roo.bootstrap.ComboBox = function(config){
10285 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10289 * Fires when the dropdown list is expanded
10290 * @param {Roo.bootstrap.ComboBox} combo This combo box
10295 * Fires when the dropdown list is collapsed
10296 * @param {Roo.bootstrap.ComboBox} combo This combo box
10300 * @event beforeselect
10301 * Fires before a list item is selected. Return false to cancel the selection.
10302 * @param {Roo.bootstrap.ComboBox} combo This combo box
10303 * @param {Roo.data.Record} record The data record returned from the underlying store
10304 * @param {Number} index The index of the selected item in the dropdown list
10306 'beforeselect' : true,
10309 * Fires when a list item is selected
10310 * @param {Roo.bootstrap.ComboBox} combo This combo box
10311 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10312 * @param {Number} index The index of the selected item in the dropdown list
10316 * @event beforequery
10317 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10318 * The event object passed has these properties:
10319 * @param {Roo.bootstrap.ComboBox} combo This combo box
10320 * @param {String} query The query
10321 * @param {Boolean} forceAll true to force "all" query
10322 * @param {Boolean} cancel true to cancel the query
10323 * @param {Object} e The query event object
10325 'beforequery': true,
10328 * Fires when the 'add' icon is pressed (add a listener to enable add button)
10329 * @param {Roo.bootstrap.ComboBox} combo This combo box
10334 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10335 * @param {Roo.bootstrap.ComboBox} combo This combo box
10336 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10341 * Fires when the remove value from the combobox array
10342 * @param {Roo.bootstrap.ComboBox} combo This combo box
10349 this.tickItems = [];
10351 this.selectedIndex = -1;
10352 if(this.mode == 'local'){
10353 if(config.queryDelay === undefined){
10354 this.queryDelay = 10;
10356 if(config.minChars === undefined){
10362 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10365 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10366 * rendering into an Roo.Editor, defaults to false)
10369 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10370 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10373 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10376 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10377 * the dropdown list (defaults to undefined, with no header element)
10381 * @cfg {String/Roo.Template} tpl The template to use to render the output
10385 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10387 listWidth: undefined,
10389 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10390 * mode = 'remote' or 'text' if mode = 'local')
10392 displayField: undefined,
10394 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10395 * mode = 'remote' or 'value' if mode = 'local').
10396 * Note: use of a valueField requires the user make a selection
10397 * in order for a value to be mapped.
10399 valueField: undefined,
10403 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10404 * field's data value (defaults to the underlying DOM element's name)
10406 hiddenName: undefined,
10408 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10412 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10414 selectedClass: 'active',
10417 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10421 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10422 * anchor positions (defaults to 'tl-bl')
10424 listAlign: 'tl-bl?',
10426 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10430 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
10431 * query specified by the allQuery config option (defaults to 'query')
10433 triggerAction: 'query',
10435 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10436 * (defaults to 4, does not apply if editable = false)
10440 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10441 * delay (typeAheadDelay) if it matches a known value (defaults to false)
10445 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10446 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10450 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10451 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
10455 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
10456 * when editable = true (defaults to false)
10458 selectOnFocus:false,
10460 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10462 queryParam: 'query',
10464 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
10465 * when mode = 'remote' (defaults to 'Loading...')
10467 loadingText: 'Loading...',
10469 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10473 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10477 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10478 * traditional select (defaults to true)
10482 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10486 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10490 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10491 * listWidth has a higher value)
10495 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10496 * allow the user to set arbitrary text into the field (defaults to false)
10498 forceSelection:false,
10500 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10501 * if typeAhead = true (defaults to 250)
10503 typeAheadDelay : 250,
10505 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10506 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10508 valueNotFoundText : undefined,
10510 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10512 blockFocus : false,
10515 * @cfg {Boolean} disableClear Disable showing of clear button.
10517 disableClear : false,
10519 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
10521 alwaysQuery : false,
10524 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
10538 btnPosition : 'right',
10539 triggerList : true,
10540 showToggleBtn : true,
10541 // element that contains real text value.. (when hidden is used..)
10543 getAutoCreate : function()
10550 if(!this.tickable){
10551 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10556 * ComboBox with tickable selections
10559 var align = this.labelAlign || this.parentLabelAlign();
10562 cls : 'form-group roo-combobox-tickable' //input-group
10568 cls : 'tickable-buttons',
10573 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10580 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10587 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10594 Roo.each(buttons.cn, function(c){
10596 c.cls += ' btn-' + _this.size;
10599 if (_this.disabled) {
10610 cls: 'form-hidden-field'
10614 cls: 'select2-choices',
10618 cls: 'select2-search-field',
10630 cls: 'select2-container input-group select2-container-multi',
10635 // cls: 'typeahead typeahead-long dropdown-menu',
10636 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
10641 if (align ==='left' && this.fieldLabel.length) {
10643 Roo.log("left and has label");
10649 cls : 'control-label col-sm-' + this.labelWidth,
10650 html : this.fieldLabel
10654 cls : "col-sm-" + (12 - this.labelWidth),
10661 } else if ( this.fieldLabel.length) {
10667 //cls : 'input-group-addon',
10668 html : this.fieldLabel
10678 Roo.log(" no label && no align");
10685 ['xs','sm','md','lg'].map(function(size){
10686 if (settings[size]) {
10687 cfg.cls += ' col-' + size + '-' + settings[size];
10696 initEvents: function()
10700 throw "can not find store for combo";
10702 this.store = Roo.factory(this.store, Roo.data);
10705 this.initTickableEvents();
10709 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10711 if(this.hiddenName){
10713 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10715 this.hiddenField.dom.value =
10716 this.hiddenValue !== undefined ? this.hiddenValue :
10717 this.value !== undefined ? this.value : '';
10719 // prevent input submission
10720 this.el.dom.removeAttribute('name');
10721 this.hiddenField.dom.setAttribute('name', this.hiddenName);
10726 // this.el.dom.setAttribute('autocomplete', 'off');
10729 var cls = 'x-combo-list';
10731 //this.list = new Roo.Layer({
10732 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10738 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10739 _this.list.setWidth(lw);
10742 this.list.on('mouseover', this.onViewOver, this);
10743 this.list.on('mousemove', this.onViewMove, this);
10745 this.list.on('scroll', this.onViewScroll, this);
10748 this.list.swallowEvent('mousewheel');
10749 this.assetHeight = 0;
10752 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10753 this.assetHeight += this.header.getHeight();
10756 this.innerList = this.list.createChild({cls:cls+'-inner'});
10757 this.innerList.on('mouseover', this.onViewOver, this);
10758 this.innerList.on('mousemove', this.onViewMove, this);
10759 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10761 if(this.allowBlank && !this.pageSize && !this.disableClear){
10762 this.footer = this.list.createChild({cls:cls+'-ft'});
10763 this.pageTb = new Roo.Toolbar(this.footer);
10767 this.footer = this.list.createChild({cls:cls+'-ft'});
10768 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10769 {pageSize: this.pageSize});
10773 if (this.pageTb && this.allowBlank && !this.disableClear) {
10775 this.pageTb.add(new Roo.Toolbar.Fill(), {
10776 cls: 'x-btn-icon x-btn-clear',
10778 handler: function()
10781 _this.clearValue();
10782 _this.onSelect(false, -1);
10787 this.assetHeight += this.footer.getHeight();
10792 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10795 this.view = new Roo.View(this.list, this.tpl, {
10796 singleSelect:true, store: this.store, selectedClass: this.selectedClass
10798 //this.view.wrapEl.setDisplayed(false);
10799 this.view.on('click', this.onViewClick, this);
10803 this.store.on('beforeload', this.onBeforeLoad, this);
10804 this.store.on('load', this.onLoad, this);
10805 this.store.on('loadexception', this.onLoadException, this);
10807 if(this.resizable){
10808 this.resizer = new Roo.Resizable(this.list, {
10809 pinned:true, handles:'se'
10811 this.resizer.on('resize', function(r, w, h){
10812 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10813 this.listWidth = w;
10814 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10815 this.restrictHeight();
10817 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10820 if(!this.editable){
10821 this.editable = true;
10822 this.setEditable(false);
10827 if (typeof(this.events.add.listeners) != 'undefined') {
10829 this.addicon = this.wrap.createChild(
10830 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
10832 this.addicon.on('click', function(e) {
10833 this.fireEvent('add', this);
10836 if (typeof(this.events.edit.listeners) != 'undefined') {
10838 this.editicon = this.wrap.createChild(
10839 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
10840 if (this.addicon) {
10841 this.editicon.setStyle('margin-left', '40px');
10843 this.editicon.on('click', function(e) {
10845 // we fire even if inothing is selected..
10846 this.fireEvent('edit', this, this.lastData );
10852 this.keyNav = new Roo.KeyNav(this.inputEl(), {
10853 "up" : function(e){
10854 this.inKeyMode = true;
10858 "down" : function(e){
10859 if(!this.isExpanded()){
10860 this.onTriggerClick();
10862 this.inKeyMode = true;
10867 "enter" : function(e){
10868 // this.onViewClick();
10872 if(this.fireEvent("specialkey", this, e)){
10873 this.onViewClick(false);
10879 "esc" : function(e){
10883 "tab" : function(e){
10886 if(this.fireEvent("specialkey", this, e)){
10887 this.onViewClick(false);
10895 doRelay : function(foo, bar, hname){
10896 if(hname == 'down' || this.scope.isExpanded()){
10897 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10906 this.queryDelay = Math.max(this.queryDelay || 10,
10907 this.mode == 'local' ? 10 : 250);
10910 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10912 if(this.typeAhead){
10913 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10915 if(this.editable !== false){
10916 this.inputEl().on("keyup", this.onKeyUp, this);
10918 if(this.forceSelection){
10919 this.inputEl().on('blur', this.doForce, this);
10923 this.choices = this.el.select('ul.select2-choices', true).first();
10924 this.searchField = this.el.select('ul li.select2-search-field', true).first();
10928 initTickableEvents: function()
10932 if(this.hiddenName){
10934 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10936 this.hiddenField.dom.value =
10937 this.hiddenValue !== undefined ? this.hiddenValue :
10938 this.value !== undefined ? this.value : '';
10940 // prevent input submission
10941 this.el.dom.removeAttribute('name');
10942 this.hiddenField.dom.setAttribute('name', this.hiddenName);
10947 // this.list = this.el.select('ul.dropdown-menu',true).first();
10949 this.choices = this.el.select('ul.select2-choices', true).first();
10950 this.searchField = this.el.select('ul li.select2-search-field', true).first();
10951 if(this.triggerList){
10952 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10955 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10956 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10958 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10959 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10961 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10962 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10964 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10965 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10966 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10969 this.cancelBtn.hide();
10974 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10975 _this.list.setWidth(lw);
10978 this.list.on('mouseover', this.onViewOver, this);
10979 this.list.on('mousemove', this.onViewMove, this);
10981 this.list.on('scroll', this.onViewScroll, this);
10984 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>';
10987 this.view = new Roo.View(this.list, this.tpl, {
10988 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10991 //this.view.wrapEl.setDisplayed(false);
10992 this.view.on('click', this.onViewClick, this);
10996 this.store.on('beforeload', this.onBeforeLoad, this);
10997 this.store.on('load', this.onLoad, this);
10998 this.store.on('loadexception', this.onLoadException, this);
11000 // this.keyNav = new Roo.KeyNav(this.inputEl(), {
11001 // "up" : function(e){
11002 // this.inKeyMode = true;
11003 // this.selectPrev();
11006 // "down" : function(e){
11007 // if(!this.isExpanded()){
11008 // this.onTriggerClick();
11010 // this.inKeyMode = true;
11011 // this.selectNext();
11015 // "enter" : function(e){
11016 //// this.onViewClick();
11018 // this.collapse();
11020 // if(this.fireEvent("specialkey", this, e)){
11021 // this.onViewClick(false);
11027 // "esc" : function(e){
11028 // this.collapse();
11031 // "tab" : function(e){
11032 // this.collapse();
11034 // if(this.fireEvent("specialkey", this, e)){
11035 // this.onViewClick(false);
11043 // doRelay : function(foo, bar, hname){
11044 // if(hname == 'down' || this.scope.isExpanded()){
11045 // return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11050 // forceKeyDown: true
11054 this.queryDelay = Math.max(this.queryDelay || 10,
11055 this.mode == 'local' ? 10 : 250);
11058 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11060 if(this.typeAhead){
11061 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11065 onDestroy : function(){
11067 this.view.setStore(null);
11068 this.view.el.removeAllListeners();
11069 this.view.el.remove();
11070 this.view.purgeListeners();
11073 this.list.dom.innerHTML = '';
11077 this.store.un('beforeload', this.onBeforeLoad, this);
11078 this.store.un('load', this.onLoad, this);
11079 this.store.un('loadexception', this.onLoadException, this);
11081 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11085 fireKey : function(e){
11086 if(e.isNavKeyPress() && !this.list.isVisible()){
11087 this.fireEvent("specialkey", this, e);
11092 onResize: function(w, h){
11093 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11095 // if(typeof w != 'number'){
11096 // // we do not handle it!?!?
11099 // var tw = this.trigger.getWidth();
11100 // // tw += this.addicon ? this.addicon.getWidth() : 0;
11101 // // tw += this.editicon ? this.editicon.getWidth() : 0;
11103 // this.inputEl().setWidth( this.adjustWidth('input', x));
11105 // //this.trigger.setStyle('left', x+'px');
11107 // if(this.list && this.listWidth === undefined){
11108 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11109 // this.list.setWidth(lw);
11110 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11118 * Allow or prevent the user from directly editing the field text. If false is passed,
11119 * the user will only be able to select from the items defined in the dropdown list. This method
11120 * is the runtime equivalent of setting the 'editable' config option at config time.
11121 * @param {Boolean} value True to allow the user to directly edit the field text
11123 setEditable : function(value){
11124 if(value == this.editable){
11127 this.editable = value;
11129 this.inputEl().dom.setAttribute('readOnly', true);
11130 this.inputEl().on('mousedown', this.onTriggerClick, this);
11131 this.inputEl().addClass('x-combo-noedit');
11133 this.inputEl().dom.setAttribute('readOnly', false);
11134 this.inputEl().un('mousedown', this.onTriggerClick, this);
11135 this.inputEl().removeClass('x-combo-noedit');
11141 onBeforeLoad : function(combo,opts){
11142 if(!this.hasFocus){
11146 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11148 this.restrictHeight();
11149 this.selectedIndex = -1;
11153 onLoad : function(){
11155 this.hasQuery = false;
11157 if(!this.hasFocus){
11161 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11162 this.loading.hide();
11165 if(this.store.getCount() > 0){
11167 // this.restrictHeight();
11168 if(this.lastQuery == this.allQuery){
11169 if(this.editable && !this.tickable){
11170 this.inputEl().dom.select();
11174 !this.selectByValue(this.value, true) &&
11175 this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' ||
11176 this.store.lastOptions.add != true)
11178 this.select(0, true);
11181 if(this.autoFocus){
11184 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11185 this.taTask.delay(this.typeAheadDelay);
11189 this.onEmptyResults();
11195 onLoadException : function()
11197 this.hasQuery = false;
11199 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11200 this.loading.hide();
11204 Roo.log(this.store.reader.jsonData);
11205 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11207 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11213 onTypeAhead : function(){
11214 if(this.store.getCount() > 0){
11215 var r = this.store.getAt(0);
11216 var newValue = r.data[this.displayField];
11217 var len = newValue.length;
11218 var selStart = this.getRawValue().length;
11220 if(selStart != len){
11221 this.setRawValue(newValue);
11222 this.selectText(selStart, newValue.length);
11228 onSelect : function(record, index){
11230 if(this.fireEvent('beforeselect', this, record, index) !== false){
11232 this.setFromData(index > -1 ? record.data : false);
11235 this.fireEvent('select', this, record, index);
11240 * Returns the currently selected field value or empty string if no value is set.
11241 * @return {String} value The selected value
11243 getValue : function(){
11246 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11249 if(this.valueField){
11250 return typeof this.value != 'undefined' ? this.value : '';
11252 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11257 * Clears any text/value currently set in the field
11259 clearValue : function(){
11260 if(this.hiddenField){
11261 this.hiddenField.dom.value = '';
11264 this.setRawValue('');
11265 this.lastSelectionText = '';
11270 * Sets the specified value into the field. If the value finds a match, the corresponding record text
11271 * will be displayed in the field. If the value does not match the data value of an existing item,
11272 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11273 * Otherwise the field will be blank (although the value will still be set).
11274 * @param {String} value The value to match
11276 setValue : function(v){
11283 if(this.valueField){
11284 var r = this.findRecord(this.valueField, v);
11286 text = r.data[this.displayField];
11287 }else if(this.valueNotFoundText !== undefined){
11288 text = this.valueNotFoundText;
11291 this.lastSelectionText = text;
11292 if(this.hiddenField){
11293 this.hiddenField.dom.value = v;
11295 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11299 * @property {Object} the last set data for the element
11304 * Sets the value of the field based on a object which is related to the record format for the store.
11305 * @param {Object} value the value to set as. or false on reset?
11307 setFromData : function(o){
11310 if(typeof o.display_name !== 'string'){
11311 for(var i=0;i<o.display_name.length;i++){
11312 this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
11320 var dv = ''; // display value
11321 var vv = ''; // value value..
11323 if (this.displayField) {
11324 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11326 // this is an error condition!!!
11327 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
11330 if(this.valueField){
11331 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11334 if(this.hiddenField){
11335 this.hiddenField.dom.value = vv;
11337 this.lastSelectionText = dv;
11338 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11342 // no hidden field.. - we store the value in 'value', but still display
11343 // display field!!!!
11344 this.lastSelectionText = dv;
11345 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11351 reset : function(){
11352 // overridden so that last data is reset..
11353 this.setValue(this.originalValue);
11354 this.clearInvalid();
11355 this.lastData = false;
11357 this.view.clearSelections();
11361 findRecord : function(prop, value){
11363 if(this.store.getCount() > 0){
11364 this.store.each(function(r){
11365 if(r.data[prop] == value){
11375 getName: function()
11377 // returns hidden if it's set..
11378 if (!this.rendered) {return ''};
11379 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
11383 onViewMove : function(e, t){
11384 this.inKeyMode = false;
11388 onViewOver : function(e, t){
11389 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11392 var item = this.view.findItemFromChild(t);
11395 var index = this.view.indexOf(item);
11396 this.select(index, false);
11401 onViewClick : function(view, doFocus, el, e)
11403 var index = this.view.getSelectedIndexes()[0];
11405 var r = this.store.getAt(index);
11409 if(e.getTarget().nodeName.toLowerCase() != 'input'){
11416 Roo.each(this.tickItems, function(v,k){
11418 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11419 _this.tickItems.splice(k, 1);
11429 this.tickItems.push(r.data);
11434 this.onSelect(r, index);
11436 if(doFocus !== false && !this.blockFocus){
11437 this.inputEl().focus();
11442 restrictHeight : function(){
11443 //this.innerList.dom.style.height = '';
11444 //var inner = this.innerList.dom;
11445 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11446 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11447 //this.list.beginUpdate();
11448 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11449 this.list.alignTo(this.inputEl(), this.listAlign);
11450 this.list.alignTo(this.inputEl(), this.listAlign);
11451 //this.list.endUpdate();
11455 onEmptyResults : function(){
11460 * Returns true if the dropdown list is expanded, else false.
11462 isExpanded : function(){
11463 return this.list.isVisible();
11467 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11468 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11469 * @param {String} value The data value of the item to select
11470 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11471 * selected item if it is not currently in view (defaults to true)
11472 * @return {Boolean} True if the value matched an item in the list, else false
11474 selectByValue : function(v, scrollIntoView){
11475 if(v !== undefined && v !== null){
11476 var r = this.findRecord(this.valueField || this.displayField, v);
11478 this.select(this.store.indexOf(r), scrollIntoView);
11486 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11487 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11488 * @param {Number} index The zero-based index of the list item to select
11489 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11490 * selected item if it is not currently in view (defaults to true)
11492 select : function(index, scrollIntoView){
11493 this.selectedIndex = index;
11494 this.view.select(index);
11495 if(scrollIntoView !== false){
11496 var el = this.view.getNode(index);
11497 if(el && !this.multiple && !this.tickable){
11498 this.list.scrollChildIntoView(el, false);
11504 selectNext : function(){
11505 var ct = this.store.getCount();
11507 if(this.selectedIndex == -1){
11509 }else if(this.selectedIndex < ct-1){
11510 this.select(this.selectedIndex+1);
11516 selectPrev : function(){
11517 var ct = this.store.getCount();
11519 if(this.selectedIndex == -1){
11521 }else if(this.selectedIndex != 0){
11522 this.select(this.selectedIndex-1);
11528 onKeyUp : function(e){
11529 if(this.editable !== false && !e.isSpecialKey()){
11530 this.lastKey = e.getKey();
11531 this.dqTask.delay(this.queryDelay);
11536 validateBlur : function(){
11537 return !this.list || !this.list.isVisible();
11541 initQuery : function(){
11542 this.doQuery(this.getRawValue());
11546 doForce : function(){
11547 if(this.inputEl().dom.value.length > 0){
11548 this.inputEl().dom.value =
11549 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11555 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
11556 * query allowing the query action to be canceled if needed.
11557 * @param {String} query The SQL query to execute
11558 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11559 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
11560 * saved in the current store (defaults to false)
11562 doQuery : function(q, forceAll){
11564 if(q === undefined || q === null){
11569 forceAll: forceAll,
11573 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11578 forceAll = qe.forceAll;
11579 if(forceAll === true || (q.length >= this.minChars)){
11581 this.hasQuery = true;
11583 if(this.lastQuery != q || this.alwaysQuery){
11584 this.lastQuery = q;
11585 if(this.mode == 'local'){
11586 this.selectedIndex = -1;
11588 this.store.clearFilter();
11590 this.store.filter(this.displayField, q);
11594 this.store.baseParams[this.queryParam] = q;
11596 var options = {params : this.getParams(q)};
11599 options.add = true;
11600 options.params.start = this.page * this.pageSize;
11603 this.store.load(options);
11605 * this code will make the page width larger, at the beginning, the list not align correctly,
11606 * we should expand the list on onLoad
11607 * so command out it
11612 this.selectedIndex = -1;
11617 this.loadNext = false;
11621 getParams : function(q){
11623 //p[this.queryParam] = q;
11627 p.limit = this.pageSize;
11633 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11635 collapse : function(){
11636 if(!this.isExpanded()){
11644 this.cancelBtn.hide();
11645 this.trigger.show();
11648 Roo.get(document).un('mousedown', this.collapseIf, this);
11649 Roo.get(document).un('mousewheel', this.collapseIf, this);
11650 if (!this.editable) {
11651 Roo.get(document).un('keydown', this.listKeyPress, this);
11653 this.fireEvent('collapse', this);
11657 collapseIf : function(e){
11658 var in_combo = e.within(this.el);
11659 var in_list = e.within(this.list);
11660 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11662 if (in_combo || in_list || is_list) {
11663 //e.stopPropagation();
11668 this.onTickableFooterButtonClick(e, false, false);
11676 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11678 expand : function(){
11680 if(this.isExpanded() || !this.hasFocus){
11684 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11685 this.list.setWidth(lw);
11692 this.restrictHeight();
11696 this.tickItems = Roo.apply([], this.item);
11699 this.cancelBtn.show();
11700 this.trigger.hide();
11704 Roo.get(document).on('mousedown', this.collapseIf, this);
11705 Roo.get(document).on('mousewheel', this.collapseIf, this);
11706 if (!this.editable) {
11707 Roo.get(document).on('keydown', this.listKeyPress, this);
11710 this.fireEvent('expand', this);
11714 // Implements the default empty TriggerField.onTriggerClick function
11715 onTriggerClick : function(e)
11717 Roo.log('trigger click');
11719 if(this.disabled || !this.triggerList){
11724 this.loadNext = false;
11726 if(this.isExpanded()){
11728 if (!this.blockFocus) {
11729 this.inputEl().focus();
11733 this.hasFocus = true;
11734 if(this.triggerAction == 'all') {
11735 this.doQuery(this.allQuery, true);
11737 this.doQuery(this.getRawValue());
11739 if (!this.blockFocus) {
11740 this.inputEl().focus();
11745 onTickableTriggerClick : function(e)
11752 this.loadNext = false;
11753 this.hasFocus = true;
11755 if(this.triggerAction == 'all') {
11756 this.doQuery(this.allQuery, true);
11758 this.doQuery(this.getRawValue());
11762 onSearchFieldClick : function(e)
11764 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11769 this.loadNext = false;
11770 this.hasFocus = true;
11772 if(this.triggerAction == 'all') {
11773 this.doQuery(this.allQuery, true);
11775 this.doQuery(this.getRawValue());
11779 listKeyPress : function(e)
11781 //Roo.log('listkeypress');
11782 // scroll to first matching element based on key pres..
11783 if (e.isSpecialKey()) {
11786 var k = String.fromCharCode(e.getKey()).toUpperCase();
11789 var csel = this.view.getSelectedNodes();
11790 var cselitem = false;
11792 var ix = this.view.indexOf(csel[0]);
11793 cselitem = this.store.getAt(ix);
11794 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11800 this.store.each(function(v) {
11802 // start at existing selection.
11803 if (cselitem.id == v.id) {
11809 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11810 match = this.store.indexOf(v);
11816 if (match === false) {
11817 return true; // no more action?
11820 this.view.select(match);
11821 var sn = Roo.get(this.view.getSelectedNodes()[0])
11822 //sn.scrollIntoView(sn.dom.parentNode, false);
11825 onViewScroll : function(e, t){
11827 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){
11831 this.hasQuery = true;
11833 this.loading = this.list.select('.loading', true).first();
11835 if(this.loading === null){
11836 this.list.createChild({
11838 cls: 'loading select2-more-results select2-active',
11839 html: 'Loading more results...'
11842 this.loading = this.list.select('.loading', true).first();
11844 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11846 this.loading.hide();
11849 this.loading.show();
11854 this.loadNext = true;
11856 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11861 addItem : function(o)
11863 var dv = ''; // display value
11865 if (this.displayField) {
11866 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11868 // this is an error condition!!!
11869 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
11876 var choice = this.choices.createChild({
11878 cls: 'select2-search-choice',
11887 cls: 'select2-search-choice-close',
11892 }, this.searchField);
11894 var close = choice.select('a.select2-search-choice-close', true).first()
11896 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11904 this.inputEl().dom.value = '';
11908 onRemoveItem : function(e, _self, o)
11910 e.preventDefault();
11911 var index = this.item.indexOf(o.data) * 1;
11914 Roo.log('not this item?!');
11918 this.item.splice(index, 1);
11923 this.fireEvent('remove', this, e);
11927 syncValue : function()
11929 if(!this.item.length){
11936 Roo.each(this.item, function(i){
11937 if(_this.valueField){
11938 value.push(i[_this.valueField]);
11945 this.value = value.join(',');
11947 if(this.hiddenField){
11948 this.hiddenField.dom.value = this.value;
11952 clearItem : function()
11954 if(!this.multiple){
11960 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11967 inputEl: function ()
11970 return this.searchField;
11972 return this.el.select('input.form-control',true).first();
11976 onTickableFooterButtonClick : function(e, btn, el)
11978 e.preventDefault();
11980 if(btn && btn.name == 'cancel'){
11981 this.tickItems = Roo.apply([], this.item);
11990 Roo.each(this.tickItems, function(o){
12001 * @cfg {Boolean} grow
12005 * @cfg {Number} growMin
12009 * @cfg {Number} growMax
12019 * Ext JS Library 1.1.1
12020 * Copyright(c) 2006-2007, Ext JS, LLC.
12022 * Originally Released Under LGPL - original licence link has changed is not relivant.
12025 * <script type="text/javascript">
12030 * @extends Roo.util.Observable
12031 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
12032 * This class also supports single and multi selection modes. <br>
12033 * Create a data model bound view:
12035 var store = new Roo.data.Store(...);
12037 var view = new Roo.View({
12039 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
12041 singleSelect: true,
12042 selectedClass: "ydataview-selected",
12046 // listen for node click?
12047 view.on("click", function(vw, index, node, e){
12048 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12052 dataModel.load("foobar.xml");
12054 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12056 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12057 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12059 * Note: old style constructor is still suported (container, template, config)
12062 * Create a new View
12063 * @param {Object} config The config object
12066 Roo.View = function(config, depreciated_tpl, depreciated_config){
12068 this.parent = false;
12070 if (typeof(depreciated_tpl) == 'undefined') {
12071 // new way.. - universal constructor.
12072 Roo.apply(this, config);
12073 this.el = Roo.get(this.el);
12076 this.el = Roo.get(config);
12077 this.tpl = depreciated_tpl;
12078 Roo.apply(this, depreciated_config);
12080 this.wrapEl = this.el.wrap().wrap();
12081 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12084 if(typeof(this.tpl) == "string"){
12085 this.tpl = new Roo.Template(this.tpl);
12087 // support xtype ctors..
12088 this.tpl = new Roo.factory(this.tpl, Roo);
12092 this.tpl.compile();
12097 * @event beforeclick
12098 * Fires before a click is processed. Returns false to cancel the default action.
12099 * @param {Roo.View} this
12100 * @param {Number} index The index of the target node
12101 * @param {HTMLElement} node The target node
12102 * @param {Roo.EventObject} e The raw event object
12104 "beforeclick" : true,
12107 * Fires when a template node is clicked.
12108 * @param {Roo.View} this
12109 * @param {Number} index The index of the target node
12110 * @param {HTMLElement} node The target node
12111 * @param {Roo.EventObject} e The raw event object
12116 * Fires when a template node is double clicked.
12117 * @param {Roo.View} this
12118 * @param {Number} index The index of the target node
12119 * @param {HTMLElement} node The target node
12120 * @param {Roo.EventObject} e The raw event object
12124 * @event contextmenu
12125 * Fires when a template node is right clicked.
12126 * @param {Roo.View} this
12127 * @param {Number} index The index of the target node
12128 * @param {HTMLElement} node The target node
12129 * @param {Roo.EventObject} e The raw event object
12131 "contextmenu" : true,
12133 * @event selectionchange
12134 * Fires when the selected nodes change.
12135 * @param {Roo.View} this
12136 * @param {Array} selections Array of the selected nodes
12138 "selectionchange" : true,
12141 * @event beforeselect
12142 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12143 * @param {Roo.View} this
12144 * @param {HTMLElement} node The node to be selected
12145 * @param {Array} selections Array of currently selected nodes
12147 "beforeselect" : true,
12149 * @event preparedata
12150 * Fires on every row to render, to allow you to change the data.
12151 * @param {Roo.View} this
12152 * @param {Object} data to be rendered (change this)
12154 "preparedata" : true
12162 "click": this.onClick,
12163 "dblclick": this.onDblClick,
12164 "contextmenu": this.onContextMenu,
12168 this.selections = [];
12170 this.cmp = new Roo.CompositeElementLite([]);
12172 this.store = Roo.factory(this.store, Roo.data);
12173 this.setStore(this.store, true);
12176 if ( this.footer && this.footer.xtype) {
12178 var fctr = this.wrapEl.appendChild(document.createElement("div"));
12180 this.footer.dataSource = this.store
12181 this.footer.container = fctr;
12182 this.footer = Roo.factory(this.footer, Roo);
12183 fctr.insertFirst(this.el);
12185 // this is a bit insane - as the paging toolbar seems to detach the el..
12186 // dom.parentNode.parentNode.parentNode
12187 // they get detached?
12191 Roo.View.superclass.constructor.call(this);
12196 Roo.extend(Roo.View, Roo.util.Observable, {
12199 * @cfg {Roo.data.Store} store Data store to load data from.
12204 * @cfg {String|Roo.Element} el The container element.
12209 * @cfg {String|Roo.Template} tpl The template used by this View
12213 * @cfg {String} dataName the named area of the template to use as the data area
12214 * Works with domtemplates roo-name="name"
12218 * @cfg {String} selectedClass The css class to add to selected nodes
12220 selectedClass : "x-view-selected",
12222 * @cfg {String} emptyText The empty text to show when nothing is loaded.
12227 * @cfg {String} text to display on mask (default Loading)
12231 * @cfg {Boolean} multiSelect Allow multiple selection
12233 multiSelect : false,
12235 * @cfg {Boolean} singleSelect Allow single selection
12237 singleSelect: false,
12240 * @cfg {Boolean} toggleSelect - selecting
12242 toggleSelect : false,
12245 * @cfg {Boolean} tickable - selecting
12250 * Returns the element this view is bound to.
12251 * @return {Roo.Element}
12253 getEl : function(){
12254 return this.wrapEl;
12260 * Refreshes the view. - called by datachanged on the store. - do not call directly.
12262 refresh : function(){
12263 Roo.log('refresh');
12266 // if we are using something like 'domtemplate', then
12267 // the what gets used is:
12268 // t.applySubtemplate(NAME, data, wrapping data..)
12269 // the outer template then get' applied with
12270 // the store 'extra data'
12271 // and the body get's added to the
12272 // roo-name="data" node?
12273 // <span class='roo-tpl-{name}'></span> ?????
12277 this.clearSelections();
12278 this.el.update("");
12280 var records = this.store.getRange();
12281 if(records.length < 1) {
12283 // is this valid?? = should it render a template??
12285 this.el.update(this.emptyText);
12289 if (this.dataName) {
12290 this.el.update(t.apply(this.store.meta)); //????
12291 el = this.el.child('.roo-tpl-' + this.dataName);
12294 for(var i = 0, len = records.length; i < len; i++){
12295 var data = this.prepareData(records[i].data, i, records[i]);
12296 this.fireEvent("preparedata", this, data, i, records[i]);
12298 var d = Roo.apply({}, data);
12301 Roo.apply(d, {'roo-id' : Roo.id()});
12305 Roo.each(this.parent.item, function(item){
12306 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12309 Roo.apply(d, {'roo-data-checked' : 'checked'});
12313 html[html.length] = Roo.util.Format.trim(
12315 t.applySubtemplate(this.dataName, d, this.store.meta) :
12322 el.update(html.join(""));
12323 this.nodes = el.dom.childNodes;
12324 this.updateIndexes(0);
12329 * Function to override to reformat the data that is sent to
12330 * the template for each node.
12331 * DEPRICATED - use the preparedata event handler.
12332 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12333 * a JSON object for an UpdateManager bound view).
12335 prepareData : function(data, index, record)
12337 this.fireEvent("preparedata", this, data, index, record);
12341 onUpdate : function(ds, record){
12342 Roo.log('on update');
12343 this.clearSelections();
12344 var index = this.store.indexOf(record);
12345 var n = this.nodes[index];
12346 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12347 n.parentNode.removeChild(n);
12348 this.updateIndexes(index, index);
12354 onAdd : function(ds, records, index)
12356 Roo.log(['on Add', ds, records, index] );
12357 this.clearSelections();
12358 if(this.nodes.length == 0){
12362 var n = this.nodes[index];
12363 for(var i = 0, len = records.length; i < len; i++){
12364 var d = this.prepareData(records[i].data, i, records[i]);
12366 this.tpl.insertBefore(n, d);
12369 this.tpl.append(this.el, d);
12372 this.updateIndexes(index);
12375 onRemove : function(ds, record, index){
12376 Roo.log('onRemove');
12377 this.clearSelections();
12378 var el = this.dataName ?
12379 this.el.child('.roo-tpl-' + this.dataName) :
12382 el.dom.removeChild(this.nodes[index]);
12383 this.updateIndexes(index);
12387 * Refresh an individual node.
12388 * @param {Number} index
12390 refreshNode : function(index){
12391 this.onUpdate(this.store, this.store.getAt(index));
12394 updateIndexes : function(startIndex, endIndex){
12395 var ns = this.nodes;
12396 startIndex = startIndex || 0;
12397 endIndex = endIndex || ns.length - 1;
12398 for(var i = startIndex; i <= endIndex; i++){
12399 ns[i].nodeIndex = i;
12404 * Changes the data store this view uses and refresh the view.
12405 * @param {Store} store
12407 setStore : function(store, initial){
12408 if(!initial && this.store){
12409 this.store.un("datachanged", this.refresh);
12410 this.store.un("add", this.onAdd);
12411 this.store.un("remove", this.onRemove);
12412 this.store.un("update", this.onUpdate);
12413 this.store.un("clear", this.refresh);
12414 this.store.un("beforeload", this.onBeforeLoad);
12415 this.store.un("load", this.onLoad);
12416 this.store.un("loadexception", this.onLoad);
12420 store.on("datachanged", this.refresh, this);
12421 store.on("add", this.onAdd, this);
12422 store.on("remove", this.onRemove, this);
12423 store.on("update", this.onUpdate, this);
12424 store.on("clear", this.refresh, this);
12425 store.on("beforeload", this.onBeforeLoad, this);
12426 store.on("load", this.onLoad, this);
12427 store.on("loadexception", this.onLoad, this);
12435 * onbeforeLoad - masks the loading area.
12438 onBeforeLoad : function(store,opts)
12440 Roo.log('onBeforeLoad');
12442 this.el.update("");
12444 this.el.mask(this.mask ? this.mask : "Loading" );
12446 onLoad : function ()
12453 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12454 * @param {HTMLElement} node
12455 * @return {HTMLElement} The template node
12457 findItemFromChild : function(node){
12458 var el = this.dataName ?
12459 this.el.child('.roo-tpl-' + this.dataName,true) :
12462 if(!node || node.parentNode == el){
12465 var p = node.parentNode;
12466 while(p && p != el){
12467 if(p.parentNode == el){
12476 onClick : function(e){
12477 var item = this.findItemFromChild(e.getTarget());
12479 var index = this.indexOf(item);
12480 if(this.onItemClick(item, index, e) !== false){
12481 this.fireEvent("click", this, index, item, e);
12484 this.clearSelections();
12489 onContextMenu : function(e){
12490 var item = this.findItemFromChild(e.getTarget());
12492 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12497 onDblClick : function(e){
12498 var item = this.findItemFromChild(e.getTarget());
12500 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12504 onItemClick : function(item, index, e)
12506 if(this.fireEvent("beforeclick", this, index, item, e) === false){
12509 if (this.toggleSelect) {
12510 var m = this.isSelected(item) ? 'unselect' : 'select';
12513 _t[m](item, true, false);
12516 if(this.multiSelect || this.singleSelect){
12517 if(this.multiSelect && e.shiftKey && this.lastSelection){
12518 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12520 this.select(item, this.multiSelect && e.ctrlKey);
12521 this.lastSelection = item;
12524 if(!this.tickable){
12525 e.preventDefault();
12533 * Get the number of selected nodes.
12536 getSelectionCount : function(){
12537 return this.selections.length;
12541 * Get the currently selected nodes.
12542 * @return {Array} An array of HTMLElements
12544 getSelectedNodes : function(){
12545 return this.selections;
12549 * Get the indexes of the selected nodes.
12552 getSelectedIndexes : function(){
12553 var indexes = [], s = this.selections;
12554 for(var i = 0, len = s.length; i < len; i++){
12555 indexes.push(s[i].nodeIndex);
12561 * Clear all selections
12562 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12564 clearSelections : function(suppressEvent){
12565 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12566 this.cmp.elements = this.selections;
12567 this.cmp.removeClass(this.selectedClass);
12568 this.selections = [];
12569 if(!suppressEvent){
12570 this.fireEvent("selectionchange", this, this.selections);
12576 * Returns true if the passed node is selected
12577 * @param {HTMLElement/Number} node The node or node index
12578 * @return {Boolean}
12580 isSelected : function(node){
12581 var s = this.selections;
12585 node = this.getNode(node);
12586 return s.indexOf(node) !== -1;
12591 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
12592 * @param {Boolean} keepExisting (optional) true to keep existing selections
12593 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12595 select : function(nodeInfo, keepExisting, suppressEvent){
12596 if(nodeInfo instanceof Array){
12598 this.clearSelections(true);
12600 for(var i = 0, len = nodeInfo.length; i < len; i++){
12601 this.select(nodeInfo[i], true, true);
12605 var node = this.getNode(nodeInfo);
12606 if(!node || this.isSelected(node)){
12607 return; // already selected.
12610 this.clearSelections(true);
12612 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12613 Roo.fly(node).addClass(this.selectedClass);
12614 this.selections.push(node);
12615 if(!suppressEvent){
12616 this.fireEvent("selectionchange", this, this.selections);
12624 * @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
12625 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12626 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12628 unselect : function(nodeInfo, keepExisting, suppressEvent)
12630 if(nodeInfo instanceof Array){
12631 Roo.each(this.selections, function(s) {
12632 this.unselect(s, nodeInfo);
12636 var node = this.getNode(nodeInfo);
12637 if(!node || !this.isSelected(node)){
12638 Roo.log("not selected");
12639 return; // not selected.
12643 Roo.each(this.selections, function(s) {
12645 Roo.fly(node).removeClass(this.selectedClass);
12652 this.selections= ns;
12653 this.fireEvent("selectionchange", this, this.selections);
12657 * Gets a template node.
12658 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12659 * @return {HTMLElement} The node or null if it wasn't found
12661 getNode : function(nodeInfo){
12662 if(typeof nodeInfo == "string"){
12663 return document.getElementById(nodeInfo);
12664 }else if(typeof nodeInfo == "number"){
12665 return this.nodes[nodeInfo];
12671 * Gets a range template nodes.
12672 * @param {Number} startIndex
12673 * @param {Number} endIndex
12674 * @return {Array} An array of nodes
12676 getNodes : function(start, end){
12677 var ns = this.nodes;
12678 start = start || 0;
12679 end = typeof end == "undefined" ? ns.length - 1 : end;
12682 for(var i = start; i <= end; i++){
12686 for(var i = start; i >= end; i--){
12694 * Finds the index of the passed node
12695 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12696 * @return {Number} The index of the node or -1
12698 indexOf : function(node){
12699 node = this.getNode(node);
12700 if(typeof node.nodeIndex == "number"){
12701 return node.nodeIndex;
12703 var ns = this.nodes;
12704 for(var i = 0, len = ns.length; i < len; i++){
12715 * based on jquery fullcalendar
12719 Roo.bootstrap = Roo.bootstrap || {};
12721 * @class Roo.bootstrap.Calendar
12722 * @extends Roo.bootstrap.Component
12723 * Bootstrap Calendar class
12724 * @cfg {Boolean} loadMask (true|false) default false
12725 * @cfg {Object} header generate the user specific header of the calendar, default false
12728 * Create a new Container
12729 * @param {Object} config The config object
12734 Roo.bootstrap.Calendar = function(config){
12735 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12739 * Fires when a date is selected
12740 * @param {DatePicker} this
12741 * @param {Date} date The selected date
12745 * @event monthchange
12746 * Fires when the displayed month changes
12747 * @param {DatePicker} this
12748 * @param {Date} date The selected month
12750 'monthchange': true,
12752 * @event evententer
12753 * Fires when mouse over an event
12754 * @param {Calendar} this
12755 * @param {event} Event
12757 'evententer': true,
12759 * @event eventleave
12760 * Fires when the mouse leaves an
12761 * @param {Calendar} this
12764 'eventleave': true,
12766 * @event eventclick
12767 * Fires when the mouse click an
12768 * @param {Calendar} this
12777 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
12780 * @cfg {Number} startDay
12781 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12789 getAutoCreate : function(){
12792 var fc_button = function(name, corner, style, content ) {
12793 return Roo.apply({},{
12795 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
12797 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12800 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12811 style : 'width:100%',
12818 cls : 'fc-header-left',
12820 fc_button('prev', 'left', 'arrow', '‹' ),
12821 fc_button('next', 'right', 'arrow', '›' ),
12822 { tag: 'span', cls: 'fc-header-space' },
12823 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
12831 cls : 'fc-header-center',
12835 cls: 'fc-header-title',
12838 html : 'month / year'
12846 cls : 'fc-header-right',
12848 /* fc_button('month', 'left', '', 'month' ),
12849 fc_button('week', '', '', 'week' ),
12850 fc_button('day', 'right', '', 'day' )
12862 header = this.header;
12865 var cal_heads = function() {
12867 // fixme - handle this.
12869 for (var i =0; i < Date.dayNames.length; i++) {
12870 var d = Date.dayNames[i];
12873 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12874 html : d.substring(0,3)
12878 ret[0].cls += ' fc-first';
12879 ret[6].cls += ' fc-last';
12882 var cal_cell = function(n) {
12885 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12890 cls: 'fc-day-number',
12894 cls: 'fc-day-content',
12898 style: 'position: relative;' // height: 17px;
12910 var cal_rows = function() {
12913 for (var r = 0; r < 6; r++) {
12920 for (var i =0; i < Date.dayNames.length; i++) {
12921 var d = Date.dayNames[i];
12922 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12925 row.cn[0].cls+=' fc-first';
12926 row.cn[0].cn[0].style = 'min-height:90px';
12927 row.cn[6].cls+=' fc-last';
12931 ret[0].cls += ' fc-first';
12932 ret[4].cls += ' fc-prev-last';
12933 ret[5].cls += ' fc-last';
12940 cls: 'fc-border-separate',
12941 style : 'width:100%',
12949 cls : 'fc-first fc-last',
12967 cls : 'fc-content',
12968 style : "position: relative;",
12971 cls : 'fc-view fc-view-month fc-grid',
12972 style : 'position: relative',
12973 unselectable : 'on',
12976 cls : 'fc-event-container',
12977 style : 'position:absolute;z-index:8;top:0;left:0;'
12995 initEvents : function()
12998 throw "can not find store for calendar";
13004 style: "text-align:center",
13008 style: "background-color:white;width:50%;margin:250 auto",
13012 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
13023 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13025 var size = this.el.select('.fc-content', true).first().getSize();
13026 this.maskEl.setSize(size.width, size.height);
13027 this.maskEl.enableDisplayMode("block");
13028 if(!this.loadMask){
13029 this.maskEl.hide();
13032 this.store = Roo.factory(this.store, Roo.data);
13033 this.store.on('load', this.onLoad, this);
13034 this.store.on('beforeload', this.onBeforeLoad, this);
13038 this.cells = this.el.select('.fc-day',true);
13039 //Roo.log(this.cells);
13040 this.textNodes = this.el.query('.fc-day-number');
13041 this.cells.addClassOnOver('fc-state-hover');
13043 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13044 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13045 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13046 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13048 this.on('monthchange', this.onMonthChange, this);
13050 this.update(new Date().clearTime());
13053 resize : function() {
13054 var sz = this.el.getSize();
13056 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13057 this.el.select('.fc-day-content div',true).setHeight(34);
13062 showPrevMonth : function(e){
13063 this.update(this.activeDate.add("mo", -1));
13065 showToday : function(e){
13066 this.update(new Date().clearTime());
13069 showNextMonth : function(e){
13070 this.update(this.activeDate.add("mo", 1));
13074 showPrevYear : function(){
13075 this.update(this.activeDate.add("y", -1));
13079 showNextYear : function(){
13080 this.update(this.activeDate.add("y", 1));
13085 update : function(date)
13087 var vd = this.activeDate;
13088 this.activeDate = date;
13089 // if(vd && this.el){
13090 // var t = date.getTime();
13091 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13092 // Roo.log('using add remove');
13094 // this.fireEvent('monthchange', this, date);
13096 // this.cells.removeClass("fc-state-highlight");
13097 // this.cells.each(function(c){
13098 // if(c.dateValue == t){
13099 // c.addClass("fc-state-highlight");
13100 // setTimeout(function(){
13101 // try{c.dom.firstChild.focus();}catch(e){}
13111 var days = date.getDaysInMonth();
13113 var firstOfMonth = date.getFirstDateOfMonth();
13114 var startingPos = firstOfMonth.getDay()-this.startDay;
13116 if(startingPos < this.startDay){
13120 var pm = date.add(Date.MONTH, -1);
13121 var prevStart = pm.getDaysInMonth()-startingPos;
13123 this.cells = this.el.select('.fc-day',true);
13124 this.textNodes = this.el.query('.fc-day-number');
13125 this.cells.addClassOnOver('fc-state-hover');
13127 var cells = this.cells.elements;
13128 var textEls = this.textNodes;
13130 Roo.each(cells, function(cell){
13131 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13134 days += startingPos;
13136 // convert everything to numbers so it's fast
13137 var day = 86400000;
13138 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13141 //Roo.log(prevStart);
13143 var today = new Date().clearTime().getTime();
13144 var sel = date.clearTime().getTime();
13145 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13146 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13147 var ddMatch = this.disabledDatesRE;
13148 var ddText = this.disabledDatesText;
13149 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13150 var ddaysText = this.disabledDaysText;
13151 var format = this.format;
13153 var setCellClass = function(cal, cell){
13157 //Roo.log('set Cell Class');
13159 var t = d.getTime();
13163 cell.dateValue = t;
13165 cell.className += " fc-today";
13166 cell.className += " fc-state-highlight";
13167 cell.title = cal.todayText;
13170 // disable highlight in other month..
13171 //cell.className += " fc-state-highlight";
13176 cell.className = " fc-state-disabled";
13177 cell.title = cal.minText;
13181 cell.className = " fc-state-disabled";
13182 cell.title = cal.maxText;
13186 if(ddays.indexOf(d.getDay()) != -1){
13187 cell.title = ddaysText;
13188 cell.className = " fc-state-disabled";
13191 if(ddMatch && format){
13192 var fvalue = d.dateFormat(format);
13193 if(ddMatch.test(fvalue)){
13194 cell.title = ddText.replace("%0", fvalue);
13195 cell.className = " fc-state-disabled";
13199 if (!cell.initialClassName) {
13200 cell.initialClassName = cell.dom.className;
13203 cell.dom.className = cell.initialClassName + ' ' + cell.className;
13208 for(; i < startingPos; i++) {
13209 textEls[i].innerHTML = (++prevStart);
13210 d.setDate(d.getDate()+1);
13212 cells[i].className = "fc-past fc-other-month";
13213 setCellClass(this, cells[i]);
13218 for(; i < days; i++){
13219 intDay = i - startingPos + 1;
13220 textEls[i].innerHTML = (intDay);
13221 d.setDate(d.getDate()+1);
13223 cells[i].className = ''; // "x-date-active";
13224 setCellClass(this, cells[i]);
13228 for(; i < 42; i++) {
13229 textEls[i].innerHTML = (++extraDays);
13230 d.setDate(d.getDate()+1);
13232 cells[i].className = "fc-future fc-other-month";
13233 setCellClass(this, cells[i]);
13236 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13238 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13240 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13241 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13243 if(totalRows != 6){
13244 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13245 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13248 this.fireEvent('monthchange', this, date);
13252 if(!this.internalRender){
13253 var main = this.el.dom.firstChild;
13254 var w = main.offsetWidth;
13255 this.el.setWidth(w + this.el.getBorderWidth("lr"));
13256 Roo.fly(main).setWidth(w);
13257 this.internalRender = true;
13258 // opera does not respect the auto grow header center column
13259 // then, after it gets a width opera refuses to recalculate
13260 // without a second pass
13261 if(Roo.isOpera && !this.secondPass){
13262 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13263 this.secondPass = true;
13264 this.update.defer(10, this, [date]);
13271 findCell : function(dt) {
13272 dt = dt.clearTime().getTime();
13274 this.cells.each(function(c){
13275 //Roo.log("check " +c.dateValue + '?=' + dt);
13276 if(c.dateValue == dt){
13286 findCells : function(ev) {
13287 var s = ev.start.clone().clearTime().getTime();
13289 var e= ev.end.clone().clearTime().getTime();
13292 this.cells.each(function(c){
13293 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13295 if(c.dateValue > e){
13298 if(c.dateValue < s){
13307 // findBestRow: function(cells)
13311 // for (var i =0 ; i < cells.length;i++) {
13312 // ret = Math.max(cells[i].rows || 0,ret);
13319 addItem : function(ev)
13321 // look for vertical location slot in
13322 var cells = this.findCells(ev);
13324 // ev.row = this.findBestRow(cells);
13326 // work out the location.
13330 for(var i =0; i < cells.length; i++) {
13332 cells[i].row = cells[0].row;
13335 cells[i].row = cells[i].row + 1;
13345 if (crow.start.getY() == cells[i].getY()) {
13347 crow.end = cells[i];
13364 cells[0].events.push(ev);
13366 this.calevents.push(ev);
13369 clearEvents: function() {
13371 if(!this.calevents){
13375 Roo.each(this.cells.elements, function(c){
13381 Roo.each(this.calevents, function(e) {
13382 Roo.each(e.els, function(el) {
13383 el.un('mouseenter' ,this.onEventEnter, this);
13384 el.un('mouseleave' ,this.onEventLeave, this);
13389 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13395 renderEvents: function()
13399 this.cells.each(function(c) {
13408 if(c.row != c.events.length){
13409 r = 4 - (4 - (c.row - c.events.length));
13412 c.events = ev.slice(0, r);
13413 c.more = ev.slice(r);
13415 if(c.more.length && c.more.length == 1){
13416 c.events.push(c.more.pop());
13419 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13423 this.cells.each(function(c) {
13425 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13428 for (var e = 0; e < c.events.length; e++){
13429 var ev = c.events[e];
13430 var rows = ev.rows;
13432 for(var i = 0; i < rows.length; i++) {
13434 // how many rows should it span..
13437 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13438 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13440 unselectable : "on",
13443 cls: 'fc-event-inner',
13447 // cls: 'fc-event-time',
13448 // html : cells.length > 1 ? '' : ev.time
13452 cls: 'fc-event-title',
13453 html : String.format('{0}', ev.title)
13460 cls: 'ui-resizable-handle ui-resizable-e',
13461 html : '  '
13468 cfg.cls += ' fc-event-start';
13470 if ((i+1) == rows.length) {
13471 cfg.cls += ' fc-event-end';
13474 var ctr = _this.el.select('.fc-event-container',true).first();
13475 var cg = ctr.createChild(cfg);
13477 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13478 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13480 var r = (c.more.length) ? 1 : 0;
13481 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
13482 cg.setWidth(ebox.right - sbox.x -2);
13484 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13485 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13486 cg.on('click', _this.onEventClick, _this, ev);
13497 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13498 style : 'position: absolute',
13499 unselectable : "on",
13502 cls: 'fc-event-inner',
13506 cls: 'fc-event-title',
13514 cls: 'ui-resizable-handle ui-resizable-e',
13515 html : '  '
13521 var ctr = _this.el.select('.fc-event-container',true).first();
13522 var cg = ctr.createChild(cfg);
13524 var sbox = c.select('.fc-day-content',true).first().getBox();
13525 var ebox = c.select('.fc-day-content',true).first().getBox();
13527 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
13528 cg.setWidth(ebox.right - sbox.x -2);
13530 cg.on('click', _this.onMoreEventClick, _this, c.more);
13540 onEventEnter: function (e, el,event,d) {
13541 this.fireEvent('evententer', this, el, event);
13544 onEventLeave: function (e, el,event,d) {
13545 this.fireEvent('eventleave', this, el, event);
13548 onEventClick: function (e, el,event,d) {
13549 this.fireEvent('eventclick', this, el, event);
13552 onMonthChange: function () {
13556 onMoreEventClick: function(e, el, more)
13560 this.calpopover.placement = 'right';
13561 this.calpopover.setTitle('More');
13563 this.calpopover.setContent('');
13565 var ctr = this.calpopover.el.select('.popover-content', true).first();
13567 Roo.each(more, function(m){
13569 cls : 'fc-event-hori fc-event-draggable',
13572 var cg = ctr.createChild(cfg);
13574 cg.on('click', _this.onEventClick, _this, m);
13577 this.calpopover.show(el);
13582 onLoad: function ()
13584 this.calevents = [];
13587 if(this.store.getCount() > 0){
13588 this.store.data.each(function(d){
13591 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13592 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13593 time : d.data.start_time,
13594 title : d.data.title,
13595 description : d.data.description,
13596 venue : d.data.venue
13601 this.renderEvents();
13603 if(this.calevents.length && this.loadMask){
13604 this.maskEl.hide();
13608 onBeforeLoad: function()
13610 this.clearEvents();
13612 this.maskEl.show();
13626 * @class Roo.bootstrap.Popover
13627 * @extends Roo.bootstrap.Component
13628 * Bootstrap Popover class
13629 * @cfg {String} html contents of the popover (or false to use children..)
13630 * @cfg {String} title of popover (or false to hide)
13631 * @cfg {String} placement how it is placed
13632 * @cfg {String} trigger click || hover (or false to trigger manually)
13633 * @cfg {String} over what (parent or false to trigger manually.)
13636 * Create a new Popover
13637 * @param {Object} config The config object
13640 Roo.bootstrap.Popover = function(config){
13641 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13644 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
13646 title: 'Fill in a title',
13649 placement : 'right',
13650 trigger : 'hover', // hover
13654 can_build_overlaid : false,
13656 getChildContainer : function()
13658 return this.el.select('.popover-content',true).first();
13661 getAutoCreate : function(){
13662 Roo.log('make popover?');
13664 cls : 'popover roo-dynamic',
13665 style: 'display:block',
13671 cls : 'popover-inner',
13675 cls: 'popover-title',
13679 cls : 'popover-content',
13690 setTitle: function(str)
13692 this.el.select('.popover-title',true).first().dom.innerHTML = str;
13694 setContent: function(str)
13696 this.el.select('.popover-content',true).first().dom.innerHTML = str;
13698 // as it get's added to the bottom of the page.
13699 onRender : function(ct, position)
13701 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13703 var cfg = Roo.apply({}, this.getAutoCreate());
13707 cfg.cls += ' ' + this.cls;
13710 cfg.style = this.style;
13712 Roo.log("adding to ")
13713 this.el = Roo.get(document.body).createChild(cfg, position);
13719 initEvents : function()
13721 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13722 this.el.enableDisplayMode('block');
13724 if (this.over === false) {
13727 if (this.triggers === false) {
13730 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13731 var triggers = this.trigger ? this.trigger.split(' ') : [];
13732 Roo.each(triggers, function(trigger) {
13734 if (trigger == 'click') {
13735 on_el.on('click', this.toggle, this);
13736 } else if (trigger != 'manual') {
13737 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
13738 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13740 on_el.on(eventIn ,this.enter, this);
13741 on_el.on(eventOut, this.leave, this);
13752 toggle : function () {
13753 this.hoverState == 'in' ? this.leave() : this.enter();
13756 enter : function () {
13759 clearTimeout(this.timeout);
13761 this.hoverState = 'in'
13763 if (!this.delay || !this.delay.show) {
13768 this.timeout = setTimeout(function () {
13769 if (_t.hoverState == 'in') {
13772 }, this.delay.show)
13774 leave : function() {
13775 clearTimeout(this.timeout);
13777 this.hoverState = 'out'
13779 if (!this.delay || !this.delay.hide) {
13784 this.timeout = setTimeout(function () {
13785 if (_t.hoverState == 'out') {
13788 }, this.delay.hide)
13791 show : function (on_el)
13794 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13797 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13798 if (this.html !== false) {
13799 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13801 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13802 if (!this.title.length) {
13803 this.el.select('.popover-title',true).hide();
13806 var placement = typeof this.placement == 'function' ?
13807 this.placement.call(this, this.el, on_el) :
13810 var autoToken = /\s?auto?\s?/i;
13811 var autoPlace = autoToken.test(placement);
13813 placement = placement.replace(autoToken, '') || 'top';
13817 //this.el.setXY([0,0]);
13819 this.el.dom.style.display='block';
13820 this.el.addClass(placement);
13822 //this.el.appendTo(on_el);
13824 var p = this.getPosition();
13825 var box = this.el.getBox();
13830 var align = Roo.bootstrap.Popover.alignment[placement]
13831 this.el.alignTo(on_el, align[0],align[1]);
13832 //var arrow = this.el.select('.arrow',true).first();
13833 //arrow.set(align[2],
13835 this.el.addClass('in');
13836 this.hoverState = null;
13838 if (this.el.hasClass('fade')) {
13845 this.el.setXY([0,0]);
13846 this.el.removeClass('in');
13853 Roo.bootstrap.Popover.alignment = {
13854 'left' : ['r-l', [-10,0], 'right'],
13855 'right' : ['l-r', [10,0], 'left'],
13856 'bottom' : ['t-b', [0,10], 'top'],
13857 'top' : [ 'b-t', [0,-10], 'bottom']
13868 * @class Roo.bootstrap.Progress
13869 * @extends Roo.bootstrap.Component
13870 * Bootstrap Progress class
13871 * @cfg {Boolean} striped striped of the progress bar
13872 * @cfg {Boolean} active animated of the progress bar
13876 * Create a new Progress
13877 * @param {Object} config The config object
13880 Roo.bootstrap.Progress = function(config){
13881 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13884 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
13889 getAutoCreate : function(){
13897 cfg.cls += ' progress-striped';
13901 cfg.cls += ' active';
13920 * @class Roo.bootstrap.ProgressBar
13921 * @extends Roo.bootstrap.Component
13922 * Bootstrap ProgressBar class
13923 * @cfg {Number} aria_valuenow aria-value now
13924 * @cfg {Number} aria_valuemin aria-value min
13925 * @cfg {Number} aria_valuemax aria-value max
13926 * @cfg {String} label label for the progress bar
13927 * @cfg {String} panel (success | info | warning | danger )
13928 * @cfg {String} role role of the progress bar
13929 * @cfg {String} sr_only text
13933 * Create a new ProgressBar
13934 * @param {Object} config The config object
13937 Roo.bootstrap.ProgressBar = function(config){
13938 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13941 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
13945 aria_valuemax : 100,
13951 getAutoCreate : function()
13956 cls: 'progress-bar',
13957 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13969 cfg.role = this.role;
13972 if(this.aria_valuenow){
13973 cfg['aria-valuenow'] = this.aria_valuenow;
13976 if(this.aria_valuemin){
13977 cfg['aria-valuemin'] = this.aria_valuemin;
13980 if(this.aria_valuemax){
13981 cfg['aria-valuemax'] = this.aria_valuemax;
13984 if(this.label && !this.sr_only){
13985 cfg.html = this.label;
13989 cfg.cls += ' progress-bar-' + this.panel;
13995 update : function(aria_valuenow)
13997 this.aria_valuenow = aria_valuenow;
13999 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14014 * @class Roo.bootstrap.TabGroup
14015 * @extends Roo.bootstrap.Column
14016 * Bootstrap Column class
14017 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14018 * @cfg {Boolean} carousel true to make the group behave like a carousel
14021 * Create a new TabGroup
14022 * @param {Object} config The config object
14025 Roo.bootstrap.TabGroup = function(config){
14026 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14028 this.navId = Roo.id();
14031 Roo.bootstrap.TabGroup.register(this);
14035 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
14038 transition : false,
14040 getAutoCreate : function()
14042 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14044 cfg.cls += ' tab-content';
14046 if (this.carousel) {
14047 cfg.cls += ' carousel slide';
14049 cls : 'carousel-inner'
14056 getChildContainer : function()
14058 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14062 * register a Navigation item
14063 * @param {Roo.bootstrap.NavItem} the navitem to add
14065 register : function(item)
14067 this.tabs.push( item);
14068 item.navId = this.navId; // not really needed..
14072 getActivePanel : function()
14075 Roo.each(this.tabs, function(t) {
14085 getPanelByName : function(n)
14088 Roo.each(this.tabs, function(t) {
14089 if (t.tabId == n) {
14097 indexOfPanel : function(p)
14100 Roo.each(this.tabs, function(t,i) {
14101 if (t.tabId == p.tabId) {
14110 * show a specific panel
14111 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14112 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14114 showPanel : function (pan)
14117 if (typeof(pan) == 'number') {
14118 pan = this.tabs[pan];
14120 if (typeof(pan) == 'string') {
14121 pan = this.getPanelByName(pan);
14123 if (pan.tabId == this.getActivePanel().tabId) {
14126 var cur = this.getActivePanel();
14128 if (false === cur.fireEvent('beforedeactivate')) {
14132 if (this.carousel) {
14133 this.transition = true;
14134 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
14135 var lr = dir == 'next' ? 'left' : 'right';
14136 pan.el.addClass(dir); // or prev
14137 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14138 cur.el.addClass(lr); // or right
14139 pan.el.addClass(lr);
14142 cur.el.on('transitionend', function() {
14143 Roo.log("trans end?");
14145 pan.el.removeClass([lr,dir]);
14146 pan.setActive(true);
14148 cur.el.removeClass([lr]);
14149 cur.setActive(false);
14151 _this.transition = false;
14153 }, this, { single: true } );
14157 cur.setActive(false);
14158 pan.setActive(true);
14162 showPanelNext : function()
14164 var i = this.indexOfPanel(this.getActivePanel());
14165 if (i > this.tabs.length) {
14168 this.showPanel(this.tabs[i+1]);
14170 showPanelPrev : function()
14172 var i = this.indexOfPanel(this.getActivePanel());
14176 this.showPanel(this.tabs[i-1]);
14187 Roo.apply(Roo.bootstrap.TabGroup, {
14191 * register a Navigation Group
14192 * @param {Roo.bootstrap.NavGroup} the navgroup to add
14194 register : function(navgrp)
14196 this.groups[navgrp.navId] = navgrp;
14200 * fetch a Navigation Group based on the navigation ID
14201 * if one does not exist , it will get created.
14202 * @param {string} the navgroup to add
14203 * @returns {Roo.bootstrap.NavGroup} the navgroup
14205 get: function(navId) {
14206 if (typeof(this.groups[navId]) == 'undefined') {
14207 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14209 return this.groups[navId] ;
14224 * @class Roo.bootstrap.TabPanel
14225 * @extends Roo.bootstrap.Component
14226 * Bootstrap TabPanel class
14227 * @cfg {Boolean} active panel active
14228 * @cfg {String} html panel content
14229 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14230 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14234 * Create a new TabPanel
14235 * @param {Object} config The config object
14238 Roo.bootstrap.TabPanel = function(config){
14239 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14243 * Fires when the active status changes
14244 * @param {Roo.bootstrap.TabPanel} this
14245 * @param {Boolean} state the new state
14250 * @event beforedeactivate
14251 * Fires before a tab is de-activated - can be used to do validation on a form.
14252 * @param {Roo.bootstrap.TabPanel} this
14253 * @return {Boolean} false if there is an error
14256 'beforedeactivate': true
14259 this.tabId = this.tabId || Roo.id();
14263 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
14270 getAutoCreate : function(){
14273 // item is needed for carousel - not sure if it has any effect otherwise
14274 cls: 'tab-pane item',
14275 html: this.html || ''
14279 cfg.cls += ' active';
14283 cfg.tabId = this.tabId;
14290 initEvents: function()
14292 Roo.log('-------- init events on tab panel ---------');
14294 var p = this.parent();
14295 this.navId = this.navId || p.navId;
14297 if (typeof(this.navId) != 'undefined') {
14298 // not really needed.. but just in case.. parent should be a NavGroup.
14299 var tg = Roo.bootstrap.TabGroup.get(this.navId);
14300 Roo.log(['register', tg, this]);
14306 onRender : function(ct, position)
14308 // Roo.log("Call onRender: " + this.xtype);
14310 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14318 setActive: function(state)
14320 Roo.log("panel - set active " + this.tabId + "=" + state);
14322 this.active = state;
14324 this.el.removeClass('active');
14326 } else if (!this.el.hasClass('active')) {
14327 this.el.addClass('active');
14329 this.fireEvent('changed', this, state);
14346 * @class Roo.bootstrap.DateField
14347 * @extends Roo.bootstrap.Input
14348 * Bootstrap DateField class
14349 * @cfg {Number} weekStart default 0
14350 * @cfg {Number} weekStart default 0
14351 * @cfg {Number} viewMode default empty, (months|years)
14352 * @cfg {Number} minViewMode default empty, (months|years)
14353 * @cfg {Number} startDate default -Infinity
14354 * @cfg {Number} endDate default Infinity
14355 * @cfg {Boolean} todayHighlight default false
14356 * @cfg {Boolean} todayBtn default false
14357 * @cfg {Boolean} calendarWeeks default false
14358 * @cfg {Object} daysOfWeekDisabled default empty
14360 * @cfg {Boolean} keyboardNavigation default true
14361 * @cfg {String} language default en
14364 * Create a new DateField
14365 * @param {Object} config The config object
14368 Roo.bootstrap.DateField = function(config){
14369 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14373 * Fires when this field show.
14374 * @param {Roo.bootstrap.DateField} this
14375 * @param {Mixed} date The date value
14380 * Fires when this field hide.
14381 * @param {Roo.bootstrap.DateField} this
14382 * @param {Mixed} date The date value
14387 * Fires when select a date.
14388 * @param {Roo.bootstrap.DateField} this
14389 * @param {Mixed} date The date value
14395 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
14398 * @cfg {String} format
14399 * The default date format string which can be overriden for localization support. The format must be
14400 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14404 * @cfg {String} altFormats
14405 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14406 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14408 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14416 todayHighlight : false,
14422 keyboardNavigation: true,
14424 calendarWeeks: false,
14426 startDate: -Infinity,
14430 daysOfWeekDisabled: [],
14434 UTCDate: function()
14436 return new Date(Date.UTC.apply(Date, arguments));
14439 UTCToday: function()
14441 var today = new Date();
14442 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14445 getDate: function() {
14446 var d = this.getUTCDate();
14447 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14450 getUTCDate: function() {
14454 setDate: function(d) {
14455 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14458 setUTCDate: function(d) {
14460 this.setValue(this.formatDate(this.date));
14463 onRender: function(ct, position)
14466 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14468 this.language = this.language || 'en';
14469 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14470 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14472 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14473 this.format = this.format || 'm/d/y';
14474 this.isInline = false;
14475 this.isInput = true;
14476 this.component = this.el.select('.add-on', true).first() || false;
14477 this.component = (this.component && this.component.length === 0) ? false : this.component;
14478 this.hasInput = this.component && this.inputEL().length;
14480 if (typeof(this.minViewMode === 'string')) {
14481 switch (this.minViewMode) {
14483 this.minViewMode = 1;
14486 this.minViewMode = 2;
14489 this.minViewMode = 0;
14494 if (typeof(this.viewMode === 'string')) {
14495 switch (this.viewMode) {
14508 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14510 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14512 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14514 this.picker().on('mousedown', this.onMousedown, this);
14515 this.picker().on('click', this.onClick, this);
14517 this.picker().addClass('datepicker-dropdown');
14519 this.startViewMode = this.viewMode;
14522 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14523 if(!this.calendarWeeks){
14528 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14529 v.attr('colspan', function(i, val){
14530 return parseInt(val) + 1;
14535 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14537 this.setStartDate(this.startDate);
14538 this.setEndDate(this.endDate);
14540 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14547 if(this.isInline) {
14552 picker : function()
14554 return this.pickerEl;
14555 // return this.el.select('.datepicker', true).first();
14558 fillDow: function()
14560 var dowCnt = this.weekStart;
14569 if(this.calendarWeeks){
14577 while (dowCnt < this.weekStart + 7) {
14581 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14585 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14588 fillMonths: function()
14591 var months = this.picker().select('>.datepicker-months td', true).first();
14593 months.dom.innerHTML = '';
14599 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14602 months.createChild(month);
14609 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;
14611 if (this.date < this.startDate) {
14612 this.viewDate = new Date(this.startDate);
14613 } else if (this.date > this.endDate) {
14614 this.viewDate = new Date(this.endDate);
14616 this.viewDate = new Date(this.date);
14624 var d = new Date(this.viewDate),
14625 year = d.getUTCFullYear(),
14626 month = d.getUTCMonth(),
14627 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14628 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14629 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14630 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14631 currentDate = this.date && this.date.valueOf(),
14632 today = this.UTCToday();
14634 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14636 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14638 // this.picker.select('>tfoot th.today').
14639 // .text(dates[this.language].today)
14640 // .toggle(this.todayBtn !== false);
14642 this.updateNavArrows();
14645 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14647 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14649 prevMonth.setUTCDate(day);
14651 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14653 var nextMonth = new Date(prevMonth);
14655 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14657 nextMonth = nextMonth.valueOf();
14659 var fillMonths = false;
14661 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14663 while(prevMonth.valueOf() < nextMonth) {
14666 if (prevMonth.getUTCDay() === this.weekStart) {
14668 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14676 if(this.calendarWeeks){
14677 // ISO 8601: First week contains first thursday.
14678 // ISO also states week starts on Monday, but we can be more abstract here.
14680 // Start of current week: based on weekstart/current date
14681 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14682 // Thursday of this week
14683 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14684 // First Thursday of year, year from thursday
14685 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14686 // Calendar week: ms between thursdays, div ms per day, div 7 days
14687 calWeek = (th - yth) / 864e5 / 7 + 1;
14689 fillMonths.cn.push({
14697 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14699 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14702 if (this.todayHighlight &&
14703 prevMonth.getUTCFullYear() == today.getFullYear() &&
14704 prevMonth.getUTCMonth() == today.getMonth() &&
14705 prevMonth.getUTCDate() == today.getDate()) {
14706 clsName += ' today';
14709 if (currentDate && prevMonth.valueOf() === currentDate) {
14710 clsName += ' active';
14713 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14714 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14715 clsName += ' disabled';
14718 fillMonths.cn.push({
14720 cls: 'day ' + clsName,
14721 html: prevMonth.getDate()
14724 prevMonth.setDate(prevMonth.getDate()+1);
14727 var currentYear = this.date && this.date.getUTCFullYear();
14728 var currentMonth = this.date && this.date.getUTCMonth();
14730 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14732 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14733 v.removeClass('active');
14735 if(currentYear === year && k === currentMonth){
14736 v.addClass('active');
14739 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14740 v.addClass('disabled');
14746 year = parseInt(year/10, 10) * 10;
14748 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14750 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14753 for (var i = -1; i < 11; i++) {
14754 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14756 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14764 showMode: function(dir)
14767 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14769 Roo.each(this.picker().select('>div',true).elements, function(v){
14770 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14773 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14778 if(this.isInline) return;
14780 this.picker().removeClass(['bottom', 'top']);
14782 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14784 * place to the top of element!
14788 this.picker().addClass('top');
14789 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14794 this.picker().addClass('bottom');
14796 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14799 parseDate : function(value)
14801 if(!value || value instanceof Date){
14804 var v = Date.parseDate(value, this.format);
14805 if (!v && this.useIso) {
14806 v = Date.parseDate(value, 'Y-m-d');
14808 if(!v && this.altFormats){
14809 if(!this.altFormatsArray){
14810 this.altFormatsArray = this.altFormats.split("|");
14812 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14813 v = Date.parseDate(value, this.altFormatsArray[i]);
14819 formatDate : function(date, fmt)
14821 return (!date || !(date instanceof Date)) ?
14822 date : date.dateFormat(fmt || this.format);
14825 onFocus : function()
14827 Roo.bootstrap.DateField.superclass.onFocus.call(this);
14831 onBlur : function()
14833 Roo.bootstrap.DateField.superclass.onBlur.call(this);
14835 var d = this.inputEl().getValue();
14844 this.picker().show();
14848 this.fireEvent('show', this, this.date);
14853 if(this.isInline) return;
14854 this.picker().hide();
14855 this.viewMode = this.startViewMode;
14858 this.fireEvent('hide', this, this.date);
14862 onMousedown: function(e)
14864 e.stopPropagation();
14865 e.preventDefault();
14870 Roo.bootstrap.DateField.superclass.keyup.call(this);
14874 setValue: function(v)
14876 var d = new Date(v).clearTime();
14878 if(isNaN(d.getTime())){
14879 this.date = this.viewDate = '';
14880 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14884 v = this.formatDate(d);
14886 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14888 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14892 this.fireEvent('select', this, this.date);
14896 getValue: function()
14898 return this.formatDate(this.date);
14901 fireKey: function(e)
14903 if (!this.picker().isVisible()){
14904 if (e.keyCode == 27) // allow escape to hide and re-show picker
14909 var dateChanged = false,
14911 newDate, newViewDate;
14916 e.preventDefault();
14920 if (!this.keyboardNavigation) break;
14921 dir = e.keyCode == 37 ? -1 : 1;
14924 newDate = this.moveYear(this.date, dir);
14925 newViewDate = this.moveYear(this.viewDate, dir);
14926 } else if (e.shiftKey){
14927 newDate = this.moveMonth(this.date, dir);
14928 newViewDate = this.moveMonth(this.viewDate, dir);
14930 newDate = new Date(this.date);
14931 newDate.setUTCDate(this.date.getUTCDate() + dir);
14932 newViewDate = new Date(this.viewDate);
14933 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14935 if (this.dateWithinRange(newDate)){
14936 this.date = newDate;
14937 this.viewDate = newViewDate;
14938 this.setValue(this.formatDate(this.date));
14940 e.preventDefault();
14941 dateChanged = true;
14946 if (!this.keyboardNavigation) break;
14947 dir = e.keyCode == 38 ? -1 : 1;
14949 newDate = this.moveYear(this.date, dir);
14950 newViewDate = this.moveYear(this.viewDate, dir);
14951 } else if (e.shiftKey){
14952 newDate = this.moveMonth(this.date, dir);
14953 newViewDate = this.moveMonth(this.viewDate, dir);
14955 newDate = new Date(this.date);
14956 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14957 newViewDate = new Date(this.viewDate);
14958 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14960 if (this.dateWithinRange(newDate)){
14961 this.date = newDate;
14962 this.viewDate = newViewDate;
14963 this.setValue(this.formatDate(this.date));
14965 e.preventDefault();
14966 dateChanged = true;
14970 this.setValue(this.formatDate(this.date));
14972 e.preventDefault();
14975 this.setValue(this.formatDate(this.date));
14989 onClick: function(e)
14991 e.stopPropagation();
14992 e.preventDefault();
14994 var target = e.getTarget();
14996 if(target.nodeName.toLowerCase() === 'i'){
14997 target = Roo.get(target).dom.parentNode;
15000 var nodeName = target.nodeName;
15001 var className = target.className;
15002 var html = target.innerHTML;
15004 switch(nodeName.toLowerCase()) {
15006 switch(className) {
15012 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15013 switch(this.viewMode){
15015 this.viewDate = this.moveMonth(this.viewDate, dir);
15019 this.viewDate = this.moveYear(this.viewDate, dir);
15025 var date = new Date();
15026 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15028 this.setValue(this.formatDate(this.date));
15035 if (className.indexOf('disabled') === -1) {
15036 this.viewDate.setUTCDate(1);
15037 if (className.indexOf('month') !== -1) {
15038 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15040 var year = parseInt(html, 10) || 0;
15041 this.viewDate.setUTCFullYear(year);
15050 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
15051 var day = parseInt(html, 10) || 1;
15052 var year = this.viewDate.getUTCFullYear(),
15053 month = this.viewDate.getUTCMonth();
15055 if (className.indexOf('old') !== -1) {
15062 } else if (className.indexOf('new') !== -1) {
15070 this.date = this.UTCDate(year, month, day,0,0,0,0);
15071 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15073 this.setValue(this.formatDate(this.date));
15080 setStartDate: function(startDate)
15082 this.startDate = startDate || -Infinity;
15083 if (this.startDate !== -Infinity) {
15084 this.startDate = this.parseDate(this.startDate);
15087 this.updateNavArrows();
15090 setEndDate: function(endDate)
15092 this.endDate = endDate || Infinity;
15093 if (this.endDate !== Infinity) {
15094 this.endDate = this.parseDate(this.endDate);
15097 this.updateNavArrows();
15100 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15102 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15103 if (typeof(this.daysOfWeekDisabled) !== 'object') {
15104 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15106 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15107 return parseInt(d, 10);
15110 this.updateNavArrows();
15113 updateNavArrows: function()
15115 var d = new Date(this.viewDate),
15116 year = d.getUTCFullYear(),
15117 month = d.getUTCMonth();
15119 Roo.each(this.picker().select('.prev', true).elements, function(v){
15121 switch (this.viewMode) {
15124 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15130 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15137 Roo.each(this.picker().select('.next', true).elements, function(v){
15139 switch (this.viewMode) {
15142 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15148 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15156 moveMonth: function(date, dir)
15158 if (!dir) return date;
15159 var new_date = new Date(date.valueOf()),
15160 day = new_date.getUTCDate(),
15161 month = new_date.getUTCMonth(),
15162 mag = Math.abs(dir),
15164 dir = dir > 0 ? 1 : -1;
15167 // If going back one month, make sure month is not current month
15168 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15170 return new_date.getUTCMonth() == month;
15172 // If going forward one month, make sure month is as expected
15173 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15175 return new_date.getUTCMonth() != new_month;
15177 new_month = month + dir;
15178 new_date.setUTCMonth(new_month);
15179 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15180 if (new_month < 0 || new_month > 11)
15181 new_month = (new_month + 12) % 12;
15183 // For magnitudes >1, move one month at a time...
15184 for (var i=0; i<mag; i++)
15185 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15186 new_date = this.moveMonth(new_date, dir);
15187 // ...then reset the day, keeping it in the new month
15188 new_month = new_date.getUTCMonth();
15189 new_date.setUTCDate(day);
15191 return new_month != new_date.getUTCMonth();
15194 // Common date-resetting loop -- if date is beyond end of month, make it
15197 new_date.setUTCDate(--day);
15198 new_date.setUTCMonth(new_month);
15203 moveYear: function(date, dir)
15205 return this.moveMonth(date, dir*12);
15208 dateWithinRange: function(date)
15210 return date >= this.startDate && date <= this.endDate;
15216 this.picker().remove();
15221 Roo.apply(Roo.bootstrap.DateField, {
15232 html: '<i class="fa fa-arrow-left"/>'
15242 html: '<i class="fa fa-arrow-right"/>'
15284 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15285 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15286 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15287 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15288 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15301 navFnc: 'FullYear',
15306 navFnc: 'FullYear',
15311 Roo.apply(Roo.bootstrap.DateField, {
15315 cls: 'datepicker dropdown-menu',
15319 cls: 'datepicker-days',
15323 cls: 'table-condensed',
15325 Roo.bootstrap.DateField.head,
15329 Roo.bootstrap.DateField.footer
15336 cls: 'datepicker-months',
15340 cls: 'table-condensed',
15342 Roo.bootstrap.DateField.head,
15343 Roo.bootstrap.DateField.content,
15344 Roo.bootstrap.DateField.footer
15351 cls: 'datepicker-years',
15355 cls: 'table-condensed',
15357 Roo.bootstrap.DateField.head,
15358 Roo.bootstrap.DateField.content,
15359 Roo.bootstrap.DateField.footer
15378 * @class Roo.bootstrap.TimeField
15379 * @extends Roo.bootstrap.Input
15380 * Bootstrap DateField class
15384 * Create a new TimeField
15385 * @param {Object} config The config object
15388 Roo.bootstrap.TimeField = function(config){
15389 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15393 * Fires when this field show.
15394 * @param {Roo.bootstrap.DateField} this
15395 * @param {Mixed} date The date value
15400 * Fires when this field hide.
15401 * @param {Roo.bootstrap.DateField} this
15402 * @param {Mixed} date The date value
15407 * Fires when select a date.
15408 * @param {Roo.bootstrap.DateField} this
15409 * @param {Mixed} date The date value
15415 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
15418 * @cfg {String} format
15419 * The default time format string which can be overriden for localization support. The format must be
15420 * valid according to {@link Date#parseDate} (defaults to 'H:i').
15424 onRender: function(ct, position)
15427 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15429 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15431 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15433 this.pop = this.picker().select('>.datepicker-time',true).first();
15434 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
15436 this.picker().on('mousedown', this.onMousedown, this);
15437 this.picker().on('click', this.onClick, this);
15439 this.picker().addClass('datepicker-dropdown');
15444 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15445 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15446 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15447 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15448 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15449 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15453 fireKey: function(e){
15454 if (!this.picker().isVisible()){
15455 if (e.keyCode == 27) // allow escape to hide and re-show picker
15460 e.preventDefault();
15468 this.onTogglePeriod();
15471 this.onIncrementMinutes();
15474 this.onDecrementMinutes();
15483 onClick: function(e) {
15484 e.stopPropagation();
15485 e.preventDefault();
15488 picker : function()
15490 return this.el.select('.datepicker', true).first();
15493 fillTime: function()
15495 var time = this.pop.select('tbody', true).first();
15497 time.dom.innerHTML = '';
15512 cls: 'hours-up glyphicon glyphicon-chevron-up'
15532 cls: 'minutes-up glyphicon glyphicon-chevron-up'
15553 cls: 'timepicker-hour',
15568 cls: 'timepicker-minute',
15583 cls: 'btn btn-primary period',
15605 cls: 'hours-down glyphicon glyphicon-chevron-down'
15625 cls: 'minutes-down glyphicon glyphicon-chevron-down'
15643 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15650 var hours = this.time.getHours();
15651 var minutes = this.time.getMinutes();
15664 hours = hours - 12;
15668 hours = '0' + hours;
15672 minutes = '0' + minutes;
15675 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15676 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15677 this.pop.select('button', true).first().dom.innerHTML = period;
15683 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15685 var cls = ['bottom'];
15687 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15694 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15699 this.picker().addClass(cls.join('-'));
15703 Roo.each(cls, function(c){
15705 _this.picker().setTop(_this.inputEl().getHeight());
15709 _this.picker().setTop(0 - _this.picker().getHeight());
15714 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15718 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15725 onFocus : function()
15727 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15731 onBlur : function()
15733 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15739 this.picker().show();
15744 this.fireEvent('show', this, this.date);
15749 this.picker().hide();
15752 this.fireEvent('hide', this, this.date);
15755 setTime : function()
15758 this.setValue(this.time.format(this.format));
15760 this.fireEvent('select', this, this.date);
15765 onMousedown: function(e){
15766 e.stopPropagation();
15767 e.preventDefault();
15770 onIncrementHours: function()
15772 Roo.log('onIncrementHours');
15773 this.time = this.time.add(Date.HOUR, 1);
15778 onDecrementHours: function()
15780 Roo.log('onDecrementHours');
15781 this.time = this.time.add(Date.HOUR, -1);
15785 onIncrementMinutes: function()
15787 Roo.log('onIncrementMinutes');
15788 this.time = this.time.add(Date.MINUTE, 1);
15792 onDecrementMinutes: function()
15794 Roo.log('onDecrementMinutes');
15795 this.time = this.time.add(Date.MINUTE, -1);
15799 onTogglePeriod: function()
15801 Roo.log('onTogglePeriod');
15802 this.time = this.time.add(Date.HOUR, 12);
15809 Roo.apply(Roo.bootstrap.TimeField, {
15839 cls: 'btn btn-info ok',
15851 Roo.apply(Roo.bootstrap.TimeField, {
15855 cls: 'datepicker dropdown-menu',
15859 cls: 'datepicker-time',
15863 cls: 'table-condensed',
15865 Roo.bootstrap.TimeField.content,
15866 Roo.bootstrap.TimeField.footer
15885 * @class Roo.bootstrap.CheckBox
15886 * @extends Roo.bootstrap.Input
15887 * Bootstrap CheckBox class
15889 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15890 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15891 * @cfg {String} boxLabel The text that appears beside the checkbox
15892 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15893 * @cfg {Boolean} checked initnal the element
15897 * Create a new CheckBox
15898 * @param {Object} config The config object
15901 Roo.bootstrap.CheckBox = function(config){
15902 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15907 * Fires when the element is checked or unchecked.
15908 * @param {Roo.bootstrap.CheckBox} this This input
15909 * @param {Boolean} checked The new checked value
15915 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
15917 inputType: 'checkbox',
15924 getAutoCreate : function()
15926 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15932 cfg.cls = 'form-group checkbox' //input-group
15940 type : this.inputType,
15941 value : (!this.checked) ? this.valueOff : this.inputValue,
15942 cls : 'roo-checkbox', //'form-box',
15943 placeholder : this.placeholder || ''
15947 if (this.weight) { // Validity check?
15948 cfg.cls += " checkbox-" + this.weight;
15951 if (this.disabled) {
15952 input.disabled=true;
15956 input.checked = this.checked;
15960 input.name = this.name;
15964 input.cls += ' input-' + this.size;
15968 ['xs','sm','md','lg'].map(function(size){
15969 if (settings[size]) {
15970 cfg.cls += ' col-' + size + '-' + settings[size];
15976 var inputblock = input;
15981 if (this.before || this.after) {
15984 cls : 'input-group',
15988 inputblock.cn.push({
15990 cls : 'input-group-addon',
15994 inputblock.cn.push(input);
15996 inputblock.cn.push({
15998 cls : 'input-group-addon',
16005 if (align ==='left' && this.fieldLabel.length) {
16006 Roo.log("left and has label");
16012 cls : 'control-label col-md-' + this.labelWidth,
16013 html : this.fieldLabel
16017 cls : "col-md-" + (12 - this.labelWidth),
16024 } else if ( this.fieldLabel.length) {
16029 tag: this.boxLabel ? 'span' : 'label',
16031 cls: 'control-label box-input-label',
16032 //cls : 'input-group-addon',
16033 html : this.fieldLabel
16043 Roo.log(" no label && no align");
16044 cfg.cn = [ inputblock ] ;
16053 html: this.boxLabel
16065 * return the real input element.
16067 inputEl: function ()
16069 return this.el.select('input.roo-checkbox',true).first();
16074 return this.el.select('label.control-label',true).first();
16077 initEvents : function()
16079 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16081 this.inputEl().on('click', this.onClick, this);
16085 onClick : function()
16087 this.setChecked(!this.checked);
16090 setChecked : function(state,suppressEvent)
16092 this.checked = state;
16094 this.inputEl().dom.checked = state;
16096 if(suppressEvent !== true){
16097 this.fireEvent('check', this, state);
16100 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16104 setValue : function(v,suppressEvent)
16106 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16120 * @class Roo.bootstrap.Radio
16121 * @extends Roo.bootstrap.CheckBox
16122 * Bootstrap Radio class
16125 * Create a new Radio
16126 * @param {Object} config The config object
16129 Roo.bootstrap.Radio = function(config){
16130 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16134 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
16136 inputType: 'radio',
16140 getAutoCreate : function()
16142 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16148 cfg.cls = 'form-group radio' //input-group
16153 type : this.inputType,
16154 value : (!this.checked) ? this.valueOff : this.inputValue,
16156 placeholder : this.placeholder || ''
16159 if (this.weight) { // Validity check?
16160 cfg.cls += " radio-" + this.weight;
16162 if (this.disabled) {
16163 input.disabled=true;
16167 input.checked = this.checked;
16171 input.name = this.name;
16175 input.cls += ' input-' + this.size;
16179 ['xs','sm','md','lg'].map(function(size){
16180 if (settings[size]) {
16181 cfg.cls += ' col-' + size + '-' + settings[size];
16185 var inputblock = input;
16187 if (this.before || this.after) {
16190 cls : 'input-group',
16194 inputblock.cn.push({
16196 cls : 'input-group-addon',
16200 inputblock.cn.push(input);
16202 inputblock.cn.push({
16204 cls : 'input-group-addon',
16211 if (align ==='left' && this.fieldLabel.length) {
16212 Roo.log("left and has label");
16218 cls : 'control-label col-md-' + this.labelWidth,
16219 html : this.fieldLabel
16223 cls : "col-md-" + (12 - this.labelWidth),
16230 } else if ( this.fieldLabel.length) {
16237 cls: 'control-label box-input-label',
16238 //cls : 'input-group-addon',
16239 html : this.fieldLabel
16249 Roo.log(" no label && no align");
16264 html: this.boxLabel
16271 inputEl: function ()
16273 return this.el.select('input.roo-radio',true).first();
16275 onClick : function()
16277 this.setChecked(true);
16280 setChecked : function(state,suppressEvent)
16283 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16284 v.dom.checked = false;
16288 this.checked = state;
16289 this.inputEl().dom.checked = state;
16291 if(suppressEvent !== true){
16292 this.fireEvent('check', this, state);
16295 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16299 getGroupValue : function()
16302 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16303 if(v.dom.checked == true){
16304 value = v.dom.value;
16312 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
16313 * @return {Mixed} value The field value
16315 getValue : function(){
16316 return this.getGroupValue();
16322 //<script type="text/javascript">
16325 * Based Ext JS Library 1.1.1
16326 * Copyright(c) 2006-2007, Ext JS, LLC.
16332 * @class Roo.HtmlEditorCore
16333 * @extends Roo.Component
16334 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16336 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16339 Roo.HtmlEditorCore = function(config){
16342 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16347 * @event initialize
16348 * Fires when the editor is fully initialized (including the iframe)
16349 * @param {Roo.HtmlEditorCore} this
16354 * Fires when the editor is first receives the focus. Any insertion must wait
16355 * until after this event.
16356 * @param {Roo.HtmlEditorCore} this
16360 * @event beforesync
16361 * Fires before the textarea is updated with content from the editor iframe. Return false
16362 * to cancel the sync.
16363 * @param {Roo.HtmlEditorCore} this
16364 * @param {String} html
16368 * @event beforepush
16369 * Fires before the iframe editor is updated with content from the textarea. Return false
16370 * to cancel the push.
16371 * @param {Roo.HtmlEditorCore} this
16372 * @param {String} html
16377 * Fires when the textarea is updated with content from the editor iframe.
16378 * @param {Roo.HtmlEditorCore} this
16379 * @param {String} html
16384 * Fires when the iframe editor is updated with content from the textarea.
16385 * @param {Roo.HtmlEditorCore} this
16386 * @param {String} html
16391 * @event editorevent
16392 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16393 * @param {Roo.HtmlEditorCore} this
16398 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16400 // defaults : white / black...
16401 this.applyBlacklists();
16408 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
16412 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
16418 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
16423 * @cfg {Number} height (in pixels)
16427 * @cfg {Number} width (in pixels)
16432 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16435 stylesheets: false,
16440 // private properties
16441 validationEvent : false,
16443 initialized : false,
16445 sourceEditMode : false,
16446 onFocus : Roo.emptyFn,
16448 hideMode:'offsets',
16452 // blacklist + whitelisted elements..
16459 * Protected method that will not generally be called directly. It
16460 * is called when the editor initializes the iframe with HTML contents. Override this method if you
16461 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16463 getDocMarkup : function(){
16466 Roo.log(this.stylesheets);
16468 // inherit styels from page...??
16469 if (this.stylesheets === false) {
16471 Roo.get(document.head).select('style').each(function(node) {
16472 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16475 Roo.get(document.head).select('link').each(function(node) {
16476 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16479 } else if (!this.stylesheets.length) {
16481 st = '<style type="text/css">' +
16482 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16485 Roo.each(this.stylesheets, function(s) {
16486 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16491 st += '<style type="text/css">' +
16492 'IMG { cursor: pointer } ' +
16496 return '<html><head>' + st +
16497 //<style type="text/css">' +
16498 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16500 ' </head><body class="roo-htmleditor-body"></body></html>';
16504 onRender : function(ct, position)
16507 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16508 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16511 this.el.dom.style.border = '0 none';
16512 this.el.dom.setAttribute('tabIndex', -1);
16513 this.el.addClass('x-hidden hide');
16517 if(Roo.isIE){ // fix IE 1px bogus margin
16518 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16522 this.frameId = Roo.id();
16526 var iframe = this.owner.wrap.createChild({
16528 cls: 'form-control', // bootstrap..
16530 name: this.frameId,
16531 frameBorder : 'no',
16532 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
16537 this.iframe = iframe.dom;
16539 this.assignDocWin();
16541 this.doc.designMode = 'on';
16544 this.doc.write(this.getDocMarkup());
16548 var task = { // must defer to wait for browser to be ready
16550 //console.log("run task?" + this.doc.readyState);
16551 this.assignDocWin();
16552 if(this.doc.body || this.doc.readyState == 'complete'){
16554 this.doc.designMode="on";
16558 Roo.TaskMgr.stop(task);
16559 this.initEditor.defer(10, this);
16566 Roo.TaskMgr.start(task);
16573 onResize : function(w, h)
16575 Roo.log('resize: ' +w + ',' + h );
16576 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16580 if(typeof w == 'number'){
16582 this.iframe.style.width = w + 'px';
16584 if(typeof h == 'number'){
16586 this.iframe.style.height = h + 'px';
16588 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16595 * Toggles the editor between standard and source edit mode.
16596 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16598 toggleSourceEdit : function(sourceEditMode){
16600 this.sourceEditMode = sourceEditMode === true;
16602 if(this.sourceEditMode){
16604 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
16607 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16608 //this.iframe.className = '';
16611 //this.setSize(this.owner.wrap.getSize());
16612 //this.fireEvent('editmodechange', this, this.sourceEditMode);
16619 * Protected method that will not generally be called directly. If you need/want
16620 * custom HTML cleanup, this is the method you should override.
16621 * @param {String} html The HTML to be cleaned
16622 * return {String} The cleaned HTML
16624 cleanHtml : function(html){
16625 html = String(html);
16626 if(html.length > 5){
16627 if(Roo.isSafari){ // strip safari nonsense
16628 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16631 if(html == ' '){
16638 * HTML Editor -> Textarea
16639 * Protected method that will not generally be called directly. Syncs the contents
16640 * of the editor iframe with the textarea.
16642 syncValue : function(){
16643 if(this.initialized){
16644 var bd = (this.doc.body || this.doc.documentElement);
16645 //this.cleanUpPaste(); -- this is done else where and causes havoc..
16646 var html = bd.innerHTML;
16648 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16649 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16651 html = '<div style="'+m[0]+'">' + html + '</div>';
16654 html = this.cleanHtml(html);
16655 // fix up the special chars.. normaly like back quotes in word...
16656 // however we do not want to do this with chinese..
16657 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16658 var cc = b.charCodeAt();
16660 (cc >= 0x4E00 && cc < 0xA000 ) ||
16661 (cc >= 0x3400 && cc < 0x4E00 ) ||
16662 (cc >= 0xf900 && cc < 0xfb00 )
16668 if(this.owner.fireEvent('beforesync', this, html) !== false){
16669 this.el.dom.value = html;
16670 this.owner.fireEvent('sync', this, html);
16676 * Protected method that will not generally be called directly. Pushes the value of the textarea
16677 * into the iframe editor.
16679 pushValue : function(){
16680 if(this.initialized){
16681 var v = this.el.dom.value.trim();
16683 // if(v.length < 1){
16687 if(this.owner.fireEvent('beforepush', this, v) !== false){
16688 var d = (this.doc.body || this.doc.documentElement);
16690 this.cleanUpPaste();
16691 this.el.dom.value = d.innerHTML;
16692 this.owner.fireEvent('push', this, v);
16698 deferFocus : function(){
16699 this.focus.defer(10, this);
16703 focus : function(){
16704 if(this.win && !this.sourceEditMode){
16711 assignDocWin: function()
16713 var iframe = this.iframe;
16716 this.doc = iframe.contentWindow.document;
16717 this.win = iframe.contentWindow;
16719 // if (!Roo.get(this.frameId)) {
16722 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16723 // this.win = Roo.get(this.frameId).dom.contentWindow;
16725 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16729 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16730 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16735 initEditor : function(){
16736 //console.log("INIT EDITOR");
16737 this.assignDocWin();
16741 this.doc.designMode="on";
16743 this.doc.write(this.getDocMarkup());
16746 var dbody = (this.doc.body || this.doc.documentElement);
16747 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16748 // this copies styles from the containing element into thsi one..
16749 // not sure why we need all of this..
16750 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16752 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16753 //ss['background-attachment'] = 'fixed'; // w3c
16754 dbody.bgProperties = 'fixed'; // ie
16755 //Roo.DomHelper.applyStyles(dbody, ss);
16756 Roo.EventManager.on(this.doc, {
16757 //'mousedown': this.onEditorEvent,
16758 'mouseup': this.onEditorEvent,
16759 'dblclick': this.onEditorEvent,
16760 'click': this.onEditorEvent,
16761 'keyup': this.onEditorEvent,
16766 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16768 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16769 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16771 this.initialized = true;
16773 this.owner.fireEvent('initialize', this);
16778 onDestroy : function(){
16784 //for (var i =0; i < this.toolbars.length;i++) {
16785 // // fixme - ask toolbars for heights?
16786 // this.toolbars[i].onDestroy();
16789 //this.wrap.dom.innerHTML = '';
16790 //this.wrap.remove();
16795 onFirstFocus : function(){
16797 this.assignDocWin();
16800 this.activated = true;
16803 if(Roo.isGecko){ // prevent silly gecko errors
16805 var s = this.win.getSelection();
16806 if(!s.focusNode || s.focusNode.nodeType != 3){
16807 var r = s.getRangeAt(0);
16808 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16813 this.execCmd('useCSS', true);
16814 this.execCmd('styleWithCSS', false);
16817 this.owner.fireEvent('activate', this);
16821 adjustFont: function(btn){
16822 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16823 //if(Roo.isSafari){ // safari
16826 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16827 if(Roo.isSafari){ // safari
16828 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16829 v = (v < 10) ? 10 : v;
16830 v = (v > 48) ? 48 : v;
16831 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16836 v = Math.max(1, v+adjust);
16838 this.execCmd('FontSize', v );
16841 onEditorEvent : function(e){
16842 this.owner.fireEvent('editorevent', this, e);
16843 // this.updateToolbar();
16844 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16847 insertTag : function(tg)
16849 // could be a bit smarter... -> wrap the current selected tRoo..
16850 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16852 range = this.createRange(this.getSelection());
16853 var wrappingNode = this.doc.createElement(tg.toLowerCase());
16854 wrappingNode.appendChild(range.extractContents());
16855 range.insertNode(wrappingNode);
16862 this.execCmd("formatblock", tg);
16866 insertText : function(txt)
16870 var range = this.createRange();
16871 range.deleteContents();
16872 //alert(Sender.getAttribute('label'));
16874 range.insertNode(this.doc.createTextNode(txt));
16880 * Executes a Midas editor command on the editor document and performs necessary focus and
16881 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16882 * @param {String} cmd The Midas command
16883 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16885 relayCmd : function(cmd, value){
16887 this.execCmd(cmd, value);
16888 this.owner.fireEvent('editorevent', this);
16889 //this.updateToolbar();
16890 this.owner.deferFocus();
16894 * Executes a Midas editor command directly on the editor document.
16895 * For visual commands, you should use {@link #relayCmd} instead.
16896 * <b>This should only be called after the editor is initialized.</b>
16897 * @param {String} cmd The Midas command
16898 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16900 execCmd : function(cmd, value){
16901 this.doc.execCommand(cmd, false, value === undefined ? null : value);
16908 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16910 * @param {String} text | dom node..
16912 insertAtCursor : function(text)
16917 if(!this.activated){
16923 var r = this.doc.selection.createRange();
16934 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16938 // from jquery ui (MIT licenced)
16940 var win = this.win;
16942 if (win.getSelection && win.getSelection().getRangeAt) {
16943 range = win.getSelection().getRangeAt(0);
16944 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16945 range.insertNode(node);
16946 } else if (win.document.selection && win.document.selection.createRange) {
16947 // no firefox support
16948 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16949 win.document.selection.createRange().pasteHTML(txt);
16951 // no firefox support
16952 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16953 this.execCmd('InsertHTML', txt);
16962 mozKeyPress : function(e){
16964 var c = e.getCharCode(), cmd;
16967 c = String.fromCharCode(c).toLowerCase();
16981 this.cleanUpPaste.defer(100, this);
16989 e.preventDefault();
16997 fixKeys : function(){ // load time branching for fastest keydown performance
16999 return function(e){
17000 var k = e.getKey(), r;
17003 r = this.doc.selection.createRange();
17006 r.pasteHTML('    ');
17013 r = this.doc.selection.createRange();
17015 var target = r.parentElement();
17016 if(!target || target.tagName.toLowerCase() != 'li'){
17018 r.pasteHTML('<br />');
17024 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17025 this.cleanUpPaste.defer(100, this);
17031 }else if(Roo.isOpera){
17032 return function(e){
17033 var k = e.getKey();
17037 this.execCmd('InsertHTML','    ');
17040 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17041 this.cleanUpPaste.defer(100, this);
17046 }else if(Roo.isSafari){
17047 return function(e){
17048 var k = e.getKey();
17052 this.execCmd('InsertText','\t');
17056 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17057 this.cleanUpPaste.defer(100, this);
17065 getAllAncestors: function()
17067 var p = this.getSelectedNode();
17070 a.push(p); // push blank onto stack..
17071 p = this.getParentElement();
17075 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17079 a.push(this.doc.body);
17083 lastSelNode : false,
17086 getSelection : function()
17088 this.assignDocWin();
17089 return Roo.isIE ? this.doc.selection : this.win.getSelection();
17092 getSelectedNode: function()
17094 // this may only work on Gecko!!!
17096 // should we cache this!!!!
17101 var range = this.createRange(this.getSelection()).cloneRange();
17104 var parent = range.parentElement();
17106 var testRange = range.duplicate();
17107 testRange.moveToElementText(parent);
17108 if (testRange.inRange(range)) {
17111 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17114 parent = parent.parentElement;
17119 // is ancestor a text element.
17120 var ac = range.commonAncestorContainer;
17121 if (ac.nodeType == 3) {
17122 ac = ac.parentNode;
17125 var ar = ac.childNodes;
17128 var other_nodes = [];
17129 var has_other_nodes = false;
17130 for (var i=0;i<ar.length;i++) {
17131 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
17134 // fullly contained node.
17136 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17141 // probably selected..
17142 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17143 other_nodes.push(ar[i]);
17147 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
17152 has_other_nodes = true;
17154 if (!nodes.length && other_nodes.length) {
17155 nodes= other_nodes;
17157 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17163 createRange: function(sel)
17165 // this has strange effects when using with
17166 // top toolbar - not sure if it's a great idea.
17167 //this.editor.contentWindow.focus();
17168 if (typeof sel != "undefined") {
17170 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17172 return this.doc.createRange();
17175 return this.doc.createRange();
17178 getParentElement: function()
17181 this.assignDocWin();
17182 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17184 var range = this.createRange(sel);
17187 var p = range.commonAncestorContainer;
17188 while (p.nodeType == 3) { // text node
17199 * Range intersection.. the hard stuff...
17203 * [ -- selected range --- ]
17207 * if end is before start or hits it. fail.
17208 * if start is after end or hits it fail.
17210 * if either hits (but other is outside. - then it's not
17216 // @see http://www.thismuchiknow.co.uk/?p=64.
17217 rangeIntersectsNode : function(range, node)
17219 var nodeRange = node.ownerDocument.createRange();
17221 nodeRange.selectNode(node);
17223 nodeRange.selectNodeContents(node);
17226 var rangeStartRange = range.cloneRange();
17227 rangeStartRange.collapse(true);
17229 var rangeEndRange = range.cloneRange();
17230 rangeEndRange.collapse(false);
17232 var nodeStartRange = nodeRange.cloneRange();
17233 nodeStartRange.collapse(true);
17235 var nodeEndRange = nodeRange.cloneRange();
17236 nodeEndRange.collapse(false);
17238 return rangeStartRange.compareBoundaryPoints(
17239 Range.START_TO_START, nodeEndRange) == -1 &&
17240 rangeEndRange.compareBoundaryPoints(
17241 Range.START_TO_START, nodeStartRange) == 1;
17245 rangeCompareNode : function(range, node)
17247 var nodeRange = node.ownerDocument.createRange();
17249 nodeRange.selectNode(node);
17251 nodeRange.selectNodeContents(node);
17255 range.collapse(true);
17257 nodeRange.collapse(true);
17259 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17260 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
17262 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17264 var nodeIsBefore = ss == 1;
17265 var nodeIsAfter = ee == -1;
17267 if (nodeIsBefore && nodeIsAfter)
17269 if (!nodeIsBefore && nodeIsAfter)
17270 return 1; //right trailed.
17272 if (nodeIsBefore && !nodeIsAfter)
17273 return 2; // left trailed.
17278 // private? - in a new class?
17279 cleanUpPaste : function()
17281 // cleans up the whole document..
17282 Roo.log('cleanuppaste');
17284 this.cleanUpChildren(this.doc.body);
17285 var clean = this.cleanWordChars(this.doc.body.innerHTML);
17286 if (clean != this.doc.body.innerHTML) {
17287 this.doc.body.innerHTML = clean;
17292 cleanWordChars : function(input) {// change the chars to hex code
17293 var he = Roo.HtmlEditorCore;
17295 var output = input;
17296 Roo.each(he.swapCodes, function(sw) {
17297 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17299 output = output.replace(swapper, sw[1]);
17306 cleanUpChildren : function (n)
17308 if (!n.childNodes.length) {
17311 for (var i = n.childNodes.length-1; i > -1 ; i--) {
17312 this.cleanUpChild(n.childNodes[i]);
17319 cleanUpChild : function (node)
17322 //console.log(node);
17323 if (node.nodeName == "#text") {
17324 // clean up silly Windows -- stuff?
17327 if (node.nodeName == "#comment") {
17328 node.parentNode.removeChild(node);
17329 // clean up silly Windows -- stuff?
17332 var lcname = node.tagName.toLowerCase();
17333 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17334 // whitelist of tags..
17336 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17338 node.parentNode.removeChild(node);
17343 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17345 // remove <a name=....> as rendering on yahoo mailer is borked with this.
17346 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17348 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17349 // remove_keep_children = true;
17352 if (remove_keep_children) {
17353 this.cleanUpChildren(node);
17354 // inserts everything just before this node...
17355 while (node.childNodes.length) {
17356 var cn = node.childNodes[0];
17357 node.removeChild(cn);
17358 node.parentNode.insertBefore(cn, node);
17360 node.parentNode.removeChild(node);
17364 if (!node.attributes || !node.attributes.length) {
17365 this.cleanUpChildren(node);
17369 function cleanAttr(n,v)
17372 if (v.match(/^\./) || v.match(/^\//)) {
17375 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17378 if (v.match(/^#/)) {
17381 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17382 node.removeAttribute(n);
17386 var cwhite = this.cwhite;
17387 var cblack = this.cblack;
17389 function cleanStyle(n,v)
17391 if (v.match(/expression/)) { //XSS?? should we even bother..
17392 node.removeAttribute(n);
17396 var parts = v.split(/;/);
17399 Roo.each(parts, function(p) {
17400 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17404 var l = p.split(':').shift().replace(/\s+/g,'');
17405 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17407 if ( cwhite.length && cblack.indexOf(l) > -1) {
17408 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17409 //node.removeAttribute(n);
17413 // only allow 'c whitelisted system attributes'
17414 if ( cwhite.length && cwhite.indexOf(l) < 0) {
17415 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17416 //node.removeAttribute(n);
17426 if (clean.length) {
17427 node.setAttribute(n, clean.join(';'));
17429 node.removeAttribute(n);
17435 for (var i = node.attributes.length-1; i > -1 ; i--) {
17436 var a = node.attributes[i];
17439 if (a.name.toLowerCase().substr(0,2)=='on') {
17440 node.removeAttribute(a.name);
17443 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17444 node.removeAttribute(a.name);
17447 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17448 cleanAttr(a.name,a.value); // fixme..
17451 if (a.name == 'style') {
17452 cleanStyle(a.name,a.value);
17455 /// clean up MS crap..
17456 // tecnically this should be a list of valid class'es..
17459 if (a.name == 'class') {
17460 if (a.value.match(/^Mso/)) {
17461 node.className = '';
17464 if (a.value.match(/body/)) {
17465 node.className = '';
17476 this.cleanUpChildren(node);
17481 * Clean up MS wordisms...
17483 cleanWord : function(node)
17486 var cleanWordChildren = function()
17488 if (!node.childNodes.length) {
17491 for (var i = node.childNodes.length-1; i > -1 ; i--) {
17492 _t.cleanWord(node.childNodes[i]);
17498 this.cleanWord(this.doc.body);
17501 if (node.nodeName == "#text") {
17502 // clean up silly Windows -- stuff?
17505 if (node.nodeName == "#comment") {
17506 node.parentNode.removeChild(node);
17507 // clean up silly Windows -- stuff?
17511 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17512 node.parentNode.removeChild(node);
17516 // remove - but keep children..
17517 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17518 while (node.childNodes.length) {
17519 var cn = node.childNodes[0];
17520 node.removeChild(cn);
17521 node.parentNode.insertBefore(cn, node);
17523 node.parentNode.removeChild(node);
17524 cleanWordChildren();
17528 if (node.className.length) {
17530 var cn = node.className.split(/\W+/);
17532 Roo.each(cn, function(cls) {
17533 if (cls.match(/Mso[a-zA-Z]+/)) {
17538 node.className = cna.length ? cna.join(' ') : '';
17540 node.removeAttribute("class");
17544 if (node.hasAttribute("lang")) {
17545 node.removeAttribute("lang");
17548 if (node.hasAttribute("style")) {
17550 var styles = node.getAttribute("style").split(";");
17552 Roo.each(styles, function(s) {
17553 if (!s.match(/:/)) {
17556 var kv = s.split(":");
17557 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17560 // what ever is left... we allow.
17563 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17564 if (!nstyle.length) {
17565 node.removeAttribute('style');
17569 cleanWordChildren();
17573 domToHTML : function(currentElement, depth, nopadtext) {
17575 depth = depth || 0;
17576 nopadtext = nopadtext || false;
17578 if (!currentElement) {
17579 return this.domToHTML(this.doc.body);
17582 //Roo.log(currentElement);
17584 var allText = false;
17585 var nodeName = currentElement.nodeName;
17586 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17588 if (nodeName == '#text') {
17589 return currentElement.nodeValue;
17594 if (nodeName != 'BODY') {
17597 // Prints the node tagName, such as <A>, <IMG>, etc
17600 for(i = 0; i < currentElement.attributes.length;i++) {
17602 var aname = currentElement.attributes.item(i).name;
17603 if (!currentElement.attributes.item(i).value.length) {
17606 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17609 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17618 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17621 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17626 // Traverse the tree
17628 var currentElementChild = currentElement.childNodes.item(i);
17629 var allText = true;
17630 var innerHTML = '';
17632 while (currentElementChild) {
17633 // Formatting code (indent the tree so it looks nice on the screen)
17634 var nopad = nopadtext;
17635 if (lastnode == 'SPAN') {
17639 if (currentElementChild.nodeName == '#text') {
17640 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17641 if (!nopad && toadd.length > 80) {
17642 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
17644 innerHTML += toadd;
17647 currentElementChild = currentElement.childNodes.item(i);
17653 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
17655 // Recursively traverse the tree structure of the child node
17656 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
17657 lastnode = currentElementChild.nodeName;
17659 currentElementChild=currentElement.childNodes.item(i);
17665 // The remaining code is mostly for formatting the tree
17666 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
17671 ret+= "</"+tagName+">";
17677 applyBlacklists : function()
17679 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
17680 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
17684 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17685 if (b.indexOf(tag) > -1) {
17688 this.white.push(tag);
17692 Roo.each(w, function(tag) {
17693 if (b.indexOf(tag) > -1) {
17696 if (this.white.indexOf(tag) > -1) {
17699 this.white.push(tag);
17704 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17705 if (w.indexOf(tag) > -1) {
17708 this.black.push(tag);
17712 Roo.each(b, function(tag) {
17713 if (w.indexOf(tag) > -1) {
17716 if (this.black.indexOf(tag) > -1) {
17719 this.black.push(tag);
17724 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
17725 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
17729 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17730 if (b.indexOf(tag) > -1) {
17733 this.cwhite.push(tag);
17737 Roo.each(w, function(tag) {
17738 if (b.indexOf(tag) > -1) {
17741 if (this.cwhite.indexOf(tag) > -1) {
17744 this.cwhite.push(tag);
17749 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17750 if (w.indexOf(tag) > -1) {
17753 this.cblack.push(tag);
17757 Roo.each(b, function(tag) {
17758 if (w.indexOf(tag) > -1) {
17761 if (this.cblack.indexOf(tag) > -1) {
17764 this.cblack.push(tag);
17769 // hide stuff that is not compatible
17783 * @event specialkey
17787 * @cfg {String} fieldClass @hide
17790 * @cfg {String} focusClass @hide
17793 * @cfg {String} autoCreate @hide
17796 * @cfg {String} inputType @hide
17799 * @cfg {String} invalidClass @hide
17802 * @cfg {String} invalidText @hide
17805 * @cfg {String} msgFx @hide
17808 * @cfg {String} validateOnBlur @hide
17812 Roo.HtmlEditorCore.white = [
17813 'area', 'br', 'img', 'input', 'hr', 'wbr',
17815 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
17816 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
17817 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
17818 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
17819 'table', 'ul', 'xmp',
17821 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
17824 'dir', 'menu', 'ol', 'ul', 'dl',
17830 Roo.HtmlEditorCore.black = [
17831 // 'embed', 'object', // enable - backend responsiblity to clean thiese
17833 'base', 'basefont', 'bgsound', 'blink', 'body',
17834 'frame', 'frameset', 'head', 'html', 'ilayer',
17835 'iframe', 'layer', 'link', 'meta', 'object',
17836 'script', 'style' ,'title', 'xml' // clean later..
17838 Roo.HtmlEditorCore.clean = [
17839 'script', 'style', 'title', 'xml'
17841 Roo.HtmlEditorCore.remove = [
17846 Roo.HtmlEditorCore.ablack = [
17850 Roo.HtmlEditorCore.aclean = [
17851 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
17855 Roo.HtmlEditorCore.pwhite= [
17856 'http', 'https', 'mailto'
17859 // white listed style attributes.
17860 Roo.HtmlEditorCore.cwhite= [
17861 // 'text-align', /// default is to allow most things..
17867 // black listed style attributes.
17868 Roo.HtmlEditorCore.cblack= [
17869 // 'font-size' -- this can be set by the project
17873 Roo.HtmlEditorCore.swapCodes =[
17892 * @class Roo.bootstrap.HtmlEditor
17893 * @extends Roo.bootstrap.TextArea
17894 * Bootstrap HtmlEditor class
17897 * Create a new HtmlEditor
17898 * @param {Object} config The config object
17901 Roo.bootstrap.HtmlEditor = function(config){
17902 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17903 if (!this.toolbars) {
17904 this.toolbars = [];
17906 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17909 * @event initialize
17910 * Fires when the editor is fully initialized (including the iframe)
17911 * @param {HtmlEditor} this
17916 * Fires when the editor is first receives the focus. Any insertion must wait
17917 * until after this event.
17918 * @param {HtmlEditor} this
17922 * @event beforesync
17923 * Fires before the textarea is updated with content from the editor iframe. Return false
17924 * to cancel the sync.
17925 * @param {HtmlEditor} this
17926 * @param {String} html
17930 * @event beforepush
17931 * Fires before the iframe editor is updated with content from the textarea. Return false
17932 * to cancel the push.
17933 * @param {HtmlEditor} this
17934 * @param {String} html
17939 * Fires when the textarea is updated with content from the editor iframe.
17940 * @param {HtmlEditor} this
17941 * @param {String} html
17946 * Fires when the iframe editor is updated with content from the textarea.
17947 * @param {HtmlEditor} this
17948 * @param {String} html
17952 * @event editmodechange
17953 * Fires when the editor switches edit modes
17954 * @param {HtmlEditor} this
17955 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17957 editmodechange: true,
17959 * @event editorevent
17960 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17961 * @param {HtmlEditor} this
17965 * @event firstfocus
17966 * Fires when on first focus - needed by toolbars..
17967 * @param {HtmlEditor} this
17972 * Auto save the htmlEditor value as a file into Events
17973 * @param {HtmlEditor} this
17977 * @event savedpreview
17978 * preview the saved version of htmlEditor
17979 * @param {HtmlEditor} this
17986 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
17990 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17995 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
18000 * @cfg {Number} height (in pixels)
18004 * @cfg {Number} width (in pixels)
18009 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18012 stylesheets: false,
18017 // private properties
18018 validationEvent : false,
18020 initialized : false,
18023 onFocus : Roo.emptyFn,
18025 hideMode:'offsets',
18028 tbContainer : false,
18030 toolbarContainer :function() {
18031 return this.wrap.select('.x-html-editor-tb',true).first();
18035 * Protected method that will not generally be called directly. It
18036 * is called when the editor creates its toolbar. Override this method if you need to
18037 * add custom toolbar buttons.
18038 * @param {HtmlEditor} editor
18040 createToolbar : function(){
18042 Roo.log("create toolbars");
18044 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18045 this.toolbars[0].render(this.toolbarContainer());
18049 // if (!editor.toolbars || !editor.toolbars.length) {
18050 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18053 // for (var i =0 ; i < editor.toolbars.length;i++) {
18054 // editor.toolbars[i] = Roo.factory(
18055 // typeof(editor.toolbars[i]) == 'string' ?
18056 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
18057 // Roo.bootstrap.HtmlEditor);
18058 // editor.toolbars[i].init(editor);
18064 onRender : function(ct, position)
18066 // Roo.log("Call onRender: " + this.xtype);
18068 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18070 this.wrap = this.inputEl().wrap({
18071 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18074 this.editorcore.onRender(ct, position);
18076 if (this.resizable) {
18077 this.resizeEl = new Roo.Resizable(this.wrap, {
18081 minHeight : this.height,
18082 height: this.height,
18083 handles : this.resizable,
18086 resize : function(r, w, h) {
18087 _t.onResize(w,h); // -something
18093 this.createToolbar(this);
18096 if(!this.width && this.resizable){
18097 this.setSize(this.wrap.getSize());
18099 if (this.resizeEl) {
18100 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18101 // should trigger onReize..
18107 onResize : function(w, h)
18109 Roo.log('resize: ' +w + ',' + h );
18110 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18114 if(this.inputEl() ){
18115 if(typeof w == 'number'){
18116 var aw = w - this.wrap.getFrameWidth('lr');
18117 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18120 if(typeof h == 'number'){
18121 var tbh = -11; // fixme it needs to tool bar size!
18122 for (var i =0; i < this.toolbars.length;i++) {
18123 // fixme - ask toolbars for heights?
18124 tbh += this.toolbars[i].el.getHeight();
18125 //if (this.toolbars[i].footer) {
18126 // tbh += this.toolbars[i].footer.el.getHeight();
18134 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18135 ah -= 5; // knock a few pixes off for look..
18136 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18140 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18141 this.editorcore.onResize(ew,eh);
18146 * Toggles the editor between standard and source edit mode.
18147 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18149 toggleSourceEdit : function(sourceEditMode)
18151 this.editorcore.toggleSourceEdit(sourceEditMode);
18153 if(this.editorcore.sourceEditMode){
18154 Roo.log('editor - showing textarea');
18157 // Roo.log(this.syncValue());
18159 this.inputEl().removeClass(['hide', 'x-hidden']);
18160 this.inputEl().dom.removeAttribute('tabIndex');
18161 this.inputEl().focus();
18163 Roo.log('editor - hiding textarea');
18165 // Roo.log(this.pushValue());
18168 this.inputEl().addClass(['hide', 'x-hidden']);
18169 this.inputEl().dom.setAttribute('tabIndex', -1);
18170 //this.deferFocus();
18173 if(this.resizable){
18174 this.setSize(this.wrap.getSize());
18177 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18180 // private (for BoxComponent)
18181 adjustSize : Roo.BoxComponent.prototype.adjustSize,
18183 // private (for BoxComponent)
18184 getResizeEl : function(){
18188 // private (for BoxComponent)
18189 getPositionEl : function(){
18194 initEvents : function(){
18195 this.originalValue = this.getValue();
18199 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18202 // markInvalid : Roo.emptyFn,
18204 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18207 // clearInvalid : Roo.emptyFn,
18209 setValue : function(v){
18210 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18211 this.editorcore.pushValue();
18216 deferFocus : function(){
18217 this.focus.defer(10, this);
18221 focus : function(){
18222 this.editorcore.focus();
18228 onDestroy : function(){
18234 for (var i =0; i < this.toolbars.length;i++) {
18235 // fixme - ask toolbars for heights?
18236 this.toolbars[i].onDestroy();
18239 this.wrap.dom.innerHTML = '';
18240 this.wrap.remove();
18245 onFirstFocus : function(){
18246 //Roo.log("onFirstFocus");
18247 this.editorcore.onFirstFocus();
18248 for (var i =0; i < this.toolbars.length;i++) {
18249 this.toolbars[i].onFirstFocus();
18255 syncValue : function()
18257 this.editorcore.syncValue();
18260 pushValue : function()
18262 this.editorcore.pushValue();
18266 // hide stuff that is not compatible
18280 * @event specialkey
18284 * @cfg {String} fieldClass @hide
18287 * @cfg {String} focusClass @hide
18290 * @cfg {String} autoCreate @hide
18293 * @cfg {String} inputType @hide
18296 * @cfg {String} invalidClass @hide
18299 * @cfg {String} invalidText @hide
18302 * @cfg {String} msgFx @hide
18305 * @cfg {String} validateOnBlur @hide
18314 Roo.namespace('Roo.bootstrap.htmleditor');
18316 * @class Roo.bootstrap.HtmlEditorToolbar1
18321 new Roo.bootstrap.HtmlEditor({
18324 new Roo.bootstrap.HtmlEditorToolbar1({
18325 disable : { fonts: 1 , format: 1, ..., ... , ...],
18331 * @cfg {Object} disable List of elements to disable..
18332 * @cfg {Array} btns List of additional buttons.
18336 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18339 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18342 Roo.apply(this, config);
18344 // default disabled, based on 'good practice'..
18345 this.disable = this.disable || {};
18346 Roo.applyIf(this.disable, {
18349 specialElements : true
18351 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18353 this.editor = config.editor;
18354 this.editorcore = config.editor.editorcore;
18356 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18358 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18359 // dont call parent... till later.
18361 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
18366 editorcore : false,
18371 "h1","h2","h3","h4","h5","h6",
18373 "abbr", "acronym", "address", "cite", "samp", "var",
18377 onRender : function(ct, position)
18379 // Roo.log("Call onRender: " + this.xtype);
18381 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18383 this.el.dom.style.marginBottom = '0';
18385 var editorcore = this.editorcore;
18386 var editor= this.editor;
18389 var btn = function(id,cmd , toggle, handler){
18391 var event = toggle ? 'toggle' : 'click';
18396 xns: Roo.bootstrap,
18399 enableToggle:toggle !== false,
18401 pressed : toggle ? false : null,
18404 a.listeners[toggle ? 'toggle' : 'click'] = function() {
18405 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
18414 xns: Roo.bootstrap,
18415 glyphicon : 'font',
18419 xns: Roo.bootstrap,
18423 Roo.each(this.formats, function(f) {
18424 style.menu.items.push({
18426 xns: Roo.bootstrap,
18427 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18432 editorcore.insertTag(this.tagname);
18439 children.push(style);
18442 btn('bold',false,true);
18443 btn('italic',false,true);
18444 btn('align-left', 'justifyleft',true);
18445 btn('align-center', 'justifycenter',true);
18446 btn('align-right' , 'justifyright',true);
18447 btn('link', false, false, function(btn) {
18448 //Roo.log("create link?");
18449 var url = prompt(this.createLinkText, this.defaultLinkValue);
18450 if(url && url != 'http:/'+'/'){
18451 this.editorcore.relayCmd('createlink', url);
18454 btn('list','insertunorderedlist',true);
18455 btn('pencil', false,true, function(btn){
18458 this.toggleSourceEdit(btn.pressed);
18464 xns: Roo.bootstrap,
18469 xns: Roo.bootstrap,
18474 cog.menu.items.push({
18476 xns: Roo.bootstrap,
18477 html : Clean styles,
18482 editorcore.insertTag(this.tagname);
18491 this.xtype = 'NavSimplebar';
18493 for(var i=0;i< children.length;i++) {
18495 this.buttons.add(this.addxtypeChild(children[i]));
18499 editor.on('editorevent', this.updateToolbar, this);
18501 onBtnClick : function(id)
18503 this.editorcore.relayCmd(id);
18504 this.editorcore.focus();
18508 * Protected method that will not generally be called directly. It triggers
18509 * a toolbar update by reading the markup state of the current selection in the editor.
18511 updateToolbar: function(){
18513 if(!this.editorcore.activated){
18514 this.editor.onFirstFocus(); // is this neeed?
18518 var btns = this.buttons;
18519 var doc = this.editorcore.doc;
18520 btns.get('bold').setActive(doc.queryCommandState('bold'));
18521 btns.get('italic').setActive(doc.queryCommandState('italic'));
18522 //btns.get('underline').setActive(doc.queryCommandState('underline'));
18524 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18525 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18526 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18528 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18529 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18532 var ans = this.editorcore.getAllAncestors();
18533 if (this.formatCombo) {
18536 var store = this.formatCombo.store;
18537 this.formatCombo.setValue("");
18538 for (var i =0; i < ans.length;i++) {
18539 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18541 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18549 // hides menus... - so this cant be on a menu...
18550 Roo.bootstrap.MenuMgr.hideAll();
18552 Roo.bootstrap.MenuMgr.hideAll();
18553 //this.editorsyncValue();
18555 onFirstFocus: function() {
18556 this.buttons.each(function(item){
18560 toggleSourceEdit : function(sourceEditMode){
18563 if(sourceEditMode){
18564 Roo.log("disabling buttons");
18565 this.buttons.each( function(item){
18566 if(item.cmd != 'pencil'){
18572 Roo.log("enabling buttons");
18573 if(this.editorcore.initialized){
18574 this.buttons.each( function(item){
18580 Roo.log("calling toggole on editor");
18581 // tell the editor that it's been pressed..
18582 this.editor.toggleSourceEdit(sourceEditMode);
18592 * @class Roo.bootstrap.Table.AbstractSelectionModel
18593 * @extends Roo.util.Observable
18594 * Abstract base class for grid SelectionModels. It provides the interface that should be
18595 * implemented by descendant classes. This class should not be directly instantiated.
18598 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18599 this.locked = false;
18600 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18604 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
18605 /** @ignore Called by the grid automatically. Do not call directly. */
18606 init : function(grid){
18612 * Locks the selections.
18615 this.locked = true;
18619 * Unlocks the selections.
18621 unlock : function(){
18622 this.locked = false;
18626 * Returns true if the selections are locked.
18627 * @return {Boolean}
18629 isLocked : function(){
18630 return this.locked;
18634 * @extends Roo.bootstrap.Table.AbstractSelectionModel
18635 * @class Roo.bootstrap.Table.RowSelectionModel
18636 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18637 * It supports multiple selections and keyboard selection/navigation.
18639 * @param {Object} config
18642 Roo.bootstrap.Table.RowSelectionModel = function(config){
18643 Roo.apply(this, config);
18644 this.selections = new Roo.util.MixedCollection(false, function(o){
18649 this.lastActive = false;
18653 * @event selectionchange
18654 * Fires when the selection changes
18655 * @param {SelectionModel} this
18657 "selectionchange" : true,
18659 * @event afterselectionchange
18660 * Fires after the selection changes (eg. by key press or clicking)
18661 * @param {SelectionModel} this
18663 "afterselectionchange" : true,
18665 * @event beforerowselect
18666 * Fires when a row is selected being selected, return false to cancel.
18667 * @param {SelectionModel} this
18668 * @param {Number} rowIndex The selected index
18669 * @param {Boolean} keepExisting False if other selections will be cleared
18671 "beforerowselect" : true,
18674 * Fires when a row is selected.
18675 * @param {SelectionModel} this
18676 * @param {Number} rowIndex The selected index
18677 * @param {Roo.data.Record} r The record
18679 "rowselect" : true,
18681 * @event rowdeselect
18682 * Fires when a row is deselected.
18683 * @param {SelectionModel} this
18684 * @param {Number} rowIndex The selected index
18686 "rowdeselect" : true
18688 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18689 this.locked = false;
18692 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
18694 * @cfg {Boolean} singleSelect
18695 * True to allow selection of only one row at a time (defaults to false)
18697 singleSelect : false,
18700 initEvents : function(){
18702 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18703 this.grid.on("mousedown", this.handleMouseDown, this);
18704 }else{ // allow click to work like normal
18705 this.grid.on("rowclick", this.handleDragableRowClick, this);
18708 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18709 "up" : function(e){
18711 this.selectPrevious(e.shiftKey);
18712 }else if(this.last !== false && this.lastActive !== false){
18713 var last = this.last;
18714 this.selectRange(this.last, this.lastActive-1);
18715 this.grid.getView().focusRow(this.lastActive);
18716 if(last !== false){
18720 this.selectFirstRow();
18722 this.fireEvent("afterselectionchange", this);
18724 "down" : function(e){
18726 this.selectNext(e.shiftKey);
18727 }else if(this.last !== false && this.lastActive !== false){
18728 var last = this.last;
18729 this.selectRange(this.last, this.lastActive+1);
18730 this.grid.getView().focusRow(this.lastActive);
18731 if(last !== false){
18735 this.selectFirstRow();
18737 this.fireEvent("afterselectionchange", this);
18742 var view = this.grid.view;
18743 view.on("refresh", this.onRefresh, this);
18744 view.on("rowupdated", this.onRowUpdated, this);
18745 view.on("rowremoved", this.onRemove, this);
18749 onRefresh : function(){
18750 var ds = this.grid.dataSource, i, v = this.grid.view;
18751 var s = this.selections;
18752 s.each(function(r){
18753 if((i = ds.indexOfId(r.id)) != -1){
18762 onRemove : function(v, index, r){
18763 this.selections.remove(r);
18767 onRowUpdated : function(v, index, r){
18768 if(this.isSelected(r)){
18769 v.onRowSelect(index);
18775 * @param {Array} records The records to select
18776 * @param {Boolean} keepExisting (optional) True to keep existing selections
18778 selectRecords : function(records, keepExisting){
18780 this.clearSelections();
18782 var ds = this.grid.dataSource;
18783 for(var i = 0, len = records.length; i < len; i++){
18784 this.selectRow(ds.indexOf(records[i]), true);
18789 * Gets the number of selected rows.
18792 getCount : function(){
18793 return this.selections.length;
18797 * Selects the first row in the grid.
18799 selectFirstRow : function(){
18804 * Select the last row.
18805 * @param {Boolean} keepExisting (optional) True to keep existing selections
18807 selectLastRow : function(keepExisting){
18808 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18812 * Selects the row immediately following the last selected row.
18813 * @param {Boolean} keepExisting (optional) True to keep existing selections
18815 selectNext : function(keepExisting){
18816 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18817 this.selectRow(this.last+1, keepExisting);
18818 this.grid.getView().focusRow(this.last);
18823 * Selects the row that precedes the last selected row.
18824 * @param {Boolean} keepExisting (optional) True to keep existing selections
18826 selectPrevious : function(keepExisting){
18828 this.selectRow(this.last-1, keepExisting);
18829 this.grid.getView().focusRow(this.last);
18834 * Returns the selected records
18835 * @return {Array} Array of selected records
18837 getSelections : function(){
18838 return [].concat(this.selections.items);
18842 * Returns the first selected record.
18845 getSelected : function(){
18846 return this.selections.itemAt(0);
18851 * Clears all selections.
18853 clearSelections : function(fast){
18854 if(this.locked) return;
18856 var ds = this.grid.dataSource;
18857 var s = this.selections;
18858 s.each(function(r){
18859 this.deselectRow(ds.indexOfId(r.id));
18863 this.selections.clear();
18870 * Selects all rows.
18872 selectAll : function(){
18873 if(this.locked) return;
18874 this.selections.clear();
18875 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18876 this.selectRow(i, true);
18881 * Returns True if there is a selection.
18882 * @return {Boolean}
18884 hasSelection : function(){
18885 return this.selections.length > 0;
18889 * Returns True if the specified row is selected.
18890 * @param {Number/Record} record The record or index of the record to check
18891 * @return {Boolean}
18893 isSelected : function(index){
18894 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18895 return (r && this.selections.key(r.id) ? true : false);
18899 * Returns True if the specified record id is selected.
18900 * @param {String} id The id of record to check
18901 * @return {Boolean}
18903 isIdSelected : function(id){
18904 return (this.selections.key(id) ? true : false);
18908 handleMouseDown : function(e, t){
18909 var view = this.grid.getView(), rowIndex;
18910 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18913 if(e.shiftKey && this.last !== false){
18914 var last = this.last;
18915 this.selectRange(last, rowIndex, e.ctrlKey);
18916 this.last = last; // reset the last
18917 view.focusRow(rowIndex);
18919 var isSelected = this.isSelected(rowIndex);
18920 if(e.button !== 0 && isSelected){
18921 view.focusRow(rowIndex);
18922 }else if(e.ctrlKey && isSelected){
18923 this.deselectRow(rowIndex);
18924 }else if(!isSelected){
18925 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18926 view.focusRow(rowIndex);
18929 this.fireEvent("afterselectionchange", this);
18932 handleDragableRowClick : function(grid, rowIndex, e)
18934 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18935 this.selectRow(rowIndex, false);
18936 grid.view.focusRow(rowIndex);
18937 this.fireEvent("afterselectionchange", this);
18942 * Selects multiple rows.
18943 * @param {Array} rows Array of the indexes of the row to select
18944 * @param {Boolean} keepExisting (optional) True to keep existing selections
18946 selectRows : function(rows, keepExisting){
18948 this.clearSelections();
18950 for(var i = 0, len = rows.length; i < len; i++){
18951 this.selectRow(rows[i], true);
18956 * Selects a range of rows. All rows in between startRow and endRow are also selected.
18957 * @param {Number} startRow The index of the first row in the range
18958 * @param {Number} endRow The index of the last row in the range
18959 * @param {Boolean} keepExisting (optional) True to retain existing selections
18961 selectRange : function(startRow, endRow, keepExisting){
18962 if(this.locked) return;
18964 this.clearSelections();
18966 if(startRow <= endRow){
18967 for(var i = startRow; i <= endRow; i++){
18968 this.selectRow(i, true);
18971 for(var i = startRow; i >= endRow; i--){
18972 this.selectRow(i, true);
18978 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18979 * @param {Number} startRow The index of the first row in the range
18980 * @param {Number} endRow The index of the last row in the range
18982 deselectRange : function(startRow, endRow, preventViewNotify){
18983 if(this.locked) return;
18984 for(var i = startRow; i <= endRow; i++){
18985 this.deselectRow(i, preventViewNotify);
18991 * @param {Number} row The index of the row to select
18992 * @param {Boolean} keepExisting (optional) True to keep existing selections
18994 selectRow : function(index, keepExisting, preventViewNotify){
18995 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18996 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18997 if(!keepExisting || this.singleSelect){
18998 this.clearSelections();
19000 var r = this.grid.dataSource.getAt(index);
19001 this.selections.add(r);
19002 this.last = this.lastActive = index;
19003 if(!preventViewNotify){
19004 this.grid.getView().onRowSelect(index);
19006 this.fireEvent("rowselect", this, index, r);
19007 this.fireEvent("selectionchange", this);
19013 * @param {Number} row The index of the row to deselect
19015 deselectRow : function(index, preventViewNotify){
19016 if(this.locked) return;
19017 if(this.last == index){
19020 if(this.lastActive == index){
19021 this.lastActive = false;
19023 var r = this.grid.dataSource.getAt(index);
19024 this.selections.remove(r);
19025 if(!preventViewNotify){
19026 this.grid.getView().onRowDeselect(index);
19028 this.fireEvent("rowdeselect", this, index);
19029 this.fireEvent("selectionchange", this);
19033 restoreLast : function(){
19035 this.last = this._last;
19040 acceptsNav : function(row, col, cm){
19041 return !cm.isHidden(col) && cm.isCellEditable(col, row);
19045 onEditorKey : function(field, e){
19046 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19051 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19053 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19055 }else if(k == e.ENTER && !e.ctrlKey){
19059 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19061 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19063 }else if(k == e.ESC){
19067 g.startEditing(newCell[0], newCell[1]);
19072 * Ext JS Library 1.1.1
19073 * Copyright(c) 2006-2007, Ext JS, LLC.
19075 * Originally Released Under LGPL - original licence link has changed is not relivant.
19078 * <script type="text/javascript">
19082 * @class Roo.bootstrap.PagingToolbar
19084 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19086 * Create a new PagingToolbar
19087 * @param {Object} config The config object
19089 Roo.bootstrap.PagingToolbar = function(config)
19091 // old args format still supported... - xtype is prefered..
19092 // created from xtype...
19093 var ds = config.dataSource;
19094 this.toolbarItems = [];
19095 if (config.items) {
19096 this.toolbarItems = config.items;
19097 // config.items = [];
19100 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19107 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19111 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19113 * @cfg {Roo.data.Store} dataSource
19114 * The underlying data store providing the paged data
19117 * @cfg {String/HTMLElement/Element} container
19118 * container The id or element that will contain the toolbar
19121 * @cfg {Boolean} displayInfo
19122 * True to display the displayMsg (defaults to false)
19125 * @cfg {Number} pageSize
19126 * The number of records to display per page (defaults to 20)
19130 * @cfg {String} displayMsg
19131 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19133 displayMsg : 'Displaying {0} - {1} of {2}',
19135 * @cfg {String} emptyMsg
19136 * The message to display when no records are found (defaults to "No data to display")
19138 emptyMsg : 'No data to display',
19140 * Customizable piece of the default paging text (defaults to "Page")
19143 beforePageText : "Page",
19145 * Customizable piece of the default paging text (defaults to "of %0")
19148 afterPageText : "of {0}",
19150 * Customizable piece of the default paging text (defaults to "First Page")
19153 firstText : "First Page",
19155 * Customizable piece of the default paging text (defaults to "Previous Page")
19158 prevText : "Previous Page",
19160 * Customizable piece of the default paging text (defaults to "Next Page")
19163 nextText : "Next Page",
19165 * Customizable piece of the default paging text (defaults to "Last Page")
19168 lastText : "Last Page",
19170 * Customizable piece of the default paging text (defaults to "Refresh")
19173 refreshText : "Refresh",
19177 onRender : function(ct, position)
19179 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19180 this.navgroup.parentId = this.id;
19181 this.navgroup.onRender(this.el, null);
19182 // add the buttons to the navgroup
19184 if(this.displayInfo){
19185 Roo.log(this.el.select('ul.navbar-nav',true).first());
19186 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19187 this.displayEl = this.el.select('.x-paging-info', true).first();
19188 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19189 // this.displayEl = navel.el.select('span',true).first();
19195 Roo.each(_this.buttons, function(e){
19196 Roo.factory(e).onRender(_this.el, null);
19200 Roo.each(_this.toolbarItems, function(e) {
19201 _this.navgroup.addItem(e);
19204 this.first = this.navgroup.addItem({
19205 tooltip: this.firstText,
19207 icon : 'fa fa-backward',
19209 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19212 this.prev = this.navgroup.addItem({
19213 tooltip: this.prevText,
19215 icon : 'fa fa-step-backward',
19217 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
19219 //this.addSeparator();
19222 var field = this.navgroup.addItem( {
19224 cls : 'x-paging-position',
19226 html : this.beforePageText +
19227 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19228 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
19231 this.field = field.el.select('input', true).first();
19232 this.field.on("keydown", this.onPagingKeydown, this);
19233 this.field.on("focus", function(){this.dom.select();});
19236 this.afterTextEl = field.el.select('.x-paging-after',true).first();
19237 //this.field.setHeight(18);
19238 //this.addSeparator();
19239 this.next = this.navgroup.addItem({
19240 tooltip: this.nextText,
19242 html : ' <i class="fa fa-step-forward">',
19244 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
19246 this.last = this.navgroup.addItem({
19247 tooltip: this.lastText,
19248 icon : 'fa fa-forward',
19251 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
19253 //this.addSeparator();
19254 this.loading = this.navgroup.addItem({
19255 tooltip: this.refreshText,
19256 icon: 'fa fa-refresh',
19258 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19264 updateInfo : function(){
19265 if(this.displayEl){
19266 var count = this.ds.getCount();
19267 var msg = count == 0 ?
19271 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
19273 this.displayEl.update(msg);
19278 onLoad : function(ds, r, o){
19279 this.cursor = o.params ? o.params.start : 0;
19280 var d = this.getPageData(),
19284 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19285 this.field.dom.value = ap;
19286 this.first.setDisabled(ap == 1);
19287 this.prev.setDisabled(ap == 1);
19288 this.next.setDisabled(ap == ps);
19289 this.last.setDisabled(ap == ps);
19290 this.loading.enable();
19295 getPageData : function(){
19296 var total = this.ds.getTotalCount();
19299 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19300 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19305 onLoadError : function(){
19306 this.loading.enable();
19310 onPagingKeydown : function(e){
19311 var k = e.getKey();
19312 var d = this.getPageData();
19314 var v = this.field.dom.value, pageNum;
19315 if(!v || isNaN(pageNum = parseInt(v, 10))){
19316 this.field.dom.value = d.activePage;
19319 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19320 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19323 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))
19325 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19326 this.field.dom.value = pageNum;
19327 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19330 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19332 var v = this.field.dom.value, pageNum;
19333 var increment = (e.shiftKey) ? 10 : 1;
19334 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19336 if(!v || isNaN(pageNum = parseInt(v, 10))) {
19337 this.field.dom.value = d.activePage;
19340 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19342 this.field.dom.value = parseInt(v, 10) + increment;
19343 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19344 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19351 beforeLoad : function(){
19353 this.loading.disable();
19358 onClick : function(which){
19365 ds.load({params:{start: 0, limit: this.pageSize}});
19368 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19371 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19374 var total = ds.getTotalCount();
19375 var extra = total % this.pageSize;
19376 var lastStart = extra ? (total - extra) : total-this.pageSize;
19377 ds.load({params:{start: lastStart, limit: this.pageSize}});
19380 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19386 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19387 * @param {Roo.data.Store} store The data store to unbind
19389 unbind : function(ds){
19390 ds.un("beforeload", this.beforeLoad, this);
19391 ds.un("load", this.onLoad, this);
19392 ds.un("loadexception", this.onLoadError, this);
19393 ds.un("remove", this.updateInfo, this);
19394 ds.un("add", this.updateInfo, this);
19395 this.ds = undefined;
19399 * Binds the paging toolbar to the specified {@link Roo.data.Store}
19400 * @param {Roo.data.Store} store The data store to bind
19402 bind : function(ds){
19403 ds.on("beforeload", this.beforeLoad, this);
19404 ds.on("load", this.onLoad, this);
19405 ds.on("loadexception", this.onLoadError, this);
19406 ds.on("remove", this.updateInfo, this);
19407 ds.on("add", this.updateInfo, this);
19418 * @class Roo.bootstrap.MessageBar
19419 * @extends Roo.bootstrap.Component
19420 * Bootstrap MessageBar class
19421 * @cfg {String} html contents of the MessageBar
19422 * @cfg {String} weight (info | success | warning | danger) default info
19423 * @cfg {String} beforeClass insert the bar before the given class
19424 * @cfg {Boolean} closable (true | false) default false
19425 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19428 * Create a new Element
19429 * @param {Object} config The config object
19432 Roo.bootstrap.MessageBar = function(config){
19433 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19436 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
19442 beforeClass: 'bootstrap-sticky-wrap',
19444 getAutoCreate : function(){
19448 cls: 'alert alert-dismissable alert-' + this.weight,
19453 html: this.html || ''
19459 cfg.cls += ' alert-messages-fixed';
19473 onRender : function(ct, position)
19475 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19478 var cfg = Roo.apply({}, this.getAutoCreate());
19482 cfg.cls += ' ' + this.cls;
19485 cfg.style = this.style;
19487 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19489 this.el.setVisibilityMode(Roo.Element.DISPLAY);
19492 this.el.select('>button.close').on('click', this.hide, this);
19498 if (!this.rendered) {
19504 this.fireEvent('show', this);
19510 if (!this.rendered) {
19516 this.fireEvent('hide', this);
19519 update : function()
19521 // var e = this.el.dom.firstChild;
19523 // if(this.closable){
19524 // e = e.nextSibling;
19527 // e.data = this.html || '';
19529 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19545 * @class Roo.bootstrap.Graph
19546 * @extends Roo.bootstrap.Component
19547 * Bootstrap Graph class
19551 @cfg {String} graphtype bar | vbar | pie
19552 @cfg {number} g_x coodinator | centre x (pie)
19553 @cfg {number} g_y coodinator | centre y (pie)
19554 @cfg {number} g_r radius (pie)
19555 @cfg {number} g_height height of the chart (respected by all elements in the set)
19556 @cfg {number} g_width width of the chart (respected by all elements in the set)
19557 @cfg {Object} title The title of the chart
19560 -opts (object) options for the chart
19562 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19563 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19565 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.
19566 o stacked (boolean) whether or not to tread values as in a stacked bar chart
19568 o stretch (boolean)
19570 -opts (object) options for the pie
19573 o startAngle (number)
19574 o endAngle (number)
19578 * Create a new Input
19579 * @param {Object} config The config object
19582 Roo.bootstrap.Graph = function(config){
19583 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19589 * The img click event for the img.
19590 * @param {Roo.EventObject} e
19596 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
19607 //g_colors: this.colors,
19614 getAutoCreate : function(){
19625 onRender : function(ct,position){
19626 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19627 this.raphael = Raphael(this.el.dom);
19629 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19630 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19631 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19632 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19634 r.text(160, 10, "Single Series Chart").attr(txtattr);
19635 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19636 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19637 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19639 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19640 r.barchart(330, 10, 300, 220, data1);
19641 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19642 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19645 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19646 // r.barchart(30, 30, 560, 250, xdata, {
19647 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19648 // axis : "0 0 1 1",
19649 // axisxlabels : xdata
19650 // //yvalues : cols,
19653 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19655 // this.load(null,xdata,{
19656 // axis : "0 0 1 1",
19657 // axisxlabels : xdata
19662 load : function(graphtype,xdata,opts){
19663 this.raphael.clear();
19665 graphtype = this.graphtype;
19670 var r = this.raphael,
19671 fin = function () {
19672 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19674 fout = function () {
19675 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19677 pfin = function() {
19678 this.sector.stop();
19679 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19682 this.label[0].stop();
19683 this.label[0].attr({ r: 7.5 });
19684 this.label[1].attr({ "font-weight": 800 });
19687 pfout = function() {
19688 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19691 this.label[0].animate({ r: 5 }, 500, "bounce");
19692 this.label[1].attr({ "font-weight": 400 });
19698 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19701 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19704 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
19705 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19707 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19714 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19719 setTitle: function(o)
19724 initEvents: function() {
19727 this.el.on('click', this.onClick, this);
19731 onClick : function(e)
19733 Roo.log('img onclick');
19734 this.fireEvent('click', this, e);
19746 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19749 * @class Roo.bootstrap.dash.NumberBox
19750 * @extends Roo.bootstrap.Component
19751 * Bootstrap NumberBox class
19752 * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19753 * @cfg {String} headline Box headline
19754 * @cfg {String} content Box content
19755 * @cfg {String} icon Box icon
19756 * @cfg {String} footer Footer text
19757 * @cfg {String} fhref Footer href
19760 * Create a new NumberBox
19761 * @param {Object} config The config object
19765 Roo.bootstrap.dash.NumberBox = function(config){
19766 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19770 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
19780 getAutoCreate : function(){
19784 cls : 'small-box bg-' + this.bgcolor,
19792 cls : 'roo-headline',
19793 html : this.headline
19797 cls : 'roo-content',
19798 html : this.content
19812 cls : 'ion ' + this.icon
19821 cls : 'small-box-footer',
19822 href : this.fhref || '#',
19826 cfg.cn.push(footer);
19833 onRender : function(ct,position){
19834 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19841 setHeadline: function (value)
19843 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19846 setFooter: function (value, href)
19848 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19851 this.el.select('a.small-box-footer',true).first().attr('href', href);
19856 setContent: function (value)
19858 this.el.select('.roo-content',true).first().dom.innerHTML = value;
19861 initEvents: function()
19875 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19878 * @class Roo.bootstrap.dash.TabBox
19879 * @extends Roo.bootstrap.Component
19880 * Bootstrap TabBox class
19881 * @cfg {String} title Title of the TabBox
19882 * @cfg {String} icon Icon of the TabBox
19883 * @cfg {Boolean} showtabs (true|false) show the tabs default true
19886 * Create a new TabBox
19887 * @param {Object} config The config object
19891 Roo.bootstrap.dash.TabBox = function(config){
19892 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19897 * When a pane is added
19898 * @param {Roo.bootstrap.dash.TabPane} pane
19905 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
19911 getChildContainer : function()
19913 return this.el.select('.tab-content', true).first();
19916 getAutoCreate : function(){
19920 cls: 'pull-left header',
19928 cls: 'fa ' + this.icon
19935 cls: 'nav-tabs-custom',
19939 cls: 'nav nav-tabs pull-right',
19946 cls: 'tab-content no-padding',
19954 initEvents : function()
19956 //Roo.log('add add pane handler');
19957 this.on('addpane', this.onAddPane, this);
19960 * Updates the box title
19961 * @param {String} html to set the title to.
19963 setTitle : function(value)
19965 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19967 onAddPane : function(pane)
19969 //Roo.log('addpane');
19971 // tabs are rendere left to right..
19972 if(!this.showtabs){
19976 var ctr = this.el.select('.nav-tabs', true).first();
19979 var existing = ctr.select('.nav-tab',true);
19980 var qty = existing.getCount();;
19983 var tab = ctr.createChild({
19985 cls : 'nav-tab' + (qty ? '' : ' active'),
19993 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19996 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19998 pane.el.addClass('active');
20003 onTabClick : function(ev,un,ob,pane)
20005 //Roo.log('tab - prev default');
20006 ev.preventDefault();
20009 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20010 pane.tab.addClass('active');
20011 //Roo.log(pane.title);
20012 this.getChildContainer().select('.tab-pane',true).removeClass('active');
20013 // technically we should have a deactivate event.. but maybe add later.
20014 // and it should not de-activate the selected tab...
20016 pane.el.addClass('active');
20017 pane.fireEvent('activate');
20032 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20034 * @class Roo.bootstrap.TabPane
20035 * @extends Roo.bootstrap.Component
20036 * Bootstrap TabPane class
20037 * @cfg {Boolean} active (false | true) Default false
20038 * @cfg {String} title title of panel
20042 * Create a new TabPane
20043 * @param {Object} config The config object
20046 Roo.bootstrap.dash.TabPane = function(config){
20047 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20053 * When a pane is activated
20054 * @param {Roo.bootstrap.dash.TabPane} pane
20061 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
20066 // the tabBox that this is attached to.
20069 getAutoCreate : function()
20077 cfg.cls += ' active';
20082 initEvents : function()
20084 //Roo.log('trigger add pane handler');
20085 this.parent().fireEvent('addpane', this)
20089 * Updates the tab title
20090 * @param {String} html to set the title to.
20092 setTitle: function(str)
20098 this.tab.select('a', true).first().dom.innerHTML = str;
20115 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20118 * @class Roo.bootstrap.menu.Menu
20119 * @extends Roo.bootstrap.Component
20120 * Bootstrap Menu class - container for Menu
20121 * @cfg {String} html Text of the menu
20122 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20123 * @cfg {String} icon Font awesome icon
20124 * @cfg {String} pos Menu align to (top | bottom) default bottom
20128 * Create a new Menu
20129 * @param {Object} config The config object
20133 Roo.bootstrap.menu.Menu = function(config){
20134 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20138 * @event beforeshow
20139 * Fires before this menu is displayed
20140 * @param {Roo.bootstrap.menu.Menu} this
20144 * @event beforehide
20145 * Fires before this menu is hidden
20146 * @param {Roo.bootstrap.menu.Menu} this
20151 * Fires after this menu is displayed
20152 * @param {Roo.bootstrap.menu.Menu} this
20157 * Fires after this menu is hidden
20158 * @param {Roo.bootstrap.menu.Menu} this
20163 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20164 * @param {Roo.bootstrap.menu.Menu} this
20165 * @param {Roo.EventObject} e
20172 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
20176 weight : 'default',
20181 getChildContainer : function() {
20182 if(this.isSubMenu){
20186 return this.el.select('ul.dropdown-menu', true).first();
20189 getAutoCreate : function()
20194 cls : 'roo-menu-text',
20202 cls : 'fa ' + this.icon
20213 cls : 'dropdown-button btn btn-' + this.weight,
20218 cls : 'dropdown-toggle btn btn-' + this.weight,
20228 cls : 'dropdown-menu'
20234 if(this.pos == 'top'){
20235 cfg.cls += ' dropup';
20238 if(this.isSubMenu){
20241 cls : 'dropdown-menu'
20248 onRender : function(ct, position)
20250 this.isSubMenu = ct.hasClass('dropdown-submenu');
20252 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20255 initEvents : function()
20257 if(this.isSubMenu){
20261 this.hidden = true;
20263 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20264 this.triggerEl.on('click', this.onTriggerPress, this);
20266 this.buttonEl = this.el.select('button.dropdown-button', true).first();
20267 this.buttonEl.on('click', this.onClick, this);
20273 if(this.isSubMenu){
20277 return this.el.select('ul.dropdown-menu', true).first();
20280 onClick : function(e)
20282 this.fireEvent("click", this, e);
20285 onTriggerPress : function(e)
20287 if (this.isVisible()) {
20294 isVisible : function(){
20295 return !this.hidden;
20300 this.fireEvent("beforeshow", this);
20302 this.hidden = false;
20303 this.el.addClass('open');
20305 Roo.get(document).on("mouseup", this.onMouseUp, this);
20307 this.fireEvent("show", this);
20314 this.fireEvent("beforehide", this);
20316 this.hidden = true;
20317 this.el.removeClass('open');
20319 Roo.get(document).un("mouseup", this.onMouseUp);
20321 this.fireEvent("hide", this);
20324 onMouseUp : function()
20338 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20341 * @class Roo.bootstrap.menu.Item
20342 * @extends Roo.bootstrap.Component
20343 * Bootstrap MenuItem class
20344 * @cfg {Boolean} submenu (true | false) default false
20345 * @cfg {String} html text of the item
20346 * @cfg {String} href the link
20347 * @cfg {Boolean} disable (true | false) default false
20348 * @cfg {Boolean} preventDefault (true | false) default true
20349 * @cfg {String} icon Font awesome icon
20350 * @cfg {String} pos Submenu align to (left | right) default right
20354 * Create a new Item
20355 * @param {Object} config The config object
20359 Roo.bootstrap.menu.Item = function(config){
20360 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20364 * Fires when the mouse is hovering over this menu
20365 * @param {Roo.bootstrap.menu.Item} this
20366 * @param {Roo.EventObject} e
20371 * Fires when the mouse exits this menu
20372 * @param {Roo.bootstrap.menu.Item} this
20373 * @param {Roo.EventObject} e
20379 * The raw click event for the entire grid.
20380 * @param {Roo.EventObject} e
20386 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
20391 preventDefault: true,
20396 getAutoCreate : function()
20401 cls : 'roo-menu-item-text',
20409 cls : 'fa ' + this.icon
20418 href : this.href || '#',
20425 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20429 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20431 if(this.pos == 'left'){
20432 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20439 initEvents : function()
20441 this.el.on('mouseover', this.onMouseOver, this);
20442 this.el.on('mouseout', this.onMouseOut, this);
20444 this.el.select('a', true).first().on('click', this.onClick, this);
20448 onClick : function(e)
20450 if(this.preventDefault){
20451 e.preventDefault();
20454 this.fireEvent("click", this, e);
20457 onMouseOver : function(e)
20459 if(this.submenu && this.pos == 'left'){
20460 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20463 this.fireEvent("mouseover", this, e);
20466 onMouseOut : function(e)
20468 this.fireEvent("mouseout", this, e);
20480 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20483 * @class Roo.bootstrap.menu.Separator
20484 * @extends Roo.bootstrap.Component
20485 * Bootstrap Separator class
20488 * Create a new Separator
20489 * @param {Object} config The config object
20493 Roo.bootstrap.menu.Separator = function(config){
20494 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20497 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
20499 getAutoCreate : function(){