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") && !e.getTarget('.user-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);
1694 if(!t || t.isContainer){
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);
1714 Roo.log('pass click event');
1718 this.fireEvent("click", this, t, e);
1722 onMouseOver : function(e){
1723 var t = this.findTargetItem(e);
1726 // if(t.canActivate && !t.disabled){
1727 // this.setActiveItem(t, true);
1731 this.fireEvent("mouseover", this, e, t);
1733 isVisible : function(){
1734 return !this.hidden;
1736 onMouseOut : function(e){
1737 var t = this.findTargetItem(e);
1740 // if(t == this.activeItem && t.shouldDeactivate(e)){
1741 // this.activeItem.deactivate();
1742 // delete this.activeItem;
1745 this.fireEvent("mouseout", this, e, t);
1750 * Displays this menu relative to another element
1751 * @param {String/HTMLElement/Roo.Element} element The element to align to
1752 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1753 * the element (defaults to this.defaultAlign)
1754 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1756 show : function(el, pos, parentMenu){
1757 this.parentMenu = parentMenu;
1761 this.fireEvent("beforeshow", this);
1762 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1765 * Displays this menu at a specific xy position
1766 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1767 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1769 showAt : function(xy, parentMenu, /* private: */_e){
1770 this.parentMenu = parentMenu;
1775 this.fireEvent("beforeshow", this);
1777 //xy = this.el.adjustForConstraints(xy);
1779 //this.el.setXY(xy);
1781 this.hideMenuItems();
1782 this.hidden = false;
1783 this.triggerEl.addClass('open');
1785 this.fireEvent("show", this);
1791 this.doFocus.defer(50, this);
1795 doFocus : function(){
1797 this.focusEl.focus();
1802 * Hides this menu and optionally all parent menus
1803 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1805 hide : function(deep){
1807 this.hideMenuItems();
1808 if(this.el && this.isVisible()){
1809 this.fireEvent("beforehide", this);
1810 if(this.activeItem){
1811 this.activeItem.deactivate();
1812 this.activeItem = null;
1814 this.triggerEl.removeClass('open');;
1816 this.fireEvent("hide", this);
1818 if(deep === true && this.parentMenu){
1819 this.parentMenu.hide(true);
1823 onTriggerPress : function(e)
1826 Roo.log('trigger press');
1827 //Roo.log(e.getTarget());
1828 // Roo.log(this.triggerEl.dom);
1829 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1832 if (this.isVisible()) {
1836 this.show(this.triggerEl, false, false);
1845 hideMenuItems : function()
1847 //$(backdrop).remove()
1848 Roo.select('.open',true).each(function(aa) {
1850 aa.removeClass('open');
1851 //var parent = getParent($(this))
1852 //var relatedTarget = { relatedTarget: this }
1854 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1855 //if (e.isDefaultPrevented()) return
1856 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1859 addxtypeChild : function (tree, cntr) {
1860 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1862 this.menuitems.add(comp);
1883 * @class Roo.bootstrap.MenuItem
1884 * @extends Roo.bootstrap.Component
1885 * Bootstrap MenuItem class
1886 * @cfg {String} html the menu label
1887 * @cfg {String} href the link
1888 * @cfg {Boolean} preventDefault (true | false) default true
1889 * @cfg {Boolean} isContainer (true | false) default false
1893 * Create a new MenuItem
1894 * @param {Object} config The config object
1898 Roo.bootstrap.MenuItem = function(config){
1899 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1904 * The raw click event for the entire grid.
1905 * @param {Roo.EventObject} e
1911 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
1915 preventDefault: true,
1916 isContainer : false,
1918 getAutoCreate : function(){
1920 if(this.isContainer){
1923 cls: 'dropdown-menu-item'
1929 cls: 'dropdown-menu-item',
1938 if (this.parent().type == 'treeview') {
1939 cfg.cls = 'treeview-menu';
1942 cfg.cn[0].href = this.href || cfg.cn[0].href ;
1943 cfg.cn[0].html = this.html || cfg.cn[0].html ;
1947 initEvents: function() {
1949 //this.el.select('a').on('click', this.onClick, this);
1952 onClick : function(e)
1954 Roo.log('item on click ');
1955 //if(this.preventDefault){
1956 // e.preventDefault();
1958 //this.parent().hideMenuItems();
1960 this.fireEvent('click', this, e);
1979 * @class Roo.bootstrap.MenuSeparator
1980 * @extends Roo.bootstrap.Component
1981 * Bootstrap MenuSeparator class
1984 * Create a new MenuItem
1985 * @param {Object} config The config object
1989 Roo.bootstrap.MenuSeparator = function(config){
1990 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1993 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
1995 getAutoCreate : function(){
2010 <div class="modal fade">
2011 <div class="modal-dialog">
2012 <div class="modal-content">
2013 <div class="modal-header">
2014 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
2015 <h4 class="modal-title">Modal title</h4>
2017 <div class="modal-body">
2018 <p>One fine body…</p>
2020 <div class="modal-footer">
2021 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2022 <button type="button" class="btn btn-primary">Save changes</button>
2024 </div><!-- /.modal-content -->
2025 </div><!-- /.modal-dialog -->
2026 </div><!-- /.modal -->
2036 * @class Roo.bootstrap.Modal
2037 * @extends Roo.bootstrap.Component
2038 * Bootstrap Modal class
2039 * @cfg {String} title Title of dialog
2040 * @cfg {Boolean} specificTitle (true|false) default false
2041 * @cfg {Array} buttons Array of buttons or standard button set..
2042 * @cfg {String} buttonPosition (left|right|center) default right
2043 * @cfg {Boolean} animate (true | false) default true
2046 * Create a new Modal Dialog
2047 * @param {Object} config The config object
2050 Roo.bootstrap.Modal = function(config){
2051 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2056 * The raw btnclick event for the button
2057 * @param {Roo.EventObject} e
2061 this.buttons = this.buttons || [];
2064 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2066 title : 'test dialog',
2073 specificTitle: false,
2075 buttonPosition: 'right',
2079 onRender : function(ct, position)
2081 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2084 var cfg = Roo.apply({}, this.getAutoCreate());
2087 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2089 //if (!cfg.name.length) {
2093 cfg.cls += ' ' + this.cls;
2096 cfg.style = this.style;
2098 this.el = Roo.get(document.body).createChild(cfg, position);
2100 //var type = this.el.dom.type;
2102 if(this.tabIndex !== undefined){
2103 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2108 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2109 this.maskEl.enableDisplayMode("block");
2111 //this.el.addClass("x-dlg-modal");
2113 if (this.buttons.length) {
2114 Roo.each(this.buttons, function(bb) {
2115 b = Roo.apply({}, bb);
2116 b.xns = b.xns || Roo.bootstrap;
2117 b.xtype = b.xtype || 'Button';
2118 if (typeof(b.listeners) == 'undefined') {
2119 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2122 var btn = Roo.factory(b);
2124 btn.onRender(this.el.select('.modal-footer div').first());
2128 // render the children.
2131 if(typeof(this.items) != 'undefined'){
2132 var items = this.items;
2135 for(var i =0;i < items.length;i++) {
2136 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2140 this.items = nitems;
2142 this.body = this.el.select('.modal-body',true).first();
2143 this.close = this.el.select('.modal-header .close', true).first();
2144 this.footer = this.el.select('.modal-footer',true).first();
2146 //this.el.addClass([this.fieldClass, this.cls]);
2149 getAutoCreate : function(){
2154 html : this.html || ''
2159 cls : 'modal-title',
2163 if(this.specificTitle){
2169 style : 'display: none',
2172 cls: "modal-dialog",
2175 cls : "modal-content",
2178 cls : 'modal-header',
2190 cls : 'modal-footer',
2194 cls: 'btn-' + this.buttonPosition
2211 modal.cls += ' fade';
2217 getChildContainer : function() {
2219 return this.el.select('.modal-body',true).first();
2222 getButtonContainer : function() {
2223 return this.el.select('.modal-footer div',true).first();
2226 initEvents : function()
2228 this.el.select('.modal-header .close').on('click', this.hide, this);
2230 // this.addxtype(this);
2234 if (!this.rendered) {
2238 this.el.setStyle('display', 'block');
2242 (function(){ _this.el.addClass('in'); }).defer(50);
2244 this.el.addClass('in');
2247 Roo.get(document.body).addClass("x-body-masked");
2248 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2250 this.el.setStyle('zIndex', '10001');
2251 this.fireEvent('show', this);
2258 Roo.get(document.body).removeClass("x-body-masked");
2259 this.el.removeClass('in');
2263 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2265 this.el.setStyle('display', 'none');
2268 this.fireEvent('hide', this);
2271 addButton : function(str, cb)
2275 var b = Roo.apply({}, { html : str } );
2276 b.xns = b.xns || Roo.bootstrap;
2277 b.xtype = b.xtype || 'Button';
2278 if (typeof(b.listeners) == 'undefined') {
2279 b.listeners = { click : cb.createDelegate(this) };
2282 var btn = Roo.factory(b);
2284 btn.onRender(this.el.select('.modal-footer div').first());
2290 setDefaultButton : function(btn)
2292 //this.el.select('.modal-footer').()
2294 resizeTo: function(w,h)
2298 setContentSize : function(w, h)
2302 onButtonClick: function(btn,e)
2305 this.fireEvent('btnclick', btn.name, e);
2307 setTitle: function(str) {
2308 this.el.select('.modal-title',true).first().dom.innerHTML = str;
2314 Roo.apply(Roo.bootstrap.Modal, {
2316 * Button config that displays a single OK button
2325 * Button config that displays Yes and No buttons
2341 * Button config that displays OK and Cancel buttons
2356 * Button config that displays Yes, No and Cancel buttons
2378 * messagebox - can be used as a replace
2382 * @class Roo.MessageBox
2383 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2387 Roo.Msg.alert('Status', 'Changes saved successfully.');
2389 // Prompt for user data:
2390 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2392 // process text value...
2396 // Show a dialog using config options:
2398 title:'Save Changes?',
2399 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2400 buttons: Roo.Msg.YESNOCANCEL,
2407 Roo.bootstrap.MessageBox = function(){
2408 var dlg, opt, mask, waitTimer;
2409 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2410 var buttons, activeTextEl, bwidth;
2414 var handleButton = function(button){
2416 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2420 var handleHide = function(){
2422 dlg.el.removeClass(opt.cls);
2425 // Roo.TaskMgr.stop(waitTimer);
2426 // waitTimer = null;
2431 var updateButtons = function(b){
2434 buttons["ok"].hide();
2435 buttons["cancel"].hide();
2436 buttons["yes"].hide();
2437 buttons["no"].hide();
2438 //dlg.footer.dom.style.display = 'none';
2441 dlg.footer.dom.style.display = '';
2442 for(var k in buttons){
2443 if(typeof buttons[k] != "function"){
2446 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2447 width += buttons[k].el.getWidth()+15;
2457 var handleEsc = function(d, k, e){
2458 if(opt && opt.closable !== false){
2468 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2469 * @return {Roo.BasicDialog} The BasicDialog element
2471 getDialog : function(){
2473 dlg = new Roo.bootstrap.Modal( {
2476 //constraintoviewport:false,
2478 //collapsible : false,
2483 //buttonAlign:"center",
2484 closeClick : function(){
2485 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2488 handleButton("cancel");
2493 dlg.on("hide", handleHide);
2495 //dlg.addKeyListener(27, handleEsc);
2497 this.buttons = buttons;
2498 var bt = this.buttonText;
2499 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2500 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2501 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2502 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2504 bodyEl = dlg.body.createChild({
2506 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2507 '<textarea class="roo-mb-textarea"></textarea>' +
2508 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2510 msgEl = bodyEl.dom.firstChild;
2511 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2512 textboxEl.enableDisplayMode();
2513 textboxEl.addKeyListener([10,13], function(){
2514 if(dlg.isVisible() && opt && opt.buttons){
2517 }else if(opt.buttons.yes){
2518 handleButton("yes");
2522 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2523 textareaEl.enableDisplayMode();
2524 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2525 progressEl.enableDisplayMode();
2526 var pf = progressEl.dom.firstChild;
2528 pp = Roo.get(pf.firstChild);
2529 pp.setHeight(pf.offsetHeight);
2537 * Updates the message box body text
2538 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2539 * the XHTML-compliant non-breaking space character '&#160;')
2540 * @return {Roo.MessageBox} This message box
2542 updateText : function(text){
2543 if(!dlg.isVisible() && !opt.width){
2544 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2546 msgEl.innerHTML = text || ' ';
2548 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2549 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2551 Math.min(opt.width || cw , this.maxWidth),
2552 Math.max(opt.minWidth || this.minWidth, bwidth)
2555 activeTextEl.setWidth(w);
2557 if(dlg.isVisible()){
2558 dlg.fixedcenter = false;
2560 // to big, make it scroll. = But as usual stupid IE does not support
2563 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2564 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2565 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2567 bodyEl.dom.style.height = '';
2568 bodyEl.dom.style.overflowY = '';
2571 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2573 bodyEl.dom.style.overflowX = '';
2576 dlg.setContentSize(w, bodyEl.getHeight());
2577 if(dlg.isVisible()){
2578 dlg.fixedcenter = true;
2584 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2585 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2586 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2587 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2588 * @return {Roo.MessageBox} This message box
2590 updateProgress : function(value, text){
2592 this.updateText(text);
2594 if (pp) { // weird bug on my firefox - for some reason this is not defined
2595 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2601 * Returns true if the message box is currently displayed
2602 * @return {Boolean} True if the message box is visible, else false
2604 isVisible : function(){
2605 return dlg && dlg.isVisible();
2609 * Hides the message box if it is displayed
2612 if(this.isVisible()){
2618 * Displays a new message box, or reinitializes an existing message box, based on the config options
2619 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2620 * The following config object properties are supported:
2622 Property Type Description
2623 ---------- --------------- ------------------------------------------------------------------------------------
2624 animEl String/Element An id or Element from which the message box should animate as it opens and
2625 closes (defaults to undefined)
2626 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2627 cancel:'Bar'}), or false to not show any buttons (defaults to false)
2628 closable Boolean False to hide the top-right close button (defaults to true). Note that
2629 progress and wait dialogs will ignore this property and always hide the
2630 close button as they can only be closed programmatically.
2631 cls String A custom CSS class to apply to the message box element
2632 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
2633 displayed (defaults to 75)
2634 fn Function A callback function to execute after closing the dialog. The arguments to the
2635 function will be btn (the name of the button that was clicked, if applicable,
2636 e.g. "ok"), and text (the value of the active text field, if applicable).
2637 Progress and wait dialogs will ignore this option since they do not respond to
2638 user actions and can only be closed programmatically, so any required function
2639 should be called by the same code after it closes the dialog.
2640 icon String A CSS class that provides a background image to be used as an icon for
2641 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2642 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
2643 minWidth Number The minimum width in pixels of the message box (defaults to 100)
2644 modal Boolean False to allow user interaction with the page while the message box is
2645 displayed (defaults to true)
2646 msg String A string that will replace the existing message box body text (defaults
2647 to the XHTML-compliant non-breaking space character ' ')
2648 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
2649 progress Boolean True to display a progress bar (defaults to false)
2650 progressText String The text to display inside the progress bar if progress = true (defaults to '')
2651 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
2652 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
2653 title String The title text
2654 value String The string value to set into the active textbox element if displayed
2655 wait Boolean True to display a progress bar (defaults to false)
2656 width Number The width of the dialog in pixels
2663 msg: 'Please enter your address:',
2665 buttons: Roo.MessageBox.OKCANCEL,
2668 animEl: 'addAddressBtn'
2671 * @param {Object} config Configuration options
2672 * @return {Roo.MessageBox} This message box
2674 show : function(options)
2677 // this causes nightmares if you show one dialog after another
2678 // especially on callbacks..
2680 if(this.isVisible()){
2683 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2684 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
2685 Roo.log("New Dialog Message:" + options.msg )
2686 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2687 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2690 var d = this.getDialog();
2692 d.setTitle(opt.title || " ");
2693 d.close.setDisplayed(opt.closable !== false);
2694 activeTextEl = textboxEl;
2695 opt.prompt = opt.prompt || (opt.multiline ? true : false);
2700 textareaEl.setHeight(typeof opt.multiline == "number" ?
2701 opt.multiline : this.defaultTextHeight);
2702 activeTextEl = textareaEl;
2711 progressEl.setDisplayed(opt.progress === true);
2712 this.updateProgress(0);
2713 activeTextEl.dom.value = opt.value || "";
2715 dlg.setDefaultButton(activeTextEl);
2717 var bs = opt.buttons;
2721 }else if(bs && bs.yes){
2722 db = buttons["yes"];
2724 dlg.setDefaultButton(db);
2726 bwidth = updateButtons(opt.buttons);
2727 this.updateText(opt.msg);
2729 d.el.addClass(opt.cls);
2731 d.proxyDrag = opt.proxyDrag === true;
2732 d.modal = opt.modal !== false;
2733 d.mask = opt.modal !== false ? mask : false;
2735 // force it to the end of the z-index stack so it gets a cursor in FF
2736 document.body.appendChild(dlg.el.dom);
2737 d.animateTarget = null;
2738 d.show(options.animEl);
2744 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
2745 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2746 * and closing the message box when the process is complete.
2747 * @param {String} title The title bar text
2748 * @param {String} msg The message box body text
2749 * @return {Roo.MessageBox} This message box
2751 progress : function(title, msg){
2758 minWidth: this.minProgressWidth,
2765 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2766 * If a callback function is passed it will be called after the user clicks the button, and the
2767 * id of the button that was clicked will be passed as the only parameter to the callback
2768 * (could also be the top-right close button).
2769 * @param {String} title The title bar text
2770 * @param {String} msg The message box body text
2771 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2772 * @param {Object} scope (optional) The scope of the callback function
2773 * @return {Roo.MessageBox} This message box
2775 alert : function(title, msg, fn, scope){
2788 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
2789 * interaction while waiting for a long-running process to complete that does not have defined intervals.
2790 * You are responsible for closing the message box when the process is complete.
2791 * @param {String} msg The message box body text
2792 * @param {String} title (optional) The title bar text
2793 * @return {Roo.MessageBox} This message box
2795 wait : function(msg, title){
2806 waitTimer = Roo.TaskMgr.start({
2808 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2816 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2817 * If a callback function is passed it will be called after the user clicks either button, and the id of the
2818 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2819 * @param {String} title The title bar text
2820 * @param {String} msg The message box body text
2821 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2822 * @param {Object} scope (optional) The scope of the callback function
2823 * @return {Roo.MessageBox} This message box
2825 confirm : function(title, msg, fn, scope){
2829 buttons: this.YESNO,
2838 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2839 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
2840 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2841 * (could also be the top-right close button) and the text that was entered will be passed as the two
2842 * parameters to the callback.
2843 * @param {String} title The title bar text
2844 * @param {String} msg The message box body text
2845 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2846 * @param {Object} scope (optional) The scope of the callback function
2847 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2848 * property, or the height in pixels to create the textbox (defaults to false / single-line)
2849 * @return {Roo.MessageBox} This message box
2851 prompt : function(title, msg, fn, scope, multiline){
2855 buttons: this.OKCANCEL,
2860 multiline: multiline,
2867 * Button config that displays a single OK button
2872 * Button config that displays Yes and No buttons
2875 YESNO : {yes:true, no:true},
2877 * Button config that displays OK and Cancel buttons
2880 OKCANCEL : {ok:true, cancel:true},
2882 * Button config that displays Yes, No and Cancel buttons
2885 YESNOCANCEL : {yes:true, no:true, cancel:true},
2888 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2891 defaultTextHeight : 75,
2893 * The maximum width in pixels of the message box (defaults to 600)
2898 * The minimum width in pixels of the message box (defaults to 100)
2903 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
2904 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2907 minProgressWidth : 250,
2909 * An object containing the default button text strings that can be overriden for localized language support.
2910 * Supported properties are: ok, cancel, yes and no.
2911 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2924 * Shorthand for {@link Roo.MessageBox}
2926 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox
2927 Roo.Msg = Roo.Msg || Roo.MessageBox;
2936 * @class Roo.bootstrap.Navbar
2937 * @extends Roo.bootstrap.Component
2938 * Bootstrap Navbar class
2941 * Create a new Navbar
2942 * @param {Object} config The config object
2946 Roo.bootstrap.Navbar = function(config){
2947 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2951 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
2960 getAutoCreate : function(){
2963 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2967 initEvents :function ()
2969 //Roo.log(this.el.select('.navbar-toggle',true));
2970 this.el.select('.navbar-toggle',true).on('click', function() {
2971 // Roo.log('click');
2972 this.el.select('.navbar-collapse',true).toggleClass('in');
2980 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2982 var size = this.el.getSize();
2983 this.maskEl.setSize(size.width, size.height);
2984 this.maskEl.enableDisplayMode("block");
2993 getChildContainer : function()
2995 if (this.el.select('.collapse').getCount()) {
2996 return this.el.select('.collapse',true).first();
3029 * @class Roo.bootstrap.NavSimplebar
3030 * @extends Roo.bootstrap.Navbar
3031 * Bootstrap Sidebar class
3033 * @cfg {Boolean} inverse is inverted color
3035 * @cfg {String} type (nav | pills | tabs)
3036 * @cfg {Boolean} arrangement stacked | justified
3037 * @cfg {String} align (left | right) alignment
3039 * @cfg {Boolean} main (true|false) main nav bar? default false
3040 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3042 * @cfg {String} tag (header|footer|nav|div) default is nav
3048 * Create a new Sidebar
3049 * @param {Object} config The config object
3053 Roo.bootstrap.NavSimplebar = function(config){
3054 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3057 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3073 getAutoCreate : function(){
3077 tag : this.tag || 'div',
3090 this.type = this.type || 'nav';
3091 if (['tabs','pills'].indexOf(this.type)!==-1) {
3092 cfg.cn[0].cls += ' nav-' + this.type
3096 if (this.type!=='nav') {
3097 Roo.log('nav type must be nav/tabs/pills')
3099 cfg.cn[0].cls += ' navbar-nav'
3105 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3106 cfg.cn[0].cls += ' nav-' + this.arrangement;
3110 if (this.align === 'right') {
3111 cfg.cn[0].cls += ' navbar-right';
3115 cfg.cls += ' navbar-inverse';
3142 * @class Roo.bootstrap.NavHeaderbar
3143 * @extends Roo.bootstrap.NavSimplebar
3144 * Bootstrap Sidebar class
3146 * @cfg {String} brand what is brand
3147 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3148 * @cfg {String} brand_href href of the brand
3149 * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3150 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3153 * Create a new Sidebar
3154 * @param {Object} config The config object
3158 Roo.bootstrap.NavHeaderbar = function(config){
3159 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3162 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3170 getAutoCreate : function(){
3173 tag: this.nav || 'nav',
3182 cls: 'navbar-header',
3187 cls: 'navbar-toggle',
3188 'data-toggle': 'collapse',
3193 html: 'Toggle navigation'
3215 cls: 'collapse navbar-collapse',
3219 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3221 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3222 cfg.cls += ' navbar-' + this.position;
3224 // tag can override this..
3226 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3229 if (this.brand !== '') {
3232 href: this.brand_href ? this.brand_href : '#',
3233 cls: 'navbar-brand',
3241 cfg.cls += ' main-nav';
3249 initEvents : function()
3251 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3253 if (this.autohide) {
3258 Roo.get(document).on('scroll',function(e) {
3259 var ns = Roo.get(document).getScroll().top;
3260 var os = prevScroll;
3264 ft.removeClass('slideDown');
3265 ft.addClass('slideUp');
3268 ft.removeClass('slideUp');
3269 ft.addClass('slideDown');
3293 * @class Roo.bootstrap.NavSidebar
3294 * @extends Roo.bootstrap.Navbar
3295 * Bootstrap Sidebar class
3298 * Create a new Sidebar
3299 * @param {Object} config The config object
3303 Roo.bootstrap.NavSidebar = function(config){
3304 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3307 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3309 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3311 getAutoCreate : function(){
3316 cls: 'sidebar sidebar-nav'
3338 * @class Roo.bootstrap.NavGroup
3339 * @extends Roo.bootstrap.Component
3340 * Bootstrap NavGroup class
3341 * @cfg {String} align left | right
3342 * @cfg {Boolean} inverse false | true
3343 * @cfg {String} type (nav|pills|tab) default nav
3344 * @cfg {String} navId - reference Id for navbar.
3348 * Create a new nav group
3349 * @param {Object} config The config object
3352 Roo.bootstrap.NavGroup = function(config){
3353 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3356 Roo.bootstrap.NavGroup.register(this);
3360 * Fires when the active item changes
3361 * @param {Roo.bootstrap.NavGroup} this
3362 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3363 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3370 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3381 getAutoCreate : function()
3383 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3390 if (['tabs','pills'].indexOf(this.type)!==-1) {
3391 cfg.cls += ' nav-' + this.type
3393 if (this.type!=='nav') {
3394 Roo.log('nav type must be nav/tabs/pills')
3396 cfg.cls += ' navbar-nav'
3399 if (this.parent().sidebar) {
3402 cls: 'dashboard-menu sidebar-menu'
3408 if (this.form === true) {
3414 if (this.align === 'right') {
3415 cfg.cls += ' navbar-right';
3417 cfg.cls += ' navbar-left';
3421 if (this.align === 'right') {
3422 cfg.cls += ' navbar-right';
3426 cfg.cls += ' navbar-inverse';
3434 * sets the active Navigation item
3435 * @param {Roo.bootstrap.NavItem} the new current navitem
3437 setActiveItem : function(item)
3440 Roo.each(this.navItems, function(v){
3445 v.setActive(false, true);
3452 item.setActive(true, true);
3453 this.fireEvent('changed', this, item, prev);
3458 * gets the active Navigation item
3459 * @return {Roo.bootstrap.NavItem} the current navitem
3461 getActive : function()
3465 Roo.each(this.navItems, function(v){
3476 indexOfNav : function()
3480 Roo.each(this.navItems, function(v,i){
3491 * adds a Navigation item
3492 * @param {Roo.bootstrap.NavItem} the navitem to add
3494 addItem : function(cfg)
3496 var cn = new Roo.bootstrap.NavItem(cfg);
3498 cn.parentId = this.id;
3499 cn.onRender(this.el, null);
3503 * register a Navigation item
3504 * @param {Roo.bootstrap.NavItem} the navitem to add
3506 register : function(item)
3508 this.navItems.push( item);
3509 item.navId = this.navId;
3514 * clear all the Navigation item
3517 clearAll : function()
3520 this.el.dom.innerHTML = '';
3523 getNavItem: function(tabId)
3526 Roo.each(this.navItems, function(e) {
3527 if (e.tabId == tabId) {
3537 setActiveNext : function()
3539 var i = this.indexOfNav(this.getActive());
3540 if (i > this.navItems.length) {
3543 this.setActiveItem(this.navItems[i+1]);
3545 setActivePrev : function()
3547 var i = this.indexOfNav(this.getActive());
3551 this.setActiveItem(this.navItems[i-1]);
3553 clearWasActive : function(except) {
3554 Roo.each(this.navItems, function(e) {
3555 if (e.tabId != except.tabId && e.was_active) {
3556 e.was_active = false;
3563 getWasActive : function ()
3566 Roo.each(this.navItems, function(e) {
3581 Roo.apply(Roo.bootstrap.NavGroup, {
3585 * register a Navigation Group
3586 * @param {Roo.bootstrap.NavGroup} the navgroup to add
3588 register : function(navgrp)
3590 this.groups[navgrp.navId] = navgrp;
3594 * fetch a Navigation Group based on the navigation ID
3595 * @param {string} the navgroup to add
3596 * @returns {Roo.bootstrap.NavGroup} the navgroup
3598 get: function(navId) {
3599 if (typeof(this.groups[navId]) == 'undefined') {
3601 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3603 return this.groups[navId] ;
3618 * @class Roo.bootstrap.NavItem
3619 * @extends Roo.bootstrap.Component
3620 * Bootstrap Navbar.NavItem class
3621 * @cfg {String} href link to
3622 * @cfg {String} html content of button
3623 * @cfg {String} badge text inside badge
3624 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3625 * @cfg {String} glyphicon name of glyphicon
3626 * @cfg {String} icon name of font awesome icon
3627 * @cfg {Boolean} active Is item active
3628 * @cfg {Boolean} disabled Is item disabled
3630 * @cfg {Boolean} preventDefault (true | false) default false
3631 * @cfg {String} tabId the tab that this item activates.
3632 * @cfg {String} tagtype (a|span) render as a href or span?
3635 * Create a new Navbar Item
3636 * @param {Object} config The config object
3638 Roo.bootstrap.NavItem = function(config){
3639 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3644 * The raw click event for the entire grid.
3645 * @param {Roo.EventObject} e
3650 * Fires when the active item active state changes
3651 * @param {Roo.bootstrap.NavItem} this
3652 * @param {boolean} state the new state
3660 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
3668 preventDefault : false,
3675 getAutoCreate : function(){
3683 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3685 if (this.disabled) {
3686 cfg.cls += ' disabled';
3689 if (this.href || this.html || this.glyphicon || this.icon) {
3693 href : this.href || "#",
3694 html: this.html || ''
3699 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3702 if(this.glyphicon) {
3703 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
3708 cfg.cn[0].html += " <span class='caret'></span>";
3712 if (this.badge !== '') {
3714 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3722 initEvents: function() {
3723 // Roo.log('init events?');
3724 // Roo.log(this.el.dom);
3725 if (typeof (this.menu) != 'undefined') {
3726 this.menu.parentType = this.xtype;
3727 this.menu.triggerEl = this.el;
3728 this.addxtype(Roo.apply({}, this.menu));
3732 this.el.select('a',true).on('click', this.onClick, this);
3733 // at this point parent should be available..
3734 this.parent().register(this);
3737 onClick : function(e)
3740 if(this.preventDefault){
3743 if (this.disabled) {
3747 var tg = Roo.bootstrap.TabGroup.get(this.navId);
3748 if (tg && tg.transition) {
3749 Roo.log("waiting for the transitionend");
3753 Roo.log("fire event clicked");
3754 if(this.fireEvent('click', this, e) === false){
3757 var p = this.parent();
3758 if (['tabs','pills'].indexOf(p.type)!==-1) {
3759 if (typeof(p.setActiveItem) !== 'undefined') {
3760 p.setActiveItem(this);
3763 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3764 if (p.parentType == 'NavHeaderbar' && !this.menu) {
3765 // remove the collapsed menu expand...
3766 p.parent().el.select('.navbar-collapse',true).removeClass('in');
3771 isActive: function () {
3774 setActive : function(state, fire, is_was_active)
3776 if (this.active && !state & this.navId) {
3777 this.was_active = true;
3778 var nv = Roo.bootstrap.NavGroup.get(this.navId);
3780 nv.clearWasActive(this);
3784 this.active = state;
3787 this.el.removeClass('active');
3788 } else if (!this.el.hasClass('active')) {
3789 this.el.addClass('active');
3792 this.fireEvent('changed', this, state);
3795 // show a panel if it's registered and related..
3797 if (!this.navId || !this.tabId || !state || is_was_active) {
3801 var tg = Roo.bootstrap.TabGroup.get(this.navId);
3805 var pan = tg.getPanelByName(this.tabId);
3809 // if we can not flip to new panel - go back to old nav highlight..
3810 if (false == tg.showPanel(pan)) {
3811 var nv = Roo.bootstrap.NavGroup.get(this.navId);
3813 var onav = nv.getWasActive();
3815 onav.setActive(true, false, true);
3824 // this should not be here...
3825 setDisabled : function(state)
3827 this.disabled = state;
3829 this.el.removeClass('disabled');
3830 } else if (!this.el.hasClass('disabled')) {
3831 this.el.addClass('disabled');
3844 * <span> icon </span>
3845 * <span> text </span>
3846 * <span>badge </span>
3850 * @class Roo.bootstrap.NavSidebarItem
3851 * @extends Roo.bootstrap.NavItem
3852 * Bootstrap Navbar.NavSidebarItem class
3854 * Create a new Navbar Button
3855 * @param {Object} config The config object
3857 Roo.bootstrap.NavSidebarItem = function(config){
3858 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3863 * The raw click event for the entire grid.
3864 * @param {Roo.EventObject} e
3869 * Fires when the active item active state changes
3870 * @param {Roo.bootstrap.NavSidebarItem} this
3871 * @param {boolean} state the new state
3879 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
3882 getAutoCreate : function(){
3887 href : this.href || '#',
3899 html : this.html || ''
3904 cfg.cls += ' active';
3908 if (this.glyphicon || this.icon) {
3909 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
3910 a.cn.push({ tag : 'i', cls : c }) ;
3915 if (this.badge !== '') {
3916 a.cn.push({ tag: 'span', cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge });
3920 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3921 a.cls += 'dropdown-toggle treeview' ;
3945 * @class Roo.bootstrap.Row
3946 * @extends Roo.bootstrap.Component
3947 * Bootstrap Row class (contains columns...)
3951 * @param {Object} config The config object
3954 Roo.bootstrap.Row = function(config){
3955 Roo.bootstrap.Row.superclass.constructor.call(this, config);
3958 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
3960 getAutoCreate : function(){
3979 * @class Roo.bootstrap.Element
3980 * @extends Roo.bootstrap.Component
3981 * Bootstrap Element class
3982 * @cfg {String} html contents of the element
3983 * @cfg {String} tag tag of the element
3984 * @cfg {String} cls class of the element
3987 * Create a new Element
3988 * @param {Object} config The config object
3991 Roo.bootstrap.Element = function(config){
3992 Roo.bootstrap.Element.superclass.constructor.call(this, config);
3995 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4002 getAutoCreate : function(){
4027 * @class Roo.bootstrap.Pagination
4028 * @extends Roo.bootstrap.Component
4029 * Bootstrap Pagination class
4030 * @cfg {String} size xs | sm | md | lg
4031 * @cfg {Boolean} inverse false | true
4034 * Create a new Pagination
4035 * @param {Object} config The config object
4038 Roo.bootstrap.Pagination = function(config){
4039 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4042 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4048 getAutoCreate : function(){
4054 cfg.cls += ' inverse';
4060 cfg.cls += " " + this.cls;
4078 * @class Roo.bootstrap.PaginationItem
4079 * @extends Roo.bootstrap.Component
4080 * Bootstrap PaginationItem class
4081 * @cfg {String} html text
4082 * @cfg {String} href the link
4083 * @cfg {Boolean} preventDefault (true | false) default true
4084 * @cfg {Boolean} active (true | false) default false
4088 * Create a new PaginationItem
4089 * @param {Object} config The config object
4093 Roo.bootstrap.PaginationItem = function(config){
4094 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4099 * The raw click event for the entire grid.
4100 * @param {Roo.EventObject} e
4106 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4110 preventDefault: true,
4114 getAutoCreate : function(){
4120 href : this.href ? this.href : '#',
4121 html : this.html ? this.html : ''
4131 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4137 initEvents: function() {
4139 this.el.on('click', this.onClick, this);
4142 onClick : function(e)
4144 Roo.log('PaginationItem on click ');
4145 if(this.preventDefault){
4149 this.fireEvent('click', this, e);
4165 * @class Roo.bootstrap.Slider
4166 * @extends Roo.bootstrap.Component
4167 * Bootstrap Slider class
4170 * Create a new Slider
4171 * @param {Object} config The config object
4174 Roo.bootstrap.Slider = function(config){
4175 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4178 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4180 getAutoCreate : function(){
4184 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4188 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4200 * Ext JS Library 1.1.1
4201 * Copyright(c) 2006-2007, Ext JS, LLC.
4203 * Originally Released Under LGPL - original licence link has changed is not relivant.
4206 * <script type="text/javascript">
4211 * @class Roo.grid.ColumnModel
4212 * @extends Roo.util.Observable
4213 * This is the default implementation of a ColumnModel used by the Grid. It defines
4214 * the columns in the grid.
4217 var colModel = new Roo.grid.ColumnModel([
4218 {header: "Ticker", width: 60, sortable: true, locked: true},
4219 {header: "Company Name", width: 150, sortable: true},
4220 {header: "Market Cap.", width: 100, sortable: true},
4221 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4222 {header: "Employees", width: 100, sortable: true, resizable: false}
4227 * The config options listed for this class are options which may appear in each
4228 * individual column definition.
4229 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4231 * @param {Object} config An Array of column config objects. See this class's
4232 * config objects for details.
4234 Roo.grid.ColumnModel = function(config){
4236 * The config passed into the constructor
4238 this.config = config;
4241 // if no id, create one
4242 // if the column does not have a dataIndex mapping,
4243 // map it to the order it is in the config
4244 for(var i = 0, len = config.length; i < len; i++){
4246 if(typeof c.dataIndex == "undefined"){
4249 if(typeof c.renderer == "string"){
4250 c.renderer = Roo.util.Format[c.renderer];
4252 if(typeof c.id == "undefined"){
4255 if(c.editor && c.editor.xtype){
4256 c.editor = Roo.factory(c.editor, Roo.grid);
4258 if(c.editor && c.editor.isFormField){
4259 c.editor = new Roo.grid.GridEditor(c.editor);
4261 this.lookup[c.id] = c;
4265 * The width of columns which have no width specified (defaults to 100)
4268 this.defaultWidth = 100;
4271 * Default sortable of columns which have no sortable specified (defaults to false)
4274 this.defaultSortable = false;
4278 * @event widthchange
4279 * Fires when the width of a column changes.
4280 * @param {ColumnModel} this
4281 * @param {Number} columnIndex The column index
4282 * @param {Number} newWidth The new width
4284 "widthchange": true,
4286 * @event headerchange
4287 * Fires when the text of a header changes.
4288 * @param {ColumnModel} this
4289 * @param {Number} columnIndex The column index
4290 * @param {Number} newText The new header text
4292 "headerchange": true,
4294 * @event hiddenchange
4295 * Fires when a column is hidden or "unhidden".
4296 * @param {ColumnModel} this
4297 * @param {Number} columnIndex The column index
4298 * @param {Boolean} hidden true if hidden, false otherwise
4300 "hiddenchange": true,
4302 * @event columnmoved
4303 * Fires when a column is moved.
4304 * @param {ColumnModel} this
4305 * @param {Number} oldIndex
4306 * @param {Number} newIndex
4308 "columnmoved" : true,
4310 * @event columlockchange
4311 * Fires when a column's locked state is changed
4312 * @param {ColumnModel} this
4313 * @param {Number} colIndex
4314 * @param {Boolean} locked true if locked
4316 "columnlockchange" : true
4318 Roo.grid.ColumnModel.superclass.constructor.call(this);
4320 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4322 * @cfg {String} header The header text to display in the Grid view.
4325 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4326 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4327 * specified, the column's index is used as an index into the Record's data Array.
4330 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4331 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4334 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4335 * Defaults to the value of the {@link #defaultSortable} property.
4336 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4339 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4342 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4345 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4348 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4351 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4352 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4353 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4354 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4357 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4360 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4364 * Returns the id of the column at the specified index.
4365 * @param {Number} index The column index
4366 * @return {String} the id
4368 getColumnId : function(index){
4369 return this.config[index].id;
4373 * Returns the column for a specified id.
4374 * @param {String} id The column id
4375 * @return {Object} the column
4377 getColumnById : function(id){
4378 return this.lookup[id];
4383 * Returns the column for a specified dataIndex.
4384 * @param {String} dataIndex The column dataIndex
4385 * @return {Object|Boolean} the column or false if not found
4387 getColumnByDataIndex: function(dataIndex){
4388 var index = this.findColumnIndex(dataIndex);
4389 return index > -1 ? this.config[index] : false;
4393 * Returns the index for a specified column id.
4394 * @param {String} id The column id
4395 * @return {Number} the index, or -1 if not found
4397 getIndexById : function(id){
4398 for(var i = 0, len = this.config.length; i < len; i++){
4399 if(this.config[i].id == id){
4407 * Returns the index for a specified column dataIndex.
4408 * @param {String} dataIndex The column dataIndex
4409 * @return {Number} the index, or -1 if not found
4412 findColumnIndex : function(dataIndex){
4413 for(var i = 0, len = this.config.length; i < len; i++){
4414 if(this.config[i].dataIndex == dataIndex){
4422 moveColumn : function(oldIndex, newIndex){
4423 var c = this.config[oldIndex];
4424 this.config.splice(oldIndex, 1);
4425 this.config.splice(newIndex, 0, c);
4426 this.dataMap = null;
4427 this.fireEvent("columnmoved", this, oldIndex, newIndex);
4430 isLocked : function(colIndex){
4431 return this.config[colIndex].locked === true;
4434 setLocked : function(colIndex, value, suppressEvent){
4435 if(this.isLocked(colIndex) == value){
4438 this.config[colIndex].locked = value;
4440 this.fireEvent("columnlockchange", this, colIndex, value);
4444 getTotalLockedWidth : function(){
4446 for(var i = 0; i < this.config.length; i++){
4447 if(this.isLocked(i) && !this.isHidden(i)){
4448 this.totalWidth += this.getColumnWidth(i);
4454 getLockedCount : function(){
4455 for(var i = 0, len = this.config.length; i < len; i++){
4456 if(!this.isLocked(i)){
4463 * Returns the number of columns.
4466 getColumnCount : function(visibleOnly){
4467 if(visibleOnly === true){
4469 for(var i = 0, len = this.config.length; i < len; i++){
4470 if(!this.isHidden(i)){
4476 return this.config.length;
4480 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4481 * @param {Function} fn
4482 * @param {Object} scope (optional)
4483 * @return {Array} result
4485 getColumnsBy : function(fn, scope){
4487 for(var i = 0, len = this.config.length; i < len; i++){
4488 var c = this.config[i];
4489 if(fn.call(scope||this, c, i) === true){
4497 * Returns true if the specified column is sortable.
4498 * @param {Number} col The column index
4501 isSortable : function(col){
4502 if(typeof this.config[col].sortable == "undefined"){
4503 return this.defaultSortable;
4505 return this.config[col].sortable;
4509 * Returns the rendering (formatting) function defined for the column.
4510 * @param {Number} col The column index.
4511 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4513 getRenderer : function(col){
4514 if(!this.config[col].renderer){
4515 return Roo.grid.ColumnModel.defaultRenderer;
4517 return this.config[col].renderer;
4521 * Sets the rendering (formatting) function for a column.
4522 * @param {Number} col The column index
4523 * @param {Function} fn The function to use to process the cell's raw data
4524 * to return HTML markup for the grid view. The render function is called with
4525 * the following parameters:<ul>
4526 * <li>Data value.</li>
4527 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4528 * <li>css A CSS style string to apply to the table cell.</li>
4529 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4530 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4531 * <li>Row index</li>
4532 * <li>Column index</li>
4533 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4535 setRenderer : function(col, fn){
4536 this.config[col].renderer = fn;
4540 * Returns the width for the specified column.
4541 * @param {Number} col The column index
4544 getColumnWidth : function(col){
4545 return this.config[col].width * 1 || this.defaultWidth;
4549 * Sets the width for a column.
4550 * @param {Number} col The column index
4551 * @param {Number} width The new width
4553 setColumnWidth : function(col, width, suppressEvent){
4554 this.config[col].width = width;
4555 this.totalWidth = null;
4557 this.fireEvent("widthchange", this, col, width);
4562 * Returns the total width of all columns.
4563 * @param {Boolean} includeHidden True to include hidden column widths
4566 getTotalWidth : function(includeHidden){
4567 if(!this.totalWidth){
4568 this.totalWidth = 0;
4569 for(var i = 0, len = this.config.length; i < len; i++){
4570 if(includeHidden || !this.isHidden(i)){
4571 this.totalWidth += this.getColumnWidth(i);
4575 return this.totalWidth;
4579 * Returns the header for the specified column.
4580 * @param {Number} col The column index
4583 getColumnHeader : function(col){
4584 return this.config[col].header;
4588 * Sets the header for a column.
4589 * @param {Number} col The column index
4590 * @param {String} header The new header
4592 setColumnHeader : function(col, header){
4593 this.config[col].header = header;
4594 this.fireEvent("headerchange", this, col, header);
4598 * Returns the tooltip for the specified column.
4599 * @param {Number} col The column index
4602 getColumnTooltip : function(col){
4603 return this.config[col].tooltip;
4606 * Sets the tooltip for a column.
4607 * @param {Number} col The column index
4608 * @param {String} tooltip The new tooltip
4610 setColumnTooltip : function(col, tooltip){
4611 this.config[col].tooltip = tooltip;
4615 * Returns the dataIndex for the specified column.
4616 * @param {Number} col The column index
4619 getDataIndex : function(col){
4620 return this.config[col].dataIndex;
4624 * Sets the dataIndex for a column.
4625 * @param {Number} col The column index
4626 * @param {Number} dataIndex The new dataIndex
4628 setDataIndex : function(col, dataIndex){
4629 this.config[col].dataIndex = dataIndex;
4635 * Returns true if the cell is editable.
4636 * @param {Number} colIndex The column index
4637 * @param {Number} rowIndex The row index
4640 isCellEditable : function(colIndex, rowIndex){
4641 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4645 * Returns the editor defined for the cell/column.
4646 * return false or null to disable editing.
4647 * @param {Number} colIndex The column index
4648 * @param {Number} rowIndex The row index
4651 getCellEditor : function(colIndex, rowIndex){
4652 return this.config[colIndex].editor;
4656 * Sets if a column is editable.
4657 * @param {Number} col The column index
4658 * @param {Boolean} editable True if the column is editable
4660 setEditable : function(col, editable){
4661 this.config[col].editable = editable;
4666 * Returns true if the column is hidden.
4667 * @param {Number} colIndex The column index
4670 isHidden : function(colIndex){
4671 return this.config[colIndex].hidden;
4676 * Returns true if the column width cannot be changed
4678 isFixed : function(colIndex){
4679 return this.config[colIndex].fixed;
4683 * Returns true if the column can be resized
4686 isResizable : function(colIndex){
4687 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4690 * Sets if a column is hidden.
4691 * @param {Number} colIndex The column index
4692 * @param {Boolean} hidden True if the column is hidden
4694 setHidden : function(colIndex, hidden){
4695 this.config[colIndex].hidden = hidden;
4696 this.totalWidth = null;
4697 this.fireEvent("hiddenchange", this, colIndex, hidden);
4701 * Sets the editor for a column.
4702 * @param {Number} col The column index
4703 * @param {Object} editor The editor object
4705 setEditor : function(col, editor){
4706 this.config[col].editor = editor;
4710 Roo.grid.ColumnModel.defaultRenderer = function(value){
4711 if(typeof value == "string" && value.length < 1){
4717 // Alias for backwards compatibility
4718 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4721 * Ext JS Library 1.1.1
4722 * Copyright(c) 2006-2007, Ext JS, LLC.
4724 * Originally Released Under LGPL - original licence link has changed is not relivant.
4727 * <script type="text/javascript">
4731 * @class Roo.LoadMask
4732 * A simple utility class for generically masking elements while loading data. If the element being masked has
4733 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4734 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
4735 * element's UpdateManager load indicator and will be destroyed after the initial load.
4737 * Create a new LoadMask
4738 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4739 * @param {Object} config The config object
4741 Roo.LoadMask = function(el, config){
4742 this.el = Roo.get(el);
4743 Roo.apply(this, config);
4745 this.store.on('beforeload', this.onBeforeLoad, this);
4746 this.store.on('load', this.onLoad, this);
4747 this.store.on('loadexception', this.onLoadException, this);
4748 this.removeMask = false;
4750 var um = this.el.getUpdateManager();
4751 um.showLoadIndicator = false; // disable the default indicator
4752 um.on('beforeupdate', this.onBeforeLoad, this);
4753 um.on('update', this.onLoad, this);
4754 um.on('failure', this.onLoad, this);
4755 this.removeMask = true;
4759 Roo.LoadMask.prototype = {
4761 * @cfg {Boolean} removeMask
4762 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4763 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
4767 * The text to display in a centered loading message box (defaults to 'Loading...')
4771 * @cfg {String} msgCls
4772 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4774 msgCls : 'x-mask-loading',
4777 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4783 * Disables the mask to prevent it from being displayed
4785 disable : function(){
4786 this.disabled = true;
4790 * Enables the mask so that it can be displayed
4792 enable : function(){
4793 this.disabled = false;
4796 onLoadException : function()
4800 if (typeof(arguments[3]) != 'undefined') {
4801 Roo.MessageBox.alert("Error loading",arguments[3]);
4805 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4806 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4815 this.el.unmask(this.removeMask);
4820 this.el.unmask(this.removeMask);
4824 onBeforeLoad : function(){
4826 this.el.mask(this.msg, this.msgCls);
4831 destroy : function(){
4833 this.store.un('beforeload', this.onBeforeLoad, this);
4834 this.store.un('load', this.onLoad, this);
4835 this.store.un('loadexception', this.onLoadException, this);
4837 var um = this.el.getUpdateManager();
4838 um.un('beforeupdate', this.onBeforeLoad, this);
4839 um.un('update', this.onLoad, this);
4840 um.un('failure', this.onLoad, this);
4851 * @class Roo.bootstrap.Table
4852 * @extends Roo.bootstrap.Component
4853 * Bootstrap Table class
4854 * @cfg {String} cls table class
4855 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4856 * @cfg {String} bgcolor Specifies the background color for a table
4857 * @cfg {Number} border Specifies whether the table cells should have borders or not
4858 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4859 * @cfg {Number} cellspacing Specifies the space between cells
4860 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4861 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4862 * @cfg {String} sortable Specifies that the table should be sortable
4863 * @cfg {String} summary Specifies a summary of the content of a table
4864 * @cfg {Number} width Specifies the width of a table
4865 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4867 * @cfg {boolean} striped Should the rows be alternative striped
4868 * @cfg {boolean} bordered Add borders to the table
4869 * @cfg {boolean} hover Add hover highlighting
4870 * @cfg {boolean} condensed Format condensed
4871 * @cfg {boolean} responsive Format condensed
4872 * @cfg {Boolean} loadMask (true|false) default false
4873 * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4874 * @cfg {Boolean} thead (true|false) generate thead, default true
4875 * @cfg {Boolean} RowSelection (true|false) default false
4876 * @cfg {Boolean} CellSelection (true|false) default false
4878 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
4882 * Create a new Table
4883 * @param {Object} config The config object
4886 Roo.bootstrap.Table = function(config){
4887 Roo.bootstrap.Table.superclass.constructor.call(this, config);
4890 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4891 this.sm = this.selModel;
4892 this.sm.xmodule = this.xmodule || false;
4894 if (this.cm && typeof(this.cm.config) == 'undefined') {
4895 this.colModel = new Roo.grid.ColumnModel(this.cm);
4896 this.cm = this.colModel;
4897 this.cm.xmodule = this.xmodule || false;
4900 this.store= Roo.factory(this.store, Roo.data);
4901 this.ds = this.store;
4902 this.ds.xmodule = this.xmodule || false;
4905 if (this.footer && this.store) {
4906 this.footer.dataSource = this.ds;
4907 this.footer = Roo.factory(this.footer);
4914 * Fires when a cell is clicked
4915 * @param {Roo.bootstrap.Table} this
4916 * @param {Roo.Element} el
4917 * @param {Number} rowIndex
4918 * @param {Number} columnIndex
4919 * @param {Roo.EventObject} e
4923 * @event celldblclick
4924 * Fires when a cell is double clicked
4925 * @param {Roo.bootstrap.Table} this
4926 * @param {Roo.Element} el
4927 * @param {Number} rowIndex
4928 * @param {Number} columnIndex
4929 * @param {Roo.EventObject} e
4931 "celldblclick" : true,
4934 * Fires when a row is clicked
4935 * @param {Roo.bootstrap.Table} this
4936 * @param {Roo.Element} el
4937 * @param {Number} rowIndex
4938 * @param {Roo.EventObject} e
4942 * @event rowdblclick
4943 * Fires when a row is double clicked
4944 * @param {Roo.bootstrap.Table} this
4945 * @param {Roo.Element} el
4946 * @param {Number} rowIndex
4947 * @param {Roo.EventObject} e
4949 "rowdblclick" : true,
4952 * Fires when a mouseover occur
4953 * @param {Roo.bootstrap.Table} this
4954 * @param {Roo.Element} el
4955 * @param {Number} rowIndex
4956 * @param {Number} columnIndex
4957 * @param {Roo.EventObject} e
4962 * Fires when a mouseout occur
4963 * @param {Roo.bootstrap.Table} this
4964 * @param {Roo.Element} el
4965 * @param {Number} rowIndex
4966 * @param {Number} columnIndex
4967 * @param {Roo.EventObject} e
4972 * Fires when a row is rendered, so you can change add a style to it.
4973 * @param {Roo.bootstrap.Table} this
4974 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
4981 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5005 RowSelection : false,
5006 CellSelection : false,
5009 // Roo.Element - the tbody
5012 getAutoCreate : function(){
5013 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5022 cfg.cls += ' table-striped';
5026 cfg.cls += ' table-hover';
5028 if (this.bordered) {
5029 cfg.cls += ' table-bordered';
5031 if (this.condensed) {
5032 cfg.cls += ' table-condensed';
5034 if (this.responsive) {
5035 cfg.cls += ' table-responsive';
5039 cfg.cls+= ' ' +this.cls;
5042 // this lot should be simplifed...
5045 cfg.align=this.align;
5048 cfg.bgcolor=this.bgcolor;
5051 cfg.border=this.border;
5053 if (this.cellpadding) {
5054 cfg.cellpadding=this.cellpadding;
5056 if (this.cellspacing) {
5057 cfg.cellspacing=this.cellspacing;
5060 cfg.frame=this.frame;
5063 cfg.rules=this.rules;
5065 if (this.sortable) {
5066 cfg.sortable=this.sortable;
5069 cfg.summary=this.summary;
5072 cfg.width=this.width;
5075 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5078 if(this.store || this.cm){
5080 cfg.cn.push(this.renderHeader());
5083 cfg.cn.push(this.renderBody());
5086 cfg.cn.push(this.renderFooter());
5089 cfg.cls+= ' TableGrid';
5092 return { cn : [ cfg ] };
5095 initEvents : function()
5097 if(!this.store || !this.cm){
5101 //Roo.log('initEvents with ds!!!!');
5103 this.mainBody = this.el.select('tbody', true).first();
5108 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5109 e.on('click', _this.sort, _this);
5112 this.el.on("click", this.onClick, this);
5113 this.el.on("dblclick", this.onDblClick, this);
5115 this.parent().el.setStyle('position', 'relative');
5117 this.footer.parentId = this.id;
5118 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5121 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5123 this.store.on('load', this.onLoad, this);
5124 this.store.on('beforeload', this.onBeforeLoad, this);
5125 this.store.on('update', this.onUpdate, this);
5129 onMouseover : function(e, el)
5131 var cell = Roo.get(el);
5137 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5138 cell = cell.findParent('td', false, true);
5141 var row = cell.findParent('tr', false, true);
5142 var cellIndex = cell.dom.cellIndex;
5143 var rowIndex = row.dom.rowIndex - 1; // start from 0
5145 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5149 onMouseout : function(e, el)
5151 var cell = Roo.get(el);
5157 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5158 cell = cell.findParent('td', false, true);
5161 var row = cell.findParent('tr', false, true);
5162 var cellIndex = cell.dom.cellIndex;
5163 var rowIndex = row.dom.rowIndex - 1; // start from 0
5165 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5169 onClick : function(e, el)
5171 var cell = Roo.get(el);
5173 if(!cell || (!this.CellSelection && !this.RowSelection)){
5178 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5179 cell = cell.findParent('td', false, true);
5182 var row = cell.findParent('tr', false, true);
5183 var cellIndex = cell.dom.cellIndex;
5184 var rowIndex = row.dom.rowIndex - 1;
5186 if(this.CellSelection){
5187 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5190 if(this.RowSelection){
5191 this.fireEvent('rowclick', this, row, rowIndex, e);
5197 onDblClick : function(e,el)
5199 var cell = Roo.get(el);
5201 if(!cell || (!this.CellSelection && !this.RowSelection)){
5205 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5206 cell = cell.findParent('td', false, true);
5209 var row = cell.findParent('tr', false, true);
5210 var cellIndex = cell.dom.cellIndex;
5211 var rowIndex = row.dom.rowIndex - 1;
5213 if(this.CellSelection){
5214 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5217 if(this.RowSelection){
5218 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5222 sort : function(e,el)
5224 var col = Roo.get(el)
5226 if(!col.hasClass('sortable')){
5230 var sort = col.attr('sort');
5233 if(col.hasClass('glyphicon-arrow-up')){
5237 this.store.sortInfo = {field : sort, direction : dir};
5240 Roo.log("calling footer first");
5241 this.footer.onClick('first');
5244 this.store.load({ params : { start : 0 } });
5248 renderHeader : function()
5257 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5259 var config = cm.config[i];
5264 html: cm.getColumnHeader(i)
5267 if(typeof(config.hidden) != 'undefined' && config.hidden){
5268 c.style += ' display:none;';
5271 if(typeof(config.dataIndex) != 'undefined'){
5272 c.sort = config.dataIndex;
5275 if(typeof(config.sortable) != 'undefined' && config.sortable){
5279 if(typeof(config.align) != 'undefined' && config.align.length){
5280 c.style += ' text-align:' + config.align + ';';
5283 if(typeof(config.width) != 'undefined'){
5284 c.style += ' width:' + config.width + 'px;';
5293 renderBody : function()
5303 colspan : this.cm.getColumnCount()
5313 renderFooter : function()
5323 colspan : this.cm.getColumnCount()
5337 Roo.log('ds onload');
5342 var ds = this.store;
5344 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5345 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5347 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5348 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5351 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5352 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5356 var tbody = this.mainBody;
5358 if(ds.getCount() > 0){
5359 ds.data.each(function(d,rowIndex){
5360 var row = this.renderRow(cm, ds, rowIndex);
5362 tbody.createChild(row);
5366 if(row.cellObjects.length){
5367 Roo.each(row.cellObjects, function(r){
5368 _this.renderCellObject(r);
5375 Roo.each(this.el.select('tbody td', true).elements, function(e){
5376 e.on('mouseover', _this.onMouseover, _this);
5379 Roo.each(this.el.select('tbody td', true).elements, function(e){
5380 e.on('mouseout', _this.onMouseout, _this);
5383 //if(this.loadMask){
5384 // this.maskEl.hide();
5389 onUpdate : function(ds,record)
5391 this.refreshRow(record);
5393 onRemove : function(ds, record, index, isUpdate){
5394 if(isUpdate !== true){
5395 this.fireEvent("beforerowremoved", this, index, record);
5397 var bt = this.mainBody.dom;
5399 bt.removeChild(bt.rows[index]);
5402 if(isUpdate !== true){
5403 //this.stripeRows(index);
5404 //this.syncRowHeights(index, index);
5406 this.fireEvent("rowremoved", this, index, record);
5411 refreshRow : function(record){
5412 var ds = this.store, index;
5413 if(typeof record == 'number'){
5415 record = ds.getAt(index);
5417 index = ds.indexOf(record);
5419 this.insertRow(ds, index, true);
5420 this.onRemove(ds, record, index+1, true);
5421 //this.syncRowHeights(index, index);
5423 this.fireEvent("rowupdated", this, index, record);
5426 insertRow : function(dm, rowIndex, isUpdate){
5429 this.fireEvent("beforerowsinserted", this, rowIndex);
5431 //var s = this.getScrollState();
5432 var row = this.renderRow(this.cm, this.store, rowIndex);
5433 // insert before rowIndex..
5434 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5438 if(row.cellObjects.length){
5439 Roo.each(row.cellObjects, function(r){
5440 _this.renderCellObject(r);
5445 this.fireEvent("rowsinserted", this, rowIndex);
5446 //this.syncRowHeights(firstRow, lastRow);
5447 //this.stripeRows(firstRow);
5454 getRowDom : function(rowIndex)
5456 // not sure if I need to check this.. but let's do it anyway..
5457 return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5458 this.mainBody.dom.rows[rowIndex] : false
5460 // returns the object tree for a tr..
5463 renderRow : function(cm, ds, rowIndex) {
5465 var d = ds.getAt(rowIndex);
5472 var cellObjects = [];
5474 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5475 var config = cm.config[i];
5477 var renderer = cm.getRenderer(i);
5481 if(typeof(renderer) !== 'undefined'){
5482 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5484 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5485 // and are rendered into the cells after the row is rendered - using the id for the element.
5487 if(typeof(value) === 'object'){
5497 rowIndex : rowIndex,
5502 this.fireEvent('rowclass', this, rowcfg);
5506 cls : rowcfg.rowClass,
5508 html: (typeof(value) === 'object') ? '' : value
5515 if(typeof(config.hidden) != 'undefined' && config.hidden){
5516 td.style += ' display:none;';
5519 if(typeof(config.align) != 'undefined' && config.align.length){
5520 td.style += ' text-align:' + config.align + ';';
5523 if(typeof(config.width) != 'undefined'){
5524 td.style += ' width:' + config.width + 'px;';
5531 row.cellObjects = cellObjects;
5539 onBeforeLoad : function()
5541 //Roo.log('ds onBeforeLoad');
5545 //if(this.loadMask){
5546 // this.maskEl.show();
5552 this.el.select('tbody', true).first().dom.innerHTML = '';
5555 getSelectionModel : function(){
5557 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5559 return this.selModel;
5562 * Render the Roo.bootstrap object from renderder
5564 renderCellObject : function(r)
5568 var t = r.cfg.render(r.container);
5571 Roo.each(r.cfg.cn, function(c){
5573 container: t.getChildContainer(),
5576 _this.renderCellObject(child);
5593 * @class Roo.bootstrap.TableCell
5594 * @extends Roo.bootstrap.Component
5595 * Bootstrap TableCell class
5596 * @cfg {String} html cell contain text
5597 * @cfg {String} cls cell class
5598 * @cfg {String} tag cell tag (td|th) default td
5599 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5600 * @cfg {String} align Aligns the content in a cell
5601 * @cfg {String} axis Categorizes cells
5602 * @cfg {String} bgcolor Specifies the background color of a cell
5603 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5604 * @cfg {Number} colspan Specifies the number of columns a cell should span
5605 * @cfg {String} headers Specifies one or more header cells a cell is related to
5606 * @cfg {Number} height Sets the height of a cell
5607 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5608 * @cfg {Number} rowspan Sets the number of rows a cell should span
5609 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5610 * @cfg {String} valign Vertical aligns the content in a cell
5611 * @cfg {Number} width Specifies the width of a cell
5614 * Create a new TableCell
5615 * @param {Object} config The config object
5618 Roo.bootstrap.TableCell = function(config){
5619 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5622 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
5642 getAutoCreate : function(){
5643 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5663 cfg.align=this.align
5669 cfg.bgcolor=this.bgcolor
5672 cfg.charoff=this.charoff
5675 cfg.colspan=this.colspan
5678 cfg.headers=this.headers
5681 cfg.height=this.height
5684 cfg.nowrap=this.nowrap
5687 cfg.rowspan=this.rowspan
5690 cfg.scope=this.scope
5693 cfg.valign=this.valign
5696 cfg.width=this.width
5715 * @class Roo.bootstrap.TableRow
5716 * @extends Roo.bootstrap.Component
5717 * Bootstrap TableRow class
5718 * @cfg {String} cls row class
5719 * @cfg {String} align Aligns the content in a table row
5720 * @cfg {String} bgcolor Specifies a background color for a table row
5721 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5722 * @cfg {String} valign Vertical aligns the content in a table row
5725 * Create a new TableRow
5726 * @param {Object} config The config object
5729 Roo.bootstrap.TableRow = function(config){
5730 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5733 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
5741 getAutoCreate : function(){
5742 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5752 cfg.align = this.align;
5755 cfg.bgcolor = this.bgcolor;
5758 cfg.charoff = this.charoff;
5761 cfg.valign = this.valign;
5779 * @class Roo.bootstrap.TableBody
5780 * @extends Roo.bootstrap.Component
5781 * Bootstrap TableBody class
5782 * @cfg {String} cls element class
5783 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5784 * @cfg {String} align Aligns the content inside the element
5785 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5786 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5789 * Create a new TableBody
5790 * @param {Object} config The config object
5793 Roo.bootstrap.TableBody = function(config){
5794 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5797 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
5805 getAutoCreate : function(){
5806 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5820 cfg.align = this.align;
5823 cfg.charoff = this.charoff;
5826 cfg.valign = this.valign;
5833 // initEvents : function()
5840 // this.store = Roo.factory(this.store, Roo.data);
5841 // this.store.on('load', this.onLoad, this);
5843 // this.store.load();
5847 // onLoad: function ()
5849 // this.fireEvent('load', this);
5859 * Ext JS Library 1.1.1
5860 * Copyright(c) 2006-2007, Ext JS, LLC.
5862 * Originally Released Under LGPL - original licence link has changed is not relivant.
5865 * <script type="text/javascript">
5868 // as we use this in bootstrap.
5869 Roo.namespace('Roo.form');
5871 * @class Roo.form.Action
5872 * Internal Class used to handle form actions
5874 * @param {Roo.form.BasicForm} el The form element or its id
5875 * @param {Object} config Configuration options
5880 // define the action interface
5881 Roo.form.Action = function(form, options){
5883 this.options = options || {};
5886 * Client Validation Failed
5889 Roo.form.Action.CLIENT_INVALID = 'client';
5891 * Server Validation Failed
5894 Roo.form.Action.SERVER_INVALID = 'server';
5896 * Connect to Server Failed
5899 Roo.form.Action.CONNECT_FAILURE = 'connect';
5901 * Reading Data from Server Failed
5904 Roo.form.Action.LOAD_FAILURE = 'load';
5906 Roo.form.Action.prototype = {
5908 failureType : undefined,
5909 response : undefined,
5913 run : function(options){
5918 success : function(response){
5923 handleResponse : function(response){
5927 // default connection failure
5928 failure : function(response){
5930 this.response = response;
5931 this.failureType = Roo.form.Action.CONNECT_FAILURE;
5932 this.form.afterAction(this, false);
5935 processResponse : function(response){
5936 this.response = response;
5937 if(!response.responseText){
5940 this.result = this.handleResponse(response);
5944 // utility functions used internally
5945 getUrl : function(appendParams){
5946 var url = this.options.url || this.form.url || this.form.el.dom.action;
5948 var p = this.getParams();
5950 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5956 getMethod : function(){
5957 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5960 getParams : function(){
5961 var bp = this.form.baseParams;
5962 var p = this.options.params;
5964 if(typeof p == "object"){
5965 p = Roo.urlEncode(Roo.applyIf(p, bp));
5966 }else if(typeof p == 'string' && bp){
5967 p += '&' + Roo.urlEncode(bp);
5970 p = Roo.urlEncode(bp);
5975 createCallback : function(){
5977 success: this.success,
5978 failure: this.failure,
5980 timeout: (this.form.timeout*1000),
5981 upload: this.form.fileUpload ? this.success : undefined
5986 Roo.form.Action.Submit = function(form, options){
5987 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5990 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5993 haveProgress : false,
5994 uploadComplete : false,
5996 // uploadProgress indicator.
5997 uploadProgress : function()
5999 if (!this.form.progressUrl) {
6003 if (!this.haveProgress) {
6004 Roo.MessageBox.progress("Uploading", "Uploading");
6006 if (this.uploadComplete) {
6007 Roo.MessageBox.hide();
6011 this.haveProgress = true;
6013 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6015 var c = new Roo.data.Connection();
6017 url : this.form.progressUrl,
6022 success : function(req){
6023 //console.log(data);
6027 rdata = Roo.decode(req.responseText)
6029 Roo.log("Invalid data from server..");
6033 if (!rdata || !rdata.success) {
6035 Roo.MessageBox.alert(Roo.encode(rdata));
6038 var data = rdata.data;
6040 if (this.uploadComplete) {
6041 Roo.MessageBox.hide();
6046 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6047 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6050 this.uploadProgress.defer(2000,this);
6053 failure: function(data) {
6054 Roo.log('progress url failed ');
6065 // run get Values on the form, so it syncs any secondary forms.
6066 this.form.getValues();
6068 var o = this.options;
6069 var method = this.getMethod();
6070 var isPost = method == 'POST';
6071 if(o.clientValidation === false || this.form.isValid()){
6073 if (this.form.progressUrl) {
6074 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6075 (new Date() * 1) + '' + Math.random());
6080 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6081 form:this.form.el.dom,
6082 url:this.getUrl(!isPost),
6084 params:isPost ? this.getParams() : null,
6085 isUpload: this.form.fileUpload
6088 this.uploadProgress();
6090 }else if (o.clientValidation !== false){ // client validation failed
6091 this.failureType = Roo.form.Action.CLIENT_INVALID;
6092 this.form.afterAction(this, false);
6096 success : function(response)
6098 this.uploadComplete= true;
6099 if (this.haveProgress) {
6100 Roo.MessageBox.hide();
6104 var result = this.processResponse(response);
6105 if(result === true || result.success){
6106 this.form.afterAction(this, true);
6110 this.form.markInvalid(result.errors);
6111 this.failureType = Roo.form.Action.SERVER_INVALID;
6113 this.form.afterAction(this, false);
6115 failure : function(response)
6117 this.uploadComplete= true;
6118 if (this.haveProgress) {
6119 Roo.MessageBox.hide();
6122 this.response = response;
6123 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6124 this.form.afterAction(this, false);
6127 handleResponse : function(response){
6128 if(this.form.errorReader){
6129 var rs = this.form.errorReader.read(response);
6132 for(var i = 0, len = rs.records.length; i < len; i++) {
6133 var r = rs.records[i];
6137 if(errors.length < 1){
6141 success : rs.success,
6147 ret = Roo.decode(response.responseText);
6151 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6161 Roo.form.Action.Load = function(form, options){
6162 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6163 this.reader = this.form.reader;
6166 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6171 Roo.Ajax.request(Roo.apply(
6172 this.createCallback(), {
6173 method:this.getMethod(),
6174 url:this.getUrl(false),
6175 params:this.getParams()
6179 success : function(response){
6181 var result = this.processResponse(response);
6182 if(result === true || !result.success || !result.data){
6183 this.failureType = Roo.form.Action.LOAD_FAILURE;
6184 this.form.afterAction(this, false);
6187 this.form.clearInvalid();
6188 this.form.setValues(result.data);
6189 this.form.afterAction(this, true);
6192 handleResponse : function(response){
6193 if(this.form.reader){
6194 var rs = this.form.reader.read(response);
6195 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6197 success : rs.success,
6201 return Roo.decode(response.responseText);
6205 Roo.form.Action.ACTION_TYPES = {
6206 'load' : Roo.form.Action.Load,
6207 'submit' : Roo.form.Action.Submit
6216 * @class Roo.bootstrap.Form
6217 * @extends Roo.bootstrap.Component
6218 * Bootstrap Form class
6219 * @cfg {String} method GET | POST (default POST)
6220 * @cfg {String} labelAlign top | left (default top)
6221 * @cfg {String} align left | right - for navbars
6222 * @cfg {Boolean} loadMask load mask when submit (default true)
6227 * @param {Object} config The config object
6231 Roo.bootstrap.Form = function(config){
6232 Roo.bootstrap.Form.superclass.constructor.call(this, config);
6235 * @event clientvalidation
6236 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6237 * @param {Form} this
6238 * @param {Boolean} valid true if the form has passed client-side validation
6240 clientvalidation: true,
6242 * @event beforeaction
6243 * Fires before any action is performed. Return false to cancel the action.
6244 * @param {Form} this
6245 * @param {Action} action The action to be performed
6249 * @event actionfailed
6250 * Fires when an action fails.
6251 * @param {Form} this
6252 * @param {Action} action The action that failed
6254 actionfailed : true,
6256 * @event actioncomplete
6257 * Fires when an action is completed.
6258 * @param {Form} this
6259 * @param {Action} action The action that completed
6261 actioncomplete : true
6266 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
6269 * @cfg {String} method
6270 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6275 * The URL to use for form actions if one isn't supplied in the action options.
6278 * @cfg {Boolean} fileUpload
6279 * Set to true if this form is a file upload.
6283 * @cfg {Object} baseParams
6284 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6288 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6292 * @cfg {Sting} align (left|right) for navbar forms
6297 activeAction : null,
6300 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6301 * element by passing it or its id or mask the form itself by passing in true.
6304 waitMsgTarget : false,
6308 getAutoCreate : function(){
6312 method : this.method || 'POST',
6313 id : this.id || Roo.id(),
6316 if (this.parent().xtype.match(/^Nav/)) {
6317 cfg.cls = 'navbar-form navbar-' + this.align;
6321 if (this.labelAlign == 'left' ) {
6322 cfg.cls += ' form-horizontal';
6328 initEvents : function()
6330 this.el.on('submit', this.onSubmit, this);
6331 // this was added as random key presses on the form where triggering form submit.
6332 this.el.on('keypress', function(e) {
6333 if (e.getCharCode() != 13) {
6336 // we might need to allow it for textareas.. and some other items.
6337 // check e.getTarget().
6339 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6343 Roo.log("keypress blocked");
6351 onSubmit : function(e){
6356 * Returns true if client-side validation on the form is successful.
6359 isValid : function(){
6360 var items = this.getItems();
6362 items.each(function(f){
6371 * Returns true if any fields in this form have changed since their original load.
6374 isDirty : function(){
6376 var items = this.getItems();
6377 items.each(function(f){
6387 * Performs a predefined action (submit or load) or custom actions you define on this form.
6388 * @param {String} actionName The name of the action type
6389 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
6390 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6391 * accept other config options):
6393 Property Type Description
6394 ---------------- --------------- ----------------------------------------------------------------------------------
6395 url String The url for the action (defaults to the form's url)
6396 method String The form method to use (defaults to the form's method, or POST if not defined)
6397 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
6398 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
6399 validate the form on the client (defaults to false)
6401 * @return {BasicForm} this
6403 doAction : function(action, options){
6404 if(typeof action == 'string'){
6405 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6407 if(this.fireEvent('beforeaction', this, action) !== false){
6408 this.beforeAction(action);
6409 action.run.defer(100, action);
6415 beforeAction : function(action){
6416 var o = action.options;
6419 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6421 // not really supported yet.. ??
6423 //if(this.waitMsgTarget === true){
6424 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6425 //}else if(this.waitMsgTarget){
6426 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6427 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6429 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6435 afterAction : function(action, success){
6436 this.activeAction = null;
6437 var o = action.options;
6439 //if(this.waitMsgTarget === true){
6441 //}else if(this.waitMsgTarget){
6442 // this.waitMsgTarget.unmask();
6444 // Roo.MessageBox.updateProgress(1);
6445 // Roo.MessageBox.hide();
6452 Roo.callback(o.success, o.scope, [this, action]);
6453 this.fireEvent('actioncomplete', this, action);
6457 // failure condition..
6458 // we have a scenario where updates need confirming.
6459 // eg. if a locking scenario exists..
6460 // we look for { errors : { needs_confirm : true }} in the response.
6462 (typeof(action.result) != 'undefined') &&
6463 (typeof(action.result.errors) != 'undefined') &&
6464 (typeof(action.result.errors.needs_confirm) != 'undefined')
6467 Roo.log("not supported yet");
6470 Roo.MessageBox.confirm(
6471 "Change requires confirmation",
6472 action.result.errorMsg,
6477 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
6487 Roo.callback(o.failure, o.scope, [this, action]);
6488 // show an error message if no failed handler is set..
6489 if (!this.hasListener('actionfailed')) {
6490 Roo.log("need to add dialog support");
6492 Roo.MessageBox.alert("Error",
6493 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6494 action.result.errorMsg :
6495 "Saving Failed, please check your entries or try again"
6500 this.fireEvent('actionfailed', this, action);
6505 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6506 * @param {String} id The value to search for
6509 findField : function(id){
6510 var items = this.getItems();
6511 var field = items.get(id);
6513 items.each(function(f){
6514 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6521 return field || null;
6524 * Mark fields in this form invalid in bulk.
6525 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6526 * @return {BasicForm} this
6528 markInvalid : function(errors){
6529 if(errors instanceof Array){
6530 for(var i = 0, len = errors.length; i < len; i++){
6531 var fieldError = errors[i];
6532 var f = this.findField(fieldError.id);
6534 f.markInvalid(fieldError.msg);
6540 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6541 field.markInvalid(errors[id]);
6545 //Roo.each(this.childForms || [], function (f) {
6546 // f.markInvalid(errors);
6553 * Set values for fields in this form in bulk.
6554 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6555 * @return {BasicForm} this
6557 setValues : function(values){
6558 if(values instanceof Array){ // array of objects
6559 for(var i = 0, len = values.length; i < len; i++){
6561 var f = this.findField(v.id);
6563 f.setValue(v.value);
6564 if(this.trackResetOnLoad){
6565 f.originalValue = f.getValue();
6569 }else{ // object hash
6572 if(typeof values[id] != 'function' && (field = this.findField(id))){
6574 if (field.setFromData &&
6576 field.displayField &&
6577 // combos' with local stores can
6578 // be queried via setValue()
6579 // to set their value..
6580 (field.store && !field.store.isLocal)
6584 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6585 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6586 field.setFromData(sd);
6589 field.setValue(values[id]);
6593 if(this.trackResetOnLoad){
6594 field.originalValue = field.getValue();
6600 //Roo.each(this.childForms || [], function (f) {
6601 // f.setValues(values);
6608 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6609 * they are returned as an array.
6610 * @param {Boolean} asString
6613 getValues : function(asString){
6614 //if (this.childForms) {
6615 // copy values from the child forms
6616 // Roo.each(this.childForms, function (f) {
6617 // this.setValues(f.getValues());
6623 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6624 if(asString === true){
6627 return Roo.urlDecode(fs);
6631 * Returns the fields in this form as an object with key/value pairs.
6632 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6635 getFieldValues : function(with_hidden)
6637 var items = this.getItems();
6639 items.each(function(f){
6643 var v = f.getValue();
6644 if (f.inputType =='radio') {
6645 if (typeof(ret[f.getName()]) == 'undefined') {
6646 ret[f.getName()] = ''; // empty..
6649 if (!f.el.dom.checked) {
6657 // not sure if this supported any more..
6658 if ((typeof(v) == 'object') && f.getRawValue) {
6659 v = f.getRawValue() ; // dates..
6661 // combo boxes where name != hiddenName...
6662 if (f.name != f.getName()) {
6663 ret[f.name] = f.getRawValue();
6665 ret[f.getName()] = v;
6672 * Clears all invalid messages in this form.
6673 * @return {BasicForm} this
6675 clearInvalid : function(){
6676 var items = this.getItems();
6678 items.each(function(f){
6689 * @return {BasicForm} this
6692 var items = this.getItems();
6693 items.each(function(f){
6697 Roo.each(this.childForms || [], function (f) {
6704 getItems : function()
6706 var r=new Roo.util.MixedCollection(false, function(o){
6707 return o.id || (o.id = Roo.id());
6709 var iter = function(el) {
6716 Roo.each(el.items,function(e) {
6735 * Ext JS Library 1.1.1
6736 * Copyright(c) 2006-2007, Ext JS, LLC.
6738 * Originally Released Under LGPL - original licence link has changed is not relivant.
6741 * <script type="text/javascript">
6744 * @class Roo.form.VTypes
6745 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6748 Roo.form.VTypes = function(){
6749 // closure these in so they are only created once.
6750 var alpha = /^[a-zA-Z_]+$/;
6751 var alphanum = /^[a-zA-Z0-9_]+$/;
6752 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6753 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6755 // All these messages and functions are configurable
6758 * The function used to validate email addresses
6759 * @param {String} value The email address
6761 'email' : function(v){
6762 return email.test(v);
6765 * The error text to display when the email validation function returns false
6768 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6770 * The keystroke filter mask to be applied on email input
6773 'emailMask' : /[a-z0-9_\.\-@]/i,
6776 * The function used to validate URLs
6777 * @param {String} value The URL
6779 'url' : function(v){
6783 * The error text to display when the url validation function returns false
6786 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6789 * The function used to validate alpha values
6790 * @param {String} value The value
6792 'alpha' : function(v){
6793 return alpha.test(v);
6796 * The error text to display when the alpha validation function returns false
6799 'alphaText' : 'This field should only contain letters and _',
6801 * The keystroke filter mask to be applied on alpha input
6804 'alphaMask' : /[a-z_]/i,
6807 * The function used to validate alphanumeric values
6808 * @param {String} value The value
6810 'alphanum' : function(v){
6811 return alphanum.test(v);
6814 * The error text to display when the alphanumeric validation function returns false
6817 'alphanumText' : 'This field should only contain letters, numbers and _',
6819 * The keystroke filter mask to be applied on alphanumeric input
6822 'alphanumMask' : /[a-z0-9_]/i
6832 * @class Roo.bootstrap.Input
6833 * @extends Roo.bootstrap.Component
6834 * Bootstrap Input class
6835 * @cfg {Boolean} disabled is it disabled
6836 * @cfg {String} fieldLabel - the label associated
6837 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6838 * @cfg {String} name name of the input
6839 * @cfg {string} fieldLabel - the label associated
6840 * @cfg {string} inputType - input / file submit ...
6841 * @cfg {string} placeholder - placeholder to put in text.
6842 * @cfg {string} before - input group add on before
6843 * @cfg {string} after - input group add on after
6844 * @cfg {string} size - (lg|sm) or leave empty..
6845 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6846 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6847 * @cfg {Number} md colspan out of 12 for computer-sized screens
6848 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6849 * @cfg {string} value default value of the input
6850 * @cfg {Number} labelWidth set the width of label (0-12)
6851 * @cfg {String} labelAlign (top|left)
6852 * @cfg {Boolean} readOnly Specifies that the field should be read-only
6853 * @cfg {String} align (left|center|right) Default left
6857 * Create a new Input
6858 * @param {Object} config The config object
6861 Roo.bootstrap.Input = function(config){
6862 Roo.bootstrap.Input.superclass.constructor.call(this, config);
6867 * Fires when this field receives input focus.
6868 * @param {Roo.form.Field} this
6873 * Fires when this field loses input focus.
6874 * @param {Roo.form.Field} this
6879 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
6880 * {@link Roo.EventObject#getKey} to determine which key was pressed.
6881 * @param {Roo.form.Field} this
6882 * @param {Roo.EventObject} e The event object
6887 * Fires just before the field blurs if the field value has changed.
6888 * @param {Roo.form.Field} this
6889 * @param {Mixed} newValue The new value
6890 * @param {Mixed} oldValue The original value
6895 * Fires after the field has been marked as invalid.
6896 * @param {Roo.form.Field} this
6897 * @param {String} msg The validation message
6902 * Fires after the field has been validated with no errors.
6903 * @param {Roo.form.Field} this
6908 * Fires after the key up
6909 * @param {Roo.form.Field} this
6910 * @param {Roo.EventObject} e The event Object
6916 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
6918 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6919 automatic validation (defaults to "keyup").
6921 validationEvent : "keyup",
6923 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6925 validateOnBlur : true,
6927 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6929 validationDelay : 250,
6931 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6933 focusClass : "x-form-focus", // not needed???
6937 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6939 invalidClass : "has-error",
6942 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6944 selectOnFocus : false,
6947 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6951 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6956 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6958 disableKeyFilter : false,
6961 * @cfg {Boolean} disabled True to disable the field (defaults to false).
6965 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6969 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6971 blankText : "This field is required",
6974 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6978 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6980 maxLength : Number.MAX_VALUE,
6982 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6984 minLengthText : "The minimum length for this field is {0}",
6986 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6988 maxLengthText : "The maximum length for this field is {0}",
6992 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6993 * If available, this function will be called only after the basic validators all return true, and will be passed the
6994 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6998 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6999 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7000 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7004 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7027 formatedValue : false,
7029 parentLabelAlign : function()
7032 while (parent.parent()) {
7033 parent = parent.parent();
7034 if (typeof(parent.labelAlign) !='undefined') {
7035 return parent.labelAlign;
7042 getAutoCreate : function(){
7044 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7050 if(this.inputType != 'hidden'){
7051 cfg.cls = 'form-group' //input-group
7057 type : this.inputType,
7059 cls : 'form-control',
7060 placeholder : this.placeholder || ''
7065 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7068 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7069 input.maxLength = this.maxLength;
7072 if (this.disabled) {
7073 input.disabled=true;
7076 if (this.readOnly) {
7077 input.readonly=true;
7081 input.name = this.name;
7084 input.cls += ' input-' + this.size;
7087 ['xs','sm','md','lg'].map(function(size){
7088 if (settings[size]) {
7089 cfg.cls += ' col-' + size + '-' + settings[size];
7093 var inputblock = input;
7095 if (this.before || this.after) {
7098 cls : 'input-group',
7101 if (this.before && typeof(this.before) == 'string') {
7103 inputblock.cn.push({
7105 cls : 'roo-input-before input-group-addon',
7109 if (this.before && typeof(this.before) == 'object') {
7110 this.before = Roo.factory(this.before);
7111 Roo.log(this.before);
7112 inputblock.cn.push({
7114 cls : 'roo-input-before input-group-' +
7115 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7119 inputblock.cn.push(input);
7121 if (this.after && typeof(this.after) == 'string') {
7122 inputblock.cn.push({
7124 cls : 'roo-input-after input-group-addon',
7128 if (this.after && typeof(this.after) == 'object') {
7129 this.after = Roo.factory(this.after);
7130 Roo.log(this.after);
7131 inputblock.cn.push({
7133 cls : 'roo-input-after input-group-' +
7134 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7139 if (align ==='left' && this.fieldLabel.length) {
7140 Roo.log("left and has label");
7146 cls : 'control-label col-sm-' + this.labelWidth,
7147 html : this.fieldLabel
7151 cls : "col-sm-" + (12 - this.labelWidth),
7158 } else if ( this.fieldLabel.length) {
7164 //cls : 'input-group-addon',
7165 html : this.fieldLabel
7175 Roo.log(" no label && no align");
7184 Roo.log('input-parentType: ' + this.parentType);
7186 if (this.parentType === 'Navbar' && this.parent().bar) {
7187 cfg.cls += ' navbar-form';
7195 * return the real input element.
7197 inputEl: function ()
7199 return this.el.select('input.form-control',true).first();
7201 setDisabled : function(v)
7203 var i = this.inputEl().dom;
7205 i.removeAttribute('disabled');
7209 i.setAttribute('disabled','true');
7211 initEvents : function()
7214 this.inputEl().on("keydown" , this.fireKey, this);
7215 this.inputEl().on("focus", this.onFocus, this);
7216 this.inputEl().on("blur", this.onBlur, this);
7218 this.inputEl().relayEvent('keyup', this);
7220 // reference to original value for reset
7221 this.originalValue = this.getValue();
7222 //Roo.form.TextField.superclass.initEvents.call(this);
7223 if(this.validationEvent == 'keyup'){
7224 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7225 this.inputEl().on('keyup', this.filterValidation, this);
7227 else if(this.validationEvent !== false){
7228 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7231 if(this.selectOnFocus){
7232 this.on("focus", this.preFocus, this);
7235 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7236 this.inputEl().on("keypress", this.filterKeys, this);
7239 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
7240 this.el.on("click", this.autoSize, this);
7243 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7244 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7247 if (typeof(this.before) == 'object') {
7248 this.before.render(this.el.select('.roo-input-before',true).first());
7250 if (typeof(this.after) == 'object') {
7251 this.after.render(this.el.select('.roo-input-after',true).first());
7256 filterValidation : function(e){
7257 if(!e.isNavKeyPress()){
7258 this.validationTask.delay(this.validationDelay);
7262 * Validates the field value
7263 * @return {Boolean} True if the value is valid, else false
7265 validate : function(){
7266 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7267 if(this.disabled || this.validateValue(this.getRawValue())){
7268 this.clearInvalid();
7276 * Validates a value according to the field's validation rules and marks the field as invalid
7277 * if the validation fails
7278 * @param {Mixed} value The value to validate
7279 * @return {Boolean} True if the value is valid, else false
7281 validateValue : function(value){
7282 if(value.length < 1) { // if it's blank
7283 if(this.allowBlank){
7284 this.clearInvalid();
7287 this.markInvalid(this.blankText);
7291 if(value.length < this.minLength){
7292 this.markInvalid(String.format(this.minLengthText, this.minLength));
7295 if(value.length > this.maxLength){
7296 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7300 var vt = Roo.form.VTypes;
7301 if(!vt[this.vtype](value, this)){
7302 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7306 if(typeof this.validator == "function"){
7307 var msg = this.validator(value);
7309 this.markInvalid(msg);
7313 if(this.regex && !this.regex.test(value)){
7314 this.markInvalid(this.regexText);
7323 fireKey : function(e){
7324 //Roo.log('field ' + e.getKey());
7325 if(e.isNavKeyPress()){
7326 this.fireEvent("specialkey", this, e);
7329 focus : function (selectText){
7331 this.inputEl().focus();
7332 if(selectText === true){
7333 this.inputEl().dom.select();
7339 onFocus : function(){
7340 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7341 // this.el.addClass(this.focusClass);
7344 this.hasFocus = true;
7345 this.startValue = this.getValue();
7346 this.fireEvent("focus", this);
7350 beforeBlur : Roo.emptyFn,
7354 onBlur : function(){
7356 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7357 //this.el.removeClass(this.focusClass);
7359 this.hasFocus = false;
7360 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7363 var v = this.getValue();
7364 if(String(v) !== String(this.startValue)){
7365 this.fireEvent('change', this, v, this.startValue);
7367 this.fireEvent("blur", this);
7371 * Resets the current field value to the originally loaded value and clears any validation messages
7374 this.setValue(this.originalValue);
7375 this.clearInvalid();
7378 * Returns the name of the field
7379 * @return {Mixed} name The name field
7381 getName: function(){
7385 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
7386 * @return {Mixed} value The field value
7388 getValue : function(){
7390 var v = this.inputEl().getValue();
7395 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
7396 * @return {Mixed} value The field value
7398 getRawValue : function(){
7399 var v = this.inputEl().getValue();
7405 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
7406 * @param {Mixed} value The value to set
7408 setRawValue : function(v){
7409 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7412 selectText : function(start, end){
7413 var v = this.getRawValue();
7415 start = start === undefined ? 0 : start;
7416 end = end === undefined ? v.length : end;
7417 var d = this.inputEl().dom;
7418 if(d.setSelectionRange){
7419 d.setSelectionRange(start, end);
7420 }else if(d.createTextRange){
7421 var range = d.createTextRange();
7422 range.moveStart("character", start);
7423 range.moveEnd("character", v.length-end);
7430 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
7431 * @param {Mixed} value The value to set
7433 setValue : function(v){
7436 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7442 processValue : function(value){
7443 if(this.stripCharsRe){
7444 var newValue = value.replace(this.stripCharsRe, '');
7445 if(newValue !== value){
7446 this.setRawValue(newValue);
7453 preFocus : function(){
7455 if(this.selectOnFocus){
7456 this.inputEl().dom.select();
7459 filterKeys : function(e){
7461 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7464 var c = e.getCharCode(), cc = String.fromCharCode(c);
7465 if(Roo.isIE && (e.isSpecialKey() || !cc)){
7468 if(!this.maskRe.test(cc)){
7473 * Clear any invalid styles/messages for this field
7475 clearInvalid : function(){
7477 if(!this.el || this.preventMark){ // not rendered
7480 this.el.removeClass(this.invalidClass);
7482 switch(this.msgTarget){
7484 this.el.dom.qtip = '';
7487 this.el.dom.title = '';
7491 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7496 this.errorIcon.dom.qtip = '';
7497 this.errorIcon.hide();
7498 this.un('resize', this.alignErrorIcon, this);
7502 var t = Roo.getDom(this.msgTarget);
7504 t.style.display = 'none';
7508 this.fireEvent('valid', this);
7511 * Mark this field as invalid
7512 * @param {String} msg The validation message
7514 markInvalid : function(msg){
7515 if(!this.el || this.preventMark){ // not rendered
7518 this.el.addClass(this.invalidClass);
7520 msg = msg || this.invalidText;
7521 switch(this.msgTarget){
7523 this.el.dom.qtip = msg;
7524 this.el.dom.qclass = 'x-form-invalid-tip';
7525 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7526 Roo.QuickTips.enable();
7530 this.el.dom.title = msg;
7534 var elp = this.el.findParent('.x-form-element', 5, true);
7535 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7536 this.errorEl.setWidth(elp.getWidth(true)-20);
7538 this.errorEl.update(msg);
7539 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7542 if(!this.errorIcon){
7543 var elp = this.el.findParent('.x-form-element', 5, true);
7544 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7546 this.alignErrorIcon();
7547 this.errorIcon.dom.qtip = msg;
7548 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7549 this.errorIcon.show();
7550 this.on('resize', this.alignErrorIcon, this);
7553 var t = Roo.getDom(this.msgTarget);
7555 t.style.display = this.msgDisplay;
7559 this.fireEvent('invalid', this, msg);
7562 SafariOnKeyDown : function(event)
7564 // this is a workaround for a password hang bug on chrome/ webkit.
7566 var isSelectAll = false;
7568 if(this.inputEl().dom.selectionEnd > 0){
7569 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7571 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7572 event.preventDefault();
7577 if(isSelectAll){ // backspace and delete key
7579 event.preventDefault();
7580 // this is very hacky as keydown always get's upper case.
7582 var cc = String.fromCharCode(event.getCharCode());
7583 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
7587 adjustWidth : function(tag, w){
7588 tag = tag.toLowerCase();
7589 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7590 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7594 if(tag == 'textarea'){
7597 }else if(Roo.isOpera){
7601 if(tag == 'textarea'){
7620 * @class Roo.bootstrap.TextArea
7621 * @extends Roo.bootstrap.Input
7622 * Bootstrap TextArea class
7623 * @cfg {Number} cols Specifies the visible width of a text area
7624 * @cfg {Number} rows Specifies the visible number of lines in a text area
7625 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7626 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7627 * @cfg {string} html text
7630 * Create a new TextArea
7631 * @param {Object} config The config object
7634 Roo.bootstrap.TextArea = function(config){
7635 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7639 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
7649 getAutoCreate : function(){
7651 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7662 value : this.value || '',
7663 html: this.html || '',
7664 cls : 'form-control',
7665 placeholder : this.placeholder || ''
7669 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7670 input.maxLength = this.maxLength;
7674 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7678 input.cols = this.cols;
7681 if (this.readOnly) {
7682 input.readonly = true;
7686 input.name = this.name;
7690 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7694 ['xs','sm','md','lg'].map(function(size){
7695 if (settings[size]) {
7696 cfg.cls += ' col-' + size + '-' + settings[size];
7700 var inputblock = input;
7702 if (this.before || this.after) {
7705 cls : 'input-group',
7709 inputblock.cn.push({
7711 cls : 'input-group-addon',
7715 inputblock.cn.push(input);
7717 inputblock.cn.push({
7719 cls : 'input-group-addon',
7726 if (align ==='left' && this.fieldLabel.length) {
7727 Roo.log("left and has label");
7733 cls : 'control-label col-sm-' + this.labelWidth,
7734 html : this.fieldLabel
7738 cls : "col-sm-" + (12 - this.labelWidth),
7745 } else if ( this.fieldLabel.length) {
7751 //cls : 'input-group-addon',
7752 html : this.fieldLabel
7762 Roo.log(" no label && no align");
7772 if (this.disabled) {
7773 input.disabled=true;
7780 * return the real textarea element.
7782 inputEl: function ()
7784 return this.el.select('textarea.form-control',true).first();
7792 * trigger field - base class for combo..
7797 * @class Roo.bootstrap.TriggerField
7798 * @extends Roo.bootstrap.Input
7799 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7800 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7801 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7802 * for which you can provide a custom implementation. For example:
7804 var trigger = new Roo.bootstrap.TriggerField();
7805 trigger.onTriggerClick = myTriggerFn;
7806 trigger.applyTo('my-field');
7809 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7810 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7811 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
7812 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7814 * Create a new TriggerField.
7815 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7816 * to the base TextField)
7818 Roo.bootstrap.TriggerField = function(config){
7819 this.mimicing = false;
7820 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7823 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
7825 * @cfg {String} triggerClass A CSS class to apply to the trigger
7828 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7832 /** @cfg {Boolean} grow @hide */
7833 /** @cfg {Number} growMin @hide */
7834 /** @cfg {Number} growMax @hide */
7840 autoSize: Roo.emptyFn,
7847 actionMode : 'wrap',
7851 getAutoCreate : function(){
7853 var align = this.labelAlign || this.parentLabelAlign();
7858 cls: 'form-group' //input-group
7865 type : this.inputType,
7866 cls : 'form-control',
7867 autocomplete: 'off',
7868 placeholder : this.placeholder || ''
7872 input.name = this.name;
7875 input.cls += ' input-' + this.size;
7878 if (this.disabled) {
7879 input.disabled=true;
7882 var inputblock = input;
7884 if (this.before || this.after) {
7887 cls : 'input-group',
7891 inputblock.cn.push({
7893 cls : 'input-group-addon',
7897 inputblock.cn.push(input);
7899 inputblock.cn.push({
7901 cls : 'input-group-addon',
7914 cls: 'form-hidden-field'
7922 Roo.log('multiple');
7930 cls: 'form-hidden-field'
7934 cls: 'select2-choices',
7938 cls: 'select2-search-field',
7951 cls: 'select2-container input-group',
7956 // cls: 'typeahead typeahead-long dropdown-menu',
7957 // style: 'display:none'
7962 if(!this.multiple && this.showToggleBtn){
7965 cls : 'input-group-addon btn dropdown-toggle',
7973 cls: 'combobox-clear',
7987 combobox.cls += ' select2-container-multi';
7990 if (align ==='left' && this.fieldLabel.length) {
7992 Roo.log("left and has label");
7998 cls : 'control-label col-sm-' + this.labelWidth,
7999 html : this.fieldLabel
8003 cls : "col-sm-" + (12 - this.labelWidth),
8010 } else if ( this.fieldLabel.length) {
8016 //cls : 'input-group-addon',
8017 html : this.fieldLabel
8027 Roo.log(" no label && no align");
8034 ['xs','sm','md','lg'].map(function(size){
8035 if (settings[size]) {
8036 cfg.cls += ' col-' + size + '-' + settings[size];
8047 onResize : function(w, h){
8048 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8049 // if(typeof w == 'number'){
8050 // var x = w - this.trigger.getWidth();
8051 // this.inputEl().setWidth(this.adjustWidth('input', x));
8052 // this.trigger.setStyle('left', x+'px');
8057 adjustSize : Roo.BoxComponent.prototype.adjustSize,
8060 getResizeEl : function(){
8061 return this.inputEl();
8065 getPositionEl : function(){
8066 return this.inputEl();
8070 alignErrorIcon : function(){
8071 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8075 initEvents : function(){
8079 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8080 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8081 if(!this.multiple && this.showToggleBtn){
8082 this.trigger = this.el.select('span.dropdown-toggle',true).first();
8083 if(this.hideTrigger){
8084 this.trigger.setDisplayed(false);
8086 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8090 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8093 //this.trigger.addClassOnOver('x-form-trigger-over');
8094 //this.trigger.addClassOnClick('x-form-trigger-click');
8097 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8101 createList : function()
8103 this.list = Roo.get(document.body).createChild({
8105 cls: 'typeahead typeahead-long dropdown-menu',
8106 style: 'display:none'
8109 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8114 initTrigger : function(){
8119 onDestroy : function(){
8121 this.trigger.removeAllListeners();
8122 // this.trigger.remove();
8125 // this.wrap.remove();
8127 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8131 onFocus : function(){
8132 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8135 this.wrap.addClass('x-trigger-wrap-focus');
8136 this.mimicing = true;
8137 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8138 if(this.monitorTab){
8139 this.el.on("keydown", this.checkTab, this);
8146 checkTab : function(e){
8147 if(e.getKey() == e.TAB){
8153 onBlur : function(){
8158 mimicBlur : function(e, t){
8160 if(!this.wrap.contains(t) && this.validateBlur()){
8167 triggerBlur : function(){
8168 this.mimicing = false;
8169 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8170 if(this.monitorTab){
8171 this.el.un("keydown", this.checkTab, this);
8173 //this.wrap.removeClass('x-trigger-wrap-focus');
8174 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8178 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8179 validateBlur : function(e, t){
8184 onDisable : function(){
8185 this.inputEl().dom.disabled = true;
8186 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8188 // this.wrap.addClass('x-item-disabled');
8193 onEnable : function(){
8194 this.inputEl().dom.disabled = false;
8195 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8197 // this.el.removeClass('x-item-disabled');
8202 onShow : function(){
8203 var ae = this.getActionEl();
8206 ae.dom.style.display = '';
8207 ae.dom.style.visibility = 'visible';
8213 onHide : function(){
8214 var ae = this.getActionEl();
8215 ae.dom.style.display = 'none';
8219 * The function that should handle the trigger's click event. This method does nothing by default until overridden
8220 * by an implementing function.
8222 * @param {EventObject} e
8224 onTriggerClick : Roo.emptyFn
8228 * Ext JS Library 1.1.1
8229 * Copyright(c) 2006-2007, Ext JS, LLC.
8231 * Originally Released Under LGPL - original licence link has changed is not relivant.
8234 * <script type="text/javascript">
8239 * @class Roo.data.SortTypes
8241 * Defines the default sorting (casting?) comparison functions used when sorting data.
8243 Roo.data.SortTypes = {
8245 * Default sort that does nothing
8246 * @param {Mixed} s The value being converted
8247 * @return {Mixed} The comparison value
8254 * The regular expression used to strip tags
8258 stripTagsRE : /<\/?[^>]+>/gi,
8261 * Strips all HTML tags to sort on text only
8262 * @param {Mixed} s The value being converted
8263 * @return {String} The comparison value
8265 asText : function(s){
8266 return String(s).replace(this.stripTagsRE, "");
8270 * Strips all HTML tags to sort on text only - Case insensitive
8271 * @param {Mixed} s The value being converted
8272 * @return {String} The comparison value
8274 asUCText : function(s){
8275 return String(s).toUpperCase().replace(this.stripTagsRE, "");
8279 * Case insensitive string
8280 * @param {Mixed} s The value being converted
8281 * @return {String} The comparison value
8283 asUCString : function(s) {
8284 return String(s).toUpperCase();
8289 * @param {Mixed} s The value being converted
8290 * @return {Number} The comparison value
8292 asDate : function(s) {
8296 if(s instanceof Date){
8299 return Date.parse(String(s));
8304 * @param {Mixed} s The value being converted
8305 * @return {Float} The comparison value
8307 asFloat : function(s) {
8308 var val = parseFloat(String(s).replace(/,/g, ""));
8309 if(isNaN(val)) val = 0;
8315 * @param {Mixed} s The value being converted
8316 * @return {Number} The comparison value
8318 asInt : function(s) {
8319 var val = parseInt(String(s).replace(/,/g, ""));
8320 if(isNaN(val)) val = 0;
8325 * Ext JS Library 1.1.1
8326 * Copyright(c) 2006-2007, Ext JS, LLC.
8328 * Originally Released Under LGPL - original licence link has changed is not relivant.
8331 * <script type="text/javascript">
8335 * @class Roo.data.Record
8336 * Instances of this class encapsulate both record <em>definition</em> information, and record
8337 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8338 * to access Records cached in an {@link Roo.data.Store} object.<br>
8340 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8341 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8344 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8346 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8347 * {@link #create}. The parameters are the same.
8348 * @param {Array} data An associative Array of data values keyed by the field name.
8349 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8350 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8351 * not specified an integer id is generated.
8353 Roo.data.Record = function(data, id){
8354 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8359 * Generate a constructor for a specific record layout.
8360 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8361 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8362 * Each field definition object may contain the following properties: <ul>
8363 * <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,
8364 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8365 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8366 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8367 * is being used, then this is a string containing the javascript expression to reference the data relative to
8368 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8369 * to the data item relative to the record element. If the mapping expression is the same as the field name,
8370 * this may be omitted.</p></li>
8371 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8372 * <ul><li>auto (Default, implies no conversion)</li>
8377 * <li>date</li></ul></p></li>
8378 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8379 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8380 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8381 * by the Reader into an object that will be stored in the Record. It is passed the
8382 * following parameters:<ul>
8383 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8385 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8387 * <br>usage:<br><pre><code>
8388 var TopicRecord = Roo.data.Record.create(
8389 {name: 'title', mapping: 'topic_title'},
8390 {name: 'author', mapping: 'username'},
8391 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8392 {name: 'lastPost', mapping: 'post_time', type: 'date'},
8393 {name: 'lastPoster', mapping: 'user2'},
8394 {name: 'excerpt', mapping: 'post_text'}
8397 var myNewRecord = new TopicRecord({
8398 title: 'Do my job please',
8401 lastPost: new Date(),
8402 lastPoster: 'Animal',
8403 excerpt: 'No way dude!'
8405 myStore.add(myNewRecord);
8410 Roo.data.Record.create = function(o){
8412 f.superclass.constructor.apply(this, arguments);
8414 Roo.extend(f, Roo.data.Record);
8415 var p = f.prototype;
8416 p.fields = new Roo.util.MixedCollection(false, function(field){
8419 for(var i = 0, len = o.length; i < len; i++){
8420 p.fields.add(new Roo.data.Field(o[i]));
8422 f.getField = function(name){
8423 return p.fields.get(name);
8428 Roo.data.Record.AUTO_ID = 1000;
8429 Roo.data.Record.EDIT = 'edit';
8430 Roo.data.Record.REJECT = 'reject';
8431 Roo.data.Record.COMMIT = 'commit';
8433 Roo.data.Record.prototype = {
8435 * Readonly flag - true if this record has been modified.
8444 join : function(store){
8449 * Set the named field to the specified value.
8450 * @param {String} name The name of the field to set.
8451 * @param {Object} value The value to set the field to.
8453 set : function(name, value){
8454 if(this.data[name] == value){
8461 if(typeof this.modified[name] == 'undefined'){
8462 this.modified[name] = this.data[name];
8464 this.data[name] = value;
8465 if(!this.editing && this.store){
8466 this.store.afterEdit(this);
8471 * Get the value of the named field.
8472 * @param {String} name The name of the field to get the value of.
8473 * @return {Object} The value of the field.
8475 get : function(name){
8476 return this.data[name];
8480 beginEdit : function(){
8481 this.editing = true;
8486 cancelEdit : function(){
8487 this.editing = false;
8488 delete this.modified;
8492 endEdit : function(){
8493 this.editing = false;
8494 if(this.dirty && this.store){
8495 this.store.afterEdit(this);
8500 * Usually called by the {@link Roo.data.Store} which owns the Record.
8501 * Rejects all changes made to the Record since either creation, or the last commit operation.
8502 * Modified fields are reverted to their original values.
8504 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8505 * of reject operations.
8507 reject : function(){
8508 var m = this.modified;
8510 if(typeof m[n] != "function"){
8511 this.data[n] = m[n];
8515 delete this.modified;
8516 this.editing = false;
8518 this.store.afterReject(this);
8523 * Usually called by the {@link Roo.data.Store} which owns the Record.
8524 * Commits all changes made to the Record since either creation, or the last commit operation.
8526 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8527 * of commit operations.
8529 commit : function(){
8531 delete this.modified;
8532 this.editing = false;
8534 this.store.afterCommit(this);
8539 hasError : function(){
8540 return this.error != null;
8544 clearError : function(){
8549 * Creates a copy of this record.
8550 * @param {String} id (optional) A new record id if you don't want to use this record's id
8553 copy : function(newId) {
8554 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8558 * Ext JS Library 1.1.1
8559 * Copyright(c) 2006-2007, Ext JS, LLC.
8561 * Originally Released Under LGPL - original licence link has changed is not relivant.
8564 * <script type="text/javascript">
8570 * @class Roo.data.Store
8571 * @extends Roo.util.Observable
8572 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8573 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8575 * 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
8576 * has no knowledge of the format of the data returned by the Proxy.<br>
8578 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8579 * instances from the data object. These records are cached and made available through accessor functions.
8581 * Creates a new Store.
8582 * @param {Object} config A config object containing the objects needed for the Store to access data,
8583 * and read the data into Records.
8585 Roo.data.Store = function(config){
8586 this.data = new Roo.util.MixedCollection(false);
8587 this.data.getKey = function(o){
8590 this.baseParams = {};
8597 "multisort" : "_multisort"
8600 if(config && config.data){
8601 this.inlineData = config.data;
8605 Roo.apply(this, config);
8607 if(this.reader){ // reader passed
8608 this.reader = Roo.factory(this.reader, Roo.data);
8609 this.reader.xmodule = this.xmodule || false;
8610 if(!this.recordType){
8611 this.recordType = this.reader.recordType;
8613 if(this.reader.onMetaChange){
8614 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8618 if(this.recordType){
8619 this.fields = this.recordType.prototype.fields;
8625 * @event datachanged
8626 * Fires when the data cache has changed, and a widget which is using this Store
8627 * as a Record cache should refresh its view.
8628 * @param {Store} this
8633 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8634 * @param {Store} this
8635 * @param {Object} meta The JSON metadata
8640 * Fires when Records have been added to the Store
8641 * @param {Store} this
8642 * @param {Roo.data.Record[]} records The array of Records added
8643 * @param {Number} index The index at which the record(s) were added
8648 * Fires when a Record has been removed from the Store
8649 * @param {Store} this
8650 * @param {Roo.data.Record} record The Record that was removed
8651 * @param {Number} index The index at which the record was removed
8656 * Fires when a Record has been updated
8657 * @param {Store} this
8658 * @param {Roo.data.Record} record The Record that was updated
8659 * @param {String} operation The update operation being performed. Value may be one of:
8661 Roo.data.Record.EDIT
8662 Roo.data.Record.REJECT
8663 Roo.data.Record.COMMIT
8669 * Fires when the data cache has been cleared.
8670 * @param {Store} this
8675 * Fires before a request is made for a new data object. If the beforeload handler returns false
8676 * the load action will be canceled.
8677 * @param {Store} this
8678 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8682 * @event beforeloadadd
8683 * Fires after a new set of Records has been loaded.
8684 * @param {Store} this
8685 * @param {Roo.data.Record[]} records The Records that were loaded
8686 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8688 beforeloadadd : true,
8691 * Fires after a new set of Records has been loaded, before they are added to the store.
8692 * @param {Store} this
8693 * @param {Roo.data.Record[]} records The Records that were loaded
8694 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8695 * @params {Object} return from reader
8699 * @event loadexception
8700 * Fires if an exception occurs in the Proxy during loading.
8701 * Called with the signature of the Proxy's "loadexception" event.
8702 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8705 * @param {Object} return from JsonData.reader() - success, totalRecords, records
8706 * @param {Object} load options
8707 * @param {Object} jsonData from your request (normally this contains the Exception)
8709 loadexception : true
8713 this.proxy = Roo.factory(this.proxy, Roo.data);
8714 this.proxy.xmodule = this.xmodule || false;
8715 this.relayEvents(this.proxy, ["loadexception"]);
8717 this.sortToggle = {};
8718 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8720 Roo.data.Store.superclass.constructor.call(this);
8722 if(this.inlineData){
8723 this.loadData(this.inlineData);
8724 delete this.inlineData;
8728 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8730 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
8731 * without a remote query - used by combo/forms at present.
8735 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8738 * @cfg {Array} data Inline data to be loaded when the store is initialized.
8741 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8742 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8745 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8746 * on any HTTP request
8749 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8752 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8756 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8757 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8762 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8763 * loaded or when a record is removed. (defaults to false).
8765 pruneModifiedRecords : false,
8771 * Add Records to the Store and fires the add event.
8772 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8774 add : function(records){
8775 records = [].concat(records);
8776 for(var i = 0, len = records.length; i < len; i++){
8777 records[i].join(this);
8779 var index = this.data.length;
8780 this.data.addAll(records);
8781 this.fireEvent("add", this, records, index);
8785 * Remove a Record from the Store and fires the remove event.
8786 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8788 remove : function(record){
8789 var index = this.data.indexOf(record);
8790 this.data.removeAt(index);
8791 if(this.pruneModifiedRecords){
8792 this.modified.remove(record);
8794 this.fireEvent("remove", this, record, index);
8798 * Remove all Records from the Store and fires the clear event.
8800 removeAll : function(){
8802 if(this.pruneModifiedRecords){
8805 this.fireEvent("clear", this);
8809 * Inserts Records to the Store at the given index and fires the add event.
8810 * @param {Number} index The start index at which to insert the passed Records.
8811 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8813 insert : function(index, records){
8814 records = [].concat(records);
8815 for(var i = 0, len = records.length; i < len; i++){
8816 this.data.insert(index, records[i]);
8817 records[i].join(this);
8819 this.fireEvent("add", this, records, index);
8823 * Get the index within the cache of the passed Record.
8824 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8825 * @return {Number} The index of the passed Record. Returns -1 if not found.
8827 indexOf : function(record){
8828 return this.data.indexOf(record);
8832 * Get the index within the cache of the Record with the passed id.
8833 * @param {String} id The id of the Record to find.
8834 * @return {Number} The index of the Record. Returns -1 if not found.
8836 indexOfId : function(id){
8837 return this.data.indexOfKey(id);
8841 * Get the Record with the specified id.
8842 * @param {String} id The id of the Record to find.
8843 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8845 getById : function(id){
8846 return this.data.key(id);
8850 * Get the Record at the specified index.
8851 * @param {Number} index The index of the Record to find.
8852 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8854 getAt : function(index){
8855 return this.data.itemAt(index);
8859 * Returns a range of Records between specified indices.
8860 * @param {Number} startIndex (optional) The starting index (defaults to 0)
8861 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8862 * @return {Roo.data.Record[]} An array of Records
8864 getRange : function(start, end){
8865 return this.data.getRange(start, end);
8869 storeOptions : function(o){
8870 o = Roo.apply({}, o);
8873 this.lastOptions = o;
8877 * Loads the Record cache from the configured Proxy using the configured Reader.
8879 * If using remote paging, then the first load call must specify the <em>start</em>
8880 * and <em>limit</em> properties in the options.params property to establish the initial
8881 * position within the dataset, and the number of Records to cache on each read from the Proxy.
8883 * <strong>It is important to note that for remote data sources, loading is asynchronous,
8884 * and this call will return before the new data has been loaded. Perform any post-processing
8885 * in a callback function, or in a "load" event handler.</strong>
8887 * @param {Object} options An object containing properties which control loading options:<ul>
8888 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8889 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8890 * passed the following arguments:<ul>
8891 * <li>r : Roo.data.Record[]</li>
8892 * <li>options: Options object from the load call</li>
8893 * <li>success: Boolean success indicator</li></ul></li>
8894 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8895 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8898 load : function(options){
8899 options = options || {};
8900 if(this.fireEvent("beforeload", this, options) !== false){
8901 this.storeOptions(options);
8902 var p = Roo.apply(options.params || {}, this.baseParams);
8903 // if meta was not loaded from remote source.. try requesting it.
8904 if (!this.reader.metaFromRemote) {
8907 if(this.sortInfo && this.remoteSort){
8908 var pn = this.paramNames;
8909 p[pn["sort"]] = this.sortInfo.field;
8910 p[pn["dir"]] = this.sortInfo.direction;
8912 if (this.multiSort) {
8913 var pn = this.paramNames;
8914 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8917 this.proxy.load(p, this.reader, this.loadRecords, this, options);
8922 * Reloads the Record cache from the configured Proxy using the configured Reader and
8923 * the options from the last load operation performed.
8924 * @param {Object} options (optional) An object containing properties which may override the options
8925 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8926 * the most recently used options are reused).
8928 reload : function(options){
8929 this.load(Roo.applyIf(options||{}, this.lastOptions));
8933 // Called as a callback by the Reader during a load operation.
8934 loadRecords : function(o, options, success){
8935 if(!o || success === false){
8936 if(success !== false){
8937 this.fireEvent("load", this, [], options, o);
8939 if(options.callback){
8940 options.callback.call(options.scope || this, [], options, false);
8944 // if data returned failure - throw an exception.
8945 if (o.success === false) {
8946 // show a message if no listener is registered.
8947 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8948 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8950 // loadmask wil be hooked into this..
8951 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8954 var r = o.records, t = o.totalRecords || r.length;
8956 this.fireEvent("beforeloadadd", this, r, options, o);
8958 if(!options || options.add !== true){
8959 if(this.pruneModifiedRecords){
8962 for(var i = 0, len = r.length; i < len; i++){
8966 this.data = this.snapshot;
8967 delete this.snapshot;
8970 this.data.addAll(r);
8971 this.totalLength = t;
8973 this.fireEvent("datachanged", this);
8975 this.totalLength = Math.max(t, this.data.length+r.length);
8978 this.fireEvent("load", this, r, options, o);
8979 if(options.callback){
8980 options.callback.call(options.scope || this, r, options, true);
8986 * Loads data from a passed data block. A Reader which understands the format of the data
8987 * must have been configured in the constructor.
8988 * @param {Object} data The data block from which to read the Records. The format of the data expected
8989 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8990 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8992 loadData : function(o, append){
8993 var r = this.reader.readRecords(o);
8994 this.loadRecords(r, {add: append}, true);
8998 * Gets the number of cached records.
9000 * <em>If using paging, this may not be the total size of the dataset. If the data object
9001 * used by the Reader contains the dataset size, then the getTotalCount() function returns
9002 * the data set size</em>
9004 getCount : function(){
9005 return this.data.length || 0;
9009 * Gets the total number of records in the dataset as returned by the server.
9011 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9012 * the dataset size</em>
9014 getTotalCount : function(){
9015 return this.totalLength || 0;
9019 * Returns the sort state of the Store as an object with two properties:
9021 field {String} The name of the field by which the Records are sorted
9022 direction {String} The sort order, "ASC" or "DESC"
9025 getSortState : function(){
9026 return this.sortInfo;
9030 applySort : function(){
9031 if(this.sortInfo && !this.remoteSort){
9032 var s = this.sortInfo, f = s.field;
9033 var st = this.fields.get(f).sortType;
9034 var fn = function(r1, r2){
9035 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9036 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9038 this.data.sort(s.direction, fn);
9039 if(this.snapshot && this.snapshot != this.data){
9040 this.snapshot.sort(s.direction, fn);
9046 * Sets the default sort column and order to be used by the next load operation.
9047 * @param {String} fieldName The name of the field to sort by.
9048 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9050 setDefaultSort : function(field, dir){
9051 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9056 * If remote sorting is used, the sort is performed on the server, and the cache is
9057 * reloaded. If local sorting is used, the cache is sorted internally.
9058 * @param {String} fieldName The name of the field to sort by.
9059 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9061 sort : function(fieldName, dir){
9062 var f = this.fields.get(fieldName);
9064 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9066 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9067 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9072 this.sortToggle[f.name] = dir;
9073 this.sortInfo = {field: f.name, direction: dir};
9074 if(!this.remoteSort){
9076 this.fireEvent("datachanged", this);
9078 this.load(this.lastOptions);
9083 * Calls the specified function for each of the Records in the cache.
9084 * @param {Function} fn The function to call. The Record is passed as the first parameter.
9085 * Returning <em>false</em> aborts and exits the iteration.
9086 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9088 each : function(fn, scope){
9089 this.data.each(fn, scope);
9093 * Gets all records modified since the last commit. Modified records are persisted across load operations
9094 * (e.g., during paging).
9095 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9097 getModifiedRecords : function(){
9098 return this.modified;
9102 createFilterFn : function(property, value, anyMatch){
9103 if(!value.exec){ // not a regex
9104 value = String(value);
9105 if(value.length == 0){
9108 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9111 return value.test(r.data[property]);
9116 * Sums the value of <i>property</i> for each record between start and end and returns the result.
9117 * @param {String} property A field on your records
9118 * @param {Number} start The record index to start at (defaults to 0)
9119 * @param {Number} end The last record index to include (defaults to length - 1)
9120 * @return {Number} The sum
9122 sum : function(property, start, end){
9123 var rs = this.data.items, v = 0;
9125 end = (end || end === 0) ? end : rs.length-1;
9127 for(var i = start; i <= end; i++){
9128 v += (rs[i].data[property] || 0);
9134 * Filter the records by a specified property.
9135 * @param {String} field A field on your records
9136 * @param {String/RegExp} value Either a string that the field
9137 * should start with or a RegExp to test against the field
9138 * @param {Boolean} anyMatch True to match any part not just the beginning
9140 filter : function(property, value, anyMatch){
9141 var fn = this.createFilterFn(property, value, anyMatch);
9142 return fn ? this.filterBy(fn) : this.clearFilter();
9146 * Filter by a function. The specified function will be called with each
9147 * record in this data source. If the function returns true the record is included,
9148 * otherwise it is filtered.
9149 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9150 * @param {Object} scope (optional) The scope of the function (defaults to this)
9152 filterBy : function(fn, scope){
9153 this.snapshot = this.snapshot || this.data;
9154 this.data = this.queryBy(fn, scope||this);
9155 this.fireEvent("datachanged", this);
9159 * Query the records by a specified property.
9160 * @param {String} field A field on your records
9161 * @param {String/RegExp} value Either a string that the field
9162 * should start with or a RegExp to test against the field
9163 * @param {Boolean} anyMatch True to match any part not just the beginning
9164 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9166 query : function(property, value, anyMatch){
9167 var fn = this.createFilterFn(property, value, anyMatch);
9168 return fn ? this.queryBy(fn) : this.data.clone();
9172 * Query by a function. The specified function will be called with each
9173 * record in this data source. If the function returns true the record is included
9175 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9176 * @param {Object} scope (optional) The scope of the function (defaults to this)
9177 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9179 queryBy : function(fn, scope){
9180 var data = this.snapshot || this.data;
9181 return data.filterBy(fn, scope||this);
9185 * Collects unique values for a particular dataIndex from this store.
9186 * @param {String} dataIndex The property to collect
9187 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9188 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9189 * @return {Array} An array of the unique values
9191 collect : function(dataIndex, allowNull, bypassFilter){
9192 var d = (bypassFilter === true && this.snapshot) ?
9193 this.snapshot.items : this.data.items;
9194 var v, sv, r = [], l = {};
9195 for(var i = 0, len = d.length; i < len; i++){
9196 v = d[i].data[dataIndex];
9198 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9207 * Revert to a view of the Record cache with no filtering applied.
9208 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9210 clearFilter : function(suppressEvent){
9211 if(this.snapshot && this.snapshot != this.data){
9212 this.data = this.snapshot;
9213 delete this.snapshot;
9214 if(suppressEvent !== true){
9215 this.fireEvent("datachanged", this);
9221 afterEdit : function(record){
9222 if(this.modified.indexOf(record) == -1){
9223 this.modified.push(record);
9225 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9229 afterReject : function(record){
9230 this.modified.remove(record);
9231 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9235 afterCommit : function(record){
9236 this.modified.remove(record);
9237 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9241 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9242 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9244 commitChanges : function(){
9245 var m = this.modified.slice(0);
9247 for(var i = 0, len = m.length; i < len; i++){
9253 * Cancel outstanding changes on all changed records.
9255 rejectChanges : function(){
9256 var m = this.modified.slice(0);
9258 for(var i = 0, len = m.length; i < len; i++){
9263 onMetaChange : function(meta, rtype, o){
9264 this.recordType = rtype;
9265 this.fields = rtype.prototype.fields;
9266 delete this.snapshot;
9267 this.sortInfo = meta.sortInfo || this.sortInfo;
9269 this.fireEvent('metachange', this, this.reader.meta);
9272 moveIndex : function(data, type)
9274 var index = this.indexOf(data);
9276 var newIndex = index + type;
9280 this.insert(newIndex, data);
9285 * Ext JS Library 1.1.1
9286 * Copyright(c) 2006-2007, Ext JS, LLC.
9288 * Originally Released Under LGPL - original licence link has changed is not relivant.
9291 * <script type="text/javascript">
9295 * @class Roo.data.SimpleStore
9296 * @extends Roo.data.Store
9297 * Small helper class to make creating Stores from Array data easier.
9298 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9299 * @cfg {Array} fields An array of field definition objects, or field name strings.
9300 * @cfg {Array} data The multi-dimensional array of data
9302 * @param {Object} config
9304 Roo.data.SimpleStore = function(config){
9305 Roo.data.SimpleStore.superclass.constructor.call(this, {
9307 reader: new Roo.data.ArrayReader({
9310 Roo.data.Record.create(config.fields)
9312 proxy : new Roo.data.MemoryProxy(config.data)
9316 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9318 * Ext JS Library 1.1.1
9319 * Copyright(c) 2006-2007, Ext JS, LLC.
9321 * Originally Released Under LGPL - original licence link has changed is not relivant.
9324 * <script type="text/javascript">
9329 * @extends Roo.data.Store
9330 * @class Roo.data.JsonStore
9331 * Small helper class to make creating Stores for JSON data easier. <br/>
9333 var store = new Roo.data.JsonStore({
9334 url: 'get-images.php',
9336 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9339 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9340 * JsonReader and HttpProxy (unless inline data is provided).</b>
9341 * @cfg {Array} fields An array of field definition objects, or field name strings.
9343 * @param {Object} config
9345 Roo.data.JsonStore = function(c){
9346 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9347 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9348 reader: new Roo.data.JsonReader(c, c.fields)
9351 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9353 * Ext JS Library 1.1.1
9354 * Copyright(c) 2006-2007, Ext JS, LLC.
9356 * Originally Released Under LGPL - original licence link has changed is not relivant.
9359 * <script type="text/javascript">
9363 Roo.data.Field = function(config){
9364 if(typeof config == "string"){
9365 config = {name: config};
9367 Roo.apply(this, config);
9373 var st = Roo.data.SortTypes;
9374 // named sortTypes are supported, here we look them up
9375 if(typeof this.sortType == "string"){
9376 this.sortType = st[this.sortType];
9379 // set default sortType for strings and dates
9383 this.sortType = st.asUCString;
9386 this.sortType = st.asDate;
9389 this.sortType = st.none;
9394 var stripRe = /[\$,%]/g;
9396 // prebuilt conversion function for this field, instead of
9397 // switching every time we're reading a value
9399 var cv, dateFormat = this.dateFormat;
9404 cv = function(v){ return v; };
9407 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9411 return v !== undefined && v !== null && v !== '' ?
9412 parseInt(String(v).replace(stripRe, ""), 10) : '';
9417 return v !== undefined && v !== null && v !== '' ?
9418 parseFloat(String(v).replace(stripRe, ""), 10) : '';
9423 cv = function(v){ return v === true || v === "true" || v == 1; };
9430 if(v instanceof Date){
9434 if(dateFormat == "timestamp"){
9435 return new Date(v*1000);
9437 return Date.parseDate(v, dateFormat);
9439 var parsed = Date.parse(v);
9440 return parsed ? new Date(parsed) : null;
9449 Roo.data.Field.prototype = {
9457 * Ext JS Library 1.1.1
9458 * Copyright(c) 2006-2007, Ext JS, LLC.
9460 * Originally Released Under LGPL - original licence link has changed is not relivant.
9463 * <script type="text/javascript">
9466 // Base class for reading structured data from a data source. This class is intended to be
9467 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9470 * @class Roo.data.DataReader
9471 * Base class for reading structured data from a data source. This class is intended to be
9472 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9475 Roo.data.DataReader = function(meta, recordType){
9479 this.recordType = recordType instanceof Array ?
9480 Roo.data.Record.create(recordType) : recordType;
9483 Roo.data.DataReader.prototype = {
9485 * Create an empty record
9486 * @param {Object} data (optional) - overlay some values
9487 * @return {Roo.data.Record} record created.
9489 newRow : function(d) {
9491 this.recordType.prototype.fields.each(function(c) {
9493 case 'int' : da[c.name] = 0; break;
9494 case 'date' : da[c.name] = new Date(); break;
9495 case 'float' : da[c.name] = 0.0; break;
9496 case 'boolean' : da[c.name] = false; break;
9497 default : da[c.name] = ""; break;
9501 return new this.recordType(Roo.apply(da, d));
9506 * Ext JS Library 1.1.1
9507 * Copyright(c) 2006-2007, Ext JS, LLC.
9509 * Originally Released Under LGPL - original licence link has changed is not relivant.
9512 * <script type="text/javascript">
9516 * @class Roo.data.DataProxy
9517 * @extends Roo.data.Observable
9518 * This class is an abstract base class for implementations which provide retrieval of
9519 * unformatted data objects.<br>
9521 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9522 * (of the appropriate type which knows how to parse the data object) to provide a block of
9523 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9525 * Custom implementations must implement the load method as described in
9526 * {@link Roo.data.HttpProxy#load}.
9528 Roo.data.DataProxy = function(){
9532 * Fires before a network request is made to retrieve a data object.
9533 * @param {Object} This DataProxy object.
9534 * @param {Object} params The params parameter to the load function.
9539 * Fires before the load method's callback is called.
9540 * @param {Object} This DataProxy object.
9541 * @param {Object} o The data object.
9542 * @param {Object} arg The callback argument object passed to the load function.
9546 * @event loadexception
9547 * Fires if an Exception occurs during data retrieval.
9548 * @param {Object} This DataProxy object.
9549 * @param {Object} o The data object.
9550 * @param {Object} arg The callback argument object passed to the load function.
9551 * @param {Object} e The Exception.
9553 loadexception : true
9555 Roo.data.DataProxy.superclass.constructor.call(this);
9558 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9561 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9565 * Ext JS Library 1.1.1
9566 * Copyright(c) 2006-2007, Ext JS, LLC.
9568 * Originally Released Under LGPL - original licence link has changed is not relivant.
9571 * <script type="text/javascript">
9574 * @class Roo.data.MemoryProxy
9575 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9576 * to the Reader when its load method is called.
9578 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9580 Roo.data.MemoryProxy = function(data){
9584 Roo.data.MemoryProxy.superclass.constructor.call(this);
9588 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9590 * Load data from the requested source (in this case an in-memory
9591 * data object passed to the constructor), read the data object into
9592 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9593 * process that block using the passed callback.
9594 * @param {Object} params This parameter is not used by the MemoryProxy class.
9595 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9596 * object into a block of Roo.data.Records.
9597 * @param {Function} callback The function into which to pass the block of Roo.data.records.
9598 * The function must be passed <ul>
9599 * <li>The Record block object</li>
9600 * <li>The "arg" argument from the load function</li>
9601 * <li>A boolean success indicator</li>
9603 * @param {Object} scope The scope in which to call the callback
9604 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9606 load : function(params, reader, callback, scope, arg){
9607 params = params || {};
9610 result = reader.readRecords(this.data);
9612 this.fireEvent("loadexception", this, arg, null, e);
9613 callback.call(scope, null, arg, false);
9616 callback.call(scope, result, arg, true);
9620 update : function(params, records){
9625 * Ext JS Library 1.1.1
9626 * Copyright(c) 2006-2007, Ext JS, LLC.
9628 * Originally Released Under LGPL - original licence link has changed is not relivant.
9631 * <script type="text/javascript">
9634 * @class Roo.data.HttpProxy
9635 * @extends Roo.data.DataProxy
9636 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9637 * configured to reference a certain URL.<br><br>
9639 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9640 * from which the running page was served.<br><br>
9642 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9644 * Be aware that to enable the browser to parse an XML document, the server must set
9645 * the Content-Type header in the HTTP response to "text/xml".
9647 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9648 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
9649 * will be used to make the request.
9651 Roo.data.HttpProxy = function(conn){
9652 Roo.data.HttpProxy.superclass.constructor.call(this);
9653 // is conn a conn config or a real conn?
9655 this.useAjax = !conn || !conn.events;
9659 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9660 // thse are take from connection...
9663 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9666 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9667 * extra parameters to each request made by this object. (defaults to undefined)
9670 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9671 * to each request made by this object. (defaults to undefined)
9674 * @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)
9677 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9680 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9686 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9690 * Return the {@link Roo.data.Connection} object being used by this Proxy.
9691 * @return {Connection} The Connection object. This object may be used to subscribe to events on
9692 * a finer-grained basis than the DataProxy events.
9694 getConnection : function(){
9695 return this.useAjax ? Roo.Ajax : this.conn;
9699 * Load data from the configured {@link Roo.data.Connection}, read the data object into
9700 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9701 * process that block using the passed callback.
9702 * @param {Object} params An object containing properties which are to be used as HTTP parameters
9703 * for the request to the remote server.
9704 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9705 * object into a block of Roo.data.Records.
9706 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9707 * The function must be passed <ul>
9708 * <li>The Record block object</li>
9709 * <li>The "arg" argument from the load function</li>
9710 * <li>A boolean success indicator</li>
9712 * @param {Object} scope The scope in which to call the callback
9713 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9715 load : function(params, reader, callback, scope, arg){
9716 if(this.fireEvent("beforeload", this, params) !== false){
9718 params : params || {},
9720 callback : callback,
9725 callback : this.loadResponse,
9729 Roo.applyIf(o, this.conn);
9730 if(this.activeRequest){
9731 Roo.Ajax.abort(this.activeRequest);
9733 this.activeRequest = Roo.Ajax.request(o);
9735 this.conn.request(o);
9738 callback.call(scope||this, null, arg, false);
9743 loadResponse : function(o, success, response){
9744 delete this.activeRequest;
9746 this.fireEvent("loadexception", this, o, response);
9747 o.request.callback.call(o.request.scope, null, o.request.arg, false);
9752 result = o.reader.read(response);
9754 this.fireEvent("loadexception", this, o, response, e);
9755 o.request.callback.call(o.request.scope, null, o.request.arg, false);
9759 this.fireEvent("load", this, o, o.request.arg);
9760 o.request.callback.call(o.request.scope, result, o.request.arg, true);
9764 update : function(dataSet){
9769 updateResponse : function(dataSet){
9774 * Ext JS Library 1.1.1
9775 * Copyright(c) 2006-2007, Ext JS, LLC.
9777 * Originally Released Under LGPL - original licence link has changed is not relivant.
9780 * <script type="text/javascript">
9784 * @class Roo.data.ScriptTagProxy
9785 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9786 * other than the originating domain of the running page.<br><br>
9788 * <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
9789 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9791 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9792 * source code that is used as the source inside a <script> tag.<br><br>
9794 * In order for the browser to process the returned data, the server must wrap the data object
9795 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9796 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9797 * depending on whether the callback name was passed:
9800 boolean scriptTag = false;
9801 String cb = request.getParameter("callback");
9804 response.setContentType("text/javascript");
9806 response.setContentType("application/x-json");
9808 Writer out = response.getWriter();
9810 out.write(cb + "(");
9812 out.print(dataBlock.toJsonString());
9819 * @param {Object} config A configuration object.
9821 Roo.data.ScriptTagProxy = function(config){
9822 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9823 Roo.apply(this, config);
9824 this.head = document.getElementsByTagName("head")[0];
9827 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9829 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9831 * @cfg {String} url The URL from which to request the data object.
9834 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9838 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9839 * the server the name of the callback function set up by the load call to process the returned data object.
9840 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9841 * javascript output which calls this named function passing the data object as its only parameter.
9843 callbackParam : "callback",
9845 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9846 * name to the request.
9851 * Load data from the configured URL, read the data object into
9852 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9853 * process that block using the passed callback.
9854 * @param {Object} params An object containing properties which are to be used as HTTP parameters
9855 * for the request to the remote server.
9856 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9857 * object into a block of Roo.data.Records.
9858 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9859 * The function must be passed <ul>
9860 * <li>The Record block object</li>
9861 * <li>The "arg" argument from the load function</li>
9862 * <li>A boolean success indicator</li>
9864 * @param {Object} scope The scope in which to call the callback
9865 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9867 load : function(params, reader, callback, scope, arg){
9868 if(this.fireEvent("beforeload", this, params) !== false){
9870 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9873 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9875 url += "&_dc=" + (new Date().getTime());
9877 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9880 cb : "stcCallback"+transId,
9881 scriptId : "stcScript"+transId,
9885 callback : callback,
9891 window[trans.cb] = function(o){
9892 conn.handleResponse(o, trans);
9895 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9897 if(this.autoAbort !== false){
9901 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9903 var script = document.createElement("script");
9904 script.setAttribute("src", url);
9905 script.setAttribute("type", "text/javascript");
9906 script.setAttribute("id", trans.scriptId);
9907 this.head.appendChild(script);
9911 callback.call(scope||this, null, arg, false);
9916 isLoading : function(){
9917 return this.trans ? true : false;
9921 * Abort the current server request.
9924 if(this.isLoading()){
9925 this.destroyTrans(this.trans);
9930 destroyTrans : function(trans, isLoaded){
9931 this.head.removeChild(document.getElementById(trans.scriptId));
9932 clearTimeout(trans.timeoutId);
9934 window[trans.cb] = undefined;
9936 delete window[trans.cb];
9939 // if hasn't been loaded, wait for load to remove it to prevent script error
9940 window[trans.cb] = function(){
9941 window[trans.cb] = undefined;
9943 delete window[trans.cb];
9950 handleResponse : function(o, trans){
9952 this.destroyTrans(trans, true);
9955 result = trans.reader.readRecords(o);
9957 this.fireEvent("loadexception", this, o, trans.arg, e);
9958 trans.callback.call(trans.scope||window, null, trans.arg, false);
9961 this.fireEvent("load", this, o, trans.arg);
9962 trans.callback.call(trans.scope||window, result, trans.arg, true);
9966 handleFailure : function(trans){
9968 this.destroyTrans(trans, false);
9969 this.fireEvent("loadexception", this, null, trans.arg);
9970 trans.callback.call(trans.scope||window, null, trans.arg, false);
9974 * Ext JS Library 1.1.1
9975 * Copyright(c) 2006-2007, Ext JS, LLC.
9977 * Originally Released Under LGPL - original licence link has changed is not relivant.
9980 * <script type="text/javascript">
9984 * @class Roo.data.JsonReader
9985 * @extends Roo.data.DataReader
9986 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9987 * based on mappings in a provided Roo.data.Record constructor.
9989 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9990 * in the reply previously.
9995 var RecordDef = Roo.data.Record.create([
9996 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
9997 {name: 'occupation'} // This field will use "occupation" as the mapping.
9999 var myReader = new Roo.data.JsonReader({
10000 totalProperty: "results", // The property which contains the total dataset size (optional)
10001 root: "rows", // The property which contains an Array of row objects
10002 id: "id" // The property within each row object that provides an ID for the record (optional)
10006 * This would consume a JSON file like this:
10008 { 'results': 2, 'rows': [
10009 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10010 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10013 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10014 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10015 * paged from the remote server.
10016 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10017 * @cfg {String} root name of the property which contains the Array of row objects.
10018 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10020 * Create a new JsonReader
10021 * @param {Object} meta Metadata configuration options
10022 * @param {Object} recordType Either an Array of field definition objects,
10023 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10025 Roo.data.JsonReader = function(meta, recordType){
10028 // set some defaults:
10029 Roo.applyIf(meta, {
10030 totalProperty: 'total',
10031 successProperty : 'success',
10036 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10038 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10041 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
10042 * Used by Store query builder to append _requestMeta to params.
10045 metaFromRemote : false,
10047 * This method is only used by a DataProxy which has retrieved data from a remote server.
10048 * @param {Object} response The XHR object which contains the JSON data in its responseText.
10049 * @return {Object} data A data block which is used by an Roo.data.Store object as
10050 * a cache of Roo.data.Records.
10052 read : function(response){
10053 var json = response.responseText;
10055 var o = /* eval:var:o */ eval("("+json+")");
10057 throw {message: "JsonReader.read: Json object not found"};
10063 this.metaFromRemote = true;
10064 this.meta = o.metaData;
10065 this.recordType = Roo.data.Record.create(o.metaData.fields);
10066 this.onMetaChange(this.meta, this.recordType, o);
10068 return this.readRecords(o);
10071 // private function a store will implement
10072 onMetaChange : function(meta, recordType, o){
10079 simpleAccess: function(obj, subsc) {
10086 getJsonAccessor: function(){
10088 return function(expr) {
10090 return(re.test(expr))
10091 ? new Function("obj", "return obj." + expr)
10096 return Roo.emptyFn;
10101 * Create a data block containing Roo.data.Records from an XML document.
10102 * @param {Object} o An object which contains an Array of row objects in the property specified
10103 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10104 * which contains the total size of the dataset.
10105 * @return {Object} data A data block which is used by an Roo.data.Store object as
10106 * a cache of Roo.data.Records.
10108 readRecords : function(o){
10110 * After any data loads, the raw JSON data is available for further custom processing.
10114 var s = this.meta, Record = this.recordType,
10115 f = Record.prototype.fields, fi = f.items, fl = f.length;
10117 // Generate extraction functions for the totalProperty, the root, the id, and for each field
10119 if(s.totalProperty) {
10120 this.getTotal = this.getJsonAccessor(s.totalProperty);
10122 if(s.successProperty) {
10123 this.getSuccess = this.getJsonAccessor(s.successProperty);
10125 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10127 var g = this.getJsonAccessor(s.id);
10128 this.getId = function(rec) {
10130 return (r === undefined || r === "") ? null : r;
10133 this.getId = function(){return null;};
10136 for(var jj = 0; jj < fl; jj++){
10138 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10139 this.ef[jj] = this.getJsonAccessor(map);
10143 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10144 if(s.totalProperty){
10145 var vt = parseInt(this.getTotal(o), 10);
10150 if(s.successProperty){
10151 var vs = this.getSuccess(o);
10152 if(vs === false || vs === 'false'){
10157 for(var i = 0; i < c; i++){
10160 var id = this.getId(n);
10161 for(var j = 0; j < fl; j++){
10163 var v = this.ef[j](n);
10165 Roo.log('missing convert for ' + f.name);
10169 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10171 var record = new Record(values, id);
10173 records[i] = record;
10179 totalRecords : totalRecords
10184 * Ext JS Library 1.1.1
10185 * Copyright(c) 2006-2007, Ext JS, LLC.
10187 * Originally Released Under LGPL - original licence link has changed is not relivant.
10190 * <script type="text/javascript">
10194 * @class Roo.data.ArrayReader
10195 * @extends Roo.data.DataReader
10196 * Data reader class to create an Array of Roo.data.Record objects from an Array.
10197 * Each element of that Array represents a row of data fields. The
10198 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10199 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10203 var RecordDef = Roo.data.Record.create([
10204 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
10205 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
10207 var myReader = new Roo.data.ArrayReader({
10208 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
10212 * This would consume an Array like this:
10214 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10216 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10218 * Create a new JsonReader
10219 * @param {Object} meta Metadata configuration options.
10220 * @param {Object} recordType Either an Array of field definition objects
10221 * as specified to {@link Roo.data.Record#create},
10222 * or an {@link Roo.data.Record} object
10223 * created using {@link Roo.data.Record#create}.
10225 Roo.data.ArrayReader = function(meta, recordType){
10226 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10229 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10231 * Create a data block containing Roo.data.Records from an XML document.
10232 * @param {Object} o An Array of row objects which represents the dataset.
10233 * @return {Object} data A data block which is used by an Roo.data.Store object as
10234 * a cache of Roo.data.Records.
10236 readRecords : function(o){
10237 var sid = this.meta ? this.meta.id : null;
10238 var recordType = this.recordType, fields = recordType.prototype.fields;
10241 for(var i = 0; i < root.length; i++){
10244 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10245 for(var j = 0, jlen = fields.length; j < jlen; j++){
10246 var f = fields.items[j];
10247 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10248 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10250 values[f.name] = v;
10252 var record = new recordType(values, id);
10254 records[records.length] = record;
10258 totalRecords : records.length
10267 * @class Roo.bootstrap.ComboBox
10268 * @extends Roo.bootstrap.TriggerField
10269 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10270 * @cfg {Boolean} append (true|false) default false
10271 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10272 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10273 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10274 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10275 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10277 * Create a new ComboBox.
10278 * @param {Object} config Configuration options
10280 Roo.bootstrap.ComboBox = function(config){
10281 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10285 * Fires when the dropdown list is expanded
10286 * @param {Roo.bootstrap.ComboBox} combo This combo box
10291 * Fires when the dropdown list is collapsed
10292 * @param {Roo.bootstrap.ComboBox} combo This combo box
10296 * @event beforeselect
10297 * Fires before a list item is selected. Return false to cancel the selection.
10298 * @param {Roo.bootstrap.ComboBox} combo This combo box
10299 * @param {Roo.data.Record} record The data record returned from the underlying store
10300 * @param {Number} index The index of the selected item in the dropdown list
10302 'beforeselect' : true,
10305 * Fires when a list item is selected
10306 * @param {Roo.bootstrap.ComboBox} combo This combo box
10307 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10308 * @param {Number} index The index of the selected item in the dropdown list
10312 * @event beforequery
10313 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10314 * The event object passed has these properties:
10315 * @param {Roo.bootstrap.ComboBox} combo This combo box
10316 * @param {String} query The query
10317 * @param {Boolean} forceAll true to force "all" query
10318 * @param {Boolean} cancel true to cancel the query
10319 * @param {Object} e The query event object
10321 'beforequery': true,
10324 * Fires when the 'add' icon is pressed (add a listener to enable add button)
10325 * @param {Roo.bootstrap.ComboBox} combo This combo box
10330 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10331 * @param {Roo.bootstrap.ComboBox} combo This combo box
10332 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10337 * Fires when the remove value from the combobox array
10338 * @param {Roo.bootstrap.ComboBox} combo This combo box
10345 this.tickItems = [];
10347 this.selectedIndex = -1;
10348 if(this.mode == 'local'){
10349 if(config.queryDelay === undefined){
10350 this.queryDelay = 10;
10352 if(config.minChars === undefined){
10358 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10361 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10362 * rendering into an Roo.Editor, defaults to false)
10365 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10366 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10369 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10372 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10373 * the dropdown list (defaults to undefined, with no header element)
10377 * @cfg {String/Roo.Template} tpl The template to use to render the output
10381 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10383 listWidth: undefined,
10385 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10386 * mode = 'remote' or 'text' if mode = 'local')
10388 displayField: undefined,
10390 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10391 * mode = 'remote' or 'value' if mode = 'local').
10392 * Note: use of a valueField requires the user make a selection
10393 * in order for a value to be mapped.
10395 valueField: undefined,
10399 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10400 * field's data value (defaults to the underlying DOM element's name)
10402 hiddenName: undefined,
10404 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10408 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10410 selectedClass: 'active',
10413 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10417 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10418 * anchor positions (defaults to 'tl-bl')
10420 listAlign: 'tl-bl?',
10422 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10426 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
10427 * query specified by the allQuery config option (defaults to 'query')
10429 triggerAction: 'query',
10431 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10432 * (defaults to 4, does not apply if editable = false)
10436 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10437 * delay (typeAheadDelay) if it matches a known value (defaults to false)
10441 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10442 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10446 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10447 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
10451 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
10452 * when editable = true (defaults to false)
10454 selectOnFocus:false,
10456 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10458 queryParam: 'query',
10460 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
10461 * when mode = 'remote' (defaults to 'Loading...')
10463 loadingText: 'Loading...',
10465 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10469 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10473 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10474 * traditional select (defaults to true)
10478 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10482 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10486 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10487 * listWidth has a higher value)
10491 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10492 * allow the user to set arbitrary text into the field (defaults to false)
10494 forceSelection:false,
10496 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10497 * if typeAhead = true (defaults to 250)
10499 typeAheadDelay : 250,
10501 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10502 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10504 valueNotFoundText : undefined,
10506 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10508 blockFocus : false,
10511 * @cfg {Boolean} disableClear Disable showing of clear button.
10513 disableClear : false,
10515 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
10517 alwaysQuery : false,
10520 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
10534 btnPosition : 'right',
10535 triggerList : true,
10536 showToggleBtn : true,
10537 // element that contains real text value.. (when hidden is used..)
10539 getAutoCreate : function()
10546 if(!this.tickable){
10547 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10552 * ComboBox with tickable selections
10555 var align = this.labelAlign || this.parentLabelAlign();
10558 cls : 'form-group roo-combobox-tickable' //input-group
10564 cls : 'tickable-buttons',
10569 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10576 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10583 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10590 Roo.each(buttons.cn, function(c){
10592 c.cls += ' btn-' + _this.size;
10595 if (_this.disabled) {
10606 cls: 'form-hidden-field'
10610 cls: 'select2-choices',
10614 cls: 'select2-search-field',
10626 cls: 'select2-container input-group select2-container-multi',
10631 // cls: 'typeahead typeahead-long dropdown-menu',
10632 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
10637 if (align ==='left' && this.fieldLabel.length) {
10639 Roo.log("left and has label");
10645 cls : 'control-label col-sm-' + this.labelWidth,
10646 html : this.fieldLabel
10650 cls : "col-sm-" + (12 - this.labelWidth),
10657 } else if ( this.fieldLabel.length) {
10663 //cls : 'input-group-addon',
10664 html : this.fieldLabel
10674 Roo.log(" no label && no align");
10681 ['xs','sm','md','lg'].map(function(size){
10682 if (settings[size]) {
10683 cfg.cls += ' col-' + size + '-' + settings[size];
10692 initEvents: function()
10696 throw "can not find store for combo";
10698 this.store = Roo.factory(this.store, Roo.data);
10701 this.initTickableEvents();
10705 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10707 if(this.hiddenName){
10709 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10711 this.hiddenField.dom.value =
10712 this.hiddenValue !== undefined ? this.hiddenValue :
10713 this.value !== undefined ? this.value : '';
10715 // prevent input submission
10716 this.el.dom.removeAttribute('name');
10717 this.hiddenField.dom.setAttribute('name', this.hiddenName);
10722 // this.el.dom.setAttribute('autocomplete', 'off');
10725 var cls = 'x-combo-list';
10727 //this.list = new Roo.Layer({
10728 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10734 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10735 _this.list.setWidth(lw);
10738 this.list.on('mouseover', this.onViewOver, this);
10739 this.list.on('mousemove', this.onViewMove, this);
10741 this.list.on('scroll', this.onViewScroll, this);
10744 this.list.swallowEvent('mousewheel');
10745 this.assetHeight = 0;
10748 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10749 this.assetHeight += this.header.getHeight();
10752 this.innerList = this.list.createChild({cls:cls+'-inner'});
10753 this.innerList.on('mouseover', this.onViewOver, this);
10754 this.innerList.on('mousemove', this.onViewMove, this);
10755 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10757 if(this.allowBlank && !this.pageSize && !this.disableClear){
10758 this.footer = this.list.createChild({cls:cls+'-ft'});
10759 this.pageTb = new Roo.Toolbar(this.footer);
10763 this.footer = this.list.createChild({cls:cls+'-ft'});
10764 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10765 {pageSize: this.pageSize});
10769 if (this.pageTb && this.allowBlank && !this.disableClear) {
10771 this.pageTb.add(new Roo.Toolbar.Fill(), {
10772 cls: 'x-btn-icon x-btn-clear',
10774 handler: function()
10777 _this.clearValue();
10778 _this.onSelect(false, -1);
10783 this.assetHeight += this.footer.getHeight();
10788 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10791 this.view = new Roo.View(this.list, this.tpl, {
10792 singleSelect:true, store: this.store, selectedClass: this.selectedClass
10794 //this.view.wrapEl.setDisplayed(false);
10795 this.view.on('click', this.onViewClick, this);
10799 this.store.on('beforeload', this.onBeforeLoad, this);
10800 this.store.on('load', this.onLoad, this);
10801 this.store.on('loadexception', this.onLoadException, this);
10803 if(this.resizable){
10804 this.resizer = new Roo.Resizable(this.list, {
10805 pinned:true, handles:'se'
10807 this.resizer.on('resize', function(r, w, h){
10808 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10809 this.listWidth = w;
10810 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10811 this.restrictHeight();
10813 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10816 if(!this.editable){
10817 this.editable = true;
10818 this.setEditable(false);
10823 if (typeof(this.events.add.listeners) != 'undefined') {
10825 this.addicon = this.wrap.createChild(
10826 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
10828 this.addicon.on('click', function(e) {
10829 this.fireEvent('add', this);
10832 if (typeof(this.events.edit.listeners) != 'undefined') {
10834 this.editicon = this.wrap.createChild(
10835 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
10836 if (this.addicon) {
10837 this.editicon.setStyle('margin-left', '40px');
10839 this.editicon.on('click', function(e) {
10841 // we fire even if inothing is selected..
10842 this.fireEvent('edit', this, this.lastData );
10848 this.keyNav = new Roo.KeyNav(this.inputEl(), {
10849 "up" : function(e){
10850 this.inKeyMode = true;
10854 "down" : function(e){
10855 if(!this.isExpanded()){
10856 this.onTriggerClick();
10858 this.inKeyMode = true;
10863 "enter" : function(e){
10864 // this.onViewClick();
10868 if(this.fireEvent("specialkey", this, e)){
10869 this.onViewClick(false);
10875 "esc" : function(e){
10879 "tab" : function(e){
10882 if(this.fireEvent("specialkey", this, e)){
10883 this.onViewClick(false);
10891 doRelay : function(foo, bar, hname){
10892 if(hname == 'down' || this.scope.isExpanded()){
10893 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10902 this.queryDelay = Math.max(this.queryDelay || 10,
10903 this.mode == 'local' ? 10 : 250);
10906 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10908 if(this.typeAhead){
10909 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10911 if(this.editable !== false){
10912 this.inputEl().on("keyup", this.onKeyUp, this);
10914 if(this.forceSelection){
10915 this.inputEl().on('blur', this.doForce, this);
10919 this.choices = this.el.select('ul.select2-choices', true).first();
10920 this.searchField = this.el.select('ul li.select2-search-field', true).first();
10924 initTickableEvents: function()
10928 if(this.hiddenName){
10930 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10932 this.hiddenField.dom.value =
10933 this.hiddenValue !== undefined ? this.hiddenValue :
10934 this.value !== undefined ? this.value : '';
10936 // prevent input submission
10937 this.el.dom.removeAttribute('name');
10938 this.hiddenField.dom.setAttribute('name', this.hiddenName);
10943 // this.list = this.el.select('ul.dropdown-menu',true).first();
10945 this.choices = this.el.select('ul.select2-choices', true).first();
10946 this.searchField = this.el.select('ul li.select2-search-field', true).first();
10947 if(this.triggerList){
10948 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10951 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10952 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10954 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10955 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10957 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10958 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10960 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10961 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10962 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10965 this.cancelBtn.hide();
10970 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10971 _this.list.setWidth(lw);
10974 this.list.on('mouseover', this.onViewOver, this);
10975 this.list.on('mousemove', this.onViewMove, this);
10977 this.list.on('scroll', this.onViewScroll, this);
10980 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>';
10983 this.view = new Roo.View(this.list, this.tpl, {
10984 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10987 //this.view.wrapEl.setDisplayed(false);
10988 this.view.on('click', this.onViewClick, this);
10992 this.store.on('beforeload', this.onBeforeLoad, this);
10993 this.store.on('load', this.onLoad, this);
10994 this.store.on('loadexception', this.onLoadException, this);
10996 // this.keyNav = new Roo.KeyNav(this.inputEl(), {
10997 // "up" : function(e){
10998 // this.inKeyMode = true;
10999 // this.selectPrev();
11002 // "down" : function(e){
11003 // if(!this.isExpanded()){
11004 // this.onTriggerClick();
11006 // this.inKeyMode = true;
11007 // this.selectNext();
11011 // "enter" : function(e){
11012 //// this.onViewClick();
11014 // this.collapse();
11016 // if(this.fireEvent("specialkey", this, e)){
11017 // this.onViewClick(false);
11023 // "esc" : function(e){
11024 // this.collapse();
11027 // "tab" : function(e){
11028 // this.collapse();
11030 // if(this.fireEvent("specialkey", this, e)){
11031 // this.onViewClick(false);
11039 // doRelay : function(foo, bar, hname){
11040 // if(hname == 'down' || this.scope.isExpanded()){
11041 // return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11046 // forceKeyDown: true
11050 this.queryDelay = Math.max(this.queryDelay || 10,
11051 this.mode == 'local' ? 10 : 250);
11054 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11056 if(this.typeAhead){
11057 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11061 onDestroy : function(){
11063 this.view.setStore(null);
11064 this.view.el.removeAllListeners();
11065 this.view.el.remove();
11066 this.view.purgeListeners();
11069 this.list.dom.innerHTML = '';
11073 this.store.un('beforeload', this.onBeforeLoad, this);
11074 this.store.un('load', this.onLoad, this);
11075 this.store.un('loadexception', this.onLoadException, this);
11077 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11081 fireKey : function(e){
11082 if(e.isNavKeyPress() && !this.list.isVisible()){
11083 this.fireEvent("specialkey", this, e);
11088 onResize: function(w, h){
11089 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11091 // if(typeof w != 'number'){
11092 // // we do not handle it!?!?
11095 // var tw = this.trigger.getWidth();
11096 // // tw += this.addicon ? this.addicon.getWidth() : 0;
11097 // // tw += this.editicon ? this.editicon.getWidth() : 0;
11099 // this.inputEl().setWidth( this.adjustWidth('input', x));
11101 // //this.trigger.setStyle('left', x+'px');
11103 // if(this.list && this.listWidth === undefined){
11104 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11105 // this.list.setWidth(lw);
11106 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11114 * Allow or prevent the user from directly editing the field text. If false is passed,
11115 * the user will only be able to select from the items defined in the dropdown list. This method
11116 * is the runtime equivalent of setting the 'editable' config option at config time.
11117 * @param {Boolean} value True to allow the user to directly edit the field text
11119 setEditable : function(value){
11120 if(value == this.editable){
11123 this.editable = value;
11125 this.inputEl().dom.setAttribute('readOnly', true);
11126 this.inputEl().on('mousedown', this.onTriggerClick, this);
11127 this.inputEl().addClass('x-combo-noedit');
11129 this.inputEl().dom.setAttribute('readOnly', false);
11130 this.inputEl().un('mousedown', this.onTriggerClick, this);
11131 this.inputEl().removeClass('x-combo-noedit');
11137 onBeforeLoad : function(combo,opts){
11138 if(!this.hasFocus){
11142 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11144 this.restrictHeight();
11145 this.selectedIndex = -1;
11149 onLoad : function(){
11151 this.hasQuery = false;
11153 if(!this.hasFocus){
11157 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11158 this.loading.hide();
11161 if(this.store.getCount() > 0){
11163 // this.restrictHeight();
11164 if(this.lastQuery == this.allQuery){
11165 if(this.editable && !this.tickable){
11166 this.inputEl().dom.select();
11170 !this.selectByValue(this.value, true) &&
11171 this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' ||
11172 this.store.lastOptions.add != true)
11174 this.select(0, true);
11177 if(this.autoFocus){
11180 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11181 this.taTask.delay(this.typeAheadDelay);
11185 this.onEmptyResults();
11191 onLoadException : function()
11193 this.hasQuery = false;
11195 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11196 this.loading.hide();
11200 Roo.log(this.store.reader.jsonData);
11201 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11203 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11209 onTypeAhead : function(){
11210 if(this.store.getCount() > 0){
11211 var r = this.store.getAt(0);
11212 var newValue = r.data[this.displayField];
11213 var len = newValue.length;
11214 var selStart = this.getRawValue().length;
11216 if(selStart != len){
11217 this.setRawValue(newValue);
11218 this.selectText(selStart, newValue.length);
11224 onSelect : function(record, index){
11226 if(this.fireEvent('beforeselect', this, record, index) !== false){
11228 this.setFromData(index > -1 ? record.data : false);
11231 this.fireEvent('select', this, record, index);
11236 * Returns the currently selected field value or empty string if no value is set.
11237 * @return {String} value The selected value
11239 getValue : function(){
11242 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11245 if(this.valueField){
11246 return typeof this.value != 'undefined' ? this.value : '';
11248 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11253 * Clears any text/value currently set in the field
11255 clearValue : function(){
11256 if(this.hiddenField){
11257 this.hiddenField.dom.value = '';
11260 this.setRawValue('');
11261 this.lastSelectionText = '';
11266 * Sets the specified value into the field. If the value finds a match, the corresponding record text
11267 * will be displayed in the field. If the value does not match the data value of an existing item,
11268 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11269 * Otherwise the field will be blank (although the value will still be set).
11270 * @param {String} value The value to match
11272 setValue : function(v){
11279 if(this.valueField){
11280 var r = this.findRecord(this.valueField, v);
11282 text = r.data[this.displayField];
11283 }else if(this.valueNotFoundText !== undefined){
11284 text = this.valueNotFoundText;
11287 this.lastSelectionText = text;
11288 if(this.hiddenField){
11289 this.hiddenField.dom.value = v;
11291 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11295 * @property {Object} the last set data for the element
11300 * Sets the value of the field based on a object which is related to the record format for the store.
11301 * @param {Object} value the value to set as. or false on reset?
11303 setFromData : function(o){
11306 if(typeof o.display_name !== 'string'){
11307 for(var i=0;i<o.display_name.length;i++){
11308 this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
11316 var dv = ''; // display value
11317 var vv = ''; // value value..
11319 if (this.displayField) {
11320 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11322 // this is an error condition!!!
11323 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
11326 if(this.valueField){
11327 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11330 if(this.hiddenField){
11331 this.hiddenField.dom.value = vv;
11333 this.lastSelectionText = dv;
11334 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11338 // no hidden field.. - we store the value in 'value', but still display
11339 // display field!!!!
11340 this.lastSelectionText = dv;
11341 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11347 reset : function(){
11348 // overridden so that last data is reset..
11349 this.setValue(this.originalValue);
11350 this.clearInvalid();
11351 this.lastData = false;
11353 this.view.clearSelections();
11357 findRecord : function(prop, value){
11359 if(this.store.getCount() > 0){
11360 this.store.each(function(r){
11361 if(r.data[prop] == value){
11371 getName: function()
11373 // returns hidden if it's set..
11374 if (!this.rendered) {return ''};
11375 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
11379 onViewMove : function(e, t){
11380 this.inKeyMode = false;
11384 onViewOver : function(e, t){
11385 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11388 var item = this.view.findItemFromChild(t);
11391 var index = this.view.indexOf(item);
11392 this.select(index, false);
11397 onViewClick : function(view, doFocus, el, e)
11399 var index = this.view.getSelectedIndexes()[0];
11401 var r = this.store.getAt(index);
11405 if(e.getTarget().nodeName.toLowerCase() != 'input'){
11412 Roo.each(this.tickItems, function(v,k){
11414 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11415 _this.tickItems.splice(k, 1);
11425 this.tickItems.push(r.data);
11430 this.onSelect(r, index);
11432 if(doFocus !== false && !this.blockFocus){
11433 this.inputEl().focus();
11438 restrictHeight : function(){
11439 //this.innerList.dom.style.height = '';
11440 //var inner = this.innerList.dom;
11441 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11442 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11443 //this.list.beginUpdate();
11444 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11445 this.list.alignTo(this.inputEl(), this.listAlign);
11446 this.list.alignTo(this.inputEl(), this.listAlign);
11447 //this.list.endUpdate();
11451 onEmptyResults : function(){
11456 * Returns true if the dropdown list is expanded, else false.
11458 isExpanded : function(){
11459 return this.list.isVisible();
11463 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11464 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11465 * @param {String} value The data value of the item to select
11466 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11467 * selected item if it is not currently in view (defaults to true)
11468 * @return {Boolean} True if the value matched an item in the list, else false
11470 selectByValue : function(v, scrollIntoView){
11471 if(v !== undefined && v !== null){
11472 var r = this.findRecord(this.valueField || this.displayField, v);
11474 this.select(this.store.indexOf(r), scrollIntoView);
11482 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11483 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11484 * @param {Number} index The zero-based index of the list item to select
11485 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11486 * selected item if it is not currently in view (defaults to true)
11488 select : function(index, scrollIntoView){
11489 this.selectedIndex = index;
11490 this.view.select(index);
11491 if(scrollIntoView !== false){
11492 var el = this.view.getNode(index);
11493 if(el && !this.multiple && !this.tickable){
11494 this.list.scrollChildIntoView(el, false);
11500 selectNext : function(){
11501 var ct = this.store.getCount();
11503 if(this.selectedIndex == -1){
11505 }else if(this.selectedIndex < ct-1){
11506 this.select(this.selectedIndex+1);
11512 selectPrev : function(){
11513 var ct = this.store.getCount();
11515 if(this.selectedIndex == -1){
11517 }else if(this.selectedIndex != 0){
11518 this.select(this.selectedIndex-1);
11524 onKeyUp : function(e){
11525 if(this.editable !== false && !e.isSpecialKey()){
11526 this.lastKey = e.getKey();
11527 this.dqTask.delay(this.queryDelay);
11532 validateBlur : function(){
11533 return !this.list || !this.list.isVisible();
11537 initQuery : function(){
11538 this.doQuery(this.getRawValue());
11542 doForce : function(){
11543 if(this.inputEl().dom.value.length > 0){
11544 this.inputEl().dom.value =
11545 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11551 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
11552 * query allowing the query action to be canceled if needed.
11553 * @param {String} query The SQL query to execute
11554 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11555 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
11556 * saved in the current store (defaults to false)
11558 doQuery : function(q, forceAll){
11560 if(q === undefined || q === null){
11565 forceAll: forceAll,
11569 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11574 forceAll = qe.forceAll;
11575 if(forceAll === true || (q.length >= this.minChars)){
11577 this.hasQuery = true;
11579 if(this.lastQuery != q || this.alwaysQuery){
11580 this.lastQuery = q;
11581 if(this.mode == 'local'){
11582 this.selectedIndex = -1;
11584 this.store.clearFilter();
11586 this.store.filter(this.displayField, q);
11590 this.store.baseParams[this.queryParam] = q;
11592 var options = {params : this.getParams(q)};
11595 options.add = true;
11596 options.params.start = this.page * this.pageSize;
11599 this.store.load(options);
11601 * this code will make the page width larger, at the beginning, the list not align correctly,
11602 * we should expand the list on onLoad
11603 * so command out it
11608 this.selectedIndex = -1;
11613 this.loadNext = false;
11617 getParams : function(q){
11619 //p[this.queryParam] = q;
11623 p.limit = this.pageSize;
11629 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11631 collapse : function(){
11632 if(!this.isExpanded()){
11640 this.cancelBtn.hide();
11641 this.trigger.show();
11644 Roo.get(document).un('mousedown', this.collapseIf, this);
11645 Roo.get(document).un('mousewheel', this.collapseIf, this);
11646 if (!this.editable) {
11647 Roo.get(document).un('keydown', this.listKeyPress, this);
11649 this.fireEvent('collapse', this);
11653 collapseIf : function(e){
11654 var in_combo = e.within(this.el);
11655 var in_list = e.within(this.list);
11656 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11658 if (in_combo || in_list || is_list) {
11659 //e.stopPropagation();
11664 this.onTickableFooterButtonClick(e, false, false);
11672 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11674 expand : function(){
11676 if(this.isExpanded() || !this.hasFocus){
11680 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11681 this.list.setWidth(lw);
11688 this.restrictHeight();
11692 this.tickItems = Roo.apply([], this.item);
11695 this.cancelBtn.show();
11696 this.trigger.hide();
11700 Roo.get(document).on('mousedown', this.collapseIf, this);
11701 Roo.get(document).on('mousewheel', this.collapseIf, this);
11702 if (!this.editable) {
11703 Roo.get(document).on('keydown', this.listKeyPress, this);
11706 this.fireEvent('expand', this);
11710 // Implements the default empty TriggerField.onTriggerClick function
11711 onTriggerClick : function(e)
11713 Roo.log('trigger click');
11715 if(this.disabled || !this.triggerList){
11720 this.loadNext = false;
11722 if(this.isExpanded()){
11724 if (!this.blockFocus) {
11725 this.inputEl().focus();
11729 this.hasFocus = true;
11730 if(this.triggerAction == 'all') {
11731 this.doQuery(this.allQuery, true);
11733 this.doQuery(this.getRawValue());
11735 if (!this.blockFocus) {
11736 this.inputEl().focus();
11741 onTickableTriggerClick : function(e)
11748 this.loadNext = false;
11749 this.hasFocus = true;
11751 if(this.triggerAction == 'all') {
11752 this.doQuery(this.allQuery, true);
11754 this.doQuery(this.getRawValue());
11758 onSearchFieldClick : function(e)
11760 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11765 this.loadNext = false;
11766 this.hasFocus = true;
11768 if(this.triggerAction == 'all') {
11769 this.doQuery(this.allQuery, true);
11771 this.doQuery(this.getRawValue());
11775 listKeyPress : function(e)
11777 //Roo.log('listkeypress');
11778 // scroll to first matching element based on key pres..
11779 if (e.isSpecialKey()) {
11782 var k = String.fromCharCode(e.getKey()).toUpperCase();
11785 var csel = this.view.getSelectedNodes();
11786 var cselitem = false;
11788 var ix = this.view.indexOf(csel[0]);
11789 cselitem = this.store.getAt(ix);
11790 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11796 this.store.each(function(v) {
11798 // start at existing selection.
11799 if (cselitem.id == v.id) {
11805 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11806 match = this.store.indexOf(v);
11812 if (match === false) {
11813 return true; // no more action?
11816 this.view.select(match);
11817 var sn = Roo.get(this.view.getSelectedNodes()[0])
11818 //sn.scrollIntoView(sn.dom.parentNode, false);
11821 onViewScroll : function(e, t){
11823 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){
11827 this.hasQuery = true;
11829 this.loading = this.list.select('.loading', true).first();
11831 if(this.loading === null){
11832 this.list.createChild({
11834 cls: 'loading select2-more-results select2-active',
11835 html: 'Loading more results...'
11838 this.loading = this.list.select('.loading', true).first();
11840 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11842 this.loading.hide();
11845 this.loading.show();
11850 this.loadNext = true;
11852 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11857 addItem : function(o)
11859 var dv = ''; // display value
11861 if (this.displayField) {
11862 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11864 // this is an error condition!!!
11865 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
11872 var choice = this.choices.createChild({
11874 cls: 'select2-search-choice',
11883 cls: 'select2-search-choice-close',
11888 }, this.searchField);
11890 var close = choice.select('a.select2-search-choice-close', true).first()
11892 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11900 this.inputEl().dom.value = '';
11904 onRemoveItem : function(e, _self, o)
11906 e.preventDefault();
11907 var index = this.item.indexOf(o.data) * 1;
11910 Roo.log('not this item?!');
11914 this.item.splice(index, 1);
11919 this.fireEvent('remove', this, e);
11923 syncValue : function()
11925 if(!this.item.length){
11932 Roo.each(this.item, function(i){
11933 if(_this.valueField){
11934 value.push(i[_this.valueField]);
11941 this.value = value.join(',');
11943 if(this.hiddenField){
11944 this.hiddenField.dom.value = this.value;
11948 clearItem : function()
11950 if(!this.multiple){
11956 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11963 inputEl: function ()
11966 return this.searchField;
11968 return this.el.select('input.form-control',true).first();
11972 onTickableFooterButtonClick : function(e, btn, el)
11974 e.preventDefault();
11976 if(btn && btn.name == 'cancel'){
11977 this.tickItems = Roo.apply([], this.item);
11986 Roo.each(this.tickItems, function(o){
11997 * @cfg {Boolean} grow
12001 * @cfg {Number} growMin
12005 * @cfg {Number} growMax
12015 * Ext JS Library 1.1.1
12016 * Copyright(c) 2006-2007, Ext JS, LLC.
12018 * Originally Released Under LGPL - original licence link has changed is not relivant.
12021 * <script type="text/javascript">
12026 * @extends Roo.util.Observable
12027 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
12028 * This class also supports single and multi selection modes. <br>
12029 * Create a data model bound view:
12031 var store = new Roo.data.Store(...);
12033 var view = new Roo.View({
12035 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
12037 singleSelect: true,
12038 selectedClass: "ydataview-selected",
12042 // listen for node click?
12043 view.on("click", function(vw, index, node, e){
12044 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12048 dataModel.load("foobar.xml");
12050 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12052 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12053 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12055 * Note: old style constructor is still suported (container, template, config)
12058 * Create a new View
12059 * @param {Object} config The config object
12062 Roo.View = function(config, depreciated_tpl, depreciated_config){
12064 this.parent = false;
12066 if (typeof(depreciated_tpl) == 'undefined') {
12067 // new way.. - universal constructor.
12068 Roo.apply(this, config);
12069 this.el = Roo.get(this.el);
12072 this.el = Roo.get(config);
12073 this.tpl = depreciated_tpl;
12074 Roo.apply(this, depreciated_config);
12076 this.wrapEl = this.el.wrap().wrap();
12077 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12080 if(typeof(this.tpl) == "string"){
12081 this.tpl = new Roo.Template(this.tpl);
12083 // support xtype ctors..
12084 this.tpl = new Roo.factory(this.tpl, Roo);
12088 this.tpl.compile();
12093 * @event beforeclick
12094 * Fires before a click is processed. Returns false to cancel the default action.
12095 * @param {Roo.View} this
12096 * @param {Number} index The index of the target node
12097 * @param {HTMLElement} node The target node
12098 * @param {Roo.EventObject} e The raw event object
12100 "beforeclick" : true,
12103 * Fires when a template node is clicked.
12104 * @param {Roo.View} this
12105 * @param {Number} index The index of the target node
12106 * @param {HTMLElement} node The target node
12107 * @param {Roo.EventObject} e The raw event object
12112 * Fires when a template node is double clicked.
12113 * @param {Roo.View} this
12114 * @param {Number} index The index of the target node
12115 * @param {HTMLElement} node The target node
12116 * @param {Roo.EventObject} e The raw event object
12120 * @event contextmenu
12121 * Fires when a template node is right clicked.
12122 * @param {Roo.View} this
12123 * @param {Number} index The index of the target node
12124 * @param {HTMLElement} node The target node
12125 * @param {Roo.EventObject} e The raw event object
12127 "contextmenu" : true,
12129 * @event selectionchange
12130 * Fires when the selected nodes change.
12131 * @param {Roo.View} this
12132 * @param {Array} selections Array of the selected nodes
12134 "selectionchange" : true,
12137 * @event beforeselect
12138 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12139 * @param {Roo.View} this
12140 * @param {HTMLElement} node The node to be selected
12141 * @param {Array} selections Array of currently selected nodes
12143 "beforeselect" : true,
12145 * @event preparedata
12146 * Fires on every row to render, to allow you to change the data.
12147 * @param {Roo.View} this
12148 * @param {Object} data to be rendered (change this)
12150 "preparedata" : true
12158 "click": this.onClick,
12159 "dblclick": this.onDblClick,
12160 "contextmenu": this.onContextMenu,
12164 this.selections = [];
12166 this.cmp = new Roo.CompositeElementLite([]);
12168 this.store = Roo.factory(this.store, Roo.data);
12169 this.setStore(this.store, true);
12172 if ( this.footer && this.footer.xtype) {
12174 var fctr = this.wrapEl.appendChild(document.createElement("div"));
12176 this.footer.dataSource = this.store
12177 this.footer.container = fctr;
12178 this.footer = Roo.factory(this.footer, Roo);
12179 fctr.insertFirst(this.el);
12181 // this is a bit insane - as the paging toolbar seems to detach the el..
12182 // dom.parentNode.parentNode.parentNode
12183 // they get detached?
12187 Roo.View.superclass.constructor.call(this);
12192 Roo.extend(Roo.View, Roo.util.Observable, {
12195 * @cfg {Roo.data.Store} store Data store to load data from.
12200 * @cfg {String|Roo.Element} el The container element.
12205 * @cfg {String|Roo.Template} tpl The template used by this View
12209 * @cfg {String} dataName the named area of the template to use as the data area
12210 * Works with domtemplates roo-name="name"
12214 * @cfg {String} selectedClass The css class to add to selected nodes
12216 selectedClass : "x-view-selected",
12218 * @cfg {String} emptyText The empty text to show when nothing is loaded.
12223 * @cfg {String} text to display on mask (default Loading)
12227 * @cfg {Boolean} multiSelect Allow multiple selection
12229 multiSelect : false,
12231 * @cfg {Boolean} singleSelect Allow single selection
12233 singleSelect: false,
12236 * @cfg {Boolean} toggleSelect - selecting
12238 toggleSelect : false,
12241 * @cfg {Boolean} tickable - selecting
12246 * Returns the element this view is bound to.
12247 * @return {Roo.Element}
12249 getEl : function(){
12250 return this.wrapEl;
12256 * Refreshes the view. - called by datachanged on the store. - do not call directly.
12258 refresh : function(){
12259 Roo.log('refresh');
12262 // if we are using something like 'domtemplate', then
12263 // the what gets used is:
12264 // t.applySubtemplate(NAME, data, wrapping data..)
12265 // the outer template then get' applied with
12266 // the store 'extra data'
12267 // and the body get's added to the
12268 // roo-name="data" node?
12269 // <span class='roo-tpl-{name}'></span> ?????
12273 this.clearSelections();
12274 this.el.update("");
12276 var records = this.store.getRange();
12277 if(records.length < 1) {
12279 // is this valid?? = should it render a template??
12281 this.el.update(this.emptyText);
12285 if (this.dataName) {
12286 this.el.update(t.apply(this.store.meta)); //????
12287 el = this.el.child('.roo-tpl-' + this.dataName);
12290 for(var i = 0, len = records.length; i < len; i++){
12291 var data = this.prepareData(records[i].data, i, records[i]);
12292 this.fireEvent("preparedata", this, data, i, records[i]);
12294 var d = Roo.apply({}, data);
12297 Roo.apply(d, {'roo-id' : Roo.id()});
12301 Roo.each(this.parent.item, function(item){
12302 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12305 Roo.apply(d, {'roo-data-checked' : 'checked'});
12309 html[html.length] = Roo.util.Format.trim(
12311 t.applySubtemplate(this.dataName, d, this.store.meta) :
12318 el.update(html.join(""));
12319 this.nodes = el.dom.childNodes;
12320 this.updateIndexes(0);
12325 * Function to override to reformat the data that is sent to
12326 * the template for each node.
12327 * DEPRICATED - use the preparedata event handler.
12328 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12329 * a JSON object for an UpdateManager bound view).
12331 prepareData : function(data, index, record)
12333 this.fireEvent("preparedata", this, data, index, record);
12337 onUpdate : function(ds, record){
12338 Roo.log('on update');
12339 this.clearSelections();
12340 var index = this.store.indexOf(record);
12341 var n = this.nodes[index];
12342 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12343 n.parentNode.removeChild(n);
12344 this.updateIndexes(index, index);
12350 onAdd : function(ds, records, index)
12352 Roo.log(['on Add', ds, records, index] );
12353 this.clearSelections();
12354 if(this.nodes.length == 0){
12358 var n = this.nodes[index];
12359 for(var i = 0, len = records.length; i < len; i++){
12360 var d = this.prepareData(records[i].data, i, records[i]);
12362 this.tpl.insertBefore(n, d);
12365 this.tpl.append(this.el, d);
12368 this.updateIndexes(index);
12371 onRemove : function(ds, record, index){
12372 Roo.log('onRemove');
12373 this.clearSelections();
12374 var el = this.dataName ?
12375 this.el.child('.roo-tpl-' + this.dataName) :
12378 el.dom.removeChild(this.nodes[index]);
12379 this.updateIndexes(index);
12383 * Refresh an individual node.
12384 * @param {Number} index
12386 refreshNode : function(index){
12387 this.onUpdate(this.store, this.store.getAt(index));
12390 updateIndexes : function(startIndex, endIndex){
12391 var ns = this.nodes;
12392 startIndex = startIndex || 0;
12393 endIndex = endIndex || ns.length - 1;
12394 for(var i = startIndex; i <= endIndex; i++){
12395 ns[i].nodeIndex = i;
12400 * Changes the data store this view uses and refresh the view.
12401 * @param {Store} store
12403 setStore : function(store, initial){
12404 if(!initial && this.store){
12405 this.store.un("datachanged", this.refresh);
12406 this.store.un("add", this.onAdd);
12407 this.store.un("remove", this.onRemove);
12408 this.store.un("update", this.onUpdate);
12409 this.store.un("clear", this.refresh);
12410 this.store.un("beforeload", this.onBeforeLoad);
12411 this.store.un("load", this.onLoad);
12412 this.store.un("loadexception", this.onLoad);
12416 store.on("datachanged", this.refresh, this);
12417 store.on("add", this.onAdd, this);
12418 store.on("remove", this.onRemove, this);
12419 store.on("update", this.onUpdate, this);
12420 store.on("clear", this.refresh, this);
12421 store.on("beforeload", this.onBeforeLoad, this);
12422 store.on("load", this.onLoad, this);
12423 store.on("loadexception", this.onLoad, this);
12431 * onbeforeLoad - masks the loading area.
12434 onBeforeLoad : function(store,opts)
12436 Roo.log('onBeforeLoad');
12438 this.el.update("");
12440 this.el.mask(this.mask ? this.mask : "Loading" );
12442 onLoad : function ()
12449 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12450 * @param {HTMLElement} node
12451 * @return {HTMLElement} The template node
12453 findItemFromChild : function(node){
12454 var el = this.dataName ?
12455 this.el.child('.roo-tpl-' + this.dataName,true) :
12458 if(!node || node.parentNode == el){
12461 var p = node.parentNode;
12462 while(p && p != el){
12463 if(p.parentNode == el){
12472 onClick : function(e){
12473 var item = this.findItemFromChild(e.getTarget());
12475 var index = this.indexOf(item);
12476 if(this.onItemClick(item, index, e) !== false){
12477 this.fireEvent("click", this, index, item, e);
12480 this.clearSelections();
12485 onContextMenu : function(e){
12486 var item = this.findItemFromChild(e.getTarget());
12488 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12493 onDblClick : function(e){
12494 var item = this.findItemFromChild(e.getTarget());
12496 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12500 onItemClick : function(item, index, e)
12502 if(this.fireEvent("beforeclick", this, index, item, e) === false){
12505 if (this.toggleSelect) {
12506 var m = this.isSelected(item) ? 'unselect' : 'select';
12509 _t[m](item, true, false);
12512 if(this.multiSelect || this.singleSelect){
12513 if(this.multiSelect && e.shiftKey && this.lastSelection){
12514 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12516 this.select(item, this.multiSelect && e.ctrlKey);
12517 this.lastSelection = item;
12520 if(!this.tickable){
12521 e.preventDefault();
12529 * Get the number of selected nodes.
12532 getSelectionCount : function(){
12533 return this.selections.length;
12537 * Get the currently selected nodes.
12538 * @return {Array} An array of HTMLElements
12540 getSelectedNodes : function(){
12541 return this.selections;
12545 * Get the indexes of the selected nodes.
12548 getSelectedIndexes : function(){
12549 var indexes = [], s = this.selections;
12550 for(var i = 0, len = s.length; i < len; i++){
12551 indexes.push(s[i].nodeIndex);
12557 * Clear all selections
12558 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12560 clearSelections : function(suppressEvent){
12561 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12562 this.cmp.elements = this.selections;
12563 this.cmp.removeClass(this.selectedClass);
12564 this.selections = [];
12565 if(!suppressEvent){
12566 this.fireEvent("selectionchange", this, this.selections);
12572 * Returns true if the passed node is selected
12573 * @param {HTMLElement/Number} node The node or node index
12574 * @return {Boolean}
12576 isSelected : function(node){
12577 var s = this.selections;
12581 node = this.getNode(node);
12582 return s.indexOf(node) !== -1;
12587 * @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
12588 * @param {Boolean} keepExisting (optional) true to keep existing selections
12589 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12591 select : function(nodeInfo, keepExisting, suppressEvent){
12592 if(nodeInfo instanceof Array){
12594 this.clearSelections(true);
12596 for(var i = 0, len = nodeInfo.length; i < len; i++){
12597 this.select(nodeInfo[i], true, true);
12601 var node = this.getNode(nodeInfo);
12602 if(!node || this.isSelected(node)){
12603 return; // already selected.
12606 this.clearSelections(true);
12608 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12609 Roo.fly(node).addClass(this.selectedClass);
12610 this.selections.push(node);
12611 if(!suppressEvent){
12612 this.fireEvent("selectionchange", this, this.selections);
12620 * @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
12621 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12622 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12624 unselect : function(nodeInfo, keepExisting, suppressEvent)
12626 if(nodeInfo instanceof Array){
12627 Roo.each(this.selections, function(s) {
12628 this.unselect(s, nodeInfo);
12632 var node = this.getNode(nodeInfo);
12633 if(!node || !this.isSelected(node)){
12634 Roo.log("not selected");
12635 return; // not selected.
12639 Roo.each(this.selections, function(s) {
12641 Roo.fly(node).removeClass(this.selectedClass);
12648 this.selections= ns;
12649 this.fireEvent("selectionchange", this, this.selections);
12653 * Gets a template node.
12654 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12655 * @return {HTMLElement} The node or null if it wasn't found
12657 getNode : function(nodeInfo){
12658 if(typeof nodeInfo == "string"){
12659 return document.getElementById(nodeInfo);
12660 }else if(typeof nodeInfo == "number"){
12661 return this.nodes[nodeInfo];
12667 * Gets a range template nodes.
12668 * @param {Number} startIndex
12669 * @param {Number} endIndex
12670 * @return {Array} An array of nodes
12672 getNodes : function(start, end){
12673 var ns = this.nodes;
12674 start = start || 0;
12675 end = typeof end == "undefined" ? ns.length - 1 : end;
12678 for(var i = start; i <= end; i++){
12682 for(var i = start; i >= end; i--){
12690 * Finds the index of the passed node
12691 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12692 * @return {Number} The index of the node or -1
12694 indexOf : function(node){
12695 node = this.getNode(node);
12696 if(typeof node.nodeIndex == "number"){
12697 return node.nodeIndex;
12699 var ns = this.nodes;
12700 for(var i = 0, len = ns.length; i < len; i++){
12711 * based on jquery fullcalendar
12715 Roo.bootstrap = Roo.bootstrap || {};
12717 * @class Roo.bootstrap.Calendar
12718 * @extends Roo.bootstrap.Component
12719 * Bootstrap Calendar class
12720 * @cfg {Boolean} loadMask (true|false) default false
12721 * @cfg {Object} header generate the user specific header of the calendar, default false
12724 * Create a new Container
12725 * @param {Object} config The config object
12730 Roo.bootstrap.Calendar = function(config){
12731 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12735 * Fires when a date is selected
12736 * @param {DatePicker} this
12737 * @param {Date} date The selected date
12741 * @event monthchange
12742 * Fires when the displayed month changes
12743 * @param {DatePicker} this
12744 * @param {Date} date The selected month
12746 'monthchange': true,
12748 * @event evententer
12749 * Fires when mouse over an event
12750 * @param {Calendar} this
12751 * @param {event} Event
12753 'evententer': true,
12755 * @event eventleave
12756 * Fires when the mouse leaves an
12757 * @param {Calendar} this
12760 'eventleave': true,
12762 * @event eventclick
12763 * Fires when the mouse click an
12764 * @param {Calendar} this
12773 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
12776 * @cfg {Number} startDay
12777 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12785 getAutoCreate : function(){
12788 var fc_button = function(name, corner, style, content ) {
12789 return Roo.apply({},{
12791 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
12793 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12796 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12807 style : 'width:100%',
12814 cls : 'fc-header-left',
12816 fc_button('prev', 'left', 'arrow', '‹' ),
12817 fc_button('next', 'right', 'arrow', '›' ),
12818 { tag: 'span', cls: 'fc-header-space' },
12819 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
12827 cls : 'fc-header-center',
12831 cls: 'fc-header-title',
12834 html : 'month / year'
12842 cls : 'fc-header-right',
12844 /* fc_button('month', 'left', '', 'month' ),
12845 fc_button('week', '', '', 'week' ),
12846 fc_button('day', 'right', '', 'day' )
12858 header = this.header;
12861 var cal_heads = function() {
12863 // fixme - handle this.
12865 for (var i =0; i < Date.dayNames.length; i++) {
12866 var d = Date.dayNames[i];
12869 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12870 html : d.substring(0,3)
12874 ret[0].cls += ' fc-first';
12875 ret[6].cls += ' fc-last';
12878 var cal_cell = function(n) {
12881 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12886 cls: 'fc-day-number',
12890 cls: 'fc-day-content',
12894 style: 'position: relative;' // height: 17px;
12906 var cal_rows = function() {
12909 for (var r = 0; r < 6; r++) {
12916 for (var i =0; i < Date.dayNames.length; i++) {
12917 var d = Date.dayNames[i];
12918 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12921 row.cn[0].cls+=' fc-first';
12922 row.cn[0].cn[0].style = 'min-height:90px';
12923 row.cn[6].cls+=' fc-last';
12927 ret[0].cls += ' fc-first';
12928 ret[4].cls += ' fc-prev-last';
12929 ret[5].cls += ' fc-last';
12936 cls: 'fc-border-separate',
12937 style : 'width:100%',
12945 cls : 'fc-first fc-last',
12963 cls : 'fc-content',
12964 style : "position: relative;",
12967 cls : 'fc-view fc-view-month fc-grid',
12968 style : 'position: relative',
12969 unselectable : 'on',
12972 cls : 'fc-event-container',
12973 style : 'position:absolute;z-index:8;top:0;left:0;'
12991 initEvents : function()
12994 throw "can not find store for calendar";
13000 style: "text-align:center",
13004 style: "background-color:white;width:50%;margin:250 auto",
13008 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
13019 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13021 var size = this.el.select('.fc-content', true).first().getSize();
13022 this.maskEl.setSize(size.width, size.height);
13023 this.maskEl.enableDisplayMode("block");
13024 if(!this.loadMask){
13025 this.maskEl.hide();
13028 this.store = Roo.factory(this.store, Roo.data);
13029 this.store.on('load', this.onLoad, this);
13030 this.store.on('beforeload', this.onBeforeLoad, this);
13034 this.cells = this.el.select('.fc-day',true);
13035 //Roo.log(this.cells);
13036 this.textNodes = this.el.query('.fc-day-number');
13037 this.cells.addClassOnOver('fc-state-hover');
13039 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13040 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13041 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13042 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13044 this.on('monthchange', this.onMonthChange, this);
13046 this.update(new Date().clearTime());
13049 resize : function() {
13050 var sz = this.el.getSize();
13052 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13053 this.el.select('.fc-day-content div',true).setHeight(34);
13058 showPrevMonth : function(e){
13059 this.update(this.activeDate.add("mo", -1));
13061 showToday : function(e){
13062 this.update(new Date().clearTime());
13065 showNextMonth : function(e){
13066 this.update(this.activeDate.add("mo", 1));
13070 showPrevYear : function(){
13071 this.update(this.activeDate.add("y", -1));
13075 showNextYear : function(){
13076 this.update(this.activeDate.add("y", 1));
13081 update : function(date)
13083 var vd = this.activeDate;
13084 this.activeDate = date;
13085 // if(vd && this.el){
13086 // var t = date.getTime();
13087 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13088 // Roo.log('using add remove');
13090 // this.fireEvent('monthchange', this, date);
13092 // this.cells.removeClass("fc-state-highlight");
13093 // this.cells.each(function(c){
13094 // if(c.dateValue == t){
13095 // c.addClass("fc-state-highlight");
13096 // setTimeout(function(){
13097 // try{c.dom.firstChild.focus();}catch(e){}
13107 var days = date.getDaysInMonth();
13109 var firstOfMonth = date.getFirstDateOfMonth();
13110 var startingPos = firstOfMonth.getDay()-this.startDay;
13112 if(startingPos < this.startDay){
13116 var pm = date.add(Date.MONTH, -1);
13117 var prevStart = pm.getDaysInMonth()-startingPos;
13119 this.cells = this.el.select('.fc-day',true);
13120 this.textNodes = this.el.query('.fc-day-number');
13121 this.cells.addClassOnOver('fc-state-hover');
13123 var cells = this.cells.elements;
13124 var textEls = this.textNodes;
13126 Roo.each(cells, function(cell){
13127 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13130 days += startingPos;
13132 // convert everything to numbers so it's fast
13133 var day = 86400000;
13134 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13137 //Roo.log(prevStart);
13139 var today = new Date().clearTime().getTime();
13140 var sel = date.clearTime().getTime();
13141 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13142 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13143 var ddMatch = this.disabledDatesRE;
13144 var ddText = this.disabledDatesText;
13145 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13146 var ddaysText = this.disabledDaysText;
13147 var format = this.format;
13149 var setCellClass = function(cal, cell){
13153 //Roo.log('set Cell Class');
13155 var t = d.getTime();
13159 cell.dateValue = t;
13161 cell.className += " fc-today";
13162 cell.className += " fc-state-highlight";
13163 cell.title = cal.todayText;
13166 // disable highlight in other month..
13167 //cell.className += " fc-state-highlight";
13172 cell.className = " fc-state-disabled";
13173 cell.title = cal.minText;
13177 cell.className = " fc-state-disabled";
13178 cell.title = cal.maxText;
13182 if(ddays.indexOf(d.getDay()) != -1){
13183 cell.title = ddaysText;
13184 cell.className = " fc-state-disabled";
13187 if(ddMatch && format){
13188 var fvalue = d.dateFormat(format);
13189 if(ddMatch.test(fvalue)){
13190 cell.title = ddText.replace("%0", fvalue);
13191 cell.className = " fc-state-disabled";
13195 if (!cell.initialClassName) {
13196 cell.initialClassName = cell.dom.className;
13199 cell.dom.className = cell.initialClassName + ' ' + cell.className;
13204 for(; i < startingPos; i++) {
13205 textEls[i].innerHTML = (++prevStart);
13206 d.setDate(d.getDate()+1);
13208 cells[i].className = "fc-past fc-other-month";
13209 setCellClass(this, cells[i]);
13214 for(; i < days; i++){
13215 intDay = i - startingPos + 1;
13216 textEls[i].innerHTML = (intDay);
13217 d.setDate(d.getDate()+1);
13219 cells[i].className = ''; // "x-date-active";
13220 setCellClass(this, cells[i]);
13224 for(; i < 42; i++) {
13225 textEls[i].innerHTML = (++extraDays);
13226 d.setDate(d.getDate()+1);
13228 cells[i].className = "fc-future fc-other-month";
13229 setCellClass(this, cells[i]);
13232 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13234 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13236 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13237 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13239 if(totalRows != 6){
13240 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13241 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13244 this.fireEvent('monthchange', this, date);
13248 if(!this.internalRender){
13249 var main = this.el.dom.firstChild;
13250 var w = main.offsetWidth;
13251 this.el.setWidth(w + this.el.getBorderWidth("lr"));
13252 Roo.fly(main).setWidth(w);
13253 this.internalRender = true;
13254 // opera does not respect the auto grow header center column
13255 // then, after it gets a width opera refuses to recalculate
13256 // without a second pass
13257 if(Roo.isOpera && !this.secondPass){
13258 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13259 this.secondPass = true;
13260 this.update.defer(10, this, [date]);
13267 findCell : function(dt) {
13268 dt = dt.clearTime().getTime();
13270 this.cells.each(function(c){
13271 //Roo.log("check " +c.dateValue + '?=' + dt);
13272 if(c.dateValue == dt){
13282 findCells : function(ev) {
13283 var s = ev.start.clone().clearTime().getTime();
13285 var e= ev.end.clone().clearTime().getTime();
13288 this.cells.each(function(c){
13289 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13291 if(c.dateValue > e){
13294 if(c.dateValue < s){
13303 // findBestRow: function(cells)
13307 // for (var i =0 ; i < cells.length;i++) {
13308 // ret = Math.max(cells[i].rows || 0,ret);
13315 addItem : function(ev)
13317 // look for vertical location slot in
13318 var cells = this.findCells(ev);
13320 // ev.row = this.findBestRow(cells);
13322 // work out the location.
13326 for(var i =0; i < cells.length; i++) {
13328 cells[i].row = cells[0].row;
13331 cells[i].row = cells[i].row + 1;
13341 if (crow.start.getY() == cells[i].getY()) {
13343 crow.end = cells[i];
13360 cells[0].events.push(ev);
13362 this.calevents.push(ev);
13365 clearEvents: function() {
13367 if(!this.calevents){
13371 Roo.each(this.cells.elements, function(c){
13377 Roo.each(this.calevents, function(e) {
13378 Roo.each(e.els, function(el) {
13379 el.un('mouseenter' ,this.onEventEnter, this);
13380 el.un('mouseleave' ,this.onEventLeave, this);
13385 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13391 renderEvents: function()
13395 this.cells.each(function(c) {
13404 if(c.row != c.events.length){
13405 r = 4 - (4 - (c.row - c.events.length));
13408 c.events = ev.slice(0, r);
13409 c.more = ev.slice(r);
13411 if(c.more.length && c.more.length == 1){
13412 c.events.push(c.more.pop());
13415 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13419 this.cells.each(function(c) {
13421 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13424 for (var e = 0; e < c.events.length; e++){
13425 var ev = c.events[e];
13426 var rows = ev.rows;
13428 for(var i = 0; i < rows.length; i++) {
13430 // how many rows should it span..
13433 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13434 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13436 unselectable : "on",
13439 cls: 'fc-event-inner',
13443 // cls: 'fc-event-time',
13444 // html : cells.length > 1 ? '' : ev.time
13448 cls: 'fc-event-title',
13449 html : String.format('{0}', ev.title)
13456 cls: 'ui-resizable-handle ui-resizable-e',
13457 html : '  '
13464 cfg.cls += ' fc-event-start';
13466 if ((i+1) == rows.length) {
13467 cfg.cls += ' fc-event-end';
13470 var ctr = _this.el.select('.fc-event-container',true).first();
13471 var cg = ctr.createChild(cfg);
13473 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13474 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13476 var r = (c.more.length) ? 1 : 0;
13477 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
13478 cg.setWidth(ebox.right - sbox.x -2);
13480 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13481 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13482 cg.on('click', _this.onEventClick, _this, ev);
13493 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13494 style : 'position: absolute',
13495 unselectable : "on",
13498 cls: 'fc-event-inner',
13502 cls: 'fc-event-title',
13510 cls: 'ui-resizable-handle ui-resizable-e',
13511 html : '  '
13517 var ctr = _this.el.select('.fc-event-container',true).first();
13518 var cg = ctr.createChild(cfg);
13520 var sbox = c.select('.fc-day-content',true).first().getBox();
13521 var ebox = c.select('.fc-day-content',true).first().getBox();
13523 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
13524 cg.setWidth(ebox.right - sbox.x -2);
13526 cg.on('click', _this.onMoreEventClick, _this, c.more);
13536 onEventEnter: function (e, el,event,d) {
13537 this.fireEvent('evententer', this, el, event);
13540 onEventLeave: function (e, el,event,d) {
13541 this.fireEvent('eventleave', this, el, event);
13544 onEventClick: function (e, el,event,d) {
13545 this.fireEvent('eventclick', this, el, event);
13548 onMonthChange: function () {
13552 onMoreEventClick: function(e, el, more)
13556 this.calpopover.placement = 'right';
13557 this.calpopover.setTitle('More');
13559 this.calpopover.setContent('');
13561 var ctr = this.calpopover.el.select('.popover-content', true).first();
13563 Roo.each(more, function(m){
13565 cls : 'fc-event-hori fc-event-draggable',
13568 var cg = ctr.createChild(cfg);
13570 cg.on('click', _this.onEventClick, _this, m);
13573 this.calpopover.show(el);
13578 onLoad: function ()
13580 this.calevents = [];
13583 if(this.store.getCount() > 0){
13584 this.store.data.each(function(d){
13587 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13588 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13589 time : d.data.start_time,
13590 title : d.data.title,
13591 description : d.data.description,
13592 venue : d.data.venue
13597 this.renderEvents();
13599 if(this.calevents.length && this.loadMask){
13600 this.maskEl.hide();
13604 onBeforeLoad: function()
13606 this.clearEvents();
13608 this.maskEl.show();
13622 * @class Roo.bootstrap.Popover
13623 * @extends Roo.bootstrap.Component
13624 * Bootstrap Popover class
13625 * @cfg {String} html contents of the popover (or false to use children..)
13626 * @cfg {String} title of popover (or false to hide)
13627 * @cfg {String} placement how it is placed
13628 * @cfg {String} trigger click || hover (or false to trigger manually)
13629 * @cfg {String} over what (parent or false to trigger manually.)
13632 * Create a new Popover
13633 * @param {Object} config The config object
13636 Roo.bootstrap.Popover = function(config){
13637 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13640 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
13642 title: 'Fill in a title',
13645 placement : 'right',
13646 trigger : 'hover', // hover
13650 can_build_overlaid : false,
13652 getChildContainer : function()
13654 return this.el.select('.popover-content',true).first();
13657 getAutoCreate : function(){
13658 Roo.log('make popover?');
13660 cls : 'popover roo-dynamic',
13661 style: 'display:block',
13667 cls : 'popover-inner',
13671 cls: 'popover-title',
13675 cls : 'popover-content',
13686 setTitle: function(str)
13688 this.el.select('.popover-title',true).first().dom.innerHTML = str;
13690 setContent: function(str)
13692 this.el.select('.popover-content',true).first().dom.innerHTML = str;
13694 // as it get's added to the bottom of the page.
13695 onRender : function(ct, position)
13697 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13699 var cfg = Roo.apply({}, this.getAutoCreate());
13703 cfg.cls += ' ' + this.cls;
13706 cfg.style = this.style;
13708 Roo.log("adding to ")
13709 this.el = Roo.get(document.body).createChild(cfg, position);
13715 initEvents : function()
13717 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13718 this.el.enableDisplayMode('block');
13720 if (this.over === false) {
13723 if (this.triggers === false) {
13726 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13727 var triggers = this.trigger ? this.trigger.split(' ') : [];
13728 Roo.each(triggers, function(trigger) {
13730 if (trigger == 'click') {
13731 on_el.on('click', this.toggle, this);
13732 } else if (trigger != 'manual') {
13733 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
13734 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13736 on_el.on(eventIn ,this.enter, this);
13737 on_el.on(eventOut, this.leave, this);
13748 toggle : function () {
13749 this.hoverState == 'in' ? this.leave() : this.enter();
13752 enter : function () {
13755 clearTimeout(this.timeout);
13757 this.hoverState = 'in'
13759 if (!this.delay || !this.delay.show) {
13764 this.timeout = setTimeout(function () {
13765 if (_t.hoverState == 'in') {
13768 }, this.delay.show)
13770 leave : function() {
13771 clearTimeout(this.timeout);
13773 this.hoverState = 'out'
13775 if (!this.delay || !this.delay.hide) {
13780 this.timeout = setTimeout(function () {
13781 if (_t.hoverState == 'out') {
13784 }, this.delay.hide)
13787 show : function (on_el)
13790 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13793 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13794 if (this.html !== false) {
13795 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13797 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13798 if (!this.title.length) {
13799 this.el.select('.popover-title',true).hide();
13802 var placement = typeof this.placement == 'function' ?
13803 this.placement.call(this, this.el, on_el) :
13806 var autoToken = /\s?auto?\s?/i;
13807 var autoPlace = autoToken.test(placement);
13809 placement = placement.replace(autoToken, '') || 'top';
13813 //this.el.setXY([0,0]);
13815 this.el.dom.style.display='block';
13816 this.el.addClass(placement);
13818 //this.el.appendTo(on_el);
13820 var p = this.getPosition();
13821 var box = this.el.getBox();
13826 var align = Roo.bootstrap.Popover.alignment[placement]
13827 this.el.alignTo(on_el, align[0],align[1]);
13828 //var arrow = this.el.select('.arrow',true).first();
13829 //arrow.set(align[2],
13831 this.el.addClass('in');
13832 this.hoverState = null;
13834 if (this.el.hasClass('fade')) {
13841 this.el.setXY([0,0]);
13842 this.el.removeClass('in');
13849 Roo.bootstrap.Popover.alignment = {
13850 'left' : ['r-l', [-10,0], 'right'],
13851 'right' : ['l-r', [10,0], 'left'],
13852 'bottom' : ['t-b', [0,10], 'top'],
13853 'top' : [ 'b-t', [0,-10], 'bottom']
13864 * @class Roo.bootstrap.Progress
13865 * @extends Roo.bootstrap.Component
13866 * Bootstrap Progress class
13867 * @cfg {Boolean} striped striped of the progress bar
13868 * @cfg {Boolean} active animated of the progress bar
13872 * Create a new Progress
13873 * @param {Object} config The config object
13876 Roo.bootstrap.Progress = function(config){
13877 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13880 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
13885 getAutoCreate : function(){
13893 cfg.cls += ' progress-striped';
13897 cfg.cls += ' active';
13916 * @class Roo.bootstrap.ProgressBar
13917 * @extends Roo.bootstrap.Component
13918 * Bootstrap ProgressBar class
13919 * @cfg {Number} aria_valuenow aria-value now
13920 * @cfg {Number} aria_valuemin aria-value min
13921 * @cfg {Number} aria_valuemax aria-value max
13922 * @cfg {String} label label for the progress bar
13923 * @cfg {String} panel (success | info | warning | danger )
13924 * @cfg {String} role role of the progress bar
13925 * @cfg {String} sr_only text
13929 * Create a new ProgressBar
13930 * @param {Object} config The config object
13933 Roo.bootstrap.ProgressBar = function(config){
13934 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13937 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
13941 aria_valuemax : 100,
13947 getAutoCreate : function()
13952 cls: 'progress-bar',
13953 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13965 cfg.role = this.role;
13968 if(this.aria_valuenow){
13969 cfg['aria-valuenow'] = this.aria_valuenow;
13972 if(this.aria_valuemin){
13973 cfg['aria-valuemin'] = this.aria_valuemin;
13976 if(this.aria_valuemax){
13977 cfg['aria-valuemax'] = this.aria_valuemax;
13980 if(this.label && !this.sr_only){
13981 cfg.html = this.label;
13985 cfg.cls += ' progress-bar-' + this.panel;
13991 update : function(aria_valuenow)
13993 this.aria_valuenow = aria_valuenow;
13995 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14010 * @class Roo.bootstrap.TabGroup
14011 * @extends Roo.bootstrap.Column
14012 * Bootstrap Column class
14013 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14014 * @cfg {Boolean} carousel true to make the group behave like a carousel
14017 * Create a new TabGroup
14018 * @param {Object} config The config object
14021 Roo.bootstrap.TabGroup = function(config){
14022 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14024 this.navId = Roo.id();
14027 Roo.bootstrap.TabGroup.register(this);
14031 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
14034 transition : false,
14036 getAutoCreate : function()
14038 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14040 cfg.cls += ' tab-content';
14042 if (this.carousel) {
14043 cfg.cls += ' carousel slide';
14045 cls : 'carousel-inner'
14052 getChildContainer : function()
14054 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14058 * register a Navigation item
14059 * @param {Roo.bootstrap.NavItem} the navitem to add
14061 register : function(item)
14063 this.tabs.push( item);
14064 item.navId = this.navId; // not really needed..
14068 getActivePanel : function()
14071 Roo.each(this.tabs, function(t) {
14081 getPanelByName : function(n)
14084 Roo.each(this.tabs, function(t) {
14085 if (t.tabId == n) {
14093 indexOfPanel : function(p)
14096 Roo.each(this.tabs, function(t,i) {
14097 if (t.tabId == p.tabId) {
14106 * show a specific panel
14107 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14108 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14110 showPanel : function (pan)
14113 if (typeof(pan) == 'number') {
14114 pan = this.tabs[pan];
14116 if (typeof(pan) == 'string') {
14117 pan = this.getPanelByName(pan);
14119 if (pan.tabId == this.getActivePanel().tabId) {
14122 var cur = this.getActivePanel();
14124 if (false === cur.fireEvent('beforedeactivate')) {
14128 if (this.carousel) {
14129 this.transition = true;
14130 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
14131 var lr = dir == 'next' ? 'left' : 'right';
14132 pan.el.addClass(dir); // or prev
14133 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14134 cur.el.addClass(lr); // or right
14135 pan.el.addClass(lr);
14138 cur.el.on('transitionend', function() {
14139 Roo.log("trans end?");
14141 pan.el.removeClass([lr,dir]);
14142 pan.setActive(true);
14144 cur.el.removeClass([lr]);
14145 cur.setActive(false);
14147 _this.transition = false;
14149 }, this, { single: true } );
14153 cur.setActive(false);
14154 pan.setActive(true);
14158 showPanelNext : function()
14160 var i = this.indexOfPanel(this.getActivePanel());
14161 if (i > this.tabs.length) {
14164 this.showPanel(this.tabs[i+1]);
14166 showPanelPrev : function()
14168 var i = this.indexOfPanel(this.getActivePanel());
14172 this.showPanel(this.tabs[i-1]);
14183 Roo.apply(Roo.bootstrap.TabGroup, {
14187 * register a Navigation Group
14188 * @param {Roo.bootstrap.NavGroup} the navgroup to add
14190 register : function(navgrp)
14192 this.groups[navgrp.navId] = navgrp;
14196 * fetch a Navigation Group based on the navigation ID
14197 * if one does not exist , it will get created.
14198 * @param {string} the navgroup to add
14199 * @returns {Roo.bootstrap.NavGroup} the navgroup
14201 get: function(navId) {
14202 if (typeof(this.groups[navId]) == 'undefined') {
14203 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14205 return this.groups[navId] ;
14220 * @class Roo.bootstrap.TabPanel
14221 * @extends Roo.bootstrap.Component
14222 * Bootstrap TabPanel class
14223 * @cfg {Boolean} active panel active
14224 * @cfg {String} html panel content
14225 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14226 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14230 * Create a new TabPanel
14231 * @param {Object} config The config object
14234 Roo.bootstrap.TabPanel = function(config){
14235 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14239 * Fires when the active status changes
14240 * @param {Roo.bootstrap.TabPanel} this
14241 * @param {Boolean} state the new state
14246 * @event beforedeactivate
14247 * Fires before a tab is de-activated - can be used to do validation on a form.
14248 * @param {Roo.bootstrap.TabPanel} this
14249 * @return {Boolean} false if there is an error
14252 'beforedeactivate': true
14255 this.tabId = this.tabId || Roo.id();
14259 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
14266 getAutoCreate : function(){
14269 // item is needed for carousel - not sure if it has any effect otherwise
14270 cls: 'tab-pane item',
14271 html: this.html || ''
14275 cfg.cls += ' active';
14279 cfg.tabId = this.tabId;
14286 initEvents: function()
14288 Roo.log('-------- init events on tab panel ---------');
14290 var p = this.parent();
14291 this.navId = this.navId || p.navId;
14293 if (typeof(this.navId) != 'undefined') {
14294 // not really needed.. but just in case.. parent should be a NavGroup.
14295 var tg = Roo.bootstrap.TabGroup.get(this.navId);
14296 Roo.log(['register', tg, this]);
14302 onRender : function(ct, position)
14304 // Roo.log("Call onRender: " + this.xtype);
14306 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14314 setActive: function(state)
14316 Roo.log("panel - set active " + this.tabId + "=" + state);
14318 this.active = state;
14320 this.el.removeClass('active');
14322 } else if (!this.el.hasClass('active')) {
14323 this.el.addClass('active');
14325 this.fireEvent('changed', this, state);
14342 * @class Roo.bootstrap.DateField
14343 * @extends Roo.bootstrap.Input
14344 * Bootstrap DateField class
14345 * @cfg {Number} weekStart default 0
14346 * @cfg {Number} weekStart default 0
14347 * @cfg {Number} viewMode default empty, (months|years)
14348 * @cfg {Number} minViewMode default empty, (months|years)
14349 * @cfg {Number} startDate default -Infinity
14350 * @cfg {Number} endDate default Infinity
14351 * @cfg {Boolean} todayHighlight default false
14352 * @cfg {Boolean} todayBtn default false
14353 * @cfg {Boolean} calendarWeeks default false
14354 * @cfg {Object} daysOfWeekDisabled default empty
14356 * @cfg {Boolean} keyboardNavigation default true
14357 * @cfg {String} language default en
14360 * Create a new DateField
14361 * @param {Object} config The config object
14364 Roo.bootstrap.DateField = function(config){
14365 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14369 * Fires when this field show.
14370 * @param {Roo.bootstrap.DateField} this
14371 * @param {Mixed} date The date value
14376 * Fires when this field hide.
14377 * @param {Roo.bootstrap.DateField} this
14378 * @param {Mixed} date The date value
14383 * Fires when select a date.
14384 * @param {Roo.bootstrap.DateField} this
14385 * @param {Mixed} date The date value
14391 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
14394 * @cfg {String} format
14395 * The default date format string which can be overriden for localization support. The format must be
14396 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14400 * @cfg {String} altFormats
14401 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14402 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14404 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14412 todayHighlight : false,
14418 keyboardNavigation: true,
14420 calendarWeeks: false,
14422 startDate: -Infinity,
14426 daysOfWeekDisabled: [],
14430 UTCDate: function()
14432 return new Date(Date.UTC.apply(Date, arguments));
14435 UTCToday: function()
14437 var today = new Date();
14438 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14441 getDate: function() {
14442 var d = this.getUTCDate();
14443 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14446 getUTCDate: function() {
14450 setDate: function(d) {
14451 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14454 setUTCDate: function(d) {
14456 this.setValue(this.formatDate(this.date));
14459 onRender: function(ct, position)
14462 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14464 this.language = this.language || 'en';
14465 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14466 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14468 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14469 this.format = this.format || 'm/d/y';
14470 this.isInline = false;
14471 this.isInput = true;
14472 this.component = this.el.select('.add-on', true).first() || false;
14473 this.component = (this.component && this.component.length === 0) ? false : this.component;
14474 this.hasInput = this.component && this.inputEL().length;
14476 if (typeof(this.minViewMode === 'string')) {
14477 switch (this.minViewMode) {
14479 this.minViewMode = 1;
14482 this.minViewMode = 2;
14485 this.minViewMode = 0;
14490 if (typeof(this.viewMode === 'string')) {
14491 switch (this.viewMode) {
14504 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14506 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14508 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14510 this.picker().on('mousedown', this.onMousedown, this);
14511 this.picker().on('click', this.onClick, this);
14513 this.picker().addClass('datepicker-dropdown');
14515 this.startViewMode = this.viewMode;
14518 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14519 if(!this.calendarWeeks){
14524 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14525 v.attr('colspan', function(i, val){
14526 return parseInt(val) + 1;
14531 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14533 this.setStartDate(this.startDate);
14534 this.setEndDate(this.endDate);
14536 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14543 if(this.isInline) {
14548 picker : function()
14550 return this.pickerEl;
14551 // return this.el.select('.datepicker', true).first();
14554 fillDow: function()
14556 var dowCnt = this.weekStart;
14565 if(this.calendarWeeks){
14573 while (dowCnt < this.weekStart + 7) {
14577 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14581 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14584 fillMonths: function()
14587 var months = this.picker().select('>.datepicker-months td', true).first();
14589 months.dom.innerHTML = '';
14595 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14598 months.createChild(month);
14605 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;
14607 if (this.date < this.startDate) {
14608 this.viewDate = new Date(this.startDate);
14609 } else if (this.date > this.endDate) {
14610 this.viewDate = new Date(this.endDate);
14612 this.viewDate = new Date(this.date);
14620 var d = new Date(this.viewDate),
14621 year = d.getUTCFullYear(),
14622 month = d.getUTCMonth(),
14623 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14624 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14625 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14626 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14627 currentDate = this.date && this.date.valueOf(),
14628 today = this.UTCToday();
14630 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14632 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14634 // this.picker.select('>tfoot th.today').
14635 // .text(dates[this.language].today)
14636 // .toggle(this.todayBtn !== false);
14638 this.updateNavArrows();
14641 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14643 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14645 prevMonth.setUTCDate(day);
14647 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14649 var nextMonth = new Date(prevMonth);
14651 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14653 nextMonth = nextMonth.valueOf();
14655 var fillMonths = false;
14657 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14659 while(prevMonth.valueOf() < nextMonth) {
14662 if (prevMonth.getUTCDay() === this.weekStart) {
14664 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14672 if(this.calendarWeeks){
14673 // ISO 8601: First week contains first thursday.
14674 // ISO also states week starts on Monday, but we can be more abstract here.
14676 // Start of current week: based on weekstart/current date
14677 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14678 // Thursday of this week
14679 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14680 // First Thursday of year, year from thursday
14681 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14682 // Calendar week: ms between thursdays, div ms per day, div 7 days
14683 calWeek = (th - yth) / 864e5 / 7 + 1;
14685 fillMonths.cn.push({
14693 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14695 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14698 if (this.todayHighlight &&
14699 prevMonth.getUTCFullYear() == today.getFullYear() &&
14700 prevMonth.getUTCMonth() == today.getMonth() &&
14701 prevMonth.getUTCDate() == today.getDate()) {
14702 clsName += ' today';
14705 if (currentDate && prevMonth.valueOf() === currentDate) {
14706 clsName += ' active';
14709 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14710 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14711 clsName += ' disabled';
14714 fillMonths.cn.push({
14716 cls: 'day ' + clsName,
14717 html: prevMonth.getDate()
14720 prevMonth.setDate(prevMonth.getDate()+1);
14723 var currentYear = this.date && this.date.getUTCFullYear();
14724 var currentMonth = this.date && this.date.getUTCMonth();
14726 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14728 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14729 v.removeClass('active');
14731 if(currentYear === year && k === currentMonth){
14732 v.addClass('active');
14735 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14736 v.addClass('disabled');
14742 year = parseInt(year/10, 10) * 10;
14744 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14746 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14749 for (var i = -1; i < 11; i++) {
14750 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14752 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14760 showMode: function(dir)
14763 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14765 Roo.each(this.picker().select('>div',true).elements, function(v){
14766 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14769 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14774 if(this.isInline) return;
14776 this.picker().removeClass(['bottom', 'top']);
14778 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14780 * place to the top of element!
14784 this.picker().addClass('top');
14785 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14790 this.picker().addClass('bottom');
14792 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14795 parseDate : function(value)
14797 if(!value || value instanceof Date){
14800 var v = Date.parseDate(value, this.format);
14801 if (!v && this.useIso) {
14802 v = Date.parseDate(value, 'Y-m-d');
14804 if(!v && this.altFormats){
14805 if(!this.altFormatsArray){
14806 this.altFormatsArray = this.altFormats.split("|");
14808 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14809 v = Date.parseDate(value, this.altFormatsArray[i]);
14815 formatDate : function(date, fmt)
14817 return (!date || !(date instanceof Date)) ?
14818 date : date.dateFormat(fmt || this.format);
14821 onFocus : function()
14823 Roo.bootstrap.DateField.superclass.onFocus.call(this);
14827 onBlur : function()
14829 Roo.bootstrap.DateField.superclass.onBlur.call(this);
14831 var d = this.inputEl().getValue();
14840 this.picker().show();
14844 this.fireEvent('show', this, this.date);
14849 if(this.isInline) return;
14850 this.picker().hide();
14851 this.viewMode = this.startViewMode;
14854 this.fireEvent('hide', this, this.date);
14858 onMousedown: function(e)
14860 e.stopPropagation();
14861 e.preventDefault();
14866 Roo.bootstrap.DateField.superclass.keyup.call(this);
14870 setValue: function(v)
14872 var d = new Date(v).clearTime();
14874 if(isNaN(d.getTime())){
14875 this.date = this.viewDate = '';
14876 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14880 v = this.formatDate(d);
14882 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14884 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14888 this.fireEvent('select', this, this.date);
14892 getValue: function()
14894 return this.formatDate(this.date);
14897 fireKey: function(e)
14899 if (!this.picker().isVisible()){
14900 if (e.keyCode == 27) // allow escape to hide and re-show picker
14905 var dateChanged = false,
14907 newDate, newViewDate;
14912 e.preventDefault();
14916 if (!this.keyboardNavigation) break;
14917 dir = e.keyCode == 37 ? -1 : 1;
14920 newDate = this.moveYear(this.date, dir);
14921 newViewDate = this.moveYear(this.viewDate, dir);
14922 } else if (e.shiftKey){
14923 newDate = this.moveMonth(this.date, dir);
14924 newViewDate = this.moveMonth(this.viewDate, dir);
14926 newDate = new Date(this.date);
14927 newDate.setUTCDate(this.date.getUTCDate() + dir);
14928 newViewDate = new Date(this.viewDate);
14929 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14931 if (this.dateWithinRange(newDate)){
14932 this.date = newDate;
14933 this.viewDate = newViewDate;
14934 this.setValue(this.formatDate(this.date));
14936 e.preventDefault();
14937 dateChanged = true;
14942 if (!this.keyboardNavigation) break;
14943 dir = e.keyCode == 38 ? -1 : 1;
14945 newDate = this.moveYear(this.date, dir);
14946 newViewDate = this.moveYear(this.viewDate, dir);
14947 } else if (e.shiftKey){
14948 newDate = this.moveMonth(this.date, dir);
14949 newViewDate = this.moveMonth(this.viewDate, dir);
14951 newDate = new Date(this.date);
14952 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14953 newViewDate = new Date(this.viewDate);
14954 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14956 if (this.dateWithinRange(newDate)){
14957 this.date = newDate;
14958 this.viewDate = newViewDate;
14959 this.setValue(this.formatDate(this.date));
14961 e.preventDefault();
14962 dateChanged = true;
14966 this.setValue(this.formatDate(this.date));
14968 e.preventDefault();
14971 this.setValue(this.formatDate(this.date));
14985 onClick: function(e)
14987 e.stopPropagation();
14988 e.preventDefault();
14990 var target = e.getTarget();
14992 if(target.nodeName.toLowerCase() === 'i'){
14993 target = Roo.get(target).dom.parentNode;
14996 var nodeName = target.nodeName;
14997 var className = target.className;
14998 var html = target.innerHTML;
15000 switch(nodeName.toLowerCase()) {
15002 switch(className) {
15008 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15009 switch(this.viewMode){
15011 this.viewDate = this.moveMonth(this.viewDate, dir);
15015 this.viewDate = this.moveYear(this.viewDate, dir);
15021 var date = new Date();
15022 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15024 this.setValue(this.formatDate(this.date));
15031 if (className.indexOf('disabled') === -1) {
15032 this.viewDate.setUTCDate(1);
15033 if (className.indexOf('month') !== -1) {
15034 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15036 var year = parseInt(html, 10) || 0;
15037 this.viewDate.setUTCFullYear(year);
15046 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
15047 var day = parseInt(html, 10) || 1;
15048 var year = this.viewDate.getUTCFullYear(),
15049 month = this.viewDate.getUTCMonth();
15051 if (className.indexOf('old') !== -1) {
15058 } else if (className.indexOf('new') !== -1) {
15066 this.date = this.UTCDate(year, month, day,0,0,0,0);
15067 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15069 this.setValue(this.formatDate(this.date));
15076 setStartDate: function(startDate)
15078 this.startDate = startDate || -Infinity;
15079 if (this.startDate !== -Infinity) {
15080 this.startDate = this.parseDate(this.startDate);
15083 this.updateNavArrows();
15086 setEndDate: function(endDate)
15088 this.endDate = endDate || Infinity;
15089 if (this.endDate !== Infinity) {
15090 this.endDate = this.parseDate(this.endDate);
15093 this.updateNavArrows();
15096 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15098 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15099 if (typeof(this.daysOfWeekDisabled) !== 'object') {
15100 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15102 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15103 return parseInt(d, 10);
15106 this.updateNavArrows();
15109 updateNavArrows: function()
15111 var d = new Date(this.viewDate),
15112 year = d.getUTCFullYear(),
15113 month = d.getUTCMonth();
15115 Roo.each(this.picker().select('.prev', true).elements, function(v){
15117 switch (this.viewMode) {
15120 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15126 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15133 Roo.each(this.picker().select('.next', true).elements, function(v){
15135 switch (this.viewMode) {
15138 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15144 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15152 moveMonth: function(date, dir)
15154 if (!dir) return date;
15155 var new_date = new Date(date.valueOf()),
15156 day = new_date.getUTCDate(),
15157 month = new_date.getUTCMonth(),
15158 mag = Math.abs(dir),
15160 dir = dir > 0 ? 1 : -1;
15163 // If going back one month, make sure month is not current month
15164 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15166 return new_date.getUTCMonth() == month;
15168 // If going forward one month, make sure month is as expected
15169 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15171 return new_date.getUTCMonth() != new_month;
15173 new_month = month + dir;
15174 new_date.setUTCMonth(new_month);
15175 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15176 if (new_month < 0 || new_month > 11)
15177 new_month = (new_month + 12) % 12;
15179 // For magnitudes >1, move one month at a time...
15180 for (var i=0; i<mag; i++)
15181 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15182 new_date = this.moveMonth(new_date, dir);
15183 // ...then reset the day, keeping it in the new month
15184 new_month = new_date.getUTCMonth();
15185 new_date.setUTCDate(day);
15187 return new_month != new_date.getUTCMonth();
15190 // Common date-resetting loop -- if date is beyond end of month, make it
15193 new_date.setUTCDate(--day);
15194 new_date.setUTCMonth(new_month);
15199 moveYear: function(date, dir)
15201 return this.moveMonth(date, dir*12);
15204 dateWithinRange: function(date)
15206 return date >= this.startDate && date <= this.endDate;
15212 this.picker().remove();
15217 Roo.apply(Roo.bootstrap.DateField, {
15228 html: '<i class="fa fa-arrow-left"/>'
15238 html: '<i class="fa fa-arrow-right"/>'
15280 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15281 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15282 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15283 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15284 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15297 navFnc: 'FullYear',
15302 navFnc: 'FullYear',
15307 Roo.apply(Roo.bootstrap.DateField, {
15311 cls: 'datepicker dropdown-menu',
15315 cls: 'datepicker-days',
15319 cls: 'table-condensed',
15321 Roo.bootstrap.DateField.head,
15325 Roo.bootstrap.DateField.footer
15332 cls: 'datepicker-months',
15336 cls: 'table-condensed',
15338 Roo.bootstrap.DateField.head,
15339 Roo.bootstrap.DateField.content,
15340 Roo.bootstrap.DateField.footer
15347 cls: 'datepicker-years',
15351 cls: 'table-condensed',
15353 Roo.bootstrap.DateField.head,
15354 Roo.bootstrap.DateField.content,
15355 Roo.bootstrap.DateField.footer
15374 * @class Roo.bootstrap.TimeField
15375 * @extends Roo.bootstrap.Input
15376 * Bootstrap DateField class
15380 * Create a new TimeField
15381 * @param {Object} config The config object
15384 Roo.bootstrap.TimeField = function(config){
15385 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15389 * Fires when this field show.
15390 * @param {Roo.bootstrap.DateField} this
15391 * @param {Mixed} date The date value
15396 * Fires when this field hide.
15397 * @param {Roo.bootstrap.DateField} this
15398 * @param {Mixed} date The date value
15403 * Fires when select a date.
15404 * @param {Roo.bootstrap.DateField} this
15405 * @param {Mixed} date The date value
15411 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
15414 * @cfg {String} format
15415 * The default time format string which can be overriden for localization support. The format must be
15416 * valid according to {@link Date#parseDate} (defaults to 'H:i').
15420 onRender: function(ct, position)
15423 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15425 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15427 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15429 this.pop = this.picker().select('>.datepicker-time',true).first();
15430 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
15432 this.picker().on('mousedown', this.onMousedown, this);
15433 this.picker().on('click', this.onClick, this);
15435 this.picker().addClass('datepicker-dropdown');
15440 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15441 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15442 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15443 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15444 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15445 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15449 fireKey: function(e){
15450 if (!this.picker().isVisible()){
15451 if (e.keyCode == 27) // allow escape to hide and re-show picker
15456 e.preventDefault();
15464 this.onTogglePeriod();
15467 this.onIncrementMinutes();
15470 this.onDecrementMinutes();
15479 onClick: function(e) {
15480 e.stopPropagation();
15481 e.preventDefault();
15484 picker : function()
15486 return this.el.select('.datepicker', true).first();
15489 fillTime: function()
15491 var time = this.pop.select('tbody', true).first();
15493 time.dom.innerHTML = '';
15508 cls: 'hours-up glyphicon glyphicon-chevron-up'
15528 cls: 'minutes-up glyphicon glyphicon-chevron-up'
15549 cls: 'timepicker-hour',
15564 cls: 'timepicker-minute',
15579 cls: 'btn btn-primary period',
15601 cls: 'hours-down glyphicon glyphicon-chevron-down'
15621 cls: 'minutes-down glyphicon glyphicon-chevron-down'
15639 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15646 var hours = this.time.getHours();
15647 var minutes = this.time.getMinutes();
15660 hours = hours - 12;
15664 hours = '0' + hours;
15668 minutes = '0' + minutes;
15671 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15672 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15673 this.pop.select('button', true).first().dom.innerHTML = period;
15679 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15681 var cls = ['bottom'];
15683 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15690 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15695 this.picker().addClass(cls.join('-'));
15699 Roo.each(cls, function(c){
15701 _this.picker().setTop(_this.inputEl().getHeight());
15705 _this.picker().setTop(0 - _this.picker().getHeight());
15710 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15714 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15721 onFocus : function()
15723 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15727 onBlur : function()
15729 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15735 this.picker().show();
15740 this.fireEvent('show', this, this.date);
15745 this.picker().hide();
15748 this.fireEvent('hide', this, this.date);
15751 setTime : function()
15754 this.setValue(this.time.format(this.format));
15756 this.fireEvent('select', this, this.date);
15761 onMousedown: function(e){
15762 e.stopPropagation();
15763 e.preventDefault();
15766 onIncrementHours: function()
15768 Roo.log('onIncrementHours');
15769 this.time = this.time.add(Date.HOUR, 1);
15774 onDecrementHours: function()
15776 Roo.log('onDecrementHours');
15777 this.time = this.time.add(Date.HOUR, -1);
15781 onIncrementMinutes: function()
15783 Roo.log('onIncrementMinutes');
15784 this.time = this.time.add(Date.MINUTE, 1);
15788 onDecrementMinutes: function()
15790 Roo.log('onDecrementMinutes');
15791 this.time = this.time.add(Date.MINUTE, -1);
15795 onTogglePeriod: function()
15797 Roo.log('onTogglePeriod');
15798 this.time = this.time.add(Date.HOUR, 12);
15805 Roo.apply(Roo.bootstrap.TimeField, {
15835 cls: 'btn btn-info ok',
15847 Roo.apply(Roo.bootstrap.TimeField, {
15851 cls: 'datepicker dropdown-menu',
15855 cls: 'datepicker-time',
15859 cls: 'table-condensed',
15861 Roo.bootstrap.TimeField.content,
15862 Roo.bootstrap.TimeField.footer
15881 * @class Roo.bootstrap.CheckBox
15882 * @extends Roo.bootstrap.Input
15883 * Bootstrap CheckBox class
15885 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15886 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15887 * @cfg {String} boxLabel The text that appears beside the checkbox
15888 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15889 * @cfg {Boolean} checked initnal the element
15893 * Create a new CheckBox
15894 * @param {Object} config The config object
15897 Roo.bootstrap.CheckBox = function(config){
15898 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15903 * Fires when the element is checked or unchecked.
15904 * @param {Roo.bootstrap.CheckBox} this This input
15905 * @param {Boolean} checked The new checked value
15911 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
15913 inputType: 'checkbox',
15920 getAutoCreate : function()
15922 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15928 cfg.cls = 'form-group checkbox' //input-group
15936 type : this.inputType,
15937 value : (!this.checked) ? this.valueOff : this.inputValue,
15938 cls : 'roo-checkbox', //'form-box',
15939 placeholder : this.placeholder || ''
15943 if (this.weight) { // Validity check?
15944 cfg.cls += " checkbox-" + this.weight;
15947 if (this.disabled) {
15948 input.disabled=true;
15952 input.checked = this.checked;
15956 input.name = this.name;
15960 input.cls += ' input-' + this.size;
15964 ['xs','sm','md','lg'].map(function(size){
15965 if (settings[size]) {
15966 cfg.cls += ' col-' + size + '-' + settings[size];
15972 var inputblock = input;
15977 if (this.before || this.after) {
15980 cls : 'input-group',
15984 inputblock.cn.push({
15986 cls : 'input-group-addon',
15990 inputblock.cn.push(input);
15992 inputblock.cn.push({
15994 cls : 'input-group-addon',
16001 if (align ==='left' && this.fieldLabel.length) {
16002 Roo.log("left and has label");
16008 cls : 'control-label col-md-' + this.labelWidth,
16009 html : this.fieldLabel
16013 cls : "col-md-" + (12 - this.labelWidth),
16020 } else if ( this.fieldLabel.length) {
16025 tag: this.boxLabel ? 'span' : 'label',
16027 cls: 'control-label box-input-label',
16028 //cls : 'input-group-addon',
16029 html : this.fieldLabel
16039 Roo.log(" no label && no align");
16040 cfg.cn = [ inputblock ] ;
16049 html: this.boxLabel
16061 * return the real input element.
16063 inputEl: function ()
16065 return this.el.select('input.roo-checkbox',true).first();
16070 return this.el.select('label.control-label',true).first();
16073 initEvents : function()
16075 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16077 this.inputEl().on('click', this.onClick, this);
16081 onClick : function()
16083 this.setChecked(!this.checked);
16086 setChecked : function(state,suppressEvent)
16088 this.checked = state;
16090 this.inputEl().dom.checked = state;
16092 if(suppressEvent !== true){
16093 this.fireEvent('check', this, state);
16096 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16100 setValue : function(v,suppressEvent)
16102 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16116 * @class Roo.bootstrap.Radio
16117 * @extends Roo.bootstrap.CheckBox
16118 * Bootstrap Radio class
16121 * Create a new Radio
16122 * @param {Object} config The config object
16125 Roo.bootstrap.Radio = function(config){
16126 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16130 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
16132 inputType: 'radio',
16136 getAutoCreate : function()
16138 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16144 cfg.cls = 'form-group radio' //input-group
16149 type : this.inputType,
16150 value : (!this.checked) ? this.valueOff : this.inputValue,
16152 placeholder : this.placeholder || ''
16155 if (this.weight) { // Validity check?
16156 cfg.cls += " radio-" + this.weight;
16158 if (this.disabled) {
16159 input.disabled=true;
16163 input.checked = this.checked;
16167 input.name = this.name;
16171 input.cls += ' input-' + this.size;
16175 ['xs','sm','md','lg'].map(function(size){
16176 if (settings[size]) {
16177 cfg.cls += ' col-' + size + '-' + settings[size];
16181 var inputblock = input;
16183 if (this.before || this.after) {
16186 cls : 'input-group',
16190 inputblock.cn.push({
16192 cls : 'input-group-addon',
16196 inputblock.cn.push(input);
16198 inputblock.cn.push({
16200 cls : 'input-group-addon',
16207 if (align ==='left' && this.fieldLabel.length) {
16208 Roo.log("left and has label");
16214 cls : 'control-label col-md-' + this.labelWidth,
16215 html : this.fieldLabel
16219 cls : "col-md-" + (12 - this.labelWidth),
16226 } else if ( this.fieldLabel.length) {
16233 cls: 'control-label box-input-label',
16234 //cls : 'input-group-addon',
16235 html : this.fieldLabel
16245 Roo.log(" no label && no align");
16260 html: this.boxLabel
16267 inputEl: function ()
16269 return this.el.select('input.roo-radio',true).first();
16271 onClick : function()
16273 this.setChecked(true);
16276 setChecked : function(state,suppressEvent)
16279 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16280 v.dom.checked = false;
16284 this.checked = state;
16285 this.inputEl().dom.checked = state;
16287 if(suppressEvent !== true){
16288 this.fireEvent('check', this, state);
16291 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16295 getGroupValue : function()
16298 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16299 if(v.dom.checked == true){
16300 value = v.dom.value;
16308 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
16309 * @return {Mixed} value The field value
16311 getValue : function(){
16312 return this.getGroupValue();
16318 //<script type="text/javascript">
16321 * Based Ext JS Library 1.1.1
16322 * Copyright(c) 2006-2007, Ext JS, LLC.
16328 * @class Roo.HtmlEditorCore
16329 * @extends Roo.Component
16330 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16332 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16335 Roo.HtmlEditorCore = function(config){
16338 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16343 * @event initialize
16344 * Fires when the editor is fully initialized (including the iframe)
16345 * @param {Roo.HtmlEditorCore} this
16350 * Fires when the editor is first receives the focus. Any insertion must wait
16351 * until after this event.
16352 * @param {Roo.HtmlEditorCore} this
16356 * @event beforesync
16357 * Fires before the textarea is updated with content from the editor iframe. Return false
16358 * to cancel the sync.
16359 * @param {Roo.HtmlEditorCore} this
16360 * @param {String} html
16364 * @event beforepush
16365 * Fires before the iframe editor is updated with content from the textarea. Return false
16366 * to cancel the push.
16367 * @param {Roo.HtmlEditorCore} this
16368 * @param {String} html
16373 * Fires when the textarea is updated with content from the editor iframe.
16374 * @param {Roo.HtmlEditorCore} this
16375 * @param {String} html
16380 * Fires when the iframe editor is updated with content from the textarea.
16381 * @param {Roo.HtmlEditorCore} this
16382 * @param {String} html
16387 * @event editorevent
16388 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16389 * @param {Roo.HtmlEditorCore} this
16394 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16396 // defaults : white / black...
16397 this.applyBlacklists();
16404 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
16408 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
16414 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
16419 * @cfg {Number} height (in pixels)
16423 * @cfg {Number} width (in pixels)
16428 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16431 stylesheets: false,
16436 // private properties
16437 validationEvent : false,
16439 initialized : false,
16441 sourceEditMode : false,
16442 onFocus : Roo.emptyFn,
16444 hideMode:'offsets',
16448 // blacklist + whitelisted elements..
16455 * Protected method that will not generally be called directly. It
16456 * is called when the editor initializes the iframe with HTML contents. Override this method if you
16457 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16459 getDocMarkup : function(){
16462 Roo.log(this.stylesheets);
16464 // inherit styels from page...??
16465 if (this.stylesheets === false) {
16467 Roo.get(document.head).select('style').each(function(node) {
16468 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16471 Roo.get(document.head).select('link').each(function(node) {
16472 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16475 } else if (!this.stylesheets.length) {
16477 st = '<style type="text/css">' +
16478 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16481 Roo.each(this.stylesheets, function(s) {
16482 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16487 st += '<style type="text/css">' +
16488 'IMG { cursor: pointer } ' +
16492 return '<html><head>' + st +
16493 //<style type="text/css">' +
16494 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16496 ' </head><body class="roo-htmleditor-body"></body></html>';
16500 onRender : function(ct, position)
16503 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16504 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16507 this.el.dom.style.border = '0 none';
16508 this.el.dom.setAttribute('tabIndex', -1);
16509 this.el.addClass('x-hidden hide');
16513 if(Roo.isIE){ // fix IE 1px bogus margin
16514 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16518 this.frameId = Roo.id();
16522 var iframe = this.owner.wrap.createChild({
16524 cls: 'form-control', // bootstrap..
16526 name: this.frameId,
16527 frameBorder : 'no',
16528 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
16533 this.iframe = iframe.dom;
16535 this.assignDocWin();
16537 this.doc.designMode = 'on';
16540 this.doc.write(this.getDocMarkup());
16544 var task = { // must defer to wait for browser to be ready
16546 //console.log("run task?" + this.doc.readyState);
16547 this.assignDocWin();
16548 if(this.doc.body || this.doc.readyState == 'complete'){
16550 this.doc.designMode="on";
16554 Roo.TaskMgr.stop(task);
16555 this.initEditor.defer(10, this);
16562 Roo.TaskMgr.start(task);
16569 onResize : function(w, h)
16571 Roo.log('resize: ' +w + ',' + h );
16572 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16576 if(typeof w == 'number'){
16578 this.iframe.style.width = w + 'px';
16580 if(typeof h == 'number'){
16582 this.iframe.style.height = h + 'px';
16584 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16591 * Toggles the editor between standard and source edit mode.
16592 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16594 toggleSourceEdit : function(sourceEditMode){
16596 this.sourceEditMode = sourceEditMode === true;
16598 if(this.sourceEditMode){
16600 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
16603 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16604 //this.iframe.className = '';
16607 //this.setSize(this.owner.wrap.getSize());
16608 //this.fireEvent('editmodechange', this, this.sourceEditMode);
16615 * Protected method that will not generally be called directly. If you need/want
16616 * custom HTML cleanup, this is the method you should override.
16617 * @param {String} html The HTML to be cleaned
16618 * return {String} The cleaned HTML
16620 cleanHtml : function(html){
16621 html = String(html);
16622 if(html.length > 5){
16623 if(Roo.isSafari){ // strip safari nonsense
16624 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16627 if(html == ' '){
16634 * HTML Editor -> Textarea
16635 * Protected method that will not generally be called directly. Syncs the contents
16636 * of the editor iframe with the textarea.
16638 syncValue : function(){
16639 if(this.initialized){
16640 var bd = (this.doc.body || this.doc.documentElement);
16641 //this.cleanUpPaste(); -- this is done else where and causes havoc..
16642 var html = bd.innerHTML;
16644 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16645 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16647 html = '<div style="'+m[0]+'">' + html + '</div>';
16650 html = this.cleanHtml(html);
16651 // fix up the special chars.. normaly like back quotes in word...
16652 // however we do not want to do this with chinese..
16653 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16654 var cc = b.charCodeAt();
16656 (cc >= 0x4E00 && cc < 0xA000 ) ||
16657 (cc >= 0x3400 && cc < 0x4E00 ) ||
16658 (cc >= 0xf900 && cc < 0xfb00 )
16664 if(this.owner.fireEvent('beforesync', this, html) !== false){
16665 this.el.dom.value = html;
16666 this.owner.fireEvent('sync', this, html);
16672 * Protected method that will not generally be called directly. Pushes the value of the textarea
16673 * into the iframe editor.
16675 pushValue : function(){
16676 if(this.initialized){
16677 var v = this.el.dom.value.trim();
16679 // if(v.length < 1){
16683 if(this.owner.fireEvent('beforepush', this, v) !== false){
16684 var d = (this.doc.body || this.doc.documentElement);
16686 this.cleanUpPaste();
16687 this.el.dom.value = d.innerHTML;
16688 this.owner.fireEvent('push', this, v);
16694 deferFocus : function(){
16695 this.focus.defer(10, this);
16699 focus : function(){
16700 if(this.win && !this.sourceEditMode){
16707 assignDocWin: function()
16709 var iframe = this.iframe;
16712 this.doc = iframe.contentWindow.document;
16713 this.win = iframe.contentWindow;
16715 // if (!Roo.get(this.frameId)) {
16718 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16719 // this.win = Roo.get(this.frameId).dom.contentWindow;
16721 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16725 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16726 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16731 initEditor : function(){
16732 //console.log("INIT EDITOR");
16733 this.assignDocWin();
16737 this.doc.designMode="on";
16739 this.doc.write(this.getDocMarkup());
16742 var dbody = (this.doc.body || this.doc.documentElement);
16743 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16744 // this copies styles from the containing element into thsi one..
16745 // not sure why we need all of this..
16746 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16748 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16749 //ss['background-attachment'] = 'fixed'; // w3c
16750 dbody.bgProperties = 'fixed'; // ie
16751 //Roo.DomHelper.applyStyles(dbody, ss);
16752 Roo.EventManager.on(this.doc, {
16753 //'mousedown': this.onEditorEvent,
16754 'mouseup': this.onEditorEvent,
16755 'dblclick': this.onEditorEvent,
16756 'click': this.onEditorEvent,
16757 'keyup': this.onEditorEvent,
16762 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16764 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16765 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16767 this.initialized = true;
16769 this.owner.fireEvent('initialize', this);
16774 onDestroy : function(){
16780 //for (var i =0; i < this.toolbars.length;i++) {
16781 // // fixme - ask toolbars for heights?
16782 // this.toolbars[i].onDestroy();
16785 //this.wrap.dom.innerHTML = '';
16786 //this.wrap.remove();
16791 onFirstFocus : function(){
16793 this.assignDocWin();
16796 this.activated = true;
16799 if(Roo.isGecko){ // prevent silly gecko errors
16801 var s = this.win.getSelection();
16802 if(!s.focusNode || s.focusNode.nodeType != 3){
16803 var r = s.getRangeAt(0);
16804 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16809 this.execCmd('useCSS', true);
16810 this.execCmd('styleWithCSS', false);
16813 this.owner.fireEvent('activate', this);
16817 adjustFont: function(btn){
16818 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16819 //if(Roo.isSafari){ // safari
16822 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16823 if(Roo.isSafari){ // safari
16824 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16825 v = (v < 10) ? 10 : v;
16826 v = (v > 48) ? 48 : v;
16827 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16832 v = Math.max(1, v+adjust);
16834 this.execCmd('FontSize', v );
16837 onEditorEvent : function(e){
16838 this.owner.fireEvent('editorevent', this, e);
16839 // this.updateToolbar();
16840 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16843 insertTag : function(tg)
16845 // could be a bit smarter... -> wrap the current selected tRoo..
16846 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16848 range = this.createRange(this.getSelection());
16849 var wrappingNode = this.doc.createElement(tg.toLowerCase());
16850 wrappingNode.appendChild(range.extractContents());
16851 range.insertNode(wrappingNode);
16858 this.execCmd("formatblock", tg);
16862 insertText : function(txt)
16866 var range = this.createRange();
16867 range.deleteContents();
16868 //alert(Sender.getAttribute('label'));
16870 range.insertNode(this.doc.createTextNode(txt));
16876 * Executes a Midas editor command on the editor document and performs necessary focus and
16877 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16878 * @param {String} cmd The Midas command
16879 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16881 relayCmd : function(cmd, value){
16883 this.execCmd(cmd, value);
16884 this.owner.fireEvent('editorevent', this);
16885 //this.updateToolbar();
16886 this.owner.deferFocus();
16890 * Executes a Midas editor command directly on the editor document.
16891 * For visual commands, you should use {@link #relayCmd} instead.
16892 * <b>This should only be called after the editor is initialized.</b>
16893 * @param {String} cmd The Midas command
16894 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16896 execCmd : function(cmd, value){
16897 this.doc.execCommand(cmd, false, value === undefined ? null : value);
16904 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16906 * @param {String} text | dom node..
16908 insertAtCursor : function(text)
16913 if(!this.activated){
16919 var r = this.doc.selection.createRange();
16930 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16934 // from jquery ui (MIT licenced)
16936 var win = this.win;
16938 if (win.getSelection && win.getSelection().getRangeAt) {
16939 range = win.getSelection().getRangeAt(0);
16940 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16941 range.insertNode(node);
16942 } else if (win.document.selection && win.document.selection.createRange) {
16943 // no firefox support
16944 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16945 win.document.selection.createRange().pasteHTML(txt);
16947 // no firefox support
16948 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16949 this.execCmd('InsertHTML', txt);
16958 mozKeyPress : function(e){
16960 var c = e.getCharCode(), cmd;
16963 c = String.fromCharCode(c).toLowerCase();
16977 this.cleanUpPaste.defer(100, this);
16985 e.preventDefault();
16993 fixKeys : function(){ // load time branching for fastest keydown performance
16995 return function(e){
16996 var k = e.getKey(), r;
16999 r = this.doc.selection.createRange();
17002 r.pasteHTML('    ');
17009 r = this.doc.selection.createRange();
17011 var target = r.parentElement();
17012 if(!target || target.tagName.toLowerCase() != 'li'){
17014 r.pasteHTML('<br />');
17020 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17021 this.cleanUpPaste.defer(100, this);
17027 }else if(Roo.isOpera){
17028 return function(e){
17029 var k = e.getKey();
17033 this.execCmd('InsertHTML','    ');
17036 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17037 this.cleanUpPaste.defer(100, this);
17042 }else if(Roo.isSafari){
17043 return function(e){
17044 var k = e.getKey();
17048 this.execCmd('InsertText','\t');
17052 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17053 this.cleanUpPaste.defer(100, this);
17061 getAllAncestors: function()
17063 var p = this.getSelectedNode();
17066 a.push(p); // push blank onto stack..
17067 p = this.getParentElement();
17071 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17075 a.push(this.doc.body);
17079 lastSelNode : false,
17082 getSelection : function()
17084 this.assignDocWin();
17085 return Roo.isIE ? this.doc.selection : this.win.getSelection();
17088 getSelectedNode: function()
17090 // this may only work on Gecko!!!
17092 // should we cache this!!!!
17097 var range = this.createRange(this.getSelection()).cloneRange();
17100 var parent = range.parentElement();
17102 var testRange = range.duplicate();
17103 testRange.moveToElementText(parent);
17104 if (testRange.inRange(range)) {
17107 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17110 parent = parent.parentElement;
17115 // is ancestor a text element.
17116 var ac = range.commonAncestorContainer;
17117 if (ac.nodeType == 3) {
17118 ac = ac.parentNode;
17121 var ar = ac.childNodes;
17124 var other_nodes = [];
17125 var has_other_nodes = false;
17126 for (var i=0;i<ar.length;i++) {
17127 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
17130 // fullly contained node.
17132 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17137 // probably selected..
17138 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17139 other_nodes.push(ar[i]);
17143 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
17148 has_other_nodes = true;
17150 if (!nodes.length && other_nodes.length) {
17151 nodes= other_nodes;
17153 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17159 createRange: function(sel)
17161 // this has strange effects when using with
17162 // top toolbar - not sure if it's a great idea.
17163 //this.editor.contentWindow.focus();
17164 if (typeof sel != "undefined") {
17166 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17168 return this.doc.createRange();
17171 return this.doc.createRange();
17174 getParentElement: function()
17177 this.assignDocWin();
17178 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17180 var range = this.createRange(sel);
17183 var p = range.commonAncestorContainer;
17184 while (p.nodeType == 3) { // text node
17195 * Range intersection.. the hard stuff...
17199 * [ -- selected range --- ]
17203 * if end is before start or hits it. fail.
17204 * if start is after end or hits it fail.
17206 * if either hits (but other is outside. - then it's not
17212 // @see http://www.thismuchiknow.co.uk/?p=64.
17213 rangeIntersectsNode : function(range, node)
17215 var nodeRange = node.ownerDocument.createRange();
17217 nodeRange.selectNode(node);
17219 nodeRange.selectNodeContents(node);
17222 var rangeStartRange = range.cloneRange();
17223 rangeStartRange.collapse(true);
17225 var rangeEndRange = range.cloneRange();
17226 rangeEndRange.collapse(false);
17228 var nodeStartRange = nodeRange.cloneRange();
17229 nodeStartRange.collapse(true);
17231 var nodeEndRange = nodeRange.cloneRange();
17232 nodeEndRange.collapse(false);
17234 return rangeStartRange.compareBoundaryPoints(
17235 Range.START_TO_START, nodeEndRange) == -1 &&
17236 rangeEndRange.compareBoundaryPoints(
17237 Range.START_TO_START, nodeStartRange) == 1;
17241 rangeCompareNode : function(range, node)
17243 var nodeRange = node.ownerDocument.createRange();
17245 nodeRange.selectNode(node);
17247 nodeRange.selectNodeContents(node);
17251 range.collapse(true);
17253 nodeRange.collapse(true);
17255 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17256 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
17258 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17260 var nodeIsBefore = ss == 1;
17261 var nodeIsAfter = ee == -1;
17263 if (nodeIsBefore && nodeIsAfter)
17265 if (!nodeIsBefore && nodeIsAfter)
17266 return 1; //right trailed.
17268 if (nodeIsBefore && !nodeIsAfter)
17269 return 2; // left trailed.
17274 // private? - in a new class?
17275 cleanUpPaste : function()
17277 // cleans up the whole document..
17278 Roo.log('cleanuppaste');
17280 this.cleanUpChildren(this.doc.body);
17281 var clean = this.cleanWordChars(this.doc.body.innerHTML);
17282 if (clean != this.doc.body.innerHTML) {
17283 this.doc.body.innerHTML = clean;
17288 cleanWordChars : function(input) {// change the chars to hex code
17289 var he = Roo.HtmlEditorCore;
17291 var output = input;
17292 Roo.each(he.swapCodes, function(sw) {
17293 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17295 output = output.replace(swapper, sw[1]);
17302 cleanUpChildren : function (n)
17304 if (!n.childNodes.length) {
17307 for (var i = n.childNodes.length-1; i > -1 ; i--) {
17308 this.cleanUpChild(n.childNodes[i]);
17315 cleanUpChild : function (node)
17318 //console.log(node);
17319 if (node.nodeName == "#text") {
17320 // clean up silly Windows -- stuff?
17323 if (node.nodeName == "#comment") {
17324 node.parentNode.removeChild(node);
17325 // clean up silly Windows -- stuff?
17328 var lcname = node.tagName.toLowerCase();
17329 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17330 // whitelist of tags..
17332 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17334 node.parentNode.removeChild(node);
17339 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17341 // remove <a name=....> as rendering on yahoo mailer is borked with this.
17342 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17344 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17345 // remove_keep_children = true;
17348 if (remove_keep_children) {
17349 this.cleanUpChildren(node);
17350 // inserts everything just before this node...
17351 while (node.childNodes.length) {
17352 var cn = node.childNodes[0];
17353 node.removeChild(cn);
17354 node.parentNode.insertBefore(cn, node);
17356 node.parentNode.removeChild(node);
17360 if (!node.attributes || !node.attributes.length) {
17361 this.cleanUpChildren(node);
17365 function cleanAttr(n,v)
17368 if (v.match(/^\./) || v.match(/^\//)) {
17371 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17374 if (v.match(/^#/)) {
17377 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17378 node.removeAttribute(n);
17382 var cwhite = this.cwhite;
17383 var cblack = this.cblack;
17385 function cleanStyle(n,v)
17387 if (v.match(/expression/)) { //XSS?? should we even bother..
17388 node.removeAttribute(n);
17392 var parts = v.split(/;/);
17395 Roo.each(parts, function(p) {
17396 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17400 var l = p.split(':').shift().replace(/\s+/g,'');
17401 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17403 if ( cwhite.length && cblack.indexOf(l) > -1) {
17404 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17405 //node.removeAttribute(n);
17409 // only allow 'c whitelisted system attributes'
17410 if ( cwhite.length && cwhite.indexOf(l) < 0) {
17411 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17412 //node.removeAttribute(n);
17422 if (clean.length) {
17423 node.setAttribute(n, clean.join(';'));
17425 node.removeAttribute(n);
17431 for (var i = node.attributes.length-1; i > -1 ; i--) {
17432 var a = node.attributes[i];
17435 if (a.name.toLowerCase().substr(0,2)=='on') {
17436 node.removeAttribute(a.name);
17439 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17440 node.removeAttribute(a.name);
17443 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17444 cleanAttr(a.name,a.value); // fixme..
17447 if (a.name == 'style') {
17448 cleanStyle(a.name,a.value);
17451 /// clean up MS crap..
17452 // tecnically this should be a list of valid class'es..
17455 if (a.name == 'class') {
17456 if (a.value.match(/^Mso/)) {
17457 node.className = '';
17460 if (a.value.match(/body/)) {
17461 node.className = '';
17472 this.cleanUpChildren(node);
17477 * Clean up MS wordisms...
17479 cleanWord : function(node)
17482 var cleanWordChildren = function()
17484 if (!node.childNodes.length) {
17487 for (var i = node.childNodes.length-1; i > -1 ; i--) {
17488 _t.cleanWord(node.childNodes[i]);
17494 this.cleanWord(this.doc.body);
17497 if (node.nodeName == "#text") {
17498 // clean up silly Windows -- stuff?
17501 if (node.nodeName == "#comment") {
17502 node.parentNode.removeChild(node);
17503 // clean up silly Windows -- stuff?
17507 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17508 node.parentNode.removeChild(node);
17512 // remove - but keep children..
17513 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17514 while (node.childNodes.length) {
17515 var cn = node.childNodes[0];
17516 node.removeChild(cn);
17517 node.parentNode.insertBefore(cn, node);
17519 node.parentNode.removeChild(node);
17520 cleanWordChildren();
17524 if (node.className.length) {
17526 var cn = node.className.split(/\W+/);
17528 Roo.each(cn, function(cls) {
17529 if (cls.match(/Mso[a-zA-Z]+/)) {
17534 node.className = cna.length ? cna.join(' ') : '';
17536 node.removeAttribute("class");
17540 if (node.hasAttribute("lang")) {
17541 node.removeAttribute("lang");
17544 if (node.hasAttribute("style")) {
17546 var styles = node.getAttribute("style").split(";");
17548 Roo.each(styles, function(s) {
17549 if (!s.match(/:/)) {
17552 var kv = s.split(":");
17553 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17556 // what ever is left... we allow.
17559 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17560 if (!nstyle.length) {
17561 node.removeAttribute('style');
17565 cleanWordChildren();
17569 domToHTML : function(currentElement, depth, nopadtext) {
17571 depth = depth || 0;
17572 nopadtext = nopadtext || false;
17574 if (!currentElement) {
17575 return this.domToHTML(this.doc.body);
17578 //Roo.log(currentElement);
17580 var allText = false;
17581 var nodeName = currentElement.nodeName;
17582 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17584 if (nodeName == '#text') {
17585 return currentElement.nodeValue;
17590 if (nodeName != 'BODY') {
17593 // Prints the node tagName, such as <A>, <IMG>, etc
17596 for(i = 0; i < currentElement.attributes.length;i++) {
17598 var aname = currentElement.attributes.item(i).name;
17599 if (!currentElement.attributes.item(i).value.length) {
17602 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17605 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17614 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17617 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17622 // Traverse the tree
17624 var currentElementChild = currentElement.childNodes.item(i);
17625 var allText = true;
17626 var innerHTML = '';
17628 while (currentElementChild) {
17629 // Formatting code (indent the tree so it looks nice on the screen)
17630 var nopad = nopadtext;
17631 if (lastnode == 'SPAN') {
17635 if (currentElementChild.nodeName == '#text') {
17636 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17637 if (!nopad && toadd.length > 80) {
17638 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
17640 innerHTML += toadd;
17643 currentElementChild = currentElement.childNodes.item(i);
17649 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
17651 // Recursively traverse the tree structure of the child node
17652 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
17653 lastnode = currentElementChild.nodeName;
17655 currentElementChild=currentElement.childNodes.item(i);
17661 // The remaining code is mostly for formatting the tree
17662 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
17667 ret+= "</"+tagName+">";
17673 applyBlacklists : function()
17675 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
17676 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
17680 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17681 if (b.indexOf(tag) > -1) {
17684 this.white.push(tag);
17688 Roo.each(w, function(tag) {
17689 if (b.indexOf(tag) > -1) {
17692 if (this.white.indexOf(tag) > -1) {
17695 this.white.push(tag);
17700 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17701 if (w.indexOf(tag) > -1) {
17704 this.black.push(tag);
17708 Roo.each(b, function(tag) {
17709 if (w.indexOf(tag) > -1) {
17712 if (this.black.indexOf(tag) > -1) {
17715 this.black.push(tag);
17720 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
17721 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
17725 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17726 if (b.indexOf(tag) > -1) {
17729 this.cwhite.push(tag);
17733 Roo.each(w, function(tag) {
17734 if (b.indexOf(tag) > -1) {
17737 if (this.cwhite.indexOf(tag) > -1) {
17740 this.cwhite.push(tag);
17745 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17746 if (w.indexOf(tag) > -1) {
17749 this.cblack.push(tag);
17753 Roo.each(b, function(tag) {
17754 if (w.indexOf(tag) > -1) {
17757 if (this.cblack.indexOf(tag) > -1) {
17760 this.cblack.push(tag);
17765 // hide stuff that is not compatible
17779 * @event specialkey
17783 * @cfg {String} fieldClass @hide
17786 * @cfg {String} focusClass @hide
17789 * @cfg {String} autoCreate @hide
17792 * @cfg {String} inputType @hide
17795 * @cfg {String} invalidClass @hide
17798 * @cfg {String} invalidText @hide
17801 * @cfg {String} msgFx @hide
17804 * @cfg {String} validateOnBlur @hide
17808 Roo.HtmlEditorCore.white = [
17809 'area', 'br', 'img', 'input', 'hr', 'wbr',
17811 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
17812 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
17813 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
17814 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
17815 'table', 'ul', 'xmp',
17817 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
17820 'dir', 'menu', 'ol', 'ul', 'dl',
17826 Roo.HtmlEditorCore.black = [
17827 // 'embed', 'object', // enable - backend responsiblity to clean thiese
17829 'base', 'basefont', 'bgsound', 'blink', 'body',
17830 'frame', 'frameset', 'head', 'html', 'ilayer',
17831 'iframe', 'layer', 'link', 'meta', 'object',
17832 'script', 'style' ,'title', 'xml' // clean later..
17834 Roo.HtmlEditorCore.clean = [
17835 'script', 'style', 'title', 'xml'
17837 Roo.HtmlEditorCore.remove = [
17842 Roo.HtmlEditorCore.ablack = [
17846 Roo.HtmlEditorCore.aclean = [
17847 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
17851 Roo.HtmlEditorCore.pwhite= [
17852 'http', 'https', 'mailto'
17855 // white listed style attributes.
17856 Roo.HtmlEditorCore.cwhite= [
17857 // 'text-align', /// default is to allow most things..
17863 // black listed style attributes.
17864 Roo.HtmlEditorCore.cblack= [
17865 // 'font-size' -- this can be set by the project
17869 Roo.HtmlEditorCore.swapCodes =[
17888 * @class Roo.bootstrap.HtmlEditor
17889 * @extends Roo.bootstrap.TextArea
17890 * Bootstrap HtmlEditor class
17893 * Create a new HtmlEditor
17894 * @param {Object} config The config object
17897 Roo.bootstrap.HtmlEditor = function(config){
17898 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17899 if (!this.toolbars) {
17900 this.toolbars = [];
17902 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17905 * @event initialize
17906 * Fires when the editor is fully initialized (including the iframe)
17907 * @param {HtmlEditor} this
17912 * Fires when the editor is first receives the focus. Any insertion must wait
17913 * until after this event.
17914 * @param {HtmlEditor} this
17918 * @event beforesync
17919 * Fires before the textarea is updated with content from the editor iframe. Return false
17920 * to cancel the sync.
17921 * @param {HtmlEditor} this
17922 * @param {String} html
17926 * @event beforepush
17927 * Fires before the iframe editor is updated with content from the textarea. Return false
17928 * to cancel the push.
17929 * @param {HtmlEditor} this
17930 * @param {String} html
17935 * Fires when the textarea is updated with content from the editor iframe.
17936 * @param {HtmlEditor} this
17937 * @param {String} html
17942 * Fires when the iframe editor is updated with content from the textarea.
17943 * @param {HtmlEditor} this
17944 * @param {String} html
17948 * @event editmodechange
17949 * Fires when the editor switches edit modes
17950 * @param {HtmlEditor} this
17951 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17953 editmodechange: true,
17955 * @event editorevent
17956 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17957 * @param {HtmlEditor} this
17961 * @event firstfocus
17962 * Fires when on first focus - needed by toolbars..
17963 * @param {HtmlEditor} this
17968 * Auto save the htmlEditor value as a file into Events
17969 * @param {HtmlEditor} this
17973 * @event savedpreview
17974 * preview the saved version of htmlEditor
17975 * @param {HtmlEditor} this
17982 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
17986 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17991 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
17996 * @cfg {Number} height (in pixels)
18000 * @cfg {Number} width (in pixels)
18005 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18008 stylesheets: false,
18013 // private properties
18014 validationEvent : false,
18016 initialized : false,
18019 onFocus : Roo.emptyFn,
18021 hideMode:'offsets',
18024 tbContainer : false,
18026 toolbarContainer :function() {
18027 return this.wrap.select('.x-html-editor-tb',true).first();
18031 * Protected method that will not generally be called directly. It
18032 * is called when the editor creates its toolbar. Override this method if you need to
18033 * add custom toolbar buttons.
18034 * @param {HtmlEditor} editor
18036 createToolbar : function(){
18038 Roo.log("create toolbars");
18040 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18041 this.toolbars[0].render(this.toolbarContainer());
18045 // if (!editor.toolbars || !editor.toolbars.length) {
18046 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18049 // for (var i =0 ; i < editor.toolbars.length;i++) {
18050 // editor.toolbars[i] = Roo.factory(
18051 // typeof(editor.toolbars[i]) == 'string' ?
18052 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
18053 // Roo.bootstrap.HtmlEditor);
18054 // editor.toolbars[i].init(editor);
18060 onRender : function(ct, position)
18062 // Roo.log("Call onRender: " + this.xtype);
18064 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18066 this.wrap = this.inputEl().wrap({
18067 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18070 this.editorcore.onRender(ct, position);
18072 if (this.resizable) {
18073 this.resizeEl = new Roo.Resizable(this.wrap, {
18077 minHeight : this.height,
18078 height: this.height,
18079 handles : this.resizable,
18082 resize : function(r, w, h) {
18083 _t.onResize(w,h); // -something
18089 this.createToolbar(this);
18092 if(!this.width && this.resizable){
18093 this.setSize(this.wrap.getSize());
18095 if (this.resizeEl) {
18096 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18097 // should trigger onReize..
18103 onResize : function(w, h)
18105 Roo.log('resize: ' +w + ',' + h );
18106 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18110 if(this.inputEl() ){
18111 if(typeof w == 'number'){
18112 var aw = w - this.wrap.getFrameWidth('lr');
18113 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18116 if(typeof h == 'number'){
18117 var tbh = -11; // fixme it needs to tool bar size!
18118 for (var i =0; i < this.toolbars.length;i++) {
18119 // fixme - ask toolbars for heights?
18120 tbh += this.toolbars[i].el.getHeight();
18121 //if (this.toolbars[i].footer) {
18122 // tbh += this.toolbars[i].footer.el.getHeight();
18130 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18131 ah -= 5; // knock a few pixes off for look..
18132 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18136 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18137 this.editorcore.onResize(ew,eh);
18142 * Toggles the editor between standard and source edit mode.
18143 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18145 toggleSourceEdit : function(sourceEditMode)
18147 this.editorcore.toggleSourceEdit(sourceEditMode);
18149 if(this.editorcore.sourceEditMode){
18150 Roo.log('editor - showing textarea');
18153 // Roo.log(this.syncValue());
18155 this.inputEl().removeClass(['hide', 'x-hidden']);
18156 this.inputEl().dom.removeAttribute('tabIndex');
18157 this.inputEl().focus();
18159 Roo.log('editor - hiding textarea');
18161 // Roo.log(this.pushValue());
18164 this.inputEl().addClass(['hide', 'x-hidden']);
18165 this.inputEl().dom.setAttribute('tabIndex', -1);
18166 //this.deferFocus();
18169 if(this.resizable){
18170 this.setSize(this.wrap.getSize());
18173 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18176 // private (for BoxComponent)
18177 adjustSize : Roo.BoxComponent.prototype.adjustSize,
18179 // private (for BoxComponent)
18180 getResizeEl : function(){
18184 // private (for BoxComponent)
18185 getPositionEl : function(){
18190 initEvents : function(){
18191 this.originalValue = this.getValue();
18195 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18198 // markInvalid : Roo.emptyFn,
18200 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18203 // clearInvalid : Roo.emptyFn,
18205 setValue : function(v){
18206 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18207 this.editorcore.pushValue();
18212 deferFocus : function(){
18213 this.focus.defer(10, this);
18217 focus : function(){
18218 this.editorcore.focus();
18224 onDestroy : function(){
18230 for (var i =0; i < this.toolbars.length;i++) {
18231 // fixme - ask toolbars for heights?
18232 this.toolbars[i].onDestroy();
18235 this.wrap.dom.innerHTML = '';
18236 this.wrap.remove();
18241 onFirstFocus : function(){
18242 //Roo.log("onFirstFocus");
18243 this.editorcore.onFirstFocus();
18244 for (var i =0; i < this.toolbars.length;i++) {
18245 this.toolbars[i].onFirstFocus();
18251 syncValue : function()
18253 this.editorcore.syncValue();
18256 pushValue : function()
18258 this.editorcore.pushValue();
18262 // hide stuff that is not compatible
18276 * @event specialkey
18280 * @cfg {String} fieldClass @hide
18283 * @cfg {String} focusClass @hide
18286 * @cfg {String} autoCreate @hide
18289 * @cfg {String} inputType @hide
18292 * @cfg {String} invalidClass @hide
18295 * @cfg {String} invalidText @hide
18298 * @cfg {String} msgFx @hide
18301 * @cfg {String} validateOnBlur @hide
18310 Roo.namespace('Roo.bootstrap.htmleditor');
18312 * @class Roo.bootstrap.HtmlEditorToolbar1
18317 new Roo.bootstrap.HtmlEditor({
18320 new Roo.bootstrap.HtmlEditorToolbar1({
18321 disable : { fonts: 1 , format: 1, ..., ... , ...],
18327 * @cfg {Object} disable List of elements to disable..
18328 * @cfg {Array} btns List of additional buttons.
18332 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18335 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18338 Roo.apply(this, config);
18340 // default disabled, based on 'good practice'..
18341 this.disable = this.disable || {};
18342 Roo.applyIf(this.disable, {
18345 specialElements : true
18347 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18349 this.editor = config.editor;
18350 this.editorcore = config.editor.editorcore;
18352 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18354 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18355 // dont call parent... till later.
18357 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
18362 editorcore : false,
18367 "h1","h2","h3","h4","h5","h6",
18369 "abbr", "acronym", "address", "cite", "samp", "var",
18373 onRender : function(ct, position)
18375 // Roo.log("Call onRender: " + this.xtype);
18377 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18379 this.el.dom.style.marginBottom = '0';
18381 var editorcore = this.editorcore;
18382 var editor= this.editor;
18385 var btn = function(id,cmd , toggle, handler){
18387 var event = toggle ? 'toggle' : 'click';
18392 xns: Roo.bootstrap,
18395 enableToggle:toggle !== false,
18397 pressed : toggle ? false : null,
18400 a.listeners[toggle ? 'toggle' : 'click'] = function() {
18401 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
18410 xns: Roo.bootstrap,
18411 glyphicon : 'font',
18415 xns: Roo.bootstrap,
18419 Roo.each(this.formats, function(f) {
18420 style.menu.items.push({
18422 xns: Roo.bootstrap,
18423 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18428 editorcore.insertTag(this.tagname);
18435 children.push(style);
18438 btn('bold',false,true);
18439 btn('italic',false,true);
18440 btn('align-left', 'justifyleft',true);
18441 btn('align-center', 'justifycenter',true);
18442 btn('align-right' , 'justifyright',true);
18443 btn('link', false, false, function(btn) {
18444 //Roo.log("create link?");
18445 var url = prompt(this.createLinkText, this.defaultLinkValue);
18446 if(url && url != 'http:/'+'/'){
18447 this.editorcore.relayCmd('createlink', url);
18450 btn('list','insertunorderedlist',true);
18451 btn('pencil', false,true, function(btn){
18454 this.toggleSourceEdit(btn.pressed);
18460 xns: Roo.bootstrap,
18465 xns: Roo.bootstrap,
18470 cog.menu.items.push({
18472 xns: Roo.bootstrap,
18473 html : Clean styles,
18478 editorcore.insertTag(this.tagname);
18487 this.xtype = 'NavSimplebar';
18489 for(var i=0;i< children.length;i++) {
18491 this.buttons.add(this.addxtypeChild(children[i]));
18495 editor.on('editorevent', this.updateToolbar, this);
18497 onBtnClick : function(id)
18499 this.editorcore.relayCmd(id);
18500 this.editorcore.focus();
18504 * Protected method that will not generally be called directly. It triggers
18505 * a toolbar update by reading the markup state of the current selection in the editor.
18507 updateToolbar: function(){
18509 if(!this.editorcore.activated){
18510 this.editor.onFirstFocus(); // is this neeed?
18514 var btns = this.buttons;
18515 var doc = this.editorcore.doc;
18516 btns.get('bold').setActive(doc.queryCommandState('bold'));
18517 btns.get('italic').setActive(doc.queryCommandState('italic'));
18518 //btns.get('underline').setActive(doc.queryCommandState('underline'));
18520 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18521 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18522 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18524 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18525 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18528 var ans = this.editorcore.getAllAncestors();
18529 if (this.formatCombo) {
18532 var store = this.formatCombo.store;
18533 this.formatCombo.setValue("");
18534 for (var i =0; i < ans.length;i++) {
18535 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18537 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18545 // hides menus... - so this cant be on a menu...
18546 Roo.bootstrap.MenuMgr.hideAll();
18548 Roo.bootstrap.MenuMgr.hideAll();
18549 //this.editorsyncValue();
18551 onFirstFocus: function() {
18552 this.buttons.each(function(item){
18556 toggleSourceEdit : function(sourceEditMode){
18559 if(sourceEditMode){
18560 Roo.log("disabling buttons");
18561 this.buttons.each( function(item){
18562 if(item.cmd != 'pencil'){
18568 Roo.log("enabling buttons");
18569 if(this.editorcore.initialized){
18570 this.buttons.each( function(item){
18576 Roo.log("calling toggole on editor");
18577 // tell the editor that it's been pressed..
18578 this.editor.toggleSourceEdit(sourceEditMode);
18588 * @class Roo.bootstrap.Table.AbstractSelectionModel
18589 * @extends Roo.util.Observable
18590 * Abstract base class for grid SelectionModels. It provides the interface that should be
18591 * implemented by descendant classes. This class should not be directly instantiated.
18594 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18595 this.locked = false;
18596 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18600 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
18601 /** @ignore Called by the grid automatically. Do not call directly. */
18602 init : function(grid){
18608 * Locks the selections.
18611 this.locked = true;
18615 * Unlocks the selections.
18617 unlock : function(){
18618 this.locked = false;
18622 * Returns true if the selections are locked.
18623 * @return {Boolean}
18625 isLocked : function(){
18626 return this.locked;
18630 * @extends Roo.bootstrap.Table.AbstractSelectionModel
18631 * @class Roo.bootstrap.Table.RowSelectionModel
18632 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18633 * It supports multiple selections and keyboard selection/navigation.
18635 * @param {Object} config
18638 Roo.bootstrap.Table.RowSelectionModel = function(config){
18639 Roo.apply(this, config);
18640 this.selections = new Roo.util.MixedCollection(false, function(o){
18645 this.lastActive = false;
18649 * @event selectionchange
18650 * Fires when the selection changes
18651 * @param {SelectionModel} this
18653 "selectionchange" : true,
18655 * @event afterselectionchange
18656 * Fires after the selection changes (eg. by key press or clicking)
18657 * @param {SelectionModel} this
18659 "afterselectionchange" : true,
18661 * @event beforerowselect
18662 * Fires when a row is selected being selected, return false to cancel.
18663 * @param {SelectionModel} this
18664 * @param {Number} rowIndex The selected index
18665 * @param {Boolean} keepExisting False if other selections will be cleared
18667 "beforerowselect" : true,
18670 * Fires when a row is selected.
18671 * @param {SelectionModel} this
18672 * @param {Number} rowIndex The selected index
18673 * @param {Roo.data.Record} r The record
18675 "rowselect" : true,
18677 * @event rowdeselect
18678 * Fires when a row is deselected.
18679 * @param {SelectionModel} this
18680 * @param {Number} rowIndex The selected index
18682 "rowdeselect" : true
18684 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18685 this.locked = false;
18688 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
18690 * @cfg {Boolean} singleSelect
18691 * True to allow selection of only one row at a time (defaults to false)
18693 singleSelect : false,
18696 initEvents : function(){
18698 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18699 this.grid.on("mousedown", this.handleMouseDown, this);
18700 }else{ // allow click to work like normal
18701 this.grid.on("rowclick", this.handleDragableRowClick, this);
18704 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18705 "up" : function(e){
18707 this.selectPrevious(e.shiftKey);
18708 }else if(this.last !== false && this.lastActive !== false){
18709 var last = this.last;
18710 this.selectRange(this.last, this.lastActive-1);
18711 this.grid.getView().focusRow(this.lastActive);
18712 if(last !== false){
18716 this.selectFirstRow();
18718 this.fireEvent("afterselectionchange", this);
18720 "down" : function(e){
18722 this.selectNext(e.shiftKey);
18723 }else if(this.last !== false && this.lastActive !== false){
18724 var last = this.last;
18725 this.selectRange(this.last, this.lastActive+1);
18726 this.grid.getView().focusRow(this.lastActive);
18727 if(last !== false){
18731 this.selectFirstRow();
18733 this.fireEvent("afterselectionchange", this);
18738 var view = this.grid.view;
18739 view.on("refresh", this.onRefresh, this);
18740 view.on("rowupdated", this.onRowUpdated, this);
18741 view.on("rowremoved", this.onRemove, this);
18745 onRefresh : function(){
18746 var ds = this.grid.dataSource, i, v = this.grid.view;
18747 var s = this.selections;
18748 s.each(function(r){
18749 if((i = ds.indexOfId(r.id)) != -1){
18758 onRemove : function(v, index, r){
18759 this.selections.remove(r);
18763 onRowUpdated : function(v, index, r){
18764 if(this.isSelected(r)){
18765 v.onRowSelect(index);
18771 * @param {Array} records The records to select
18772 * @param {Boolean} keepExisting (optional) True to keep existing selections
18774 selectRecords : function(records, keepExisting){
18776 this.clearSelections();
18778 var ds = this.grid.dataSource;
18779 for(var i = 0, len = records.length; i < len; i++){
18780 this.selectRow(ds.indexOf(records[i]), true);
18785 * Gets the number of selected rows.
18788 getCount : function(){
18789 return this.selections.length;
18793 * Selects the first row in the grid.
18795 selectFirstRow : function(){
18800 * Select the last row.
18801 * @param {Boolean} keepExisting (optional) True to keep existing selections
18803 selectLastRow : function(keepExisting){
18804 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18808 * Selects the row immediately following the last selected row.
18809 * @param {Boolean} keepExisting (optional) True to keep existing selections
18811 selectNext : function(keepExisting){
18812 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18813 this.selectRow(this.last+1, keepExisting);
18814 this.grid.getView().focusRow(this.last);
18819 * Selects the row that precedes the last selected row.
18820 * @param {Boolean} keepExisting (optional) True to keep existing selections
18822 selectPrevious : function(keepExisting){
18824 this.selectRow(this.last-1, keepExisting);
18825 this.grid.getView().focusRow(this.last);
18830 * Returns the selected records
18831 * @return {Array} Array of selected records
18833 getSelections : function(){
18834 return [].concat(this.selections.items);
18838 * Returns the first selected record.
18841 getSelected : function(){
18842 return this.selections.itemAt(0);
18847 * Clears all selections.
18849 clearSelections : function(fast){
18850 if(this.locked) return;
18852 var ds = this.grid.dataSource;
18853 var s = this.selections;
18854 s.each(function(r){
18855 this.deselectRow(ds.indexOfId(r.id));
18859 this.selections.clear();
18866 * Selects all rows.
18868 selectAll : function(){
18869 if(this.locked) return;
18870 this.selections.clear();
18871 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18872 this.selectRow(i, true);
18877 * Returns True if there is a selection.
18878 * @return {Boolean}
18880 hasSelection : function(){
18881 return this.selections.length > 0;
18885 * Returns True if the specified row is selected.
18886 * @param {Number/Record} record The record or index of the record to check
18887 * @return {Boolean}
18889 isSelected : function(index){
18890 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18891 return (r && this.selections.key(r.id) ? true : false);
18895 * Returns True if the specified record id is selected.
18896 * @param {String} id The id of record to check
18897 * @return {Boolean}
18899 isIdSelected : function(id){
18900 return (this.selections.key(id) ? true : false);
18904 handleMouseDown : function(e, t){
18905 var view = this.grid.getView(), rowIndex;
18906 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18909 if(e.shiftKey && this.last !== false){
18910 var last = this.last;
18911 this.selectRange(last, rowIndex, e.ctrlKey);
18912 this.last = last; // reset the last
18913 view.focusRow(rowIndex);
18915 var isSelected = this.isSelected(rowIndex);
18916 if(e.button !== 0 && isSelected){
18917 view.focusRow(rowIndex);
18918 }else if(e.ctrlKey && isSelected){
18919 this.deselectRow(rowIndex);
18920 }else if(!isSelected){
18921 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18922 view.focusRow(rowIndex);
18925 this.fireEvent("afterselectionchange", this);
18928 handleDragableRowClick : function(grid, rowIndex, e)
18930 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18931 this.selectRow(rowIndex, false);
18932 grid.view.focusRow(rowIndex);
18933 this.fireEvent("afterselectionchange", this);
18938 * Selects multiple rows.
18939 * @param {Array} rows Array of the indexes of the row to select
18940 * @param {Boolean} keepExisting (optional) True to keep existing selections
18942 selectRows : function(rows, keepExisting){
18944 this.clearSelections();
18946 for(var i = 0, len = rows.length; i < len; i++){
18947 this.selectRow(rows[i], true);
18952 * Selects a range of rows. All rows in between startRow and endRow are also selected.
18953 * @param {Number} startRow The index of the first row in the range
18954 * @param {Number} endRow The index of the last row in the range
18955 * @param {Boolean} keepExisting (optional) True to retain existing selections
18957 selectRange : function(startRow, endRow, keepExisting){
18958 if(this.locked) return;
18960 this.clearSelections();
18962 if(startRow <= endRow){
18963 for(var i = startRow; i <= endRow; i++){
18964 this.selectRow(i, true);
18967 for(var i = startRow; i >= endRow; i--){
18968 this.selectRow(i, true);
18974 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18975 * @param {Number} startRow The index of the first row in the range
18976 * @param {Number} endRow The index of the last row in the range
18978 deselectRange : function(startRow, endRow, preventViewNotify){
18979 if(this.locked) return;
18980 for(var i = startRow; i <= endRow; i++){
18981 this.deselectRow(i, preventViewNotify);
18987 * @param {Number} row The index of the row to select
18988 * @param {Boolean} keepExisting (optional) True to keep existing selections
18990 selectRow : function(index, keepExisting, preventViewNotify){
18991 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18992 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18993 if(!keepExisting || this.singleSelect){
18994 this.clearSelections();
18996 var r = this.grid.dataSource.getAt(index);
18997 this.selections.add(r);
18998 this.last = this.lastActive = index;
18999 if(!preventViewNotify){
19000 this.grid.getView().onRowSelect(index);
19002 this.fireEvent("rowselect", this, index, r);
19003 this.fireEvent("selectionchange", this);
19009 * @param {Number} row The index of the row to deselect
19011 deselectRow : function(index, preventViewNotify){
19012 if(this.locked) return;
19013 if(this.last == index){
19016 if(this.lastActive == index){
19017 this.lastActive = false;
19019 var r = this.grid.dataSource.getAt(index);
19020 this.selections.remove(r);
19021 if(!preventViewNotify){
19022 this.grid.getView().onRowDeselect(index);
19024 this.fireEvent("rowdeselect", this, index);
19025 this.fireEvent("selectionchange", this);
19029 restoreLast : function(){
19031 this.last = this._last;
19036 acceptsNav : function(row, col, cm){
19037 return !cm.isHidden(col) && cm.isCellEditable(col, row);
19041 onEditorKey : function(field, e){
19042 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19047 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19049 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19051 }else if(k == e.ENTER && !e.ctrlKey){
19055 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19057 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19059 }else if(k == e.ESC){
19063 g.startEditing(newCell[0], newCell[1]);
19068 * Ext JS Library 1.1.1
19069 * Copyright(c) 2006-2007, Ext JS, LLC.
19071 * Originally Released Under LGPL - original licence link has changed is not relivant.
19074 * <script type="text/javascript">
19078 * @class Roo.bootstrap.PagingToolbar
19080 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19082 * Create a new PagingToolbar
19083 * @param {Object} config The config object
19085 Roo.bootstrap.PagingToolbar = function(config)
19087 // old args format still supported... - xtype is prefered..
19088 // created from xtype...
19089 var ds = config.dataSource;
19090 this.toolbarItems = [];
19091 if (config.items) {
19092 this.toolbarItems = config.items;
19093 // config.items = [];
19096 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19103 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19107 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19109 * @cfg {Roo.data.Store} dataSource
19110 * The underlying data store providing the paged data
19113 * @cfg {String/HTMLElement/Element} container
19114 * container The id or element that will contain the toolbar
19117 * @cfg {Boolean} displayInfo
19118 * True to display the displayMsg (defaults to false)
19121 * @cfg {Number} pageSize
19122 * The number of records to display per page (defaults to 20)
19126 * @cfg {String} displayMsg
19127 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19129 displayMsg : 'Displaying {0} - {1} of {2}',
19131 * @cfg {String} emptyMsg
19132 * The message to display when no records are found (defaults to "No data to display")
19134 emptyMsg : 'No data to display',
19136 * Customizable piece of the default paging text (defaults to "Page")
19139 beforePageText : "Page",
19141 * Customizable piece of the default paging text (defaults to "of %0")
19144 afterPageText : "of {0}",
19146 * Customizable piece of the default paging text (defaults to "First Page")
19149 firstText : "First Page",
19151 * Customizable piece of the default paging text (defaults to "Previous Page")
19154 prevText : "Previous Page",
19156 * Customizable piece of the default paging text (defaults to "Next Page")
19159 nextText : "Next Page",
19161 * Customizable piece of the default paging text (defaults to "Last Page")
19164 lastText : "Last Page",
19166 * Customizable piece of the default paging text (defaults to "Refresh")
19169 refreshText : "Refresh",
19173 onRender : function(ct, position)
19175 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19176 this.navgroup.parentId = this.id;
19177 this.navgroup.onRender(this.el, null);
19178 // add the buttons to the navgroup
19180 if(this.displayInfo){
19181 Roo.log(this.el.select('ul.navbar-nav',true).first());
19182 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19183 this.displayEl = this.el.select('.x-paging-info', true).first();
19184 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19185 // this.displayEl = navel.el.select('span',true).first();
19191 Roo.each(_this.buttons, function(e){
19192 Roo.factory(e).onRender(_this.el, null);
19196 Roo.each(_this.toolbarItems, function(e) {
19197 _this.navgroup.addItem(e);
19200 this.first = this.navgroup.addItem({
19201 tooltip: this.firstText,
19203 icon : 'fa fa-backward',
19205 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19208 this.prev = this.navgroup.addItem({
19209 tooltip: this.prevText,
19211 icon : 'fa fa-step-backward',
19213 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
19215 //this.addSeparator();
19218 var field = this.navgroup.addItem( {
19220 cls : 'x-paging-position',
19222 html : this.beforePageText +
19223 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19224 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
19227 this.field = field.el.select('input', true).first();
19228 this.field.on("keydown", this.onPagingKeydown, this);
19229 this.field.on("focus", function(){this.dom.select();});
19232 this.afterTextEl = field.el.select('.x-paging-after',true).first();
19233 //this.field.setHeight(18);
19234 //this.addSeparator();
19235 this.next = this.navgroup.addItem({
19236 tooltip: this.nextText,
19238 html : ' <i class="fa fa-step-forward">',
19240 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
19242 this.last = this.navgroup.addItem({
19243 tooltip: this.lastText,
19244 icon : 'fa fa-forward',
19247 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
19249 //this.addSeparator();
19250 this.loading = this.navgroup.addItem({
19251 tooltip: this.refreshText,
19252 icon: 'fa fa-refresh',
19254 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19260 updateInfo : function(){
19261 if(this.displayEl){
19262 var count = this.ds.getCount();
19263 var msg = count == 0 ?
19267 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
19269 this.displayEl.update(msg);
19274 onLoad : function(ds, r, o){
19275 this.cursor = o.params ? o.params.start : 0;
19276 var d = this.getPageData(),
19280 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19281 this.field.dom.value = ap;
19282 this.first.setDisabled(ap == 1);
19283 this.prev.setDisabled(ap == 1);
19284 this.next.setDisabled(ap == ps);
19285 this.last.setDisabled(ap == ps);
19286 this.loading.enable();
19291 getPageData : function(){
19292 var total = this.ds.getTotalCount();
19295 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19296 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19301 onLoadError : function(){
19302 this.loading.enable();
19306 onPagingKeydown : function(e){
19307 var k = e.getKey();
19308 var d = this.getPageData();
19310 var v = this.field.dom.value, pageNum;
19311 if(!v || isNaN(pageNum = parseInt(v, 10))){
19312 this.field.dom.value = d.activePage;
19315 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19316 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19319 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))
19321 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19322 this.field.dom.value = pageNum;
19323 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19326 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19328 var v = this.field.dom.value, pageNum;
19329 var increment = (e.shiftKey) ? 10 : 1;
19330 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19332 if(!v || isNaN(pageNum = parseInt(v, 10))) {
19333 this.field.dom.value = d.activePage;
19336 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19338 this.field.dom.value = parseInt(v, 10) + increment;
19339 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19340 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19347 beforeLoad : function(){
19349 this.loading.disable();
19354 onClick : function(which){
19361 ds.load({params:{start: 0, limit: this.pageSize}});
19364 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19367 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19370 var total = ds.getTotalCount();
19371 var extra = total % this.pageSize;
19372 var lastStart = extra ? (total - extra) : total-this.pageSize;
19373 ds.load({params:{start: lastStart, limit: this.pageSize}});
19376 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19382 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19383 * @param {Roo.data.Store} store The data store to unbind
19385 unbind : function(ds){
19386 ds.un("beforeload", this.beforeLoad, this);
19387 ds.un("load", this.onLoad, this);
19388 ds.un("loadexception", this.onLoadError, this);
19389 ds.un("remove", this.updateInfo, this);
19390 ds.un("add", this.updateInfo, this);
19391 this.ds = undefined;
19395 * Binds the paging toolbar to the specified {@link Roo.data.Store}
19396 * @param {Roo.data.Store} store The data store to bind
19398 bind : function(ds){
19399 ds.on("beforeload", this.beforeLoad, this);
19400 ds.on("load", this.onLoad, this);
19401 ds.on("loadexception", this.onLoadError, this);
19402 ds.on("remove", this.updateInfo, this);
19403 ds.on("add", this.updateInfo, this);
19414 * @class Roo.bootstrap.MessageBar
19415 * @extends Roo.bootstrap.Component
19416 * Bootstrap MessageBar class
19417 * @cfg {String} html contents of the MessageBar
19418 * @cfg {String} weight (info | success | warning | danger) default info
19419 * @cfg {String} beforeClass insert the bar before the given class
19420 * @cfg {Boolean} closable (true | false) default false
19421 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19424 * Create a new Element
19425 * @param {Object} config The config object
19428 Roo.bootstrap.MessageBar = function(config){
19429 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19432 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
19438 beforeClass: 'bootstrap-sticky-wrap',
19440 getAutoCreate : function(){
19444 cls: 'alert alert-dismissable alert-' + this.weight,
19449 html: this.html || ''
19455 cfg.cls += ' alert-messages-fixed';
19469 onRender : function(ct, position)
19471 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19474 var cfg = Roo.apply({}, this.getAutoCreate());
19478 cfg.cls += ' ' + this.cls;
19481 cfg.style = this.style;
19483 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19485 this.el.setVisibilityMode(Roo.Element.DISPLAY);
19488 this.el.select('>button.close').on('click', this.hide, this);
19494 if (!this.rendered) {
19500 this.fireEvent('show', this);
19506 if (!this.rendered) {
19512 this.fireEvent('hide', this);
19515 update : function()
19517 // var e = this.el.dom.firstChild;
19519 // if(this.closable){
19520 // e = e.nextSibling;
19523 // e.data = this.html || '';
19525 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19541 * @class Roo.bootstrap.Graph
19542 * @extends Roo.bootstrap.Component
19543 * Bootstrap Graph class
19547 @cfg {String} graphtype bar | vbar | pie
19548 @cfg {number} g_x coodinator | centre x (pie)
19549 @cfg {number} g_y coodinator | centre y (pie)
19550 @cfg {number} g_r radius (pie)
19551 @cfg {number} g_height height of the chart (respected by all elements in the set)
19552 @cfg {number} g_width width of the chart (respected by all elements in the set)
19553 @cfg {Object} title The title of the chart
19556 -opts (object) options for the chart
19558 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19559 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19561 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.
19562 o stacked (boolean) whether or not to tread values as in a stacked bar chart
19564 o stretch (boolean)
19566 -opts (object) options for the pie
19569 o startAngle (number)
19570 o endAngle (number)
19574 * Create a new Input
19575 * @param {Object} config The config object
19578 Roo.bootstrap.Graph = function(config){
19579 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19585 * The img click event for the img.
19586 * @param {Roo.EventObject} e
19592 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
19603 //g_colors: this.colors,
19610 getAutoCreate : function(){
19621 onRender : function(ct,position){
19622 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19623 this.raphael = Raphael(this.el.dom);
19625 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19626 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19627 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19628 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19630 r.text(160, 10, "Single Series Chart").attr(txtattr);
19631 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19632 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19633 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19635 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19636 r.barchart(330, 10, 300, 220, data1);
19637 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19638 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19641 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19642 // r.barchart(30, 30, 560, 250, xdata, {
19643 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19644 // axis : "0 0 1 1",
19645 // axisxlabels : xdata
19646 // //yvalues : cols,
19649 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19651 // this.load(null,xdata,{
19652 // axis : "0 0 1 1",
19653 // axisxlabels : xdata
19658 load : function(graphtype,xdata,opts){
19659 this.raphael.clear();
19661 graphtype = this.graphtype;
19666 var r = this.raphael,
19667 fin = function () {
19668 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19670 fout = function () {
19671 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19673 pfin = function() {
19674 this.sector.stop();
19675 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19678 this.label[0].stop();
19679 this.label[0].attr({ r: 7.5 });
19680 this.label[1].attr({ "font-weight": 800 });
19683 pfout = function() {
19684 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19687 this.label[0].animate({ r: 5 }, 500, "bounce");
19688 this.label[1].attr({ "font-weight": 400 });
19694 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19697 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19700 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
19701 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19703 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19710 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19715 setTitle: function(o)
19720 initEvents: function() {
19723 this.el.on('click', this.onClick, this);
19727 onClick : function(e)
19729 Roo.log('img onclick');
19730 this.fireEvent('click', this, e);
19742 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19745 * @class Roo.bootstrap.dash.NumberBox
19746 * @extends Roo.bootstrap.Component
19747 * Bootstrap NumberBox class
19748 * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19749 * @cfg {String} headline Box headline
19750 * @cfg {String} content Box content
19751 * @cfg {String} icon Box icon
19752 * @cfg {String} footer Footer text
19753 * @cfg {String} fhref Footer href
19756 * Create a new NumberBox
19757 * @param {Object} config The config object
19761 Roo.bootstrap.dash.NumberBox = function(config){
19762 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19766 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
19776 getAutoCreate : function(){
19780 cls : 'small-box bg-' + this.bgcolor,
19788 cls : 'roo-headline',
19789 html : this.headline
19793 cls : 'roo-content',
19794 html : this.content
19808 cls : 'ion ' + this.icon
19817 cls : 'small-box-footer',
19818 href : this.fhref || '#',
19822 cfg.cn.push(footer);
19829 onRender : function(ct,position){
19830 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19837 setHeadline: function (value)
19839 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19842 setFooter: function (value, href)
19844 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19847 this.el.select('a.small-box-footer',true).first().attr('href', href);
19852 setContent: function (value)
19854 this.el.select('.roo-content',true).first().dom.innerHTML = value;
19857 initEvents: function()
19871 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19874 * @class Roo.bootstrap.dash.TabBox
19875 * @extends Roo.bootstrap.Component
19876 * Bootstrap TabBox class
19877 * @cfg {String} title Title of the TabBox
19878 * @cfg {String} icon Icon of the TabBox
19879 * @cfg {Boolean} showtabs (true|false) show the tabs default true
19882 * Create a new TabBox
19883 * @param {Object} config The config object
19887 Roo.bootstrap.dash.TabBox = function(config){
19888 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19893 * When a pane is added
19894 * @param {Roo.bootstrap.dash.TabPane} pane
19901 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
19907 getChildContainer : function()
19909 return this.el.select('.tab-content', true).first();
19912 getAutoCreate : function(){
19916 cls: 'pull-left header',
19924 cls: 'fa ' + this.icon
19931 cls: 'nav-tabs-custom',
19935 cls: 'nav nav-tabs pull-right',
19942 cls: 'tab-content no-padding',
19950 initEvents : function()
19952 //Roo.log('add add pane handler');
19953 this.on('addpane', this.onAddPane, this);
19956 * Updates the box title
19957 * @param {String} html to set the title to.
19959 setTitle : function(value)
19961 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19963 onAddPane : function(pane)
19965 //Roo.log('addpane');
19967 // tabs are rendere left to right..
19968 if(!this.showtabs){
19972 var ctr = this.el.select('.nav-tabs', true).first();
19975 var existing = ctr.select('.nav-tab',true);
19976 var qty = existing.getCount();;
19979 var tab = ctr.createChild({
19981 cls : 'nav-tab' + (qty ? '' : ' active'),
19989 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19992 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19994 pane.el.addClass('active');
19999 onTabClick : function(ev,un,ob,pane)
20001 //Roo.log('tab - prev default');
20002 ev.preventDefault();
20005 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20006 pane.tab.addClass('active');
20007 //Roo.log(pane.title);
20008 this.getChildContainer().select('.tab-pane',true).removeClass('active');
20009 // technically we should have a deactivate event.. but maybe add later.
20010 // and it should not de-activate the selected tab...
20012 pane.el.addClass('active');
20013 pane.fireEvent('activate');
20028 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20030 * @class Roo.bootstrap.TabPane
20031 * @extends Roo.bootstrap.Component
20032 * Bootstrap TabPane class
20033 * @cfg {Boolean} active (false | true) Default false
20034 * @cfg {String} title title of panel
20038 * Create a new TabPane
20039 * @param {Object} config The config object
20042 Roo.bootstrap.dash.TabPane = function(config){
20043 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20049 * When a pane is activated
20050 * @param {Roo.bootstrap.dash.TabPane} pane
20057 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
20062 // the tabBox that this is attached to.
20065 getAutoCreate : function()
20073 cfg.cls += ' active';
20078 initEvents : function()
20080 //Roo.log('trigger add pane handler');
20081 this.parent().fireEvent('addpane', this)
20085 * Updates the tab title
20086 * @param {String} html to set the title to.
20088 setTitle: function(str)
20094 this.tab.select('a', true).first().dom.innerHTML = str;
20111 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20114 * @class Roo.bootstrap.menu.Menu
20115 * @extends Roo.bootstrap.Component
20116 * Bootstrap Menu class - container for Menu
20117 * @cfg {String} html Text of the menu
20118 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20119 * @cfg {String} icon Font awesome icon
20120 * @cfg {String} pos Menu align to (top | bottom) default bottom
20124 * Create a new Menu
20125 * @param {Object} config The config object
20129 Roo.bootstrap.menu.Menu = function(config){
20130 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20134 * @event beforeshow
20135 * Fires before this menu is displayed
20136 * @param {Roo.bootstrap.menu.Menu} this
20140 * @event beforehide
20141 * Fires before this menu is hidden
20142 * @param {Roo.bootstrap.menu.Menu} this
20147 * Fires after this menu is displayed
20148 * @param {Roo.bootstrap.menu.Menu} this
20153 * Fires after this menu is hidden
20154 * @param {Roo.bootstrap.menu.Menu} this
20159 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20160 * @param {Roo.bootstrap.menu.Menu} this
20161 * @param {Roo.EventObject} e
20168 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
20172 weight : 'default',
20177 getChildContainer : function() {
20178 if(this.isSubMenu){
20182 return this.el.select('ul.dropdown-menu', true).first();
20185 getAutoCreate : function()
20190 cls : 'roo-menu-text',
20198 cls : 'fa ' + this.icon
20209 cls : 'dropdown-button btn btn-' + this.weight,
20214 cls : 'dropdown-toggle btn btn-' + this.weight,
20224 cls : 'dropdown-menu'
20230 if(this.pos == 'top'){
20231 cfg.cls += ' dropup';
20234 if(this.isSubMenu){
20237 cls : 'dropdown-menu'
20244 onRender : function(ct, position)
20246 this.isSubMenu = ct.hasClass('dropdown-submenu');
20248 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20251 initEvents : function()
20253 if(this.isSubMenu){
20257 this.hidden = true;
20259 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20260 this.triggerEl.on('click', this.onTriggerPress, this);
20262 this.buttonEl = this.el.select('button.dropdown-button', true).first();
20263 this.buttonEl.on('click', this.onClick, this);
20269 if(this.isSubMenu){
20273 return this.el.select('ul.dropdown-menu', true).first();
20276 onClick : function(e)
20278 this.fireEvent("click", this, e);
20281 onTriggerPress : function(e)
20283 if (this.isVisible()) {
20290 isVisible : function(){
20291 return !this.hidden;
20296 this.fireEvent("beforeshow", this);
20298 this.hidden = false;
20299 this.el.addClass('open');
20301 Roo.get(document).on("mouseup", this.onMouseUp, this);
20303 this.fireEvent("show", this);
20310 this.fireEvent("beforehide", this);
20312 this.hidden = true;
20313 this.el.removeClass('open');
20315 Roo.get(document).un("mouseup", this.onMouseUp);
20317 this.fireEvent("hide", this);
20320 onMouseUp : function()
20334 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20337 * @class Roo.bootstrap.menu.Item
20338 * @extends Roo.bootstrap.Component
20339 * Bootstrap MenuItem class
20340 * @cfg {Boolean} submenu (true | false) default false
20341 * @cfg {String} html text of the item
20342 * @cfg {String} href the link
20343 * @cfg {Boolean} disable (true | false) default false
20344 * @cfg {Boolean} preventDefault (true | false) default true
20345 * @cfg {String} icon Font awesome icon
20346 * @cfg {String} pos Submenu align to (left | right) default right
20350 * Create a new Item
20351 * @param {Object} config The config object
20355 Roo.bootstrap.menu.Item = function(config){
20356 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20360 * Fires when the mouse is hovering over this menu
20361 * @param {Roo.bootstrap.menu.Item} this
20362 * @param {Roo.EventObject} e
20367 * Fires when the mouse exits this menu
20368 * @param {Roo.bootstrap.menu.Item} this
20369 * @param {Roo.EventObject} e
20375 * The raw click event for the entire grid.
20376 * @param {Roo.EventObject} e
20382 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
20387 preventDefault: true,
20392 getAutoCreate : function()
20397 cls : 'roo-menu-item-text',
20405 cls : 'fa ' + this.icon
20414 href : this.href || '#',
20421 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20425 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20427 if(this.pos == 'left'){
20428 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20435 initEvents : function()
20437 this.el.on('mouseover', this.onMouseOver, this);
20438 this.el.on('mouseout', this.onMouseOut, this);
20440 this.el.select('a', true).first().on('click', this.onClick, this);
20444 onClick : function(e)
20446 if(this.preventDefault){
20447 e.preventDefault();
20450 this.fireEvent("click", this, e);
20453 onMouseOver : function(e)
20455 if(this.submenu && this.pos == 'left'){
20456 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20459 this.fireEvent("mouseover", this, e);
20462 onMouseOut : function(e)
20464 this.fireEvent("mouseout", this, e);
20476 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20479 * @class Roo.bootstrap.menu.Separator
20480 * @extends Roo.bootstrap.Component
20481 * Bootstrap Separator class
20484 * Create a new Separator
20485 * @param {Object} config The config object
20489 Roo.bootstrap.menu.Separator = function(config){
20490 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20493 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
20495 getAutoCreate : function(){