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){
11310 var dv = ''; // display value
11311 var vv = ''; // value value..
11313 if (this.displayField) {
11314 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11316 // this is an error condition!!!
11317 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
11320 if(this.valueField){
11321 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11324 if(this.hiddenField){
11325 this.hiddenField.dom.value = vv;
11327 this.lastSelectionText = dv;
11328 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11332 // no hidden field.. - we store the value in 'value', but still display
11333 // display field!!!!
11334 this.lastSelectionText = dv;
11335 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11341 reset : function(){
11342 // overridden so that last data is reset..
11343 this.setValue(this.originalValue);
11344 this.clearInvalid();
11345 this.lastData = false;
11347 this.view.clearSelections();
11351 findRecord : function(prop, value){
11353 if(this.store.getCount() > 0){
11354 this.store.each(function(r){
11355 if(r.data[prop] == value){
11365 getName: function()
11367 // returns hidden if it's set..
11368 if (!this.rendered) {return ''};
11369 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
11373 onViewMove : function(e, t){
11374 this.inKeyMode = false;
11378 onViewOver : function(e, t){
11379 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11382 var item = this.view.findItemFromChild(t);
11385 var index = this.view.indexOf(item);
11386 this.select(index, false);
11391 onViewClick : function(view, doFocus, el, e)
11393 var index = this.view.getSelectedIndexes()[0];
11395 var r = this.store.getAt(index);
11399 if(e.getTarget().nodeName.toLowerCase() != 'input'){
11406 Roo.each(this.tickItems, function(v,k){
11408 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11409 _this.tickItems.splice(k, 1);
11419 this.tickItems.push(r.data);
11424 this.onSelect(r, index);
11426 if(doFocus !== false && !this.blockFocus){
11427 this.inputEl().focus();
11432 restrictHeight : function(){
11433 //this.innerList.dom.style.height = '';
11434 //var inner = this.innerList.dom;
11435 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11436 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11437 //this.list.beginUpdate();
11438 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11439 this.list.alignTo(this.inputEl(), this.listAlign);
11440 this.list.alignTo(this.inputEl(), this.listAlign);
11441 //this.list.endUpdate();
11445 onEmptyResults : function(){
11450 * Returns true if the dropdown list is expanded, else false.
11452 isExpanded : function(){
11453 return this.list.isVisible();
11457 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11458 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11459 * @param {String} value The data value of the item to select
11460 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11461 * selected item if it is not currently in view (defaults to true)
11462 * @return {Boolean} True if the value matched an item in the list, else false
11464 selectByValue : function(v, scrollIntoView){
11465 if(v !== undefined && v !== null){
11466 var r = this.findRecord(this.valueField || this.displayField, v);
11468 this.select(this.store.indexOf(r), scrollIntoView);
11476 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11477 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11478 * @param {Number} index The zero-based index of the list item to select
11479 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11480 * selected item if it is not currently in view (defaults to true)
11482 select : function(index, scrollIntoView){
11483 this.selectedIndex = index;
11484 this.view.select(index);
11485 if(scrollIntoView !== false){
11486 var el = this.view.getNode(index);
11487 if(el && !this.multiple && !this.tickable){
11488 this.list.scrollChildIntoView(el, false);
11494 selectNext : function(){
11495 var ct = this.store.getCount();
11497 if(this.selectedIndex == -1){
11499 }else if(this.selectedIndex < ct-1){
11500 this.select(this.selectedIndex+1);
11506 selectPrev : function(){
11507 var ct = this.store.getCount();
11509 if(this.selectedIndex == -1){
11511 }else if(this.selectedIndex != 0){
11512 this.select(this.selectedIndex-1);
11518 onKeyUp : function(e){
11519 if(this.editable !== false && !e.isSpecialKey()){
11520 this.lastKey = e.getKey();
11521 this.dqTask.delay(this.queryDelay);
11526 validateBlur : function(){
11527 return !this.list || !this.list.isVisible();
11531 initQuery : function(){
11532 this.doQuery(this.getRawValue());
11536 doForce : function(){
11537 if(this.inputEl().dom.value.length > 0){
11538 this.inputEl().dom.value =
11539 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11545 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
11546 * query allowing the query action to be canceled if needed.
11547 * @param {String} query The SQL query to execute
11548 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11549 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
11550 * saved in the current store (defaults to false)
11552 doQuery : function(q, forceAll){
11554 if(q === undefined || q === null){
11559 forceAll: forceAll,
11563 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11568 forceAll = qe.forceAll;
11569 if(forceAll === true || (q.length >= this.minChars)){
11571 this.hasQuery = true;
11573 if(this.lastQuery != q || this.alwaysQuery){
11574 this.lastQuery = q;
11575 if(this.mode == 'local'){
11576 this.selectedIndex = -1;
11578 this.store.clearFilter();
11580 this.store.filter(this.displayField, q);
11584 this.store.baseParams[this.queryParam] = q;
11586 var options = {params : this.getParams(q)};
11589 options.add = true;
11590 options.params.start = this.page * this.pageSize;
11593 this.store.load(options);
11595 * this code will make the page width larger, at the beginning, the list not align correctly,
11596 * we should expand the list on onLoad
11597 * so command out it
11602 this.selectedIndex = -1;
11607 this.loadNext = false;
11611 getParams : function(q){
11613 //p[this.queryParam] = q;
11617 p.limit = this.pageSize;
11623 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11625 collapse : function(){
11626 if(!this.isExpanded()){
11634 this.cancelBtn.hide();
11635 this.trigger.show();
11638 Roo.get(document).un('mousedown', this.collapseIf, this);
11639 Roo.get(document).un('mousewheel', this.collapseIf, this);
11640 if (!this.editable) {
11641 Roo.get(document).un('keydown', this.listKeyPress, this);
11643 this.fireEvent('collapse', this);
11647 collapseIf : function(e){
11648 var in_combo = e.within(this.el);
11649 var in_list = e.within(this.list);
11650 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11652 if (in_combo || in_list || is_list) {
11653 //e.stopPropagation();
11658 this.onTickableFooterButtonClick(e, false, false);
11666 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11668 expand : function(){
11670 if(this.isExpanded() || !this.hasFocus){
11674 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11675 this.list.setWidth(lw);
11682 this.restrictHeight();
11686 this.tickItems = Roo.apply([], this.item);
11689 this.cancelBtn.show();
11690 this.trigger.hide();
11694 Roo.get(document).on('mousedown', this.collapseIf, this);
11695 Roo.get(document).on('mousewheel', this.collapseIf, this);
11696 if (!this.editable) {
11697 Roo.get(document).on('keydown', this.listKeyPress, this);
11700 this.fireEvent('expand', this);
11704 // Implements the default empty TriggerField.onTriggerClick function
11705 onTriggerClick : function(e)
11707 Roo.log('trigger click');
11709 if(this.disabled || !this.triggerList){
11714 this.loadNext = false;
11716 if(this.isExpanded()){
11718 if (!this.blockFocus) {
11719 this.inputEl().focus();
11723 this.hasFocus = true;
11724 if(this.triggerAction == 'all') {
11725 this.doQuery(this.allQuery, true);
11727 this.doQuery(this.getRawValue());
11729 if (!this.blockFocus) {
11730 this.inputEl().focus();
11735 onTickableTriggerClick : function(e)
11742 this.loadNext = false;
11743 this.hasFocus = true;
11745 if(this.triggerAction == 'all') {
11746 this.doQuery(this.allQuery, true);
11748 this.doQuery(this.getRawValue());
11752 onSearchFieldClick : function(e)
11754 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11759 this.loadNext = false;
11760 this.hasFocus = true;
11762 if(this.triggerAction == 'all') {
11763 this.doQuery(this.allQuery, true);
11765 this.doQuery(this.getRawValue());
11769 listKeyPress : function(e)
11771 //Roo.log('listkeypress');
11772 // scroll to first matching element based on key pres..
11773 if (e.isSpecialKey()) {
11776 var k = String.fromCharCode(e.getKey()).toUpperCase();
11779 var csel = this.view.getSelectedNodes();
11780 var cselitem = false;
11782 var ix = this.view.indexOf(csel[0]);
11783 cselitem = this.store.getAt(ix);
11784 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11790 this.store.each(function(v) {
11792 // start at existing selection.
11793 if (cselitem.id == v.id) {
11799 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11800 match = this.store.indexOf(v);
11806 if (match === false) {
11807 return true; // no more action?
11810 this.view.select(match);
11811 var sn = Roo.get(this.view.getSelectedNodes()[0])
11812 //sn.scrollIntoView(sn.dom.parentNode, false);
11815 onViewScroll : function(e, t){
11817 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){
11821 this.hasQuery = true;
11823 this.loading = this.list.select('.loading', true).first();
11825 if(this.loading === null){
11826 this.list.createChild({
11828 cls: 'loading select2-more-results select2-active',
11829 html: 'Loading more results...'
11832 this.loading = this.list.select('.loading', true).first();
11834 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11836 this.loading.hide();
11839 this.loading.show();
11844 this.loadNext = true;
11846 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11851 addItem : function(o)
11853 var dv = ''; // display value
11855 if (this.displayField) {
11856 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11858 // this is an error condition!!!
11859 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
11866 var choice = this.choices.createChild({
11868 cls: 'select2-search-choice',
11877 cls: 'select2-search-choice-close',
11882 }, this.searchField);
11884 var close = choice.select('a.select2-search-choice-close', true).first()
11886 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11894 this.inputEl().dom.value = '';
11898 onRemoveItem : function(e, _self, o)
11900 e.preventDefault();
11901 var index = this.item.indexOf(o.data) * 1;
11904 Roo.log('not this item?!');
11908 this.item.splice(index, 1);
11913 this.fireEvent('remove', this, e);
11917 syncValue : function()
11919 if(!this.item.length){
11926 Roo.each(this.item, function(i){
11927 if(_this.valueField){
11928 value.push(i[_this.valueField]);
11935 this.value = value.join(',');
11937 if(this.hiddenField){
11938 this.hiddenField.dom.value = this.value;
11942 clearItem : function()
11944 if(!this.multiple){
11950 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11957 inputEl: function ()
11960 return this.searchField;
11962 return this.el.select('input.form-control',true).first();
11966 onTickableFooterButtonClick : function(e, btn, el)
11968 e.preventDefault();
11970 if(btn && btn.name == 'cancel'){
11971 this.tickItems = Roo.apply([], this.item);
11980 Roo.each(this.tickItems, function(o){
11991 * @cfg {Boolean} grow
11995 * @cfg {Number} growMin
11999 * @cfg {Number} growMax
12009 * Ext JS Library 1.1.1
12010 * Copyright(c) 2006-2007, Ext JS, LLC.
12012 * Originally Released Under LGPL - original licence link has changed is not relivant.
12015 * <script type="text/javascript">
12020 * @extends Roo.util.Observable
12021 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
12022 * This class also supports single and multi selection modes. <br>
12023 * Create a data model bound view:
12025 var store = new Roo.data.Store(...);
12027 var view = new Roo.View({
12029 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
12031 singleSelect: true,
12032 selectedClass: "ydataview-selected",
12036 // listen for node click?
12037 view.on("click", function(vw, index, node, e){
12038 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12042 dataModel.load("foobar.xml");
12044 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12046 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12047 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12049 * Note: old style constructor is still suported (container, template, config)
12052 * Create a new View
12053 * @param {Object} config The config object
12056 Roo.View = function(config, depreciated_tpl, depreciated_config){
12058 this.parent = false;
12060 if (typeof(depreciated_tpl) == 'undefined') {
12061 // new way.. - universal constructor.
12062 Roo.apply(this, config);
12063 this.el = Roo.get(this.el);
12066 this.el = Roo.get(config);
12067 this.tpl = depreciated_tpl;
12068 Roo.apply(this, depreciated_config);
12070 this.wrapEl = this.el.wrap().wrap();
12071 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12074 if(typeof(this.tpl) == "string"){
12075 this.tpl = new Roo.Template(this.tpl);
12077 // support xtype ctors..
12078 this.tpl = new Roo.factory(this.tpl, Roo);
12082 this.tpl.compile();
12087 * @event beforeclick
12088 * Fires before a click is processed. Returns false to cancel the default action.
12089 * @param {Roo.View} this
12090 * @param {Number} index The index of the target node
12091 * @param {HTMLElement} node The target node
12092 * @param {Roo.EventObject} e The raw event object
12094 "beforeclick" : true,
12097 * Fires when a template node is clicked.
12098 * @param {Roo.View} this
12099 * @param {Number} index The index of the target node
12100 * @param {HTMLElement} node The target node
12101 * @param {Roo.EventObject} e The raw event object
12106 * Fires when a template node is double clicked.
12107 * @param {Roo.View} this
12108 * @param {Number} index The index of the target node
12109 * @param {HTMLElement} node The target node
12110 * @param {Roo.EventObject} e The raw event object
12114 * @event contextmenu
12115 * Fires when a template node is right clicked.
12116 * @param {Roo.View} this
12117 * @param {Number} index The index of the target node
12118 * @param {HTMLElement} node The target node
12119 * @param {Roo.EventObject} e The raw event object
12121 "contextmenu" : true,
12123 * @event selectionchange
12124 * Fires when the selected nodes change.
12125 * @param {Roo.View} this
12126 * @param {Array} selections Array of the selected nodes
12128 "selectionchange" : true,
12131 * @event beforeselect
12132 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12133 * @param {Roo.View} this
12134 * @param {HTMLElement} node The node to be selected
12135 * @param {Array} selections Array of currently selected nodes
12137 "beforeselect" : true,
12139 * @event preparedata
12140 * Fires on every row to render, to allow you to change the data.
12141 * @param {Roo.View} this
12142 * @param {Object} data to be rendered (change this)
12144 "preparedata" : true
12152 "click": this.onClick,
12153 "dblclick": this.onDblClick,
12154 "contextmenu": this.onContextMenu,
12158 this.selections = [];
12160 this.cmp = new Roo.CompositeElementLite([]);
12162 this.store = Roo.factory(this.store, Roo.data);
12163 this.setStore(this.store, true);
12166 if ( this.footer && this.footer.xtype) {
12168 var fctr = this.wrapEl.appendChild(document.createElement("div"));
12170 this.footer.dataSource = this.store
12171 this.footer.container = fctr;
12172 this.footer = Roo.factory(this.footer, Roo);
12173 fctr.insertFirst(this.el);
12175 // this is a bit insane - as the paging toolbar seems to detach the el..
12176 // dom.parentNode.parentNode.parentNode
12177 // they get detached?
12181 Roo.View.superclass.constructor.call(this);
12186 Roo.extend(Roo.View, Roo.util.Observable, {
12189 * @cfg {Roo.data.Store} store Data store to load data from.
12194 * @cfg {String|Roo.Element} el The container element.
12199 * @cfg {String|Roo.Template} tpl The template used by this View
12203 * @cfg {String} dataName the named area of the template to use as the data area
12204 * Works with domtemplates roo-name="name"
12208 * @cfg {String} selectedClass The css class to add to selected nodes
12210 selectedClass : "x-view-selected",
12212 * @cfg {String} emptyText The empty text to show when nothing is loaded.
12217 * @cfg {String} text to display on mask (default Loading)
12221 * @cfg {Boolean} multiSelect Allow multiple selection
12223 multiSelect : false,
12225 * @cfg {Boolean} singleSelect Allow single selection
12227 singleSelect: false,
12230 * @cfg {Boolean} toggleSelect - selecting
12232 toggleSelect : false,
12235 * @cfg {Boolean} tickable - selecting
12240 * Returns the element this view is bound to.
12241 * @return {Roo.Element}
12243 getEl : function(){
12244 return this.wrapEl;
12250 * Refreshes the view. - called by datachanged on the store. - do not call directly.
12252 refresh : function(){
12253 Roo.log('refresh');
12256 // if we are using something like 'domtemplate', then
12257 // the what gets used is:
12258 // t.applySubtemplate(NAME, data, wrapping data..)
12259 // the outer template then get' applied with
12260 // the store 'extra data'
12261 // and the body get's added to the
12262 // roo-name="data" node?
12263 // <span class='roo-tpl-{name}'></span> ?????
12267 this.clearSelections();
12268 this.el.update("");
12270 var records = this.store.getRange();
12271 if(records.length < 1) {
12273 // is this valid?? = should it render a template??
12275 this.el.update(this.emptyText);
12279 if (this.dataName) {
12280 this.el.update(t.apply(this.store.meta)); //????
12281 el = this.el.child('.roo-tpl-' + this.dataName);
12284 for(var i = 0, len = records.length; i < len; i++){
12285 var data = this.prepareData(records[i].data, i, records[i]);
12286 this.fireEvent("preparedata", this, data, i, records[i]);
12288 var d = Roo.apply({}, data);
12291 Roo.apply(d, {'roo-id' : Roo.id()});
12295 Roo.each(this.parent.item, function(item){
12296 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12299 Roo.apply(d, {'roo-data-checked' : 'checked'});
12303 html[html.length] = Roo.util.Format.trim(
12305 t.applySubtemplate(this.dataName, d, this.store.meta) :
12312 el.update(html.join(""));
12313 this.nodes = el.dom.childNodes;
12314 this.updateIndexes(0);
12319 * Function to override to reformat the data that is sent to
12320 * the template for each node.
12321 * DEPRICATED - use the preparedata event handler.
12322 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12323 * a JSON object for an UpdateManager bound view).
12325 prepareData : function(data, index, record)
12327 this.fireEvent("preparedata", this, data, index, record);
12331 onUpdate : function(ds, record){
12332 Roo.log('on update');
12333 this.clearSelections();
12334 var index = this.store.indexOf(record);
12335 var n = this.nodes[index];
12336 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12337 n.parentNode.removeChild(n);
12338 this.updateIndexes(index, index);
12344 onAdd : function(ds, records, index)
12346 Roo.log(['on Add', ds, records, index] );
12347 this.clearSelections();
12348 if(this.nodes.length == 0){
12352 var n = this.nodes[index];
12353 for(var i = 0, len = records.length; i < len; i++){
12354 var d = this.prepareData(records[i].data, i, records[i]);
12356 this.tpl.insertBefore(n, d);
12359 this.tpl.append(this.el, d);
12362 this.updateIndexes(index);
12365 onRemove : function(ds, record, index){
12366 Roo.log('onRemove');
12367 this.clearSelections();
12368 var el = this.dataName ?
12369 this.el.child('.roo-tpl-' + this.dataName) :
12372 el.dom.removeChild(this.nodes[index]);
12373 this.updateIndexes(index);
12377 * Refresh an individual node.
12378 * @param {Number} index
12380 refreshNode : function(index){
12381 this.onUpdate(this.store, this.store.getAt(index));
12384 updateIndexes : function(startIndex, endIndex){
12385 var ns = this.nodes;
12386 startIndex = startIndex || 0;
12387 endIndex = endIndex || ns.length - 1;
12388 for(var i = startIndex; i <= endIndex; i++){
12389 ns[i].nodeIndex = i;
12394 * Changes the data store this view uses and refresh the view.
12395 * @param {Store} store
12397 setStore : function(store, initial){
12398 if(!initial && this.store){
12399 this.store.un("datachanged", this.refresh);
12400 this.store.un("add", this.onAdd);
12401 this.store.un("remove", this.onRemove);
12402 this.store.un("update", this.onUpdate);
12403 this.store.un("clear", this.refresh);
12404 this.store.un("beforeload", this.onBeforeLoad);
12405 this.store.un("load", this.onLoad);
12406 this.store.un("loadexception", this.onLoad);
12410 store.on("datachanged", this.refresh, this);
12411 store.on("add", this.onAdd, this);
12412 store.on("remove", this.onRemove, this);
12413 store.on("update", this.onUpdate, this);
12414 store.on("clear", this.refresh, this);
12415 store.on("beforeload", this.onBeforeLoad, this);
12416 store.on("load", this.onLoad, this);
12417 store.on("loadexception", this.onLoad, this);
12425 * onbeforeLoad - masks the loading area.
12428 onBeforeLoad : function(store,opts)
12430 Roo.log('onBeforeLoad');
12432 this.el.update("");
12434 this.el.mask(this.mask ? this.mask : "Loading" );
12436 onLoad : function ()
12443 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12444 * @param {HTMLElement} node
12445 * @return {HTMLElement} The template node
12447 findItemFromChild : function(node){
12448 var el = this.dataName ?
12449 this.el.child('.roo-tpl-' + this.dataName,true) :
12452 if(!node || node.parentNode == el){
12455 var p = node.parentNode;
12456 while(p && p != el){
12457 if(p.parentNode == el){
12466 onClick : function(e){
12467 var item = this.findItemFromChild(e.getTarget());
12469 var index = this.indexOf(item);
12470 if(this.onItemClick(item, index, e) !== false){
12471 this.fireEvent("click", this, index, item, e);
12474 this.clearSelections();
12479 onContextMenu : function(e){
12480 var item = this.findItemFromChild(e.getTarget());
12482 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12487 onDblClick : function(e){
12488 var item = this.findItemFromChild(e.getTarget());
12490 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12494 onItemClick : function(item, index, e)
12496 if(this.fireEvent("beforeclick", this, index, item, e) === false){
12499 if (this.toggleSelect) {
12500 var m = this.isSelected(item) ? 'unselect' : 'select';
12503 _t[m](item, true, false);
12506 if(this.multiSelect || this.singleSelect){
12507 if(this.multiSelect && e.shiftKey && this.lastSelection){
12508 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12510 this.select(item, this.multiSelect && e.ctrlKey);
12511 this.lastSelection = item;
12514 if(!this.tickable){
12515 e.preventDefault();
12523 * Get the number of selected nodes.
12526 getSelectionCount : function(){
12527 return this.selections.length;
12531 * Get the currently selected nodes.
12532 * @return {Array} An array of HTMLElements
12534 getSelectedNodes : function(){
12535 return this.selections;
12539 * Get the indexes of the selected nodes.
12542 getSelectedIndexes : function(){
12543 var indexes = [], s = this.selections;
12544 for(var i = 0, len = s.length; i < len; i++){
12545 indexes.push(s[i].nodeIndex);
12551 * Clear all selections
12552 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12554 clearSelections : function(suppressEvent){
12555 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12556 this.cmp.elements = this.selections;
12557 this.cmp.removeClass(this.selectedClass);
12558 this.selections = [];
12559 if(!suppressEvent){
12560 this.fireEvent("selectionchange", this, this.selections);
12566 * Returns true if the passed node is selected
12567 * @param {HTMLElement/Number} node The node or node index
12568 * @return {Boolean}
12570 isSelected : function(node){
12571 var s = this.selections;
12575 node = this.getNode(node);
12576 return s.indexOf(node) !== -1;
12581 * @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
12582 * @param {Boolean} keepExisting (optional) true to keep existing selections
12583 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12585 select : function(nodeInfo, keepExisting, suppressEvent){
12586 if(nodeInfo instanceof Array){
12588 this.clearSelections(true);
12590 for(var i = 0, len = nodeInfo.length; i < len; i++){
12591 this.select(nodeInfo[i], true, true);
12595 var node = this.getNode(nodeInfo);
12596 if(!node || this.isSelected(node)){
12597 return; // already selected.
12600 this.clearSelections(true);
12602 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12603 Roo.fly(node).addClass(this.selectedClass);
12604 this.selections.push(node);
12605 if(!suppressEvent){
12606 this.fireEvent("selectionchange", this, this.selections);
12614 * @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
12615 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12616 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12618 unselect : function(nodeInfo, keepExisting, suppressEvent)
12620 if(nodeInfo instanceof Array){
12621 Roo.each(this.selections, function(s) {
12622 this.unselect(s, nodeInfo);
12626 var node = this.getNode(nodeInfo);
12627 if(!node || !this.isSelected(node)){
12628 Roo.log("not selected");
12629 return; // not selected.
12633 Roo.each(this.selections, function(s) {
12635 Roo.fly(node).removeClass(this.selectedClass);
12642 this.selections= ns;
12643 this.fireEvent("selectionchange", this, this.selections);
12647 * Gets a template node.
12648 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12649 * @return {HTMLElement} The node or null if it wasn't found
12651 getNode : function(nodeInfo){
12652 if(typeof nodeInfo == "string"){
12653 return document.getElementById(nodeInfo);
12654 }else if(typeof nodeInfo == "number"){
12655 return this.nodes[nodeInfo];
12661 * Gets a range template nodes.
12662 * @param {Number} startIndex
12663 * @param {Number} endIndex
12664 * @return {Array} An array of nodes
12666 getNodes : function(start, end){
12667 var ns = this.nodes;
12668 start = start || 0;
12669 end = typeof end == "undefined" ? ns.length - 1 : end;
12672 for(var i = start; i <= end; i++){
12676 for(var i = start; i >= end; i--){
12684 * Finds the index of the passed node
12685 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12686 * @return {Number} The index of the node or -1
12688 indexOf : function(node){
12689 node = this.getNode(node);
12690 if(typeof node.nodeIndex == "number"){
12691 return node.nodeIndex;
12693 var ns = this.nodes;
12694 for(var i = 0, len = ns.length; i < len; i++){
12705 * based on jquery fullcalendar
12709 Roo.bootstrap = Roo.bootstrap || {};
12711 * @class Roo.bootstrap.Calendar
12712 * @extends Roo.bootstrap.Component
12713 * Bootstrap Calendar class
12714 * @cfg {Boolean} loadMask (true|false) default false
12715 * @cfg {Object} header generate the user specific header of the calendar, default false
12718 * Create a new Container
12719 * @param {Object} config The config object
12724 Roo.bootstrap.Calendar = function(config){
12725 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12729 * Fires when a date is selected
12730 * @param {DatePicker} this
12731 * @param {Date} date The selected date
12735 * @event monthchange
12736 * Fires when the displayed month changes
12737 * @param {DatePicker} this
12738 * @param {Date} date The selected month
12740 'monthchange': true,
12742 * @event evententer
12743 * Fires when mouse over an event
12744 * @param {Calendar} this
12745 * @param {event} Event
12747 'evententer': true,
12749 * @event eventleave
12750 * Fires when the mouse leaves an
12751 * @param {Calendar} this
12754 'eventleave': true,
12756 * @event eventclick
12757 * Fires when the mouse click an
12758 * @param {Calendar} this
12767 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
12770 * @cfg {Number} startDay
12771 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12779 getAutoCreate : function(){
12782 var fc_button = function(name, corner, style, content ) {
12783 return Roo.apply({},{
12785 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
12787 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12790 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12801 style : 'width:100%',
12808 cls : 'fc-header-left',
12810 fc_button('prev', 'left', 'arrow', '‹' ),
12811 fc_button('next', 'right', 'arrow', '›' ),
12812 { tag: 'span', cls: 'fc-header-space' },
12813 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
12821 cls : 'fc-header-center',
12825 cls: 'fc-header-title',
12828 html : 'month / year'
12836 cls : 'fc-header-right',
12838 /* fc_button('month', 'left', '', 'month' ),
12839 fc_button('week', '', '', 'week' ),
12840 fc_button('day', 'right', '', 'day' )
12852 header = this.header;
12855 var cal_heads = function() {
12857 // fixme - handle this.
12859 for (var i =0; i < Date.dayNames.length; i++) {
12860 var d = Date.dayNames[i];
12863 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12864 html : d.substring(0,3)
12868 ret[0].cls += ' fc-first';
12869 ret[6].cls += ' fc-last';
12872 var cal_cell = function(n) {
12875 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12880 cls: 'fc-day-number',
12884 cls: 'fc-day-content',
12888 style: 'position: relative;' // height: 17px;
12900 var cal_rows = function() {
12903 for (var r = 0; r < 6; r++) {
12910 for (var i =0; i < Date.dayNames.length; i++) {
12911 var d = Date.dayNames[i];
12912 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12915 row.cn[0].cls+=' fc-first';
12916 row.cn[0].cn[0].style = 'min-height:90px';
12917 row.cn[6].cls+=' fc-last';
12921 ret[0].cls += ' fc-first';
12922 ret[4].cls += ' fc-prev-last';
12923 ret[5].cls += ' fc-last';
12930 cls: 'fc-border-separate',
12931 style : 'width:100%',
12939 cls : 'fc-first fc-last',
12957 cls : 'fc-content',
12958 style : "position: relative;",
12961 cls : 'fc-view fc-view-month fc-grid',
12962 style : 'position: relative',
12963 unselectable : 'on',
12966 cls : 'fc-event-container',
12967 style : 'position:absolute;z-index:8;top:0;left:0;'
12985 initEvents : function()
12988 throw "can not find store for calendar";
12994 style: "text-align:center",
12998 style: "background-color:white;width:50%;margin:250 auto",
13002 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
13013 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13015 var size = this.el.select('.fc-content', true).first().getSize();
13016 this.maskEl.setSize(size.width, size.height);
13017 this.maskEl.enableDisplayMode("block");
13018 if(!this.loadMask){
13019 this.maskEl.hide();
13022 this.store = Roo.factory(this.store, Roo.data);
13023 this.store.on('load', this.onLoad, this);
13024 this.store.on('beforeload', this.onBeforeLoad, this);
13028 this.cells = this.el.select('.fc-day',true);
13029 //Roo.log(this.cells);
13030 this.textNodes = this.el.query('.fc-day-number');
13031 this.cells.addClassOnOver('fc-state-hover');
13033 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13034 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13035 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13036 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13038 this.on('monthchange', this.onMonthChange, this);
13040 this.update(new Date().clearTime());
13043 resize : function() {
13044 var sz = this.el.getSize();
13046 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13047 this.el.select('.fc-day-content div',true).setHeight(34);
13052 showPrevMonth : function(e){
13053 this.update(this.activeDate.add("mo", -1));
13055 showToday : function(e){
13056 this.update(new Date().clearTime());
13059 showNextMonth : function(e){
13060 this.update(this.activeDate.add("mo", 1));
13064 showPrevYear : function(){
13065 this.update(this.activeDate.add("y", -1));
13069 showNextYear : function(){
13070 this.update(this.activeDate.add("y", 1));
13075 update : function(date)
13077 var vd = this.activeDate;
13078 this.activeDate = date;
13079 // if(vd && this.el){
13080 // var t = date.getTime();
13081 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13082 // Roo.log('using add remove');
13084 // this.fireEvent('monthchange', this, date);
13086 // this.cells.removeClass("fc-state-highlight");
13087 // this.cells.each(function(c){
13088 // if(c.dateValue == t){
13089 // c.addClass("fc-state-highlight");
13090 // setTimeout(function(){
13091 // try{c.dom.firstChild.focus();}catch(e){}
13101 var days = date.getDaysInMonth();
13103 var firstOfMonth = date.getFirstDateOfMonth();
13104 var startingPos = firstOfMonth.getDay()-this.startDay;
13106 if(startingPos < this.startDay){
13110 var pm = date.add(Date.MONTH, -1);
13111 var prevStart = pm.getDaysInMonth()-startingPos;
13113 this.cells = this.el.select('.fc-day',true);
13114 this.textNodes = this.el.query('.fc-day-number');
13115 this.cells.addClassOnOver('fc-state-hover');
13117 var cells = this.cells.elements;
13118 var textEls = this.textNodes;
13120 Roo.each(cells, function(cell){
13121 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13124 days += startingPos;
13126 // convert everything to numbers so it's fast
13127 var day = 86400000;
13128 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13131 //Roo.log(prevStart);
13133 var today = new Date().clearTime().getTime();
13134 var sel = date.clearTime().getTime();
13135 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13136 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13137 var ddMatch = this.disabledDatesRE;
13138 var ddText = this.disabledDatesText;
13139 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13140 var ddaysText = this.disabledDaysText;
13141 var format = this.format;
13143 var setCellClass = function(cal, cell){
13147 //Roo.log('set Cell Class');
13149 var t = d.getTime();
13153 cell.dateValue = t;
13155 cell.className += " fc-today";
13156 cell.className += " fc-state-highlight";
13157 cell.title = cal.todayText;
13160 // disable highlight in other month..
13161 //cell.className += " fc-state-highlight";
13166 cell.className = " fc-state-disabled";
13167 cell.title = cal.minText;
13171 cell.className = " fc-state-disabled";
13172 cell.title = cal.maxText;
13176 if(ddays.indexOf(d.getDay()) != -1){
13177 cell.title = ddaysText;
13178 cell.className = " fc-state-disabled";
13181 if(ddMatch && format){
13182 var fvalue = d.dateFormat(format);
13183 if(ddMatch.test(fvalue)){
13184 cell.title = ddText.replace("%0", fvalue);
13185 cell.className = " fc-state-disabled";
13189 if (!cell.initialClassName) {
13190 cell.initialClassName = cell.dom.className;
13193 cell.dom.className = cell.initialClassName + ' ' + cell.className;
13198 for(; i < startingPos; i++) {
13199 textEls[i].innerHTML = (++prevStart);
13200 d.setDate(d.getDate()+1);
13202 cells[i].className = "fc-past fc-other-month";
13203 setCellClass(this, cells[i]);
13208 for(; i < days; i++){
13209 intDay = i - startingPos + 1;
13210 textEls[i].innerHTML = (intDay);
13211 d.setDate(d.getDate()+1);
13213 cells[i].className = ''; // "x-date-active";
13214 setCellClass(this, cells[i]);
13218 for(; i < 42; i++) {
13219 textEls[i].innerHTML = (++extraDays);
13220 d.setDate(d.getDate()+1);
13222 cells[i].className = "fc-future fc-other-month";
13223 setCellClass(this, cells[i]);
13226 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13228 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13230 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13231 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13233 if(totalRows != 6){
13234 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13235 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13238 this.fireEvent('monthchange', this, date);
13242 if(!this.internalRender){
13243 var main = this.el.dom.firstChild;
13244 var w = main.offsetWidth;
13245 this.el.setWidth(w + this.el.getBorderWidth("lr"));
13246 Roo.fly(main).setWidth(w);
13247 this.internalRender = true;
13248 // opera does not respect the auto grow header center column
13249 // then, after it gets a width opera refuses to recalculate
13250 // without a second pass
13251 if(Roo.isOpera && !this.secondPass){
13252 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13253 this.secondPass = true;
13254 this.update.defer(10, this, [date]);
13261 findCell : function(dt) {
13262 dt = dt.clearTime().getTime();
13264 this.cells.each(function(c){
13265 //Roo.log("check " +c.dateValue + '?=' + dt);
13266 if(c.dateValue == dt){
13276 findCells : function(ev) {
13277 var s = ev.start.clone().clearTime().getTime();
13279 var e= ev.end.clone().clearTime().getTime();
13282 this.cells.each(function(c){
13283 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13285 if(c.dateValue > e){
13288 if(c.dateValue < s){
13297 // findBestRow: function(cells)
13301 // for (var i =0 ; i < cells.length;i++) {
13302 // ret = Math.max(cells[i].rows || 0,ret);
13309 addItem : function(ev)
13311 // look for vertical location slot in
13312 var cells = this.findCells(ev);
13314 // ev.row = this.findBestRow(cells);
13316 // work out the location.
13320 for(var i =0; i < cells.length; i++) {
13322 cells[i].row = cells[0].row;
13325 cells[i].row = cells[i].row + 1;
13335 if (crow.start.getY() == cells[i].getY()) {
13337 crow.end = cells[i];
13354 cells[0].events.push(ev);
13356 this.calevents.push(ev);
13359 clearEvents: function() {
13361 if(!this.calevents){
13365 Roo.each(this.cells.elements, function(c){
13371 Roo.each(this.calevents, function(e) {
13372 Roo.each(e.els, function(el) {
13373 el.un('mouseenter' ,this.onEventEnter, this);
13374 el.un('mouseleave' ,this.onEventLeave, this);
13379 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13385 renderEvents: function()
13389 this.cells.each(function(c) {
13398 if(c.row != c.events.length){
13399 r = 4 - (4 - (c.row - c.events.length));
13402 c.events = ev.slice(0, r);
13403 c.more = ev.slice(r);
13405 if(c.more.length && c.more.length == 1){
13406 c.events.push(c.more.pop());
13409 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13413 this.cells.each(function(c) {
13415 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13418 for (var e = 0; e < c.events.length; e++){
13419 var ev = c.events[e];
13420 var rows = ev.rows;
13422 for(var i = 0; i < rows.length; i++) {
13424 // how many rows should it span..
13427 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13428 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13430 unselectable : "on",
13433 cls: 'fc-event-inner',
13437 // cls: 'fc-event-time',
13438 // html : cells.length > 1 ? '' : ev.time
13442 cls: 'fc-event-title',
13443 html : String.format('{0}', ev.title)
13450 cls: 'ui-resizable-handle ui-resizable-e',
13451 html : '  '
13458 cfg.cls += ' fc-event-start';
13460 if ((i+1) == rows.length) {
13461 cfg.cls += ' fc-event-end';
13464 var ctr = _this.el.select('.fc-event-container',true).first();
13465 var cg = ctr.createChild(cfg);
13467 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13468 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13470 var r = (c.more.length) ? 1 : 0;
13471 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
13472 cg.setWidth(ebox.right - sbox.x -2);
13474 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13475 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13476 cg.on('click', _this.onEventClick, _this, ev);
13487 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13488 style : 'position: absolute',
13489 unselectable : "on",
13492 cls: 'fc-event-inner',
13496 cls: 'fc-event-title',
13504 cls: 'ui-resizable-handle ui-resizable-e',
13505 html : '  '
13511 var ctr = _this.el.select('.fc-event-container',true).first();
13512 var cg = ctr.createChild(cfg);
13514 var sbox = c.select('.fc-day-content',true).first().getBox();
13515 var ebox = c.select('.fc-day-content',true).first().getBox();
13517 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
13518 cg.setWidth(ebox.right - sbox.x -2);
13520 cg.on('click', _this.onMoreEventClick, _this, c.more);
13530 onEventEnter: function (e, el,event,d) {
13531 this.fireEvent('evententer', this, el, event);
13534 onEventLeave: function (e, el,event,d) {
13535 this.fireEvent('eventleave', this, el, event);
13538 onEventClick: function (e, el,event,d) {
13539 this.fireEvent('eventclick', this, el, event);
13542 onMonthChange: function () {
13546 onMoreEventClick: function(e, el, more)
13550 this.calpopover.placement = 'right';
13551 this.calpopover.setTitle('More');
13553 this.calpopover.setContent('');
13555 var ctr = this.calpopover.el.select('.popover-content', true).first();
13557 Roo.each(more, function(m){
13559 cls : 'fc-event-hori fc-event-draggable',
13562 var cg = ctr.createChild(cfg);
13564 cg.on('click', _this.onEventClick, _this, m);
13567 this.calpopover.show(el);
13572 onLoad: function ()
13574 this.calevents = [];
13577 if(this.store.getCount() > 0){
13578 this.store.data.each(function(d){
13581 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13582 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13583 time : d.data.start_time,
13584 title : d.data.title,
13585 description : d.data.description,
13586 venue : d.data.venue
13591 this.renderEvents();
13593 if(this.calevents.length && this.loadMask){
13594 this.maskEl.hide();
13598 onBeforeLoad: function()
13600 this.clearEvents();
13602 this.maskEl.show();
13616 * @class Roo.bootstrap.Popover
13617 * @extends Roo.bootstrap.Component
13618 * Bootstrap Popover class
13619 * @cfg {String} html contents of the popover (or false to use children..)
13620 * @cfg {String} title of popover (or false to hide)
13621 * @cfg {String} placement how it is placed
13622 * @cfg {String} trigger click || hover (or false to trigger manually)
13623 * @cfg {String} over what (parent or false to trigger manually.)
13626 * Create a new Popover
13627 * @param {Object} config The config object
13630 Roo.bootstrap.Popover = function(config){
13631 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13634 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
13636 title: 'Fill in a title',
13639 placement : 'right',
13640 trigger : 'hover', // hover
13644 can_build_overlaid : false,
13646 getChildContainer : function()
13648 return this.el.select('.popover-content',true).first();
13651 getAutoCreate : function(){
13652 Roo.log('make popover?');
13654 cls : 'popover roo-dynamic',
13655 style: 'display:block',
13661 cls : 'popover-inner',
13665 cls: 'popover-title',
13669 cls : 'popover-content',
13680 setTitle: function(str)
13682 this.el.select('.popover-title',true).first().dom.innerHTML = str;
13684 setContent: function(str)
13686 this.el.select('.popover-content',true).first().dom.innerHTML = str;
13688 // as it get's added to the bottom of the page.
13689 onRender : function(ct, position)
13691 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13693 var cfg = Roo.apply({}, this.getAutoCreate());
13697 cfg.cls += ' ' + this.cls;
13700 cfg.style = this.style;
13702 Roo.log("adding to ")
13703 this.el = Roo.get(document.body).createChild(cfg, position);
13709 initEvents : function()
13711 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13712 this.el.enableDisplayMode('block');
13714 if (this.over === false) {
13717 if (this.triggers === false) {
13720 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13721 var triggers = this.trigger ? this.trigger.split(' ') : [];
13722 Roo.each(triggers, function(trigger) {
13724 if (trigger == 'click') {
13725 on_el.on('click', this.toggle, this);
13726 } else if (trigger != 'manual') {
13727 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
13728 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13730 on_el.on(eventIn ,this.enter, this);
13731 on_el.on(eventOut, this.leave, this);
13742 toggle : function () {
13743 this.hoverState == 'in' ? this.leave() : this.enter();
13746 enter : function () {
13749 clearTimeout(this.timeout);
13751 this.hoverState = 'in'
13753 if (!this.delay || !this.delay.show) {
13758 this.timeout = setTimeout(function () {
13759 if (_t.hoverState == 'in') {
13762 }, this.delay.show)
13764 leave : function() {
13765 clearTimeout(this.timeout);
13767 this.hoverState = 'out'
13769 if (!this.delay || !this.delay.hide) {
13774 this.timeout = setTimeout(function () {
13775 if (_t.hoverState == 'out') {
13778 }, this.delay.hide)
13781 show : function (on_el)
13784 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13787 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13788 if (this.html !== false) {
13789 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13791 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13792 if (!this.title.length) {
13793 this.el.select('.popover-title',true).hide();
13796 var placement = typeof this.placement == 'function' ?
13797 this.placement.call(this, this.el, on_el) :
13800 var autoToken = /\s?auto?\s?/i;
13801 var autoPlace = autoToken.test(placement);
13803 placement = placement.replace(autoToken, '') || 'top';
13807 //this.el.setXY([0,0]);
13809 this.el.dom.style.display='block';
13810 this.el.addClass(placement);
13812 //this.el.appendTo(on_el);
13814 var p = this.getPosition();
13815 var box = this.el.getBox();
13820 var align = Roo.bootstrap.Popover.alignment[placement]
13821 this.el.alignTo(on_el, align[0],align[1]);
13822 //var arrow = this.el.select('.arrow',true).first();
13823 //arrow.set(align[2],
13825 this.el.addClass('in');
13826 this.hoverState = null;
13828 if (this.el.hasClass('fade')) {
13835 this.el.setXY([0,0]);
13836 this.el.removeClass('in');
13843 Roo.bootstrap.Popover.alignment = {
13844 'left' : ['r-l', [-10,0], 'right'],
13845 'right' : ['l-r', [10,0], 'left'],
13846 'bottom' : ['t-b', [0,10], 'top'],
13847 'top' : [ 'b-t', [0,-10], 'bottom']
13858 * @class Roo.bootstrap.Progress
13859 * @extends Roo.bootstrap.Component
13860 * Bootstrap Progress class
13861 * @cfg {Boolean} striped striped of the progress bar
13862 * @cfg {Boolean} active animated of the progress bar
13866 * Create a new Progress
13867 * @param {Object} config The config object
13870 Roo.bootstrap.Progress = function(config){
13871 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13874 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
13879 getAutoCreate : function(){
13887 cfg.cls += ' progress-striped';
13891 cfg.cls += ' active';
13910 * @class Roo.bootstrap.ProgressBar
13911 * @extends Roo.bootstrap.Component
13912 * Bootstrap ProgressBar class
13913 * @cfg {Number} aria_valuenow aria-value now
13914 * @cfg {Number} aria_valuemin aria-value min
13915 * @cfg {Number} aria_valuemax aria-value max
13916 * @cfg {String} label label for the progress bar
13917 * @cfg {String} panel (success | info | warning | danger )
13918 * @cfg {String} role role of the progress bar
13919 * @cfg {String} sr_only text
13923 * Create a new ProgressBar
13924 * @param {Object} config The config object
13927 Roo.bootstrap.ProgressBar = function(config){
13928 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13931 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
13935 aria_valuemax : 100,
13941 getAutoCreate : function()
13946 cls: 'progress-bar',
13947 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13959 cfg.role = this.role;
13962 if(this.aria_valuenow){
13963 cfg['aria-valuenow'] = this.aria_valuenow;
13966 if(this.aria_valuemin){
13967 cfg['aria-valuemin'] = this.aria_valuemin;
13970 if(this.aria_valuemax){
13971 cfg['aria-valuemax'] = this.aria_valuemax;
13974 if(this.label && !this.sr_only){
13975 cfg.html = this.label;
13979 cfg.cls += ' progress-bar-' + this.panel;
13985 update : function(aria_valuenow)
13987 this.aria_valuenow = aria_valuenow;
13989 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14004 * @class Roo.bootstrap.TabGroup
14005 * @extends Roo.bootstrap.Column
14006 * Bootstrap Column class
14007 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14008 * @cfg {Boolean} carousel true to make the group behave like a carousel
14011 * Create a new TabGroup
14012 * @param {Object} config The config object
14015 Roo.bootstrap.TabGroup = function(config){
14016 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14018 this.navId = Roo.id();
14021 Roo.bootstrap.TabGroup.register(this);
14025 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
14028 transition : false,
14030 getAutoCreate : function()
14032 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14034 cfg.cls += ' tab-content';
14036 if (this.carousel) {
14037 cfg.cls += ' carousel slide';
14039 cls : 'carousel-inner'
14046 getChildContainer : function()
14048 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14052 * register a Navigation item
14053 * @param {Roo.bootstrap.NavItem} the navitem to add
14055 register : function(item)
14057 this.tabs.push( item);
14058 item.navId = this.navId; // not really needed..
14062 getActivePanel : function()
14065 Roo.each(this.tabs, function(t) {
14075 getPanelByName : function(n)
14078 Roo.each(this.tabs, function(t) {
14079 if (t.tabId == n) {
14087 indexOfPanel : function(p)
14090 Roo.each(this.tabs, function(t,i) {
14091 if (t.tabId == p.tabId) {
14100 * show a specific panel
14101 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14102 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14104 showPanel : function (pan)
14107 if (typeof(pan) == 'number') {
14108 pan = this.tabs[pan];
14110 if (typeof(pan) == 'string') {
14111 pan = this.getPanelByName(pan);
14113 if (pan.tabId == this.getActivePanel().tabId) {
14116 var cur = this.getActivePanel();
14118 if (false === cur.fireEvent('beforedeactivate')) {
14122 if (this.carousel) {
14123 this.transition = true;
14124 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
14125 var lr = dir == 'next' ? 'left' : 'right';
14126 pan.el.addClass(dir); // or prev
14127 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14128 cur.el.addClass(lr); // or right
14129 pan.el.addClass(lr);
14132 cur.el.on('transitionend', function() {
14133 Roo.log("trans end?");
14135 pan.el.removeClass([lr,dir]);
14136 pan.setActive(true);
14138 cur.el.removeClass([lr]);
14139 cur.setActive(false);
14141 _this.transition = false;
14143 }, this, { single: true } );
14147 cur.setActive(false);
14148 pan.setActive(true);
14152 showPanelNext : function()
14154 var i = this.indexOfPanel(this.getActivePanel());
14155 if (i > this.tabs.length) {
14158 this.showPanel(this.tabs[i+1]);
14160 showPanelPrev : function()
14162 var i = this.indexOfPanel(this.getActivePanel());
14166 this.showPanel(this.tabs[i-1]);
14177 Roo.apply(Roo.bootstrap.TabGroup, {
14181 * register a Navigation Group
14182 * @param {Roo.bootstrap.NavGroup} the navgroup to add
14184 register : function(navgrp)
14186 this.groups[navgrp.navId] = navgrp;
14190 * fetch a Navigation Group based on the navigation ID
14191 * if one does not exist , it will get created.
14192 * @param {string} the navgroup to add
14193 * @returns {Roo.bootstrap.NavGroup} the navgroup
14195 get: function(navId) {
14196 if (typeof(this.groups[navId]) == 'undefined') {
14197 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14199 return this.groups[navId] ;
14214 * @class Roo.bootstrap.TabPanel
14215 * @extends Roo.bootstrap.Component
14216 * Bootstrap TabPanel class
14217 * @cfg {Boolean} active panel active
14218 * @cfg {String} html panel content
14219 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14220 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14224 * Create a new TabPanel
14225 * @param {Object} config The config object
14228 Roo.bootstrap.TabPanel = function(config){
14229 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14233 * Fires when the active status changes
14234 * @param {Roo.bootstrap.TabPanel} this
14235 * @param {Boolean} state the new state
14240 * @event beforedeactivate
14241 * Fires before a tab is de-activated - can be used to do validation on a form.
14242 * @param {Roo.bootstrap.TabPanel} this
14243 * @return {Boolean} false if there is an error
14246 'beforedeactivate': true
14249 this.tabId = this.tabId || Roo.id();
14253 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
14260 getAutoCreate : function(){
14263 // item is needed for carousel - not sure if it has any effect otherwise
14264 cls: 'tab-pane item',
14265 html: this.html || ''
14269 cfg.cls += ' active';
14273 cfg.tabId = this.tabId;
14280 initEvents: function()
14282 Roo.log('-------- init events on tab panel ---------');
14284 var p = this.parent();
14285 this.navId = this.navId || p.navId;
14287 if (typeof(this.navId) != 'undefined') {
14288 // not really needed.. but just in case.. parent should be a NavGroup.
14289 var tg = Roo.bootstrap.TabGroup.get(this.navId);
14290 Roo.log(['register', tg, this]);
14296 onRender : function(ct, position)
14298 // Roo.log("Call onRender: " + this.xtype);
14300 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14308 setActive: function(state)
14310 Roo.log("panel - set active " + this.tabId + "=" + state);
14312 this.active = state;
14314 this.el.removeClass('active');
14316 } else if (!this.el.hasClass('active')) {
14317 this.el.addClass('active');
14319 this.fireEvent('changed', this, state);
14336 * @class Roo.bootstrap.DateField
14337 * @extends Roo.bootstrap.Input
14338 * Bootstrap DateField class
14339 * @cfg {Number} weekStart default 0
14340 * @cfg {Number} weekStart default 0
14341 * @cfg {Number} viewMode default empty, (months|years)
14342 * @cfg {Number} minViewMode default empty, (months|years)
14343 * @cfg {Number} startDate default -Infinity
14344 * @cfg {Number} endDate default Infinity
14345 * @cfg {Boolean} todayHighlight default false
14346 * @cfg {Boolean} todayBtn default false
14347 * @cfg {Boolean} calendarWeeks default false
14348 * @cfg {Object} daysOfWeekDisabled default empty
14350 * @cfg {Boolean} keyboardNavigation default true
14351 * @cfg {String} language default en
14354 * Create a new DateField
14355 * @param {Object} config The config object
14358 Roo.bootstrap.DateField = function(config){
14359 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14363 * Fires when this field show.
14364 * @param {Roo.bootstrap.DateField} this
14365 * @param {Mixed} date The date value
14370 * Fires when this field hide.
14371 * @param {Roo.bootstrap.DateField} this
14372 * @param {Mixed} date The date value
14377 * Fires when select a date.
14378 * @param {Roo.bootstrap.DateField} this
14379 * @param {Mixed} date The date value
14385 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
14388 * @cfg {String} format
14389 * The default date format string which can be overriden for localization support. The format must be
14390 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14394 * @cfg {String} altFormats
14395 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14396 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14398 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14406 todayHighlight : false,
14412 keyboardNavigation: true,
14414 calendarWeeks: false,
14416 startDate: -Infinity,
14420 daysOfWeekDisabled: [],
14424 UTCDate: function()
14426 return new Date(Date.UTC.apply(Date, arguments));
14429 UTCToday: function()
14431 var today = new Date();
14432 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14435 getDate: function() {
14436 var d = this.getUTCDate();
14437 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14440 getUTCDate: function() {
14444 setDate: function(d) {
14445 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14448 setUTCDate: function(d) {
14450 this.setValue(this.formatDate(this.date));
14453 onRender: function(ct, position)
14456 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14458 this.language = this.language || 'en';
14459 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14460 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14462 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14463 this.format = this.format || 'm/d/y';
14464 this.isInline = false;
14465 this.isInput = true;
14466 this.component = this.el.select('.add-on', true).first() || false;
14467 this.component = (this.component && this.component.length === 0) ? false : this.component;
14468 this.hasInput = this.component && this.inputEL().length;
14470 if (typeof(this.minViewMode === 'string')) {
14471 switch (this.minViewMode) {
14473 this.minViewMode = 1;
14476 this.minViewMode = 2;
14479 this.minViewMode = 0;
14484 if (typeof(this.viewMode === 'string')) {
14485 switch (this.viewMode) {
14498 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14500 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14502 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14504 this.picker().on('mousedown', this.onMousedown, this);
14505 this.picker().on('click', this.onClick, this);
14507 this.picker().addClass('datepicker-dropdown');
14509 this.startViewMode = this.viewMode;
14512 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14513 if(!this.calendarWeeks){
14518 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14519 v.attr('colspan', function(i, val){
14520 return parseInt(val) + 1;
14525 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14527 this.setStartDate(this.startDate);
14528 this.setEndDate(this.endDate);
14530 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14537 if(this.isInline) {
14542 picker : function()
14544 return this.pickerEl;
14545 // return this.el.select('.datepicker', true).first();
14548 fillDow: function()
14550 var dowCnt = this.weekStart;
14559 if(this.calendarWeeks){
14567 while (dowCnt < this.weekStart + 7) {
14571 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14575 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14578 fillMonths: function()
14581 var months = this.picker().select('>.datepicker-months td', true).first();
14583 months.dom.innerHTML = '';
14589 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14592 months.createChild(month);
14599 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;
14601 if (this.date < this.startDate) {
14602 this.viewDate = new Date(this.startDate);
14603 } else if (this.date > this.endDate) {
14604 this.viewDate = new Date(this.endDate);
14606 this.viewDate = new Date(this.date);
14614 var d = new Date(this.viewDate),
14615 year = d.getUTCFullYear(),
14616 month = d.getUTCMonth(),
14617 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14618 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14619 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14620 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14621 currentDate = this.date && this.date.valueOf(),
14622 today = this.UTCToday();
14624 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14626 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14628 // this.picker.select('>tfoot th.today').
14629 // .text(dates[this.language].today)
14630 // .toggle(this.todayBtn !== false);
14632 this.updateNavArrows();
14635 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14637 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14639 prevMonth.setUTCDate(day);
14641 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14643 var nextMonth = new Date(prevMonth);
14645 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14647 nextMonth = nextMonth.valueOf();
14649 var fillMonths = false;
14651 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14653 while(prevMonth.valueOf() < nextMonth) {
14656 if (prevMonth.getUTCDay() === this.weekStart) {
14658 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14666 if(this.calendarWeeks){
14667 // ISO 8601: First week contains first thursday.
14668 // ISO also states week starts on Monday, but we can be more abstract here.
14670 // Start of current week: based on weekstart/current date
14671 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14672 // Thursday of this week
14673 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14674 // First Thursday of year, year from thursday
14675 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14676 // Calendar week: ms between thursdays, div ms per day, div 7 days
14677 calWeek = (th - yth) / 864e5 / 7 + 1;
14679 fillMonths.cn.push({
14687 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14689 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14692 if (this.todayHighlight &&
14693 prevMonth.getUTCFullYear() == today.getFullYear() &&
14694 prevMonth.getUTCMonth() == today.getMonth() &&
14695 prevMonth.getUTCDate() == today.getDate()) {
14696 clsName += ' today';
14699 if (currentDate && prevMonth.valueOf() === currentDate) {
14700 clsName += ' active';
14703 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14704 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14705 clsName += ' disabled';
14708 fillMonths.cn.push({
14710 cls: 'day ' + clsName,
14711 html: prevMonth.getDate()
14714 prevMonth.setDate(prevMonth.getDate()+1);
14717 var currentYear = this.date && this.date.getUTCFullYear();
14718 var currentMonth = this.date && this.date.getUTCMonth();
14720 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14722 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14723 v.removeClass('active');
14725 if(currentYear === year && k === currentMonth){
14726 v.addClass('active');
14729 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14730 v.addClass('disabled');
14736 year = parseInt(year/10, 10) * 10;
14738 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14740 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14743 for (var i = -1; i < 11; i++) {
14744 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14746 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14754 showMode: function(dir)
14757 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14759 Roo.each(this.picker().select('>div',true).elements, function(v){
14760 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14763 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14768 if(this.isInline) return;
14770 this.picker().removeClass(['bottom', 'top']);
14772 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14774 * place to the top of element!
14778 this.picker().addClass('top');
14779 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14784 this.picker().addClass('bottom');
14786 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14789 parseDate : function(value)
14791 if(!value || value instanceof Date){
14794 var v = Date.parseDate(value, this.format);
14795 if (!v && this.useIso) {
14796 v = Date.parseDate(value, 'Y-m-d');
14798 if(!v && this.altFormats){
14799 if(!this.altFormatsArray){
14800 this.altFormatsArray = this.altFormats.split("|");
14802 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14803 v = Date.parseDate(value, this.altFormatsArray[i]);
14809 formatDate : function(date, fmt)
14811 return (!date || !(date instanceof Date)) ?
14812 date : date.dateFormat(fmt || this.format);
14815 onFocus : function()
14817 Roo.bootstrap.DateField.superclass.onFocus.call(this);
14821 onBlur : function()
14823 Roo.bootstrap.DateField.superclass.onBlur.call(this);
14825 var d = this.inputEl().getValue();
14834 this.picker().show();
14838 this.fireEvent('show', this, this.date);
14843 if(this.isInline) return;
14844 this.picker().hide();
14845 this.viewMode = this.startViewMode;
14848 this.fireEvent('hide', this, this.date);
14852 onMousedown: function(e)
14854 e.stopPropagation();
14855 e.preventDefault();
14860 Roo.bootstrap.DateField.superclass.keyup.call(this);
14864 setValue: function(v)
14866 var d = new Date(v).clearTime();
14868 if(isNaN(d.getTime())){
14869 this.date = this.viewDate = '';
14870 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14874 v = this.formatDate(d);
14876 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14878 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14882 this.fireEvent('select', this, this.date);
14886 getValue: function()
14888 return this.formatDate(this.date);
14891 fireKey: function(e)
14893 if (!this.picker().isVisible()){
14894 if (e.keyCode == 27) // allow escape to hide and re-show picker
14899 var dateChanged = false,
14901 newDate, newViewDate;
14906 e.preventDefault();
14910 if (!this.keyboardNavigation) break;
14911 dir = e.keyCode == 37 ? -1 : 1;
14914 newDate = this.moveYear(this.date, dir);
14915 newViewDate = this.moveYear(this.viewDate, dir);
14916 } else if (e.shiftKey){
14917 newDate = this.moveMonth(this.date, dir);
14918 newViewDate = this.moveMonth(this.viewDate, dir);
14920 newDate = new Date(this.date);
14921 newDate.setUTCDate(this.date.getUTCDate() + dir);
14922 newViewDate = new Date(this.viewDate);
14923 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14925 if (this.dateWithinRange(newDate)){
14926 this.date = newDate;
14927 this.viewDate = newViewDate;
14928 this.setValue(this.formatDate(this.date));
14930 e.preventDefault();
14931 dateChanged = true;
14936 if (!this.keyboardNavigation) break;
14937 dir = e.keyCode == 38 ? -1 : 1;
14939 newDate = this.moveYear(this.date, dir);
14940 newViewDate = this.moveYear(this.viewDate, dir);
14941 } else if (e.shiftKey){
14942 newDate = this.moveMonth(this.date, dir);
14943 newViewDate = this.moveMonth(this.viewDate, dir);
14945 newDate = new Date(this.date);
14946 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14947 newViewDate = new Date(this.viewDate);
14948 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14950 if (this.dateWithinRange(newDate)){
14951 this.date = newDate;
14952 this.viewDate = newViewDate;
14953 this.setValue(this.formatDate(this.date));
14955 e.preventDefault();
14956 dateChanged = true;
14960 this.setValue(this.formatDate(this.date));
14962 e.preventDefault();
14965 this.setValue(this.formatDate(this.date));
14979 onClick: function(e)
14981 e.stopPropagation();
14982 e.preventDefault();
14984 var target = e.getTarget();
14986 if(target.nodeName.toLowerCase() === 'i'){
14987 target = Roo.get(target).dom.parentNode;
14990 var nodeName = target.nodeName;
14991 var className = target.className;
14992 var html = target.innerHTML;
14994 switch(nodeName.toLowerCase()) {
14996 switch(className) {
15002 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15003 switch(this.viewMode){
15005 this.viewDate = this.moveMonth(this.viewDate, dir);
15009 this.viewDate = this.moveYear(this.viewDate, dir);
15015 var date = new Date();
15016 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15018 this.setValue(this.formatDate(this.date));
15025 if (className.indexOf('disabled') === -1) {
15026 this.viewDate.setUTCDate(1);
15027 if (className.indexOf('month') !== -1) {
15028 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15030 var year = parseInt(html, 10) || 0;
15031 this.viewDate.setUTCFullYear(year);
15040 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
15041 var day = parseInt(html, 10) || 1;
15042 var year = this.viewDate.getUTCFullYear(),
15043 month = this.viewDate.getUTCMonth();
15045 if (className.indexOf('old') !== -1) {
15052 } else if (className.indexOf('new') !== -1) {
15060 this.date = this.UTCDate(year, month, day,0,0,0,0);
15061 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15063 this.setValue(this.formatDate(this.date));
15070 setStartDate: function(startDate)
15072 this.startDate = startDate || -Infinity;
15073 if (this.startDate !== -Infinity) {
15074 this.startDate = this.parseDate(this.startDate);
15077 this.updateNavArrows();
15080 setEndDate: function(endDate)
15082 this.endDate = endDate || Infinity;
15083 if (this.endDate !== Infinity) {
15084 this.endDate = this.parseDate(this.endDate);
15087 this.updateNavArrows();
15090 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15092 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15093 if (typeof(this.daysOfWeekDisabled) !== 'object') {
15094 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15096 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15097 return parseInt(d, 10);
15100 this.updateNavArrows();
15103 updateNavArrows: function()
15105 var d = new Date(this.viewDate),
15106 year = d.getUTCFullYear(),
15107 month = d.getUTCMonth();
15109 Roo.each(this.picker().select('.prev', true).elements, function(v){
15111 switch (this.viewMode) {
15114 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15120 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15127 Roo.each(this.picker().select('.next', true).elements, function(v){
15129 switch (this.viewMode) {
15132 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15138 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15146 moveMonth: function(date, dir)
15148 if (!dir) return date;
15149 var new_date = new Date(date.valueOf()),
15150 day = new_date.getUTCDate(),
15151 month = new_date.getUTCMonth(),
15152 mag = Math.abs(dir),
15154 dir = dir > 0 ? 1 : -1;
15157 // If going back one month, make sure month is not current month
15158 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15160 return new_date.getUTCMonth() == month;
15162 // If going forward one month, make sure month is as expected
15163 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15165 return new_date.getUTCMonth() != new_month;
15167 new_month = month + dir;
15168 new_date.setUTCMonth(new_month);
15169 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15170 if (new_month < 0 || new_month > 11)
15171 new_month = (new_month + 12) % 12;
15173 // For magnitudes >1, move one month at a time...
15174 for (var i=0; i<mag; i++)
15175 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15176 new_date = this.moveMonth(new_date, dir);
15177 // ...then reset the day, keeping it in the new month
15178 new_month = new_date.getUTCMonth();
15179 new_date.setUTCDate(day);
15181 return new_month != new_date.getUTCMonth();
15184 // Common date-resetting loop -- if date is beyond end of month, make it
15187 new_date.setUTCDate(--day);
15188 new_date.setUTCMonth(new_month);
15193 moveYear: function(date, dir)
15195 return this.moveMonth(date, dir*12);
15198 dateWithinRange: function(date)
15200 return date >= this.startDate && date <= this.endDate;
15206 this.picker().remove();
15211 Roo.apply(Roo.bootstrap.DateField, {
15222 html: '<i class="fa fa-arrow-left"/>'
15232 html: '<i class="fa fa-arrow-right"/>'
15274 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15275 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15276 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15277 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15278 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15291 navFnc: 'FullYear',
15296 navFnc: 'FullYear',
15301 Roo.apply(Roo.bootstrap.DateField, {
15305 cls: 'datepicker dropdown-menu',
15309 cls: 'datepicker-days',
15313 cls: 'table-condensed',
15315 Roo.bootstrap.DateField.head,
15319 Roo.bootstrap.DateField.footer
15326 cls: 'datepicker-months',
15330 cls: 'table-condensed',
15332 Roo.bootstrap.DateField.head,
15333 Roo.bootstrap.DateField.content,
15334 Roo.bootstrap.DateField.footer
15341 cls: 'datepicker-years',
15345 cls: 'table-condensed',
15347 Roo.bootstrap.DateField.head,
15348 Roo.bootstrap.DateField.content,
15349 Roo.bootstrap.DateField.footer
15368 * @class Roo.bootstrap.TimeField
15369 * @extends Roo.bootstrap.Input
15370 * Bootstrap DateField class
15374 * Create a new TimeField
15375 * @param {Object} config The config object
15378 Roo.bootstrap.TimeField = function(config){
15379 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15383 * Fires when this field show.
15384 * @param {Roo.bootstrap.DateField} this
15385 * @param {Mixed} date The date value
15390 * Fires when this field hide.
15391 * @param {Roo.bootstrap.DateField} this
15392 * @param {Mixed} date The date value
15397 * Fires when select a date.
15398 * @param {Roo.bootstrap.DateField} this
15399 * @param {Mixed} date The date value
15405 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
15408 * @cfg {String} format
15409 * The default time format string which can be overriden for localization support. The format must be
15410 * valid according to {@link Date#parseDate} (defaults to 'H:i').
15414 onRender: function(ct, position)
15417 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15419 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15421 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15423 this.pop = this.picker().select('>.datepicker-time',true).first();
15424 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
15426 this.picker().on('mousedown', this.onMousedown, this);
15427 this.picker().on('click', this.onClick, this);
15429 this.picker().addClass('datepicker-dropdown');
15434 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15435 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15436 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15437 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15438 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15439 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15443 fireKey: function(e){
15444 if (!this.picker().isVisible()){
15445 if (e.keyCode == 27) // allow escape to hide and re-show picker
15450 e.preventDefault();
15458 this.onTogglePeriod();
15461 this.onIncrementMinutes();
15464 this.onDecrementMinutes();
15473 onClick: function(e) {
15474 e.stopPropagation();
15475 e.preventDefault();
15478 picker : function()
15480 return this.el.select('.datepicker', true).first();
15483 fillTime: function()
15485 var time = this.pop.select('tbody', true).first();
15487 time.dom.innerHTML = '';
15502 cls: 'hours-up glyphicon glyphicon-chevron-up'
15522 cls: 'minutes-up glyphicon glyphicon-chevron-up'
15543 cls: 'timepicker-hour',
15558 cls: 'timepicker-minute',
15573 cls: 'btn btn-primary period',
15595 cls: 'hours-down glyphicon glyphicon-chevron-down'
15615 cls: 'minutes-down glyphicon glyphicon-chevron-down'
15633 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15640 var hours = this.time.getHours();
15641 var minutes = this.time.getMinutes();
15654 hours = hours - 12;
15658 hours = '0' + hours;
15662 minutes = '0' + minutes;
15665 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15666 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15667 this.pop.select('button', true).first().dom.innerHTML = period;
15673 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15675 var cls = ['bottom'];
15677 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15684 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15689 this.picker().addClass(cls.join('-'));
15693 Roo.each(cls, function(c){
15695 _this.picker().setTop(_this.inputEl().getHeight());
15699 _this.picker().setTop(0 - _this.picker().getHeight());
15704 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15708 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15715 onFocus : function()
15717 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15721 onBlur : function()
15723 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15729 this.picker().show();
15734 this.fireEvent('show', this, this.date);
15739 this.picker().hide();
15742 this.fireEvent('hide', this, this.date);
15745 setTime : function()
15748 this.setValue(this.time.format(this.format));
15750 this.fireEvent('select', this, this.date);
15755 onMousedown: function(e){
15756 e.stopPropagation();
15757 e.preventDefault();
15760 onIncrementHours: function()
15762 Roo.log('onIncrementHours');
15763 this.time = this.time.add(Date.HOUR, 1);
15768 onDecrementHours: function()
15770 Roo.log('onDecrementHours');
15771 this.time = this.time.add(Date.HOUR, -1);
15775 onIncrementMinutes: function()
15777 Roo.log('onIncrementMinutes');
15778 this.time = this.time.add(Date.MINUTE, 1);
15782 onDecrementMinutes: function()
15784 Roo.log('onDecrementMinutes');
15785 this.time = this.time.add(Date.MINUTE, -1);
15789 onTogglePeriod: function()
15791 Roo.log('onTogglePeriod');
15792 this.time = this.time.add(Date.HOUR, 12);
15799 Roo.apply(Roo.bootstrap.TimeField, {
15829 cls: 'btn btn-info ok',
15841 Roo.apply(Roo.bootstrap.TimeField, {
15845 cls: 'datepicker dropdown-menu',
15849 cls: 'datepicker-time',
15853 cls: 'table-condensed',
15855 Roo.bootstrap.TimeField.content,
15856 Roo.bootstrap.TimeField.footer
15875 * @class Roo.bootstrap.CheckBox
15876 * @extends Roo.bootstrap.Input
15877 * Bootstrap CheckBox class
15879 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15880 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15881 * @cfg {String} boxLabel The text that appears beside the checkbox
15882 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15883 * @cfg {Boolean} checked initnal the element
15887 * Create a new CheckBox
15888 * @param {Object} config The config object
15891 Roo.bootstrap.CheckBox = function(config){
15892 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15897 * Fires when the element is checked or unchecked.
15898 * @param {Roo.bootstrap.CheckBox} this This input
15899 * @param {Boolean} checked The new checked value
15905 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
15907 inputType: 'checkbox',
15914 getAutoCreate : function()
15916 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15922 cfg.cls = 'form-group checkbox' //input-group
15930 type : this.inputType,
15931 value : (!this.checked) ? this.valueOff : this.inputValue,
15932 cls : 'roo-checkbox', //'form-box',
15933 placeholder : this.placeholder || ''
15937 if (this.weight) { // Validity check?
15938 cfg.cls += " checkbox-" + this.weight;
15941 if (this.disabled) {
15942 input.disabled=true;
15946 input.checked = this.checked;
15950 input.name = this.name;
15954 input.cls += ' input-' + this.size;
15958 ['xs','sm','md','lg'].map(function(size){
15959 if (settings[size]) {
15960 cfg.cls += ' col-' + size + '-' + settings[size];
15966 var inputblock = input;
15971 if (this.before || this.after) {
15974 cls : 'input-group',
15978 inputblock.cn.push({
15980 cls : 'input-group-addon',
15984 inputblock.cn.push(input);
15986 inputblock.cn.push({
15988 cls : 'input-group-addon',
15995 if (align ==='left' && this.fieldLabel.length) {
15996 Roo.log("left and has label");
16002 cls : 'control-label col-md-' + this.labelWidth,
16003 html : this.fieldLabel
16007 cls : "col-md-" + (12 - this.labelWidth),
16014 } else if ( this.fieldLabel.length) {
16019 tag: this.boxLabel ? 'span' : 'label',
16021 cls: 'control-label box-input-label',
16022 //cls : 'input-group-addon',
16023 html : this.fieldLabel
16033 Roo.log(" no label && no align");
16034 cfg.cn = [ inputblock ] ;
16043 html: this.boxLabel
16055 * return the real input element.
16057 inputEl: function ()
16059 return this.el.select('input.roo-checkbox',true).first();
16064 return this.el.select('label.control-label',true).first();
16067 initEvents : function()
16069 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16071 this.inputEl().on('click', this.onClick, this);
16075 onClick : function()
16077 this.setChecked(!this.checked);
16080 setChecked : function(state,suppressEvent)
16082 this.checked = state;
16084 this.inputEl().dom.checked = state;
16086 if(suppressEvent !== true){
16087 this.fireEvent('check', this, state);
16090 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16094 setValue : function(v,suppressEvent)
16096 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16110 * @class Roo.bootstrap.Radio
16111 * @extends Roo.bootstrap.CheckBox
16112 * Bootstrap Radio class
16115 * Create a new Radio
16116 * @param {Object} config The config object
16119 Roo.bootstrap.Radio = function(config){
16120 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16124 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
16126 inputType: 'radio',
16130 getAutoCreate : function()
16132 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16138 cfg.cls = 'form-group radio' //input-group
16143 type : this.inputType,
16144 value : (!this.checked) ? this.valueOff : this.inputValue,
16146 placeholder : this.placeholder || ''
16149 if (this.weight) { // Validity check?
16150 cfg.cls += " radio-" + this.weight;
16152 if (this.disabled) {
16153 input.disabled=true;
16157 input.checked = this.checked;
16161 input.name = this.name;
16165 input.cls += ' input-' + this.size;
16169 ['xs','sm','md','lg'].map(function(size){
16170 if (settings[size]) {
16171 cfg.cls += ' col-' + size + '-' + settings[size];
16175 var inputblock = input;
16177 if (this.before || this.after) {
16180 cls : 'input-group',
16184 inputblock.cn.push({
16186 cls : 'input-group-addon',
16190 inputblock.cn.push(input);
16192 inputblock.cn.push({
16194 cls : 'input-group-addon',
16201 if (align ==='left' && this.fieldLabel.length) {
16202 Roo.log("left and has label");
16208 cls : 'control-label col-md-' + this.labelWidth,
16209 html : this.fieldLabel
16213 cls : "col-md-" + (12 - this.labelWidth),
16220 } else if ( this.fieldLabel.length) {
16227 cls: 'control-label box-input-label',
16228 //cls : 'input-group-addon',
16229 html : this.fieldLabel
16239 Roo.log(" no label && no align");
16254 html: this.boxLabel
16261 inputEl: function ()
16263 return this.el.select('input.roo-radio',true).first();
16265 onClick : function()
16267 this.setChecked(true);
16270 setChecked : function(state,suppressEvent)
16273 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16274 v.dom.checked = false;
16278 this.checked = state;
16279 this.inputEl().dom.checked = state;
16281 if(suppressEvent !== true){
16282 this.fireEvent('check', this, state);
16285 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16289 getGroupValue : function()
16292 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16293 if(v.dom.checked == true){
16294 value = v.dom.value;
16302 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
16303 * @return {Mixed} value The field value
16305 getValue : function(){
16306 return this.getGroupValue();
16312 //<script type="text/javascript">
16315 * Based Ext JS Library 1.1.1
16316 * Copyright(c) 2006-2007, Ext JS, LLC.
16322 * @class Roo.HtmlEditorCore
16323 * @extends Roo.Component
16324 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16326 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16329 Roo.HtmlEditorCore = function(config){
16332 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16337 * @event initialize
16338 * Fires when the editor is fully initialized (including the iframe)
16339 * @param {Roo.HtmlEditorCore} this
16344 * Fires when the editor is first receives the focus. Any insertion must wait
16345 * until after this event.
16346 * @param {Roo.HtmlEditorCore} this
16350 * @event beforesync
16351 * Fires before the textarea is updated with content from the editor iframe. Return false
16352 * to cancel the sync.
16353 * @param {Roo.HtmlEditorCore} this
16354 * @param {String} html
16358 * @event beforepush
16359 * Fires before the iframe editor is updated with content from the textarea. Return false
16360 * to cancel the push.
16361 * @param {Roo.HtmlEditorCore} this
16362 * @param {String} html
16367 * Fires when the textarea is updated with content from the editor iframe.
16368 * @param {Roo.HtmlEditorCore} this
16369 * @param {String} html
16374 * Fires when the iframe editor is updated with content from the textarea.
16375 * @param {Roo.HtmlEditorCore} this
16376 * @param {String} html
16381 * @event editorevent
16382 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16383 * @param {Roo.HtmlEditorCore} this
16388 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16390 // defaults : white / black...
16391 this.applyBlacklists();
16398 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
16402 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
16408 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
16413 * @cfg {Number} height (in pixels)
16417 * @cfg {Number} width (in pixels)
16422 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16425 stylesheets: false,
16430 // private properties
16431 validationEvent : false,
16433 initialized : false,
16435 sourceEditMode : false,
16436 onFocus : Roo.emptyFn,
16438 hideMode:'offsets',
16442 // blacklist + whitelisted elements..
16449 * Protected method that will not generally be called directly. It
16450 * is called when the editor initializes the iframe with HTML contents. Override this method if you
16451 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16453 getDocMarkup : function(){
16456 Roo.log(this.stylesheets);
16458 // inherit styels from page...??
16459 if (this.stylesheets === false) {
16461 Roo.get(document.head).select('style').each(function(node) {
16462 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16465 Roo.get(document.head).select('link').each(function(node) {
16466 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16469 } else if (!this.stylesheets.length) {
16471 st = '<style type="text/css">' +
16472 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16475 Roo.each(this.stylesheets, function(s) {
16476 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16481 st += '<style type="text/css">' +
16482 'IMG { cursor: pointer } ' +
16486 return '<html><head>' + st +
16487 //<style type="text/css">' +
16488 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16490 ' </head><body class="roo-htmleditor-body"></body></html>';
16494 onRender : function(ct, position)
16497 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16498 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16501 this.el.dom.style.border = '0 none';
16502 this.el.dom.setAttribute('tabIndex', -1);
16503 this.el.addClass('x-hidden hide');
16507 if(Roo.isIE){ // fix IE 1px bogus margin
16508 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16512 this.frameId = Roo.id();
16516 var iframe = this.owner.wrap.createChild({
16518 cls: 'form-control', // bootstrap..
16520 name: this.frameId,
16521 frameBorder : 'no',
16522 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
16527 this.iframe = iframe.dom;
16529 this.assignDocWin();
16531 this.doc.designMode = 'on';
16534 this.doc.write(this.getDocMarkup());
16538 var task = { // must defer to wait for browser to be ready
16540 //console.log("run task?" + this.doc.readyState);
16541 this.assignDocWin();
16542 if(this.doc.body || this.doc.readyState == 'complete'){
16544 this.doc.designMode="on";
16548 Roo.TaskMgr.stop(task);
16549 this.initEditor.defer(10, this);
16556 Roo.TaskMgr.start(task);
16563 onResize : function(w, h)
16565 Roo.log('resize: ' +w + ',' + h );
16566 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16570 if(typeof w == 'number'){
16572 this.iframe.style.width = w + 'px';
16574 if(typeof h == 'number'){
16576 this.iframe.style.height = h + 'px';
16578 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16585 * Toggles the editor between standard and source edit mode.
16586 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16588 toggleSourceEdit : function(sourceEditMode){
16590 this.sourceEditMode = sourceEditMode === true;
16592 if(this.sourceEditMode){
16594 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
16597 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16598 //this.iframe.className = '';
16601 //this.setSize(this.owner.wrap.getSize());
16602 //this.fireEvent('editmodechange', this, this.sourceEditMode);
16609 * Protected method that will not generally be called directly. If you need/want
16610 * custom HTML cleanup, this is the method you should override.
16611 * @param {String} html The HTML to be cleaned
16612 * return {String} The cleaned HTML
16614 cleanHtml : function(html){
16615 html = String(html);
16616 if(html.length > 5){
16617 if(Roo.isSafari){ // strip safari nonsense
16618 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16621 if(html == ' '){
16628 * HTML Editor -> Textarea
16629 * Protected method that will not generally be called directly. Syncs the contents
16630 * of the editor iframe with the textarea.
16632 syncValue : function(){
16633 if(this.initialized){
16634 var bd = (this.doc.body || this.doc.documentElement);
16635 //this.cleanUpPaste(); -- this is done else where and causes havoc..
16636 var html = bd.innerHTML;
16638 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16639 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16641 html = '<div style="'+m[0]+'">' + html + '</div>';
16644 html = this.cleanHtml(html);
16645 // fix up the special chars.. normaly like back quotes in word...
16646 // however we do not want to do this with chinese..
16647 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16648 var cc = b.charCodeAt();
16650 (cc >= 0x4E00 && cc < 0xA000 ) ||
16651 (cc >= 0x3400 && cc < 0x4E00 ) ||
16652 (cc >= 0xf900 && cc < 0xfb00 )
16658 if(this.owner.fireEvent('beforesync', this, html) !== false){
16659 this.el.dom.value = html;
16660 this.owner.fireEvent('sync', this, html);
16666 * Protected method that will not generally be called directly. Pushes the value of the textarea
16667 * into the iframe editor.
16669 pushValue : function(){
16670 if(this.initialized){
16671 var v = this.el.dom.value.trim();
16673 // if(v.length < 1){
16677 if(this.owner.fireEvent('beforepush', this, v) !== false){
16678 var d = (this.doc.body || this.doc.documentElement);
16680 this.cleanUpPaste();
16681 this.el.dom.value = d.innerHTML;
16682 this.owner.fireEvent('push', this, v);
16688 deferFocus : function(){
16689 this.focus.defer(10, this);
16693 focus : function(){
16694 if(this.win && !this.sourceEditMode){
16701 assignDocWin: function()
16703 var iframe = this.iframe;
16706 this.doc = iframe.contentWindow.document;
16707 this.win = iframe.contentWindow;
16709 // if (!Roo.get(this.frameId)) {
16712 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16713 // this.win = Roo.get(this.frameId).dom.contentWindow;
16715 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16719 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16720 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16725 initEditor : function(){
16726 //console.log("INIT EDITOR");
16727 this.assignDocWin();
16731 this.doc.designMode="on";
16733 this.doc.write(this.getDocMarkup());
16736 var dbody = (this.doc.body || this.doc.documentElement);
16737 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16738 // this copies styles from the containing element into thsi one..
16739 // not sure why we need all of this..
16740 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16742 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16743 //ss['background-attachment'] = 'fixed'; // w3c
16744 dbody.bgProperties = 'fixed'; // ie
16745 //Roo.DomHelper.applyStyles(dbody, ss);
16746 Roo.EventManager.on(this.doc, {
16747 //'mousedown': this.onEditorEvent,
16748 'mouseup': this.onEditorEvent,
16749 'dblclick': this.onEditorEvent,
16750 'click': this.onEditorEvent,
16751 'keyup': this.onEditorEvent,
16756 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16758 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16759 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16761 this.initialized = true;
16763 this.owner.fireEvent('initialize', this);
16768 onDestroy : function(){
16774 //for (var i =0; i < this.toolbars.length;i++) {
16775 // // fixme - ask toolbars for heights?
16776 // this.toolbars[i].onDestroy();
16779 //this.wrap.dom.innerHTML = '';
16780 //this.wrap.remove();
16785 onFirstFocus : function(){
16787 this.assignDocWin();
16790 this.activated = true;
16793 if(Roo.isGecko){ // prevent silly gecko errors
16795 var s = this.win.getSelection();
16796 if(!s.focusNode || s.focusNode.nodeType != 3){
16797 var r = s.getRangeAt(0);
16798 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16803 this.execCmd('useCSS', true);
16804 this.execCmd('styleWithCSS', false);
16807 this.owner.fireEvent('activate', this);
16811 adjustFont: function(btn){
16812 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16813 //if(Roo.isSafari){ // safari
16816 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16817 if(Roo.isSafari){ // safari
16818 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16819 v = (v < 10) ? 10 : v;
16820 v = (v > 48) ? 48 : v;
16821 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16826 v = Math.max(1, v+adjust);
16828 this.execCmd('FontSize', v );
16831 onEditorEvent : function(e){
16832 this.owner.fireEvent('editorevent', this, e);
16833 // this.updateToolbar();
16834 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16837 insertTag : function(tg)
16839 // could be a bit smarter... -> wrap the current selected tRoo..
16840 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16842 range = this.createRange(this.getSelection());
16843 var wrappingNode = this.doc.createElement(tg.toLowerCase());
16844 wrappingNode.appendChild(range.extractContents());
16845 range.insertNode(wrappingNode);
16852 this.execCmd("formatblock", tg);
16856 insertText : function(txt)
16860 var range = this.createRange();
16861 range.deleteContents();
16862 //alert(Sender.getAttribute('label'));
16864 range.insertNode(this.doc.createTextNode(txt));
16870 * Executes a Midas editor command on the editor document and performs necessary focus and
16871 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16872 * @param {String} cmd The Midas command
16873 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16875 relayCmd : function(cmd, value){
16877 this.execCmd(cmd, value);
16878 this.owner.fireEvent('editorevent', this);
16879 //this.updateToolbar();
16880 this.owner.deferFocus();
16884 * Executes a Midas editor command directly on the editor document.
16885 * For visual commands, you should use {@link #relayCmd} instead.
16886 * <b>This should only be called after the editor is initialized.</b>
16887 * @param {String} cmd The Midas command
16888 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16890 execCmd : function(cmd, value){
16891 this.doc.execCommand(cmd, false, value === undefined ? null : value);
16898 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16900 * @param {String} text | dom node..
16902 insertAtCursor : function(text)
16907 if(!this.activated){
16913 var r = this.doc.selection.createRange();
16924 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16928 // from jquery ui (MIT licenced)
16930 var win = this.win;
16932 if (win.getSelection && win.getSelection().getRangeAt) {
16933 range = win.getSelection().getRangeAt(0);
16934 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16935 range.insertNode(node);
16936 } else if (win.document.selection && win.document.selection.createRange) {
16937 // no firefox support
16938 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16939 win.document.selection.createRange().pasteHTML(txt);
16941 // no firefox support
16942 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16943 this.execCmd('InsertHTML', txt);
16952 mozKeyPress : function(e){
16954 var c = e.getCharCode(), cmd;
16957 c = String.fromCharCode(c).toLowerCase();
16971 this.cleanUpPaste.defer(100, this);
16979 e.preventDefault();
16987 fixKeys : function(){ // load time branching for fastest keydown performance
16989 return function(e){
16990 var k = e.getKey(), r;
16993 r = this.doc.selection.createRange();
16996 r.pasteHTML('    ');
17003 r = this.doc.selection.createRange();
17005 var target = r.parentElement();
17006 if(!target || target.tagName.toLowerCase() != 'li'){
17008 r.pasteHTML('<br />');
17014 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17015 this.cleanUpPaste.defer(100, this);
17021 }else if(Roo.isOpera){
17022 return function(e){
17023 var k = e.getKey();
17027 this.execCmd('InsertHTML','    ');
17030 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17031 this.cleanUpPaste.defer(100, this);
17036 }else if(Roo.isSafari){
17037 return function(e){
17038 var k = e.getKey();
17042 this.execCmd('InsertText','\t');
17046 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17047 this.cleanUpPaste.defer(100, this);
17055 getAllAncestors: function()
17057 var p = this.getSelectedNode();
17060 a.push(p); // push blank onto stack..
17061 p = this.getParentElement();
17065 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17069 a.push(this.doc.body);
17073 lastSelNode : false,
17076 getSelection : function()
17078 this.assignDocWin();
17079 return Roo.isIE ? this.doc.selection : this.win.getSelection();
17082 getSelectedNode: function()
17084 // this may only work on Gecko!!!
17086 // should we cache this!!!!
17091 var range = this.createRange(this.getSelection()).cloneRange();
17094 var parent = range.parentElement();
17096 var testRange = range.duplicate();
17097 testRange.moveToElementText(parent);
17098 if (testRange.inRange(range)) {
17101 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17104 parent = parent.parentElement;
17109 // is ancestor a text element.
17110 var ac = range.commonAncestorContainer;
17111 if (ac.nodeType == 3) {
17112 ac = ac.parentNode;
17115 var ar = ac.childNodes;
17118 var other_nodes = [];
17119 var has_other_nodes = false;
17120 for (var i=0;i<ar.length;i++) {
17121 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
17124 // fullly contained node.
17126 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17131 // probably selected..
17132 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17133 other_nodes.push(ar[i]);
17137 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
17142 has_other_nodes = true;
17144 if (!nodes.length && other_nodes.length) {
17145 nodes= other_nodes;
17147 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17153 createRange: function(sel)
17155 // this has strange effects when using with
17156 // top toolbar - not sure if it's a great idea.
17157 //this.editor.contentWindow.focus();
17158 if (typeof sel != "undefined") {
17160 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17162 return this.doc.createRange();
17165 return this.doc.createRange();
17168 getParentElement: function()
17171 this.assignDocWin();
17172 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17174 var range = this.createRange(sel);
17177 var p = range.commonAncestorContainer;
17178 while (p.nodeType == 3) { // text node
17189 * Range intersection.. the hard stuff...
17193 * [ -- selected range --- ]
17197 * if end is before start or hits it. fail.
17198 * if start is after end or hits it fail.
17200 * if either hits (but other is outside. - then it's not
17206 // @see http://www.thismuchiknow.co.uk/?p=64.
17207 rangeIntersectsNode : function(range, node)
17209 var nodeRange = node.ownerDocument.createRange();
17211 nodeRange.selectNode(node);
17213 nodeRange.selectNodeContents(node);
17216 var rangeStartRange = range.cloneRange();
17217 rangeStartRange.collapse(true);
17219 var rangeEndRange = range.cloneRange();
17220 rangeEndRange.collapse(false);
17222 var nodeStartRange = nodeRange.cloneRange();
17223 nodeStartRange.collapse(true);
17225 var nodeEndRange = nodeRange.cloneRange();
17226 nodeEndRange.collapse(false);
17228 return rangeStartRange.compareBoundaryPoints(
17229 Range.START_TO_START, nodeEndRange) == -1 &&
17230 rangeEndRange.compareBoundaryPoints(
17231 Range.START_TO_START, nodeStartRange) == 1;
17235 rangeCompareNode : function(range, node)
17237 var nodeRange = node.ownerDocument.createRange();
17239 nodeRange.selectNode(node);
17241 nodeRange.selectNodeContents(node);
17245 range.collapse(true);
17247 nodeRange.collapse(true);
17249 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17250 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
17252 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17254 var nodeIsBefore = ss == 1;
17255 var nodeIsAfter = ee == -1;
17257 if (nodeIsBefore && nodeIsAfter)
17259 if (!nodeIsBefore && nodeIsAfter)
17260 return 1; //right trailed.
17262 if (nodeIsBefore && !nodeIsAfter)
17263 return 2; // left trailed.
17268 // private? - in a new class?
17269 cleanUpPaste : function()
17271 // cleans up the whole document..
17272 Roo.log('cleanuppaste');
17274 this.cleanUpChildren(this.doc.body);
17275 var clean = this.cleanWordChars(this.doc.body.innerHTML);
17276 if (clean != this.doc.body.innerHTML) {
17277 this.doc.body.innerHTML = clean;
17282 cleanWordChars : function(input) {// change the chars to hex code
17283 var he = Roo.HtmlEditorCore;
17285 var output = input;
17286 Roo.each(he.swapCodes, function(sw) {
17287 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17289 output = output.replace(swapper, sw[1]);
17296 cleanUpChildren : function (n)
17298 if (!n.childNodes.length) {
17301 for (var i = n.childNodes.length-1; i > -1 ; i--) {
17302 this.cleanUpChild(n.childNodes[i]);
17309 cleanUpChild : function (node)
17312 //console.log(node);
17313 if (node.nodeName == "#text") {
17314 // clean up silly Windows -- stuff?
17317 if (node.nodeName == "#comment") {
17318 node.parentNode.removeChild(node);
17319 // clean up silly Windows -- stuff?
17322 var lcname = node.tagName.toLowerCase();
17323 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17324 // whitelist of tags..
17326 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17328 node.parentNode.removeChild(node);
17333 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17335 // remove <a name=....> as rendering on yahoo mailer is borked with this.
17336 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17338 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17339 // remove_keep_children = true;
17342 if (remove_keep_children) {
17343 this.cleanUpChildren(node);
17344 // inserts everything just before this node...
17345 while (node.childNodes.length) {
17346 var cn = node.childNodes[0];
17347 node.removeChild(cn);
17348 node.parentNode.insertBefore(cn, node);
17350 node.parentNode.removeChild(node);
17354 if (!node.attributes || !node.attributes.length) {
17355 this.cleanUpChildren(node);
17359 function cleanAttr(n,v)
17362 if (v.match(/^\./) || v.match(/^\//)) {
17365 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17368 if (v.match(/^#/)) {
17371 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17372 node.removeAttribute(n);
17376 var cwhite = this.cwhite;
17377 var cblack = this.cblack;
17379 function cleanStyle(n,v)
17381 if (v.match(/expression/)) { //XSS?? should we even bother..
17382 node.removeAttribute(n);
17386 var parts = v.split(/;/);
17389 Roo.each(parts, function(p) {
17390 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17394 var l = p.split(':').shift().replace(/\s+/g,'');
17395 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17397 if ( cwhite.length && cblack.indexOf(l) > -1) {
17398 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17399 //node.removeAttribute(n);
17403 // only allow 'c whitelisted system attributes'
17404 if ( cwhite.length && cwhite.indexOf(l) < 0) {
17405 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17406 //node.removeAttribute(n);
17416 if (clean.length) {
17417 node.setAttribute(n, clean.join(';'));
17419 node.removeAttribute(n);
17425 for (var i = node.attributes.length-1; i > -1 ; i--) {
17426 var a = node.attributes[i];
17429 if (a.name.toLowerCase().substr(0,2)=='on') {
17430 node.removeAttribute(a.name);
17433 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17434 node.removeAttribute(a.name);
17437 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17438 cleanAttr(a.name,a.value); // fixme..
17441 if (a.name == 'style') {
17442 cleanStyle(a.name,a.value);
17445 /// clean up MS crap..
17446 // tecnically this should be a list of valid class'es..
17449 if (a.name == 'class') {
17450 if (a.value.match(/^Mso/)) {
17451 node.className = '';
17454 if (a.value.match(/body/)) {
17455 node.className = '';
17466 this.cleanUpChildren(node);
17471 * Clean up MS wordisms...
17473 cleanWord : function(node)
17476 var cleanWordChildren = function()
17478 if (!node.childNodes.length) {
17481 for (var i = node.childNodes.length-1; i > -1 ; i--) {
17482 _t.cleanWord(node.childNodes[i]);
17488 this.cleanWord(this.doc.body);
17491 if (node.nodeName == "#text") {
17492 // clean up silly Windows -- stuff?
17495 if (node.nodeName == "#comment") {
17496 node.parentNode.removeChild(node);
17497 // clean up silly Windows -- stuff?
17501 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17502 node.parentNode.removeChild(node);
17506 // remove - but keep children..
17507 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17508 while (node.childNodes.length) {
17509 var cn = node.childNodes[0];
17510 node.removeChild(cn);
17511 node.parentNode.insertBefore(cn, node);
17513 node.parentNode.removeChild(node);
17514 cleanWordChildren();
17518 if (node.className.length) {
17520 var cn = node.className.split(/\W+/);
17522 Roo.each(cn, function(cls) {
17523 if (cls.match(/Mso[a-zA-Z]+/)) {
17528 node.className = cna.length ? cna.join(' ') : '';
17530 node.removeAttribute("class");
17534 if (node.hasAttribute("lang")) {
17535 node.removeAttribute("lang");
17538 if (node.hasAttribute("style")) {
17540 var styles = node.getAttribute("style").split(";");
17542 Roo.each(styles, function(s) {
17543 if (!s.match(/:/)) {
17546 var kv = s.split(":");
17547 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17550 // what ever is left... we allow.
17553 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17554 if (!nstyle.length) {
17555 node.removeAttribute('style');
17559 cleanWordChildren();
17563 domToHTML : function(currentElement, depth, nopadtext) {
17565 depth = depth || 0;
17566 nopadtext = nopadtext || false;
17568 if (!currentElement) {
17569 return this.domToHTML(this.doc.body);
17572 //Roo.log(currentElement);
17574 var allText = false;
17575 var nodeName = currentElement.nodeName;
17576 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17578 if (nodeName == '#text') {
17579 return currentElement.nodeValue;
17584 if (nodeName != 'BODY') {
17587 // Prints the node tagName, such as <A>, <IMG>, etc
17590 for(i = 0; i < currentElement.attributes.length;i++) {
17592 var aname = currentElement.attributes.item(i).name;
17593 if (!currentElement.attributes.item(i).value.length) {
17596 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17599 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17608 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17611 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17616 // Traverse the tree
17618 var currentElementChild = currentElement.childNodes.item(i);
17619 var allText = true;
17620 var innerHTML = '';
17622 while (currentElementChild) {
17623 // Formatting code (indent the tree so it looks nice on the screen)
17624 var nopad = nopadtext;
17625 if (lastnode == 'SPAN') {
17629 if (currentElementChild.nodeName == '#text') {
17630 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17631 if (!nopad && toadd.length > 80) {
17632 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
17634 innerHTML += toadd;
17637 currentElementChild = currentElement.childNodes.item(i);
17643 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
17645 // Recursively traverse the tree structure of the child node
17646 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
17647 lastnode = currentElementChild.nodeName;
17649 currentElementChild=currentElement.childNodes.item(i);
17655 // The remaining code is mostly for formatting the tree
17656 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
17661 ret+= "</"+tagName+">";
17667 applyBlacklists : function()
17669 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
17670 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
17674 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17675 if (b.indexOf(tag) > -1) {
17678 this.white.push(tag);
17682 Roo.each(w, function(tag) {
17683 if (b.indexOf(tag) > -1) {
17686 if (this.white.indexOf(tag) > -1) {
17689 this.white.push(tag);
17694 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17695 if (w.indexOf(tag) > -1) {
17698 this.black.push(tag);
17702 Roo.each(b, function(tag) {
17703 if (w.indexOf(tag) > -1) {
17706 if (this.black.indexOf(tag) > -1) {
17709 this.black.push(tag);
17714 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
17715 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
17719 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17720 if (b.indexOf(tag) > -1) {
17723 this.cwhite.push(tag);
17727 Roo.each(w, function(tag) {
17728 if (b.indexOf(tag) > -1) {
17731 if (this.cwhite.indexOf(tag) > -1) {
17734 this.cwhite.push(tag);
17739 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17740 if (w.indexOf(tag) > -1) {
17743 this.cblack.push(tag);
17747 Roo.each(b, function(tag) {
17748 if (w.indexOf(tag) > -1) {
17751 if (this.cblack.indexOf(tag) > -1) {
17754 this.cblack.push(tag);
17759 // hide stuff that is not compatible
17773 * @event specialkey
17777 * @cfg {String} fieldClass @hide
17780 * @cfg {String} focusClass @hide
17783 * @cfg {String} autoCreate @hide
17786 * @cfg {String} inputType @hide
17789 * @cfg {String} invalidClass @hide
17792 * @cfg {String} invalidText @hide
17795 * @cfg {String} msgFx @hide
17798 * @cfg {String} validateOnBlur @hide
17802 Roo.HtmlEditorCore.white = [
17803 'area', 'br', 'img', 'input', 'hr', 'wbr',
17805 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
17806 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
17807 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
17808 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
17809 'table', 'ul', 'xmp',
17811 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
17814 'dir', 'menu', 'ol', 'ul', 'dl',
17820 Roo.HtmlEditorCore.black = [
17821 // 'embed', 'object', // enable - backend responsiblity to clean thiese
17823 'base', 'basefont', 'bgsound', 'blink', 'body',
17824 'frame', 'frameset', 'head', 'html', 'ilayer',
17825 'iframe', 'layer', 'link', 'meta', 'object',
17826 'script', 'style' ,'title', 'xml' // clean later..
17828 Roo.HtmlEditorCore.clean = [
17829 'script', 'style', 'title', 'xml'
17831 Roo.HtmlEditorCore.remove = [
17836 Roo.HtmlEditorCore.ablack = [
17840 Roo.HtmlEditorCore.aclean = [
17841 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
17845 Roo.HtmlEditorCore.pwhite= [
17846 'http', 'https', 'mailto'
17849 // white listed style attributes.
17850 Roo.HtmlEditorCore.cwhite= [
17851 // 'text-align', /// default is to allow most things..
17857 // black listed style attributes.
17858 Roo.HtmlEditorCore.cblack= [
17859 // 'font-size' -- this can be set by the project
17863 Roo.HtmlEditorCore.swapCodes =[
17882 * @class Roo.bootstrap.HtmlEditor
17883 * @extends Roo.bootstrap.TextArea
17884 * Bootstrap HtmlEditor class
17887 * Create a new HtmlEditor
17888 * @param {Object} config The config object
17891 Roo.bootstrap.HtmlEditor = function(config){
17892 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17893 if (!this.toolbars) {
17894 this.toolbars = [];
17896 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17899 * @event initialize
17900 * Fires when the editor is fully initialized (including the iframe)
17901 * @param {HtmlEditor} this
17906 * Fires when the editor is first receives the focus. Any insertion must wait
17907 * until after this event.
17908 * @param {HtmlEditor} this
17912 * @event beforesync
17913 * Fires before the textarea is updated with content from the editor iframe. Return false
17914 * to cancel the sync.
17915 * @param {HtmlEditor} this
17916 * @param {String} html
17920 * @event beforepush
17921 * Fires before the iframe editor is updated with content from the textarea. Return false
17922 * to cancel the push.
17923 * @param {HtmlEditor} this
17924 * @param {String} html
17929 * Fires when the textarea is updated with content from the editor iframe.
17930 * @param {HtmlEditor} this
17931 * @param {String} html
17936 * Fires when the iframe editor is updated with content from the textarea.
17937 * @param {HtmlEditor} this
17938 * @param {String} html
17942 * @event editmodechange
17943 * Fires when the editor switches edit modes
17944 * @param {HtmlEditor} this
17945 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17947 editmodechange: true,
17949 * @event editorevent
17950 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17951 * @param {HtmlEditor} this
17955 * @event firstfocus
17956 * Fires when on first focus - needed by toolbars..
17957 * @param {HtmlEditor} this
17962 * Auto save the htmlEditor value as a file into Events
17963 * @param {HtmlEditor} this
17967 * @event savedpreview
17968 * preview the saved version of htmlEditor
17969 * @param {HtmlEditor} this
17976 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
17980 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17985 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
17990 * @cfg {Number} height (in pixels)
17994 * @cfg {Number} width (in pixels)
17999 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18002 stylesheets: false,
18007 // private properties
18008 validationEvent : false,
18010 initialized : false,
18013 onFocus : Roo.emptyFn,
18015 hideMode:'offsets',
18018 tbContainer : false,
18020 toolbarContainer :function() {
18021 return this.wrap.select('.x-html-editor-tb',true).first();
18025 * Protected method that will not generally be called directly. It
18026 * is called when the editor creates its toolbar. Override this method if you need to
18027 * add custom toolbar buttons.
18028 * @param {HtmlEditor} editor
18030 createToolbar : function(){
18032 Roo.log("create toolbars");
18034 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18035 this.toolbars[0].render(this.toolbarContainer());
18039 // if (!editor.toolbars || !editor.toolbars.length) {
18040 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18043 // for (var i =0 ; i < editor.toolbars.length;i++) {
18044 // editor.toolbars[i] = Roo.factory(
18045 // typeof(editor.toolbars[i]) == 'string' ?
18046 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
18047 // Roo.bootstrap.HtmlEditor);
18048 // editor.toolbars[i].init(editor);
18054 onRender : function(ct, position)
18056 // Roo.log("Call onRender: " + this.xtype);
18058 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18060 this.wrap = this.inputEl().wrap({
18061 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18064 this.editorcore.onRender(ct, position);
18066 if (this.resizable) {
18067 this.resizeEl = new Roo.Resizable(this.wrap, {
18071 minHeight : this.height,
18072 height: this.height,
18073 handles : this.resizable,
18076 resize : function(r, w, h) {
18077 _t.onResize(w,h); // -something
18083 this.createToolbar(this);
18086 if(!this.width && this.resizable){
18087 this.setSize(this.wrap.getSize());
18089 if (this.resizeEl) {
18090 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18091 // should trigger onReize..
18097 onResize : function(w, h)
18099 Roo.log('resize: ' +w + ',' + h );
18100 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18104 if(this.inputEl() ){
18105 if(typeof w == 'number'){
18106 var aw = w - this.wrap.getFrameWidth('lr');
18107 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18110 if(typeof h == 'number'){
18111 var tbh = -11; // fixme it needs to tool bar size!
18112 for (var i =0; i < this.toolbars.length;i++) {
18113 // fixme - ask toolbars for heights?
18114 tbh += this.toolbars[i].el.getHeight();
18115 //if (this.toolbars[i].footer) {
18116 // tbh += this.toolbars[i].footer.el.getHeight();
18124 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18125 ah -= 5; // knock a few pixes off for look..
18126 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18130 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18131 this.editorcore.onResize(ew,eh);
18136 * Toggles the editor between standard and source edit mode.
18137 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18139 toggleSourceEdit : function(sourceEditMode)
18141 this.editorcore.toggleSourceEdit(sourceEditMode);
18143 if(this.editorcore.sourceEditMode){
18144 Roo.log('editor - showing textarea');
18147 // Roo.log(this.syncValue());
18149 this.inputEl().removeClass(['hide', 'x-hidden']);
18150 this.inputEl().dom.removeAttribute('tabIndex');
18151 this.inputEl().focus();
18153 Roo.log('editor - hiding textarea');
18155 // Roo.log(this.pushValue());
18158 this.inputEl().addClass(['hide', 'x-hidden']);
18159 this.inputEl().dom.setAttribute('tabIndex', -1);
18160 //this.deferFocus();
18163 if(this.resizable){
18164 this.setSize(this.wrap.getSize());
18167 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18170 // private (for BoxComponent)
18171 adjustSize : Roo.BoxComponent.prototype.adjustSize,
18173 // private (for BoxComponent)
18174 getResizeEl : function(){
18178 // private (for BoxComponent)
18179 getPositionEl : function(){
18184 initEvents : function(){
18185 this.originalValue = this.getValue();
18189 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18192 // markInvalid : Roo.emptyFn,
18194 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18197 // clearInvalid : Roo.emptyFn,
18199 setValue : function(v){
18200 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18201 this.editorcore.pushValue();
18206 deferFocus : function(){
18207 this.focus.defer(10, this);
18211 focus : function(){
18212 this.editorcore.focus();
18218 onDestroy : function(){
18224 for (var i =0; i < this.toolbars.length;i++) {
18225 // fixme - ask toolbars for heights?
18226 this.toolbars[i].onDestroy();
18229 this.wrap.dom.innerHTML = '';
18230 this.wrap.remove();
18235 onFirstFocus : function(){
18236 //Roo.log("onFirstFocus");
18237 this.editorcore.onFirstFocus();
18238 for (var i =0; i < this.toolbars.length;i++) {
18239 this.toolbars[i].onFirstFocus();
18245 syncValue : function()
18247 this.editorcore.syncValue();
18250 pushValue : function()
18252 this.editorcore.pushValue();
18256 // hide stuff that is not compatible
18270 * @event specialkey
18274 * @cfg {String} fieldClass @hide
18277 * @cfg {String} focusClass @hide
18280 * @cfg {String} autoCreate @hide
18283 * @cfg {String} inputType @hide
18286 * @cfg {String} invalidClass @hide
18289 * @cfg {String} invalidText @hide
18292 * @cfg {String} msgFx @hide
18295 * @cfg {String} validateOnBlur @hide
18304 Roo.namespace('Roo.bootstrap.htmleditor');
18306 * @class Roo.bootstrap.HtmlEditorToolbar1
18311 new Roo.bootstrap.HtmlEditor({
18314 new Roo.bootstrap.HtmlEditorToolbar1({
18315 disable : { fonts: 1 , format: 1, ..., ... , ...],
18321 * @cfg {Object} disable List of elements to disable..
18322 * @cfg {Array} btns List of additional buttons.
18326 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18329 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18332 Roo.apply(this, config);
18334 // default disabled, based on 'good practice'..
18335 this.disable = this.disable || {};
18336 Roo.applyIf(this.disable, {
18339 specialElements : true
18341 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18343 this.editor = config.editor;
18344 this.editorcore = config.editor.editorcore;
18346 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18348 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18349 // dont call parent... till later.
18351 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
18356 editorcore : false,
18361 "h1","h2","h3","h4","h5","h6",
18363 "abbr", "acronym", "address", "cite", "samp", "var",
18367 onRender : function(ct, position)
18369 // Roo.log("Call onRender: " + this.xtype);
18371 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18373 this.el.dom.style.marginBottom = '0';
18375 var editorcore = this.editorcore;
18376 var editor= this.editor;
18379 var btn = function(id,cmd , toggle, handler){
18381 var event = toggle ? 'toggle' : 'click';
18386 xns: Roo.bootstrap,
18389 enableToggle:toggle !== false,
18391 pressed : toggle ? false : null,
18394 a.listeners[toggle ? 'toggle' : 'click'] = function() {
18395 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
18404 xns: Roo.bootstrap,
18405 glyphicon : 'font',
18409 xns: Roo.bootstrap,
18413 Roo.each(this.formats, function(f) {
18414 style.menu.items.push({
18416 xns: Roo.bootstrap,
18417 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18422 editorcore.insertTag(this.tagname);
18429 children.push(style);
18432 btn('bold',false,true);
18433 btn('italic',false,true);
18434 btn('align-left', 'justifyleft',true);
18435 btn('align-center', 'justifycenter',true);
18436 btn('align-right' , 'justifyright',true);
18437 btn('link', false, false, function(btn) {
18438 //Roo.log("create link?");
18439 var url = prompt(this.createLinkText, this.defaultLinkValue);
18440 if(url && url != 'http:/'+'/'){
18441 this.editorcore.relayCmd('createlink', url);
18444 btn('list','insertunorderedlist',true);
18445 btn('pencil', false,true, function(btn){
18448 this.toggleSourceEdit(btn.pressed);
18454 xns: Roo.bootstrap,
18459 xns: Roo.bootstrap,
18464 cog.menu.items.push({
18466 xns: Roo.bootstrap,
18467 html : Clean styles,
18472 editorcore.insertTag(this.tagname);
18481 this.xtype = 'NavSimplebar';
18483 for(var i=0;i< children.length;i++) {
18485 this.buttons.add(this.addxtypeChild(children[i]));
18489 editor.on('editorevent', this.updateToolbar, this);
18491 onBtnClick : function(id)
18493 this.editorcore.relayCmd(id);
18494 this.editorcore.focus();
18498 * Protected method that will not generally be called directly. It triggers
18499 * a toolbar update by reading the markup state of the current selection in the editor.
18501 updateToolbar: function(){
18503 if(!this.editorcore.activated){
18504 this.editor.onFirstFocus(); // is this neeed?
18508 var btns = this.buttons;
18509 var doc = this.editorcore.doc;
18510 btns.get('bold').setActive(doc.queryCommandState('bold'));
18511 btns.get('italic').setActive(doc.queryCommandState('italic'));
18512 //btns.get('underline').setActive(doc.queryCommandState('underline'));
18514 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18515 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18516 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18518 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18519 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18522 var ans = this.editorcore.getAllAncestors();
18523 if (this.formatCombo) {
18526 var store = this.formatCombo.store;
18527 this.formatCombo.setValue("");
18528 for (var i =0; i < ans.length;i++) {
18529 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18531 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18539 // hides menus... - so this cant be on a menu...
18540 Roo.bootstrap.MenuMgr.hideAll();
18542 Roo.bootstrap.MenuMgr.hideAll();
18543 //this.editorsyncValue();
18545 onFirstFocus: function() {
18546 this.buttons.each(function(item){
18550 toggleSourceEdit : function(sourceEditMode){
18553 if(sourceEditMode){
18554 Roo.log("disabling buttons");
18555 this.buttons.each( function(item){
18556 if(item.cmd != 'pencil'){
18562 Roo.log("enabling buttons");
18563 if(this.editorcore.initialized){
18564 this.buttons.each( function(item){
18570 Roo.log("calling toggole on editor");
18571 // tell the editor that it's been pressed..
18572 this.editor.toggleSourceEdit(sourceEditMode);
18582 * @class Roo.bootstrap.Table.AbstractSelectionModel
18583 * @extends Roo.util.Observable
18584 * Abstract base class for grid SelectionModels. It provides the interface that should be
18585 * implemented by descendant classes. This class should not be directly instantiated.
18588 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18589 this.locked = false;
18590 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18594 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
18595 /** @ignore Called by the grid automatically. Do not call directly. */
18596 init : function(grid){
18602 * Locks the selections.
18605 this.locked = true;
18609 * Unlocks the selections.
18611 unlock : function(){
18612 this.locked = false;
18616 * Returns true if the selections are locked.
18617 * @return {Boolean}
18619 isLocked : function(){
18620 return this.locked;
18624 * @extends Roo.bootstrap.Table.AbstractSelectionModel
18625 * @class Roo.bootstrap.Table.RowSelectionModel
18626 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18627 * It supports multiple selections and keyboard selection/navigation.
18629 * @param {Object} config
18632 Roo.bootstrap.Table.RowSelectionModel = function(config){
18633 Roo.apply(this, config);
18634 this.selections = new Roo.util.MixedCollection(false, function(o){
18639 this.lastActive = false;
18643 * @event selectionchange
18644 * Fires when the selection changes
18645 * @param {SelectionModel} this
18647 "selectionchange" : true,
18649 * @event afterselectionchange
18650 * Fires after the selection changes (eg. by key press or clicking)
18651 * @param {SelectionModel} this
18653 "afterselectionchange" : true,
18655 * @event beforerowselect
18656 * Fires when a row is selected being selected, return false to cancel.
18657 * @param {SelectionModel} this
18658 * @param {Number} rowIndex The selected index
18659 * @param {Boolean} keepExisting False if other selections will be cleared
18661 "beforerowselect" : true,
18664 * Fires when a row is selected.
18665 * @param {SelectionModel} this
18666 * @param {Number} rowIndex The selected index
18667 * @param {Roo.data.Record} r The record
18669 "rowselect" : true,
18671 * @event rowdeselect
18672 * Fires when a row is deselected.
18673 * @param {SelectionModel} this
18674 * @param {Number} rowIndex The selected index
18676 "rowdeselect" : true
18678 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18679 this.locked = false;
18682 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
18684 * @cfg {Boolean} singleSelect
18685 * True to allow selection of only one row at a time (defaults to false)
18687 singleSelect : false,
18690 initEvents : function(){
18692 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18693 this.grid.on("mousedown", this.handleMouseDown, this);
18694 }else{ // allow click to work like normal
18695 this.grid.on("rowclick", this.handleDragableRowClick, this);
18698 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18699 "up" : function(e){
18701 this.selectPrevious(e.shiftKey);
18702 }else if(this.last !== false && this.lastActive !== false){
18703 var last = this.last;
18704 this.selectRange(this.last, this.lastActive-1);
18705 this.grid.getView().focusRow(this.lastActive);
18706 if(last !== false){
18710 this.selectFirstRow();
18712 this.fireEvent("afterselectionchange", this);
18714 "down" : function(e){
18716 this.selectNext(e.shiftKey);
18717 }else if(this.last !== false && this.lastActive !== false){
18718 var last = this.last;
18719 this.selectRange(this.last, this.lastActive+1);
18720 this.grid.getView().focusRow(this.lastActive);
18721 if(last !== false){
18725 this.selectFirstRow();
18727 this.fireEvent("afterselectionchange", this);
18732 var view = this.grid.view;
18733 view.on("refresh", this.onRefresh, this);
18734 view.on("rowupdated", this.onRowUpdated, this);
18735 view.on("rowremoved", this.onRemove, this);
18739 onRefresh : function(){
18740 var ds = this.grid.dataSource, i, v = this.grid.view;
18741 var s = this.selections;
18742 s.each(function(r){
18743 if((i = ds.indexOfId(r.id)) != -1){
18752 onRemove : function(v, index, r){
18753 this.selections.remove(r);
18757 onRowUpdated : function(v, index, r){
18758 if(this.isSelected(r)){
18759 v.onRowSelect(index);
18765 * @param {Array} records The records to select
18766 * @param {Boolean} keepExisting (optional) True to keep existing selections
18768 selectRecords : function(records, keepExisting){
18770 this.clearSelections();
18772 var ds = this.grid.dataSource;
18773 for(var i = 0, len = records.length; i < len; i++){
18774 this.selectRow(ds.indexOf(records[i]), true);
18779 * Gets the number of selected rows.
18782 getCount : function(){
18783 return this.selections.length;
18787 * Selects the first row in the grid.
18789 selectFirstRow : function(){
18794 * Select the last row.
18795 * @param {Boolean} keepExisting (optional) True to keep existing selections
18797 selectLastRow : function(keepExisting){
18798 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18802 * Selects the row immediately following the last selected row.
18803 * @param {Boolean} keepExisting (optional) True to keep existing selections
18805 selectNext : function(keepExisting){
18806 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18807 this.selectRow(this.last+1, keepExisting);
18808 this.grid.getView().focusRow(this.last);
18813 * Selects the row that precedes the last selected row.
18814 * @param {Boolean} keepExisting (optional) True to keep existing selections
18816 selectPrevious : function(keepExisting){
18818 this.selectRow(this.last-1, keepExisting);
18819 this.grid.getView().focusRow(this.last);
18824 * Returns the selected records
18825 * @return {Array} Array of selected records
18827 getSelections : function(){
18828 return [].concat(this.selections.items);
18832 * Returns the first selected record.
18835 getSelected : function(){
18836 return this.selections.itemAt(0);
18841 * Clears all selections.
18843 clearSelections : function(fast){
18844 if(this.locked) return;
18846 var ds = this.grid.dataSource;
18847 var s = this.selections;
18848 s.each(function(r){
18849 this.deselectRow(ds.indexOfId(r.id));
18853 this.selections.clear();
18860 * Selects all rows.
18862 selectAll : function(){
18863 if(this.locked) return;
18864 this.selections.clear();
18865 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18866 this.selectRow(i, true);
18871 * Returns True if there is a selection.
18872 * @return {Boolean}
18874 hasSelection : function(){
18875 return this.selections.length > 0;
18879 * Returns True if the specified row is selected.
18880 * @param {Number/Record} record The record or index of the record to check
18881 * @return {Boolean}
18883 isSelected : function(index){
18884 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18885 return (r && this.selections.key(r.id) ? true : false);
18889 * Returns True if the specified record id is selected.
18890 * @param {String} id The id of record to check
18891 * @return {Boolean}
18893 isIdSelected : function(id){
18894 return (this.selections.key(id) ? true : false);
18898 handleMouseDown : function(e, t){
18899 var view = this.grid.getView(), rowIndex;
18900 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18903 if(e.shiftKey && this.last !== false){
18904 var last = this.last;
18905 this.selectRange(last, rowIndex, e.ctrlKey);
18906 this.last = last; // reset the last
18907 view.focusRow(rowIndex);
18909 var isSelected = this.isSelected(rowIndex);
18910 if(e.button !== 0 && isSelected){
18911 view.focusRow(rowIndex);
18912 }else if(e.ctrlKey && isSelected){
18913 this.deselectRow(rowIndex);
18914 }else if(!isSelected){
18915 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18916 view.focusRow(rowIndex);
18919 this.fireEvent("afterselectionchange", this);
18922 handleDragableRowClick : function(grid, rowIndex, e)
18924 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18925 this.selectRow(rowIndex, false);
18926 grid.view.focusRow(rowIndex);
18927 this.fireEvent("afterselectionchange", this);
18932 * Selects multiple rows.
18933 * @param {Array} rows Array of the indexes of the row to select
18934 * @param {Boolean} keepExisting (optional) True to keep existing selections
18936 selectRows : function(rows, keepExisting){
18938 this.clearSelections();
18940 for(var i = 0, len = rows.length; i < len; i++){
18941 this.selectRow(rows[i], true);
18946 * Selects a range of rows. All rows in between startRow and endRow are also selected.
18947 * @param {Number} startRow The index of the first row in the range
18948 * @param {Number} endRow The index of the last row in the range
18949 * @param {Boolean} keepExisting (optional) True to retain existing selections
18951 selectRange : function(startRow, endRow, keepExisting){
18952 if(this.locked) return;
18954 this.clearSelections();
18956 if(startRow <= endRow){
18957 for(var i = startRow; i <= endRow; i++){
18958 this.selectRow(i, true);
18961 for(var i = startRow; i >= endRow; i--){
18962 this.selectRow(i, true);
18968 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18969 * @param {Number} startRow The index of the first row in the range
18970 * @param {Number} endRow The index of the last row in the range
18972 deselectRange : function(startRow, endRow, preventViewNotify){
18973 if(this.locked) return;
18974 for(var i = startRow; i <= endRow; i++){
18975 this.deselectRow(i, preventViewNotify);
18981 * @param {Number} row The index of the row to select
18982 * @param {Boolean} keepExisting (optional) True to keep existing selections
18984 selectRow : function(index, keepExisting, preventViewNotify){
18985 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18986 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18987 if(!keepExisting || this.singleSelect){
18988 this.clearSelections();
18990 var r = this.grid.dataSource.getAt(index);
18991 this.selections.add(r);
18992 this.last = this.lastActive = index;
18993 if(!preventViewNotify){
18994 this.grid.getView().onRowSelect(index);
18996 this.fireEvent("rowselect", this, index, r);
18997 this.fireEvent("selectionchange", this);
19003 * @param {Number} row The index of the row to deselect
19005 deselectRow : function(index, preventViewNotify){
19006 if(this.locked) return;
19007 if(this.last == index){
19010 if(this.lastActive == index){
19011 this.lastActive = false;
19013 var r = this.grid.dataSource.getAt(index);
19014 this.selections.remove(r);
19015 if(!preventViewNotify){
19016 this.grid.getView().onRowDeselect(index);
19018 this.fireEvent("rowdeselect", this, index);
19019 this.fireEvent("selectionchange", this);
19023 restoreLast : function(){
19025 this.last = this._last;
19030 acceptsNav : function(row, col, cm){
19031 return !cm.isHidden(col) && cm.isCellEditable(col, row);
19035 onEditorKey : function(field, e){
19036 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19041 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19043 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19045 }else if(k == e.ENTER && !e.ctrlKey){
19049 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19051 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19053 }else if(k == e.ESC){
19057 g.startEditing(newCell[0], newCell[1]);
19062 * Ext JS Library 1.1.1
19063 * Copyright(c) 2006-2007, Ext JS, LLC.
19065 * Originally Released Under LGPL - original licence link has changed is not relivant.
19068 * <script type="text/javascript">
19072 * @class Roo.bootstrap.PagingToolbar
19074 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19076 * Create a new PagingToolbar
19077 * @param {Object} config The config object
19079 Roo.bootstrap.PagingToolbar = function(config)
19081 // old args format still supported... - xtype is prefered..
19082 // created from xtype...
19083 var ds = config.dataSource;
19084 this.toolbarItems = [];
19085 if (config.items) {
19086 this.toolbarItems = config.items;
19087 // config.items = [];
19090 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19097 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19101 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19103 * @cfg {Roo.data.Store} dataSource
19104 * The underlying data store providing the paged data
19107 * @cfg {String/HTMLElement/Element} container
19108 * container The id or element that will contain the toolbar
19111 * @cfg {Boolean} displayInfo
19112 * True to display the displayMsg (defaults to false)
19115 * @cfg {Number} pageSize
19116 * The number of records to display per page (defaults to 20)
19120 * @cfg {String} displayMsg
19121 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19123 displayMsg : 'Displaying {0} - {1} of {2}',
19125 * @cfg {String} emptyMsg
19126 * The message to display when no records are found (defaults to "No data to display")
19128 emptyMsg : 'No data to display',
19130 * Customizable piece of the default paging text (defaults to "Page")
19133 beforePageText : "Page",
19135 * Customizable piece of the default paging text (defaults to "of %0")
19138 afterPageText : "of {0}",
19140 * Customizable piece of the default paging text (defaults to "First Page")
19143 firstText : "First Page",
19145 * Customizable piece of the default paging text (defaults to "Previous Page")
19148 prevText : "Previous Page",
19150 * Customizable piece of the default paging text (defaults to "Next Page")
19153 nextText : "Next Page",
19155 * Customizable piece of the default paging text (defaults to "Last Page")
19158 lastText : "Last Page",
19160 * Customizable piece of the default paging text (defaults to "Refresh")
19163 refreshText : "Refresh",
19167 onRender : function(ct, position)
19169 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19170 this.navgroup.parentId = this.id;
19171 this.navgroup.onRender(this.el, null);
19172 // add the buttons to the navgroup
19174 if(this.displayInfo){
19175 Roo.log(this.el.select('ul.navbar-nav',true).first());
19176 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19177 this.displayEl = this.el.select('.x-paging-info', true).first();
19178 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19179 // this.displayEl = navel.el.select('span',true).first();
19185 Roo.each(_this.buttons, function(e){
19186 Roo.factory(e).onRender(_this.el, null);
19190 Roo.each(_this.toolbarItems, function(e) {
19191 _this.navgroup.addItem(e);
19194 this.first = this.navgroup.addItem({
19195 tooltip: this.firstText,
19197 icon : 'fa fa-backward',
19199 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19202 this.prev = this.navgroup.addItem({
19203 tooltip: this.prevText,
19205 icon : 'fa fa-step-backward',
19207 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
19209 //this.addSeparator();
19212 var field = this.navgroup.addItem( {
19214 cls : 'x-paging-position',
19216 html : this.beforePageText +
19217 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19218 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
19221 this.field = field.el.select('input', true).first();
19222 this.field.on("keydown", this.onPagingKeydown, this);
19223 this.field.on("focus", function(){this.dom.select();});
19226 this.afterTextEl = field.el.select('.x-paging-after',true).first();
19227 //this.field.setHeight(18);
19228 //this.addSeparator();
19229 this.next = this.navgroup.addItem({
19230 tooltip: this.nextText,
19232 html : ' <i class="fa fa-step-forward">',
19234 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
19236 this.last = this.navgroup.addItem({
19237 tooltip: this.lastText,
19238 icon : 'fa fa-forward',
19241 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
19243 //this.addSeparator();
19244 this.loading = this.navgroup.addItem({
19245 tooltip: this.refreshText,
19246 icon: 'fa fa-refresh',
19248 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19254 updateInfo : function(){
19255 if(this.displayEl){
19256 var count = this.ds.getCount();
19257 var msg = count == 0 ?
19261 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
19263 this.displayEl.update(msg);
19268 onLoad : function(ds, r, o){
19269 this.cursor = o.params ? o.params.start : 0;
19270 var d = this.getPageData(),
19274 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19275 this.field.dom.value = ap;
19276 this.first.setDisabled(ap == 1);
19277 this.prev.setDisabled(ap == 1);
19278 this.next.setDisabled(ap == ps);
19279 this.last.setDisabled(ap == ps);
19280 this.loading.enable();
19285 getPageData : function(){
19286 var total = this.ds.getTotalCount();
19289 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19290 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19295 onLoadError : function(){
19296 this.loading.enable();
19300 onPagingKeydown : function(e){
19301 var k = e.getKey();
19302 var d = this.getPageData();
19304 var v = this.field.dom.value, pageNum;
19305 if(!v || isNaN(pageNum = parseInt(v, 10))){
19306 this.field.dom.value = d.activePage;
19309 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19310 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19313 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))
19315 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19316 this.field.dom.value = pageNum;
19317 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19320 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19322 var v = this.field.dom.value, pageNum;
19323 var increment = (e.shiftKey) ? 10 : 1;
19324 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19326 if(!v || isNaN(pageNum = parseInt(v, 10))) {
19327 this.field.dom.value = d.activePage;
19330 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19332 this.field.dom.value = parseInt(v, 10) + increment;
19333 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19334 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19341 beforeLoad : function(){
19343 this.loading.disable();
19348 onClick : function(which){
19355 ds.load({params:{start: 0, limit: this.pageSize}});
19358 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19361 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19364 var total = ds.getTotalCount();
19365 var extra = total % this.pageSize;
19366 var lastStart = extra ? (total - extra) : total-this.pageSize;
19367 ds.load({params:{start: lastStart, limit: this.pageSize}});
19370 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19376 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19377 * @param {Roo.data.Store} store The data store to unbind
19379 unbind : function(ds){
19380 ds.un("beforeload", this.beforeLoad, this);
19381 ds.un("load", this.onLoad, this);
19382 ds.un("loadexception", this.onLoadError, this);
19383 ds.un("remove", this.updateInfo, this);
19384 ds.un("add", this.updateInfo, this);
19385 this.ds = undefined;
19389 * Binds the paging toolbar to the specified {@link Roo.data.Store}
19390 * @param {Roo.data.Store} store The data store to bind
19392 bind : function(ds){
19393 ds.on("beforeload", this.beforeLoad, this);
19394 ds.on("load", this.onLoad, this);
19395 ds.on("loadexception", this.onLoadError, this);
19396 ds.on("remove", this.updateInfo, this);
19397 ds.on("add", this.updateInfo, this);
19408 * @class Roo.bootstrap.MessageBar
19409 * @extends Roo.bootstrap.Component
19410 * Bootstrap MessageBar class
19411 * @cfg {String} html contents of the MessageBar
19412 * @cfg {String} weight (info | success | warning | danger) default info
19413 * @cfg {String} beforeClass insert the bar before the given class
19414 * @cfg {Boolean} closable (true | false) default false
19415 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19418 * Create a new Element
19419 * @param {Object} config The config object
19422 Roo.bootstrap.MessageBar = function(config){
19423 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19426 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
19432 beforeClass: 'bootstrap-sticky-wrap',
19434 getAutoCreate : function(){
19438 cls: 'alert alert-dismissable alert-' + this.weight,
19443 html: this.html || ''
19449 cfg.cls += ' alert-messages-fixed';
19463 onRender : function(ct, position)
19465 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19468 var cfg = Roo.apply({}, this.getAutoCreate());
19472 cfg.cls += ' ' + this.cls;
19475 cfg.style = this.style;
19477 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19479 this.el.setVisibilityMode(Roo.Element.DISPLAY);
19482 this.el.select('>button.close').on('click', this.hide, this);
19488 if (!this.rendered) {
19494 this.fireEvent('show', this);
19500 if (!this.rendered) {
19506 this.fireEvent('hide', this);
19509 update : function()
19511 // var e = this.el.dom.firstChild;
19513 // if(this.closable){
19514 // e = e.nextSibling;
19517 // e.data = this.html || '';
19519 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19535 * @class Roo.bootstrap.Graph
19536 * @extends Roo.bootstrap.Component
19537 * Bootstrap Graph class
19541 @cfg {String} graphtype bar | vbar | pie
19542 @cfg {number} g_x coodinator | centre x (pie)
19543 @cfg {number} g_y coodinator | centre y (pie)
19544 @cfg {number} g_r radius (pie)
19545 @cfg {number} g_height height of the chart (respected by all elements in the set)
19546 @cfg {number} g_width width of the chart (respected by all elements in the set)
19547 @cfg {Object} title The title of the chart
19550 -opts (object) options for the chart
19552 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19553 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19555 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.
19556 o stacked (boolean) whether or not to tread values as in a stacked bar chart
19558 o stretch (boolean)
19560 -opts (object) options for the pie
19563 o startAngle (number)
19564 o endAngle (number)
19568 * Create a new Input
19569 * @param {Object} config The config object
19572 Roo.bootstrap.Graph = function(config){
19573 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19579 * The img click event for the img.
19580 * @param {Roo.EventObject} e
19586 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
19597 //g_colors: this.colors,
19604 getAutoCreate : function(){
19615 onRender : function(ct,position){
19616 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19617 this.raphael = Raphael(this.el.dom);
19619 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19620 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19621 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19622 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19624 r.text(160, 10, "Single Series Chart").attr(txtattr);
19625 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19626 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19627 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19629 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19630 r.barchart(330, 10, 300, 220, data1);
19631 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19632 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19635 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19636 // r.barchart(30, 30, 560, 250, xdata, {
19637 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19638 // axis : "0 0 1 1",
19639 // axisxlabels : xdata
19640 // //yvalues : cols,
19643 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19645 // this.load(null,xdata,{
19646 // axis : "0 0 1 1",
19647 // axisxlabels : xdata
19652 load : function(graphtype,xdata,opts){
19653 this.raphael.clear();
19655 graphtype = this.graphtype;
19660 var r = this.raphael,
19661 fin = function () {
19662 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19664 fout = function () {
19665 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19667 pfin = function() {
19668 this.sector.stop();
19669 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19672 this.label[0].stop();
19673 this.label[0].attr({ r: 7.5 });
19674 this.label[1].attr({ "font-weight": 800 });
19677 pfout = function() {
19678 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19681 this.label[0].animate({ r: 5 }, 500, "bounce");
19682 this.label[1].attr({ "font-weight": 400 });
19688 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19691 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19694 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
19695 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19697 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19704 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19709 setTitle: function(o)
19714 initEvents: function() {
19717 this.el.on('click', this.onClick, this);
19721 onClick : function(e)
19723 Roo.log('img onclick');
19724 this.fireEvent('click', this, e);
19736 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19739 * @class Roo.bootstrap.dash.NumberBox
19740 * @extends Roo.bootstrap.Component
19741 * Bootstrap NumberBox class
19742 * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19743 * @cfg {String} headline Box headline
19744 * @cfg {String} content Box content
19745 * @cfg {String} icon Box icon
19746 * @cfg {String} footer Footer text
19747 * @cfg {String} fhref Footer href
19750 * Create a new NumberBox
19751 * @param {Object} config The config object
19755 Roo.bootstrap.dash.NumberBox = function(config){
19756 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19760 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
19770 getAutoCreate : function(){
19774 cls : 'small-box bg-' + this.bgcolor,
19782 cls : 'roo-headline',
19783 html : this.headline
19787 cls : 'roo-content',
19788 html : this.content
19802 cls : 'ion ' + this.icon
19811 cls : 'small-box-footer',
19812 href : this.fhref || '#',
19816 cfg.cn.push(footer);
19823 onRender : function(ct,position){
19824 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19831 setHeadline: function (value)
19833 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19836 setFooter: function (value, href)
19838 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19841 this.el.select('a.small-box-footer',true).first().attr('href', href);
19846 setContent: function (value)
19848 this.el.select('.roo-content',true).first().dom.innerHTML = value;
19851 initEvents: function()
19865 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19868 * @class Roo.bootstrap.dash.TabBox
19869 * @extends Roo.bootstrap.Component
19870 * Bootstrap TabBox class
19871 * @cfg {String} title Title of the TabBox
19872 * @cfg {String} icon Icon of the TabBox
19873 * @cfg {Boolean} showtabs (true|false) show the tabs default true
19876 * Create a new TabBox
19877 * @param {Object} config The config object
19881 Roo.bootstrap.dash.TabBox = function(config){
19882 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19887 * When a pane is added
19888 * @param {Roo.bootstrap.dash.TabPane} pane
19895 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
19901 getChildContainer : function()
19903 return this.el.select('.tab-content', true).first();
19906 getAutoCreate : function(){
19910 cls: 'pull-left header',
19918 cls: 'fa ' + this.icon
19925 cls: 'nav-tabs-custom',
19929 cls: 'nav nav-tabs pull-right',
19936 cls: 'tab-content no-padding',
19944 initEvents : function()
19946 //Roo.log('add add pane handler');
19947 this.on('addpane', this.onAddPane, this);
19950 * Updates the box title
19951 * @param {String} html to set the title to.
19953 setTitle : function(value)
19955 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19957 onAddPane : function(pane)
19959 //Roo.log('addpane');
19961 // tabs are rendere left to right..
19962 if(!this.showtabs){
19966 var ctr = this.el.select('.nav-tabs', true).first();
19969 var existing = ctr.select('.nav-tab',true);
19970 var qty = existing.getCount();;
19973 var tab = ctr.createChild({
19975 cls : 'nav-tab' + (qty ? '' : ' active'),
19983 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19986 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19988 pane.el.addClass('active');
19993 onTabClick : function(ev,un,ob,pane)
19995 //Roo.log('tab - prev default');
19996 ev.preventDefault();
19999 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20000 pane.tab.addClass('active');
20001 //Roo.log(pane.title);
20002 this.getChildContainer().select('.tab-pane',true).removeClass('active');
20003 // technically we should have a deactivate event.. but maybe add later.
20004 // and it should not de-activate the selected tab...
20006 pane.el.addClass('active');
20007 pane.fireEvent('activate');
20022 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20024 * @class Roo.bootstrap.TabPane
20025 * @extends Roo.bootstrap.Component
20026 * Bootstrap TabPane class
20027 * @cfg {Boolean} active (false | true) Default false
20028 * @cfg {String} title title of panel
20032 * Create a new TabPane
20033 * @param {Object} config The config object
20036 Roo.bootstrap.dash.TabPane = function(config){
20037 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20043 * When a pane is activated
20044 * @param {Roo.bootstrap.dash.TabPane} pane
20051 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
20056 // the tabBox that this is attached to.
20059 getAutoCreate : function()
20067 cfg.cls += ' active';
20072 initEvents : function()
20074 //Roo.log('trigger add pane handler');
20075 this.parent().fireEvent('addpane', this)
20079 * Updates the tab title
20080 * @param {String} html to set the title to.
20082 setTitle: function(str)
20088 this.tab.select('a', true).first().dom.innerHTML = str;
20105 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20108 * @class Roo.bootstrap.menu.Menu
20109 * @extends Roo.bootstrap.Component
20110 * Bootstrap Menu class - container for Menu
20111 * @cfg {String} html Text of the menu
20112 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20113 * @cfg {String} icon Font awesome icon
20114 * @cfg {String} pos Menu align to (top | bottom) default bottom
20118 * Create a new Menu
20119 * @param {Object} config The config object
20123 Roo.bootstrap.menu.Menu = function(config){
20124 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20128 * @event beforeshow
20129 * Fires before this menu is displayed
20130 * @param {Roo.bootstrap.menu.Menu} this
20134 * @event beforehide
20135 * Fires before this menu is hidden
20136 * @param {Roo.bootstrap.menu.Menu} this
20141 * Fires after this menu is displayed
20142 * @param {Roo.bootstrap.menu.Menu} this
20147 * Fires after this menu is hidden
20148 * @param {Roo.bootstrap.menu.Menu} this
20153 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20154 * @param {Roo.bootstrap.menu.Menu} this
20155 * @param {Roo.EventObject} e
20162 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
20166 weight : 'default',
20171 getChildContainer : function() {
20172 if(this.isSubMenu){
20176 return this.el.select('ul.dropdown-menu', true).first();
20179 getAutoCreate : function()
20184 cls : 'roo-menu-text',
20192 cls : 'fa ' + this.icon
20203 cls : 'dropdown-button btn btn-' + this.weight,
20208 cls : 'dropdown-toggle btn btn-' + this.weight,
20218 cls : 'dropdown-menu'
20224 if(this.pos == 'top'){
20225 cfg.cls += ' dropup';
20228 if(this.isSubMenu){
20231 cls : 'dropdown-menu'
20238 onRender : function(ct, position)
20240 this.isSubMenu = ct.hasClass('dropdown-submenu');
20242 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20245 initEvents : function()
20247 if(this.isSubMenu){
20251 this.hidden = true;
20253 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20254 this.triggerEl.on('click', this.onTriggerPress, this);
20256 this.buttonEl = this.el.select('button.dropdown-button', true).first();
20257 this.buttonEl.on('click', this.onClick, this);
20263 if(this.isSubMenu){
20267 return this.el.select('ul.dropdown-menu', true).first();
20270 onClick : function(e)
20272 this.fireEvent("click", this, e);
20275 onTriggerPress : function(e)
20277 if (this.isVisible()) {
20284 isVisible : function(){
20285 return !this.hidden;
20290 this.fireEvent("beforeshow", this);
20292 this.hidden = false;
20293 this.el.addClass('open');
20295 Roo.get(document).on("mouseup", this.onMouseUp, this);
20297 this.fireEvent("show", this);
20304 this.fireEvent("beforehide", this);
20306 this.hidden = true;
20307 this.el.removeClass('open');
20309 Roo.get(document).un("mouseup", this.onMouseUp);
20311 this.fireEvent("hide", this);
20314 onMouseUp : function()
20328 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20331 * @class Roo.bootstrap.menu.Item
20332 * @extends Roo.bootstrap.Component
20333 * Bootstrap MenuItem class
20334 * @cfg {Boolean} submenu (true | false) default false
20335 * @cfg {String} html text of the item
20336 * @cfg {String} href the link
20337 * @cfg {Boolean} disable (true | false) default false
20338 * @cfg {Boolean} preventDefault (true | false) default true
20339 * @cfg {String} icon Font awesome icon
20340 * @cfg {String} pos Submenu align to (left | right) default right
20344 * Create a new Item
20345 * @param {Object} config The config object
20349 Roo.bootstrap.menu.Item = function(config){
20350 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20354 * Fires when the mouse is hovering over this menu
20355 * @param {Roo.bootstrap.menu.Item} this
20356 * @param {Roo.EventObject} e
20361 * Fires when the mouse exits this menu
20362 * @param {Roo.bootstrap.menu.Item} this
20363 * @param {Roo.EventObject} e
20369 * The raw click event for the entire grid.
20370 * @param {Roo.EventObject} e
20376 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
20381 preventDefault: true,
20386 getAutoCreate : function()
20391 cls : 'roo-menu-item-text',
20399 cls : 'fa ' + this.icon
20408 href : this.href || '#',
20415 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20419 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20421 if(this.pos == 'left'){
20422 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20429 initEvents : function()
20431 this.el.on('mouseover', this.onMouseOver, this);
20432 this.el.on('mouseout', this.onMouseOut, this);
20434 this.el.select('a', true).first().on('click', this.onClick, this);
20438 onClick : function(e)
20440 if(this.preventDefault){
20441 e.preventDefault();
20444 this.fireEvent("click", this, e);
20447 onMouseOver : function(e)
20449 if(this.submenu && this.pos == 'left'){
20450 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20453 this.fireEvent("mouseover", this, e);
20456 onMouseOut : function(e)
20458 this.fireEvent("mouseout", this, e);
20470 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20473 * @class Roo.bootstrap.menu.Separator
20474 * @extends Roo.bootstrap.Component
20475 * Bootstrap Separator class
20478 * Create a new Separator
20479 * @param {Object} config The config object
20483 Roo.bootstrap.menu.Separator = function(config){
20484 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20487 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
20489 getAutoCreate : function(){