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()
3724 if (typeof (this.menu) != 'undefined') {
3725 this.menu.parentType = this.xtype;
3726 this.menu.triggerEl = this.el;
3727 this.addxtype(Roo.apply({}, this.menu));
3730 this.el.select('a',true).on('click', this.onClick, this);
3732 if(this.tagtype == 'span'){
3733 this.el.select('span',true).on('click', this.onClick, this);
3736 // at this point parent should be available..
3737 this.parent().register(this);
3740 onClick : function(e)
3743 if(this.preventDefault){
3746 if (this.disabled) {
3750 var tg = Roo.bootstrap.TabGroup.get(this.navId);
3751 if (tg && tg.transition) {
3752 Roo.log("waiting for the transitionend");
3756 Roo.log("fire event clicked");
3757 if(this.fireEvent('click', this, e) === false){
3761 if(this.tagtype == 'span'){
3765 var p = this.parent();
3766 if (['tabs','pills'].indexOf(p.type)!==-1) {
3767 if (typeof(p.setActiveItem) !== 'undefined') {
3768 p.setActiveItem(this);
3771 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3772 if (p.parentType == 'NavHeaderbar' && !this.menu) {
3773 // remove the collapsed menu expand...
3774 p.parent().el.select('.navbar-collapse',true).removeClass('in');
3779 isActive: function () {
3782 setActive : function(state, fire, is_was_active)
3784 if (this.active && !state & this.navId) {
3785 this.was_active = true;
3786 var nv = Roo.bootstrap.NavGroup.get(this.navId);
3788 nv.clearWasActive(this);
3792 this.active = state;
3795 this.el.removeClass('active');
3796 } else if (!this.el.hasClass('active')) {
3797 this.el.addClass('active');
3800 this.fireEvent('changed', this, state);
3803 // show a panel if it's registered and related..
3805 if (!this.navId || !this.tabId || !state || is_was_active) {
3809 var tg = Roo.bootstrap.TabGroup.get(this.navId);
3813 var pan = tg.getPanelByName(this.tabId);
3817 // if we can not flip to new panel - go back to old nav highlight..
3818 if (false == tg.showPanel(pan)) {
3819 var nv = Roo.bootstrap.NavGroup.get(this.navId);
3821 var onav = nv.getWasActive();
3823 onav.setActive(true, false, true);
3832 // this should not be here...
3833 setDisabled : function(state)
3835 this.disabled = state;
3837 this.el.removeClass('disabled');
3838 } else if (!this.el.hasClass('disabled')) {
3839 this.el.addClass('disabled');
3852 * <span> icon </span>
3853 * <span> text </span>
3854 * <span>badge </span>
3858 * @class Roo.bootstrap.NavSidebarItem
3859 * @extends Roo.bootstrap.NavItem
3860 * Bootstrap Navbar.NavSidebarItem class
3862 * Create a new Navbar Button
3863 * @param {Object} config The config object
3865 Roo.bootstrap.NavSidebarItem = function(config){
3866 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3871 * The raw click event for the entire grid.
3872 * @param {Roo.EventObject} e
3877 * Fires when the active item active state changes
3878 * @param {Roo.bootstrap.NavSidebarItem} this
3879 * @param {boolean} state the new state
3887 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
3890 getAutoCreate : function(){
3895 href : this.href || '#',
3907 html : this.html || ''
3912 cfg.cls += ' active';
3916 if (this.glyphicon || this.icon) {
3917 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
3918 a.cn.push({ tag : 'i', cls : c }) ;
3923 if (this.badge !== '') {
3924 a.cn.push({ tag: 'span', cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge });
3928 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3929 a.cls += 'dropdown-toggle treeview' ;
3953 * @class Roo.bootstrap.Row
3954 * @extends Roo.bootstrap.Component
3955 * Bootstrap Row class (contains columns...)
3959 * @param {Object} config The config object
3962 Roo.bootstrap.Row = function(config){
3963 Roo.bootstrap.Row.superclass.constructor.call(this, config);
3966 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
3968 getAutoCreate : function(){
3987 * @class Roo.bootstrap.Element
3988 * @extends Roo.bootstrap.Component
3989 * Bootstrap Element class
3990 * @cfg {String} html contents of the element
3991 * @cfg {String} tag tag of the element
3992 * @cfg {String} cls class of the element
3995 * Create a new Element
3996 * @param {Object} config The config object
3999 Roo.bootstrap.Element = function(config){
4000 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4003 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4010 getAutoCreate : function(){
4035 * @class Roo.bootstrap.Pagination
4036 * @extends Roo.bootstrap.Component
4037 * Bootstrap Pagination class
4038 * @cfg {String} size xs | sm | md | lg
4039 * @cfg {Boolean} inverse false | true
4042 * Create a new Pagination
4043 * @param {Object} config The config object
4046 Roo.bootstrap.Pagination = function(config){
4047 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4050 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4056 getAutoCreate : function(){
4062 cfg.cls += ' inverse';
4068 cfg.cls += " " + this.cls;
4086 * @class Roo.bootstrap.PaginationItem
4087 * @extends Roo.bootstrap.Component
4088 * Bootstrap PaginationItem class
4089 * @cfg {String} html text
4090 * @cfg {String} href the link
4091 * @cfg {Boolean} preventDefault (true | false) default true
4092 * @cfg {Boolean} active (true | false) default false
4096 * Create a new PaginationItem
4097 * @param {Object} config The config object
4101 Roo.bootstrap.PaginationItem = function(config){
4102 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4107 * The raw click event for the entire grid.
4108 * @param {Roo.EventObject} e
4114 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4118 preventDefault: true,
4122 getAutoCreate : function(){
4128 href : this.href ? this.href : '#',
4129 html : this.html ? this.html : ''
4139 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4145 initEvents: function() {
4147 this.el.on('click', this.onClick, this);
4150 onClick : function(e)
4152 Roo.log('PaginationItem on click ');
4153 if(this.preventDefault){
4157 this.fireEvent('click', this, e);
4173 * @class Roo.bootstrap.Slider
4174 * @extends Roo.bootstrap.Component
4175 * Bootstrap Slider class
4178 * Create a new Slider
4179 * @param {Object} config The config object
4182 Roo.bootstrap.Slider = function(config){
4183 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4186 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4188 getAutoCreate : function(){
4192 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4196 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4208 * Ext JS Library 1.1.1
4209 * Copyright(c) 2006-2007, Ext JS, LLC.
4211 * Originally Released Under LGPL - original licence link has changed is not relivant.
4214 * <script type="text/javascript">
4219 * @class Roo.grid.ColumnModel
4220 * @extends Roo.util.Observable
4221 * This is the default implementation of a ColumnModel used by the Grid. It defines
4222 * the columns in the grid.
4225 var colModel = new Roo.grid.ColumnModel([
4226 {header: "Ticker", width: 60, sortable: true, locked: true},
4227 {header: "Company Name", width: 150, sortable: true},
4228 {header: "Market Cap.", width: 100, sortable: true},
4229 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4230 {header: "Employees", width: 100, sortable: true, resizable: false}
4235 * The config options listed for this class are options which may appear in each
4236 * individual column definition.
4237 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4239 * @param {Object} config An Array of column config objects. See this class's
4240 * config objects for details.
4242 Roo.grid.ColumnModel = function(config){
4244 * The config passed into the constructor
4246 this.config = config;
4249 // if no id, create one
4250 // if the column does not have a dataIndex mapping,
4251 // map it to the order it is in the config
4252 for(var i = 0, len = config.length; i < len; i++){
4254 if(typeof c.dataIndex == "undefined"){
4257 if(typeof c.renderer == "string"){
4258 c.renderer = Roo.util.Format[c.renderer];
4260 if(typeof c.id == "undefined"){
4263 if(c.editor && c.editor.xtype){
4264 c.editor = Roo.factory(c.editor, Roo.grid);
4266 if(c.editor && c.editor.isFormField){
4267 c.editor = new Roo.grid.GridEditor(c.editor);
4269 this.lookup[c.id] = c;
4273 * The width of columns which have no width specified (defaults to 100)
4276 this.defaultWidth = 100;
4279 * Default sortable of columns which have no sortable specified (defaults to false)
4282 this.defaultSortable = false;
4286 * @event widthchange
4287 * Fires when the width of a column changes.
4288 * @param {ColumnModel} this
4289 * @param {Number} columnIndex The column index
4290 * @param {Number} newWidth The new width
4292 "widthchange": true,
4294 * @event headerchange
4295 * Fires when the text of a header changes.
4296 * @param {ColumnModel} this
4297 * @param {Number} columnIndex The column index
4298 * @param {Number} newText The new header text
4300 "headerchange": true,
4302 * @event hiddenchange
4303 * Fires when a column is hidden or "unhidden".
4304 * @param {ColumnModel} this
4305 * @param {Number} columnIndex The column index
4306 * @param {Boolean} hidden true if hidden, false otherwise
4308 "hiddenchange": true,
4310 * @event columnmoved
4311 * Fires when a column is moved.
4312 * @param {ColumnModel} this
4313 * @param {Number} oldIndex
4314 * @param {Number} newIndex
4316 "columnmoved" : true,
4318 * @event columlockchange
4319 * Fires when a column's locked state is changed
4320 * @param {ColumnModel} this
4321 * @param {Number} colIndex
4322 * @param {Boolean} locked true if locked
4324 "columnlockchange" : true
4326 Roo.grid.ColumnModel.superclass.constructor.call(this);
4328 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4330 * @cfg {String} header The header text to display in the Grid view.
4333 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4334 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4335 * specified, the column's index is used as an index into the Record's data Array.
4338 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4339 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4342 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4343 * Defaults to the value of the {@link #defaultSortable} property.
4344 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4347 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4350 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4353 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4356 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4359 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4360 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4361 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4362 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4365 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4368 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4372 * Returns the id of the column at the specified index.
4373 * @param {Number} index The column index
4374 * @return {String} the id
4376 getColumnId : function(index){
4377 return this.config[index].id;
4381 * Returns the column for a specified id.
4382 * @param {String} id The column id
4383 * @return {Object} the column
4385 getColumnById : function(id){
4386 return this.lookup[id];
4391 * Returns the column for a specified dataIndex.
4392 * @param {String} dataIndex The column dataIndex
4393 * @return {Object|Boolean} the column or false if not found
4395 getColumnByDataIndex: function(dataIndex){
4396 var index = this.findColumnIndex(dataIndex);
4397 return index > -1 ? this.config[index] : false;
4401 * Returns the index for a specified column id.
4402 * @param {String} id The column id
4403 * @return {Number} the index, or -1 if not found
4405 getIndexById : function(id){
4406 for(var i = 0, len = this.config.length; i < len; i++){
4407 if(this.config[i].id == id){
4415 * Returns the index for a specified column dataIndex.
4416 * @param {String} dataIndex The column dataIndex
4417 * @return {Number} the index, or -1 if not found
4420 findColumnIndex : function(dataIndex){
4421 for(var i = 0, len = this.config.length; i < len; i++){
4422 if(this.config[i].dataIndex == dataIndex){
4430 moveColumn : function(oldIndex, newIndex){
4431 var c = this.config[oldIndex];
4432 this.config.splice(oldIndex, 1);
4433 this.config.splice(newIndex, 0, c);
4434 this.dataMap = null;
4435 this.fireEvent("columnmoved", this, oldIndex, newIndex);
4438 isLocked : function(colIndex){
4439 return this.config[colIndex].locked === true;
4442 setLocked : function(colIndex, value, suppressEvent){
4443 if(this.isLocked(colIndex) == value){
4446 this.config[colIndex].locked = value;
4448 this.fireEvent("columnlockchange", this, colIndex, value);
4452 getTotalLockedWidth : function(){
4454 for(var i = 0; i < this.config.length; i++){
4455 if(this.isLocked(i) && !this.isHidden(i)){
4456 this.totalWidth += this.getColumnWidth(i);
4462 getLockedCount : function(){
4463 for(var i = 0, len = this.config.length; i < len; i++){
4464 if(!this.isLocked(i)){
4471 * Returns the number of columns.
4474 getColumnCount : function(visibleOnly){
4475 if(visibleOnly === true){
4477 for(var i = 0, len = this.config.length; i < len; i++){
4478 if(!this.isHidden(i)){
4484 return this.config.length;
4488 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4489 * @param {Function} fn
4490 * @param {Object} scope (optional)
4491 * @return {Array} result
4493 getColumnsBy : function(fn, scope){
4495 for(var i = 0, len = this.config.length; i < len; i++){
4496 var c = this.config[i];
4497 if(fn.call(scope||this, c, i) === true){
4505 * Returns true if the specified column is sortable.
4506 * @param {Number} col The column index
4509 isSortable : function(col){
4510 if(typeof this.config[col].sortable == "undefined"){
4511 return this.defaultSortable;
4513 return this.config[col].sortable;
4517 * Returns the rendering (formatting) function defined for the column.
4518 * @param {Number} col The column index.
4519 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4521 getRenderer : function(col){
4522 if(!this.config[col].renderer){
4523 return Roo.grid.ColumnModel.defaultRenderer;
4525 return this.config[col].renderer;
4529 * Sets the rendering (formatting) function for a column.
4530 * @param {Number} col The column index
4531 * @param {Function} fn The function to use to process the cell's raw data
4532 * to return HTML markup for the grid view. The render function is called with
4533 * the following parameters:<ul>
4534 * <li>Data value.</li>
4535 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4536 * <li>css A CSS style string to apply to the table cell.</li>
4537 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4538 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4539 * <li>Row index</li>
4540 * <li>Column index</li>
4541 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4543 setRenderer : function(col, fn){
4544 this.config[col].renderer = fn;
4548 * Returns the width for the specified column.
4549 * @param {Number} col The column index
4552 getColumnWidth : function(col){
4553 return this.config[col].width * 1 || this.defaultWidth;
4557 * Sets the width for a column.
4558 * @param {Number} col The column index
4559 * @param {Number} width The new width
4561 setColumnWidth : function(col, width, suppressEvent){
4562 this.config[col].width = width;
4563 this.totalWidth = null;
4565 this.fireEvent("widthchange", this, col, width);
4570 * Returns the total width of all columns.
4571 * @param {Boolean} includeHidden True to include hidden column widths
4574 getTotalWidth : function(includeHidden){
4575 if(!this.totalWidth){
4576 this.totalWidth = 0;
4577 for(var i = 0, len = this.config.length; i < len; i++){
4578 if(includeHidden || !this.isHidden(i)){
4579 this.totalWidth += this.getColumnWidth(i);
4583 return this.totalWidth;
4587 * Returns the header for the specified column.
4588 * @param {Number} col The column index
4591 getColumnHeader : function(col){
4592 return this.config[col].header;
4596 * Sets the header for a column.
4597 * @param {Number} col The column index
4598 * @param {String} header The new header
4600 setColumnHeader : function(col, header){
4601 this.config[col].header = header;
4602 this.fireEvent("headerchange", this, col, header);
4606 * Returns the tooltip for the specified column.
4607 * @param {Number} col The column index
4610 getColumnTooltip : function(col){
4611 return this.config[col].tooltip;
4614 * Sets the tooltip for a column.
4615 * @param {Number} col The column index
4616 * @param {String} tooltip The new tooltip
4618 setColumnTooltip : function(col, tooltip){
4619 this.config[col].tooltip = tooltip;
4623 * Returns the dataIndex for the specified column.
4624 * @param {Number} col The column index
4627 getDataIndex : function(col){
4628 return this.config[col].dataIndex;
4632 * Sets the dataIndex for a column.
4633 * @param {Number} col The column index
4634 * @param {Number} dataIndex The new dataIndex
4636 setDataIndex : function(col, dataIndex){
4637 this.config[col].dataIndex = dataIndex;
4643 * Returns true if the cell is editable.
4644 * @param {Number} colIndex The column index
4645 * @param {Number} rowIndex The row index
4648 isCellEditable : function(colIndex, rowIndex){
4649 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4653 * Returns the editor defined for the cell/column.
4654 * return false or null to disable editing.
4655 * @param {Number} colIndex The column index
4656 * @param {Number} rowIndex The row index
4659 getCellEditor : function(colIndex, rowIndex){
4660 return this.config[colIndex].editor;
4664 * Sets if a column is editable.
4665 * @param {Number} col The column index
4666 * @param {Boolean} editable True if the column is editable
4668 setEditable : function(col, editable){
4669 this.config[col].editable = editable;
4674 * Returns true if the column is hidden.
4675 * @param {Number} colIndex The column index
4678 isHidden : function(colIndex){
4679 return this.config[colIndex].hidden;
4684 * Returns true if the column width cannot be changed
4686 isFixed : function(colIndex){
4687 return this.config[colIndex].fixed;
4691 * Returns true if the column can be resized
4694 isResizable : function(colIndex){
4695 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4698 * Sets if a column is hidden.
4699 * @param {Number} colIndex The column index
4700 * @param {Boolean} hidden True if the column is hidden
4702 setHidden : function(colIndex, hidden){
4703 this.config[colIndex].hidden = hidden;
4704 this.totalWidth = null;
4705 this.fireEvent("hiddenchange", this, colIndex, hidden);
4709 * Sets the editor for a column.
4710 * @param {Number} col The column index
4711 * @param {Object} editor The editor object
4713 setEditor : function(col, editor){
4714 this.config[col].editor = editor;
4718 Roo.grid.ColumnModel.defaultRenderer = function(value){
4719 if(typeof value == "string" && value.length < 1){
4725 // Alias for backwards compatibility
4726 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4729 * Ext JS Library 1.1.1
4730 * Copyright(c) 2006-2007, Ext JS, LLC.
4732 * Originally Released Under LGPL - original licence link has changed is not relivant.
4735 * <script type="text/javascript">
4739 * @class Roo.LoadMask
4740 * A simple utility class for generically masking elements while loading data. If the element being masked has
4741 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4742 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
4743 * element's UpdateManager load indicator and will be destroyed after the initial load.
4745 * Create a new LoadMask
4746 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4747 * @param {Object} config The config object
4749 Roo.LoadMask = function(el, config){
4750 this.el = Roo.get(el);
4751 Roo.apply(this, config);
4753 this.store.on('beforeload', this.onBeforeLoad, this);
4754 this.store.on('load', this.onLoad, this);
4755 this.store.on('loadexception', this.onLoadException, this);
4756 this.removeMask = false;
4758 var um = this.el.getUpdateManager();
4759 um.showLoadIndicator = false; // disable the default indicator
4760 um.on('beforeupdate', this.onBeforeLoad, this);
4761 um.on('update', this.onLoad, this);
4762 um.on('failure', this.onLoad, this);
4763 this.removeMask = true;
4767 Roo.LoadMask.prototype = {
4769 * @cfg {Boolean} removeMask
4770 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4771 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
4775 * The text to display in a centered loading message box (defaults to 'Loading...')
4779 * @cfg {String} msgCls
4780 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4782 msgCls : 'x-mask-loading',
4785 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4791 * Disables the mask to prevent it from being displayed
4793 disable : function(){
4794 this.disabled = true;
4798 * Enables the mask so that it can be displayed
4800 enable : function(){
4801 this.disabled = false;
4804 onLoadException : function()
4808 if (typeof(arguments[3]) != 'undefined') {
4809 Roo.MessageBox.alert("Error loading",arguments[3]);
4813 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4814 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4823 this.el.unmask(this.removeMask);
4828 this.el.unmask(this.removeMask);
4832 onBeforeLoad : function(){
4834 this.el.mask(this.msg, this.msgCls);
4839 destroy : function(){
4841 this.store.un('beforeload', this.onBeforeLoad, this);
4842 this.store.un('load', this.onLoad, this);
4843 this.store.un('loadexception', this.onLoadException, this);
4845 var um = this.el.getUpdateManager();
4846 um.un('beforeupdate', this.onBeforeLoad, this);
4847 um.un('update', this.onLoad, this);
4848 um.un('failure', this.onLoad, this);
4859 * @class Roo.bootstrap.Table
4860 * @extends Roo.bootstrap.Component
4861 * Bootstrap Table class
4862 * @cfg {String} cls table class
4863 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4864 * @cfg {String} bgcolor Specifies the background color for a table
4865 * @cfg {Number} border Specifies whether the table cells should have borders or not
4866 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4867 * @cfg {Number} cellspacing Specifies the space between cells
4868 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4869 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4870 * @cfg {String} sortable Specifies that the table should be sortable
4871 * @cfg {String} summary Specifies a summary of the content of a table
4872 * @cfg {Number} width Specifies the width of a table
4873 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4875 * @cfg {boolean} striped Should the rows be alternative striped
4876 * @cfg {boolean} bordered Add borders to the table
4877 * @cfg {boolean} hover Add hover highlighting
4878 * @cfg {boolean} condensed Format condensed
4879 * @cfg {boolean} responsive Format condensed
4880 * @cfg {Boolean} loadMask (true|false) default false
4881 * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4882 * @cfg {Boolean} thead (true|false) generate thead, default true
4883 * @cfg {Boolean} RowSelection (true|false) default false
4884 * @cfg {Boolean} CellSelection (true|false) default false
4886 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
4890 * Create a new Table
4891 * @param {Object} config The config object
4894 Roo.bootstrap.Table = function(config){
4895 Roo.bootstrap.Table.superclass.constructor.call(this, config);
4898 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4899 this.sm = this.selModel;
4900 this.sm.xmodule = this.xmodule || false;
4902 if (this.cm && typeof(this.cm.config) == 'undefined') {
4903 this.colModel = new Roo.grid.ColumnModel(this.cm);
4904 this.cm = this.colModel;
4905 this.cm.xmodule = this.xmodule || false;
4908 this.store= Roo.factory(this.store, Roo.data);
4909 this.ds = this.store;
4910 this.ds.xmodule = this.xmodule || false;
4913 if (this.footer && this.store) {
4914 this.footer.dataSource = this.ds;
4915 this.footer = Roo.factory(this.footer);
4922 * Fires when a cell is clicked
4923 * @param {Roo.bootstrap.Table} this
4924 * @param {Roo.Element} el
4925 * @param {Number} rowIndex
4926 * @param {Number} columnIndex
4927 * @param {Roo.EventObject} e
4931 * @event celldblclick
4932 * Fires when a cell is double clicked
4933 * @param {Roo.bootstrap.Table} this
4934 * @param {Roo.Element} el
4935 * @param {Number} rowIndex
4936 * @param {Number} columnIndex
4937 * @param {Roo.EventObject} e
4939 "celldblclick" : true,
4942 * Fires when a row is clicked
4943 * @param {Roo.bootstrap.Table} this
4944 * @param {Roo.Element} el
4945 * @param {Number} rowIndex
4946 * @param {Roo.EventObject} e
4950 * @event rowdblclick
4951 * Fires when a row is double clicked
4952 * @param {Roo.bootstrap.Table} this
4953 * @param {Roo.Element} el
4954 * @param {Number} rowIndex
4955 * @param {Roo.EventObject} e
4957 "rowdblclick" : true,
4960 * Fires when a mouseover occur
4961 * @param {Roo.bootstrap.Table} this
4962 * @param {Roo.Element} el
4963 * @param {Number} rowIndex
4964 * @param {Number} columnIndex
4965 * @param {Roo.EventObject} e
4970 * Fires when a mouseout occur
4971 * @param {Roo.bootstrap.Table} this
4972 * @param {Roo.Element} el
4973 * @param {Number} rowIndex
4974 * @param {Number} columnIndex
4975 * @param {Roo.EventObject} e
4980 * Fires when a row is rendered, so you can change add a style to it.
4981 * @param {Roo.bootstrap.Table} this
4982 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
4989 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5013 RowSelection : false,
5014 CellSelection : false,
5017 // Roo.Element - the tbody
5020 getAutoCreate : function(){
5021 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5030 cfg.cls += ' table-striped';
5034 cfg.cls += ' table-hover';
5036 if (this.bordered) {
5037 cfg.cls += ' table-bordered';
5039 if (this.condensed) {
5040 cfg.cls += ' table-condensed';
5042 if (this.responsive) {
5043 cfg.cls += ' table-responsive';
5047 cfg.cls+= ' ' +this.cls;
5050 // this lot should be simplifed...
5053 cfg.align=this.align;
5056 cfg.bgcolor=this.bgcolor;
5059 cfg.border=this.border;
5061 if (this.cellpadding) {
5062 cfg.cellpadding=this.cellpadding;
5064 if (this.cellspacing) {
5065 cfg.cellspacing=this.cellspacing;
5068 cfg.frame=this.frame;
5071 cfg.rules=this.rules;
5073 if (this.sortable) {
5074 cfg.sortable=this.sortable;
5077 cfg.summary=this.summary;
5080 cfg.width=this.width;
5083 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5086 if(this.store || this.cm){
5088 cfg.cn.push(this.renderHeader());
5091 cfg.cn.push(this.renderBody());
5094 cfg.cn.push(this.renderFooter());
5097 cfg.cls+= ' TableGrid';
5100 return { cn : [ cfg ] };
5103 initEvents : function()
5105 if(!this.store || !this.cm){
5109 //Roo.log('initEvents with ds!!!!');
5111 this.mainBody = this.el.select('tbody', true).first();
5116 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5117 e.on('click', _this.sort, _this);
5120 this.el.on("click", this.onClick, this);
5121 this.el.on("dblclick", this.onDblClick, this);
5123 this.parent().el.setStyle('position', 'relative');
5125 this.footer.parentId = this.id;
5126 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5129 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5131 this.store.on('load', this.onLoad, this);
5132 this.store.on('beforeload', this.onBeforeLoad, this);
5133 this.store.on('update', this.onUpdate, this);
5137 onMouseover : function(e, el)
5139 var cell = Roo.get(el);
5145 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5146 cell = cell.findParent('td', false, true);
5149 var row = cell.findParent('tr', false, true);
5150 var cellIndex = cell.dom.cellIndex;
5151 var rowIndex = row.dom.rowIndex - 1; // start from 0
5153 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5157 onMouseout : function(e, el)
5159 var cell = Roo.get(el);
5165 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5166 cell = cell.findParent('td', false, true);
5169 var row = cell.findParent('tr', false, true);
5170 var cellIndex = cell.dom.cellIndex;
5171 var rowIndex = row.dom.rowIndex - 1; // start from 0
5173 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5177 onClick : function(e, el)
5179 var cell = Roo.get(el);
5181 if(!cell || (!this.CellSelection && !this.RowSelection)){
5186 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5187 cell = cell.findParent('td', false, true);
5190 var row = cell.findParent('tr', false, true);
5191 var cellIndex = cell.dom.cellIndex;
5192 var rowIndex = row.dom.rowIndex - 1;
5194 if(this.CellSelection){
5195 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5198 if(this.RowSelection){
5199 this.fireEvent('rowclick', this, row, rowIndex, e);
5205 onDblClick : function(e,el)
5207 var cell = Roo.get(el);
5209 if(!cell || (!this.CellSelection && !this.RowSelection)){
5213 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5214 cell = cell.findParent('td', false, true);
5217 var row = cell.findParent('tr', false, true);
5218 var cellIndex = cell.dom.cellIndex;
5219 var rowIndex = row.dom.rowIndex - 1;
5221 if(this.CellSelection){
5222 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5225 if(this.RowSelection){
5226 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5230 sort : function(e,el)
5232 var col = Roo.get(el)
5234 if(!col.hasClass('sortable')){
5238 var sort = col.attr('sort');
5241 if(col.hasClass('glyphicon-arrow-up')){
5245 this.store.sortInfo = {field : sort, direction : dir};
5248 Roo.log("calling footer first");
5249 this.footer.onClick('first');
5252 this.store.load({ params : { start : 0 } });
5256 renderHeader : function()
5265 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5267 var config = cm.config[i];
5272 html: cm.getColumnHeader(i)
5275 if(typeof(config.hidden) != 'undefined' && config.hidden){
5276 c.style += ' display:none;';
5279 if(typeof(config.dataIndex) != 'undefined'){
5280 c.sort = config.dataIndex;
5283 if(typeof(config.sortable) != 'undefined' && config.sortable){
5287 if(typeof(config.align) != 'undefined' && config.align.length){
5288 c.style += ' text-align:' + config.align + ';';
5291 if(typeof(config.width) != 'undefined'){
5292 c.style += ' width:' + config.width + 'px;';
5301 renderBody : function()
5311 colspan : this.cm.getColumnCount()
5321 renderFooter : function()
5331 colspan : this.cm.getColumnCount()
5345 Roo.log('ds onload');
5350 var ds = this.store;
5352 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5353 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5355 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5356 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5359 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5360 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5364 var tbody = this.mainBody;
5366 if(ds.getCount() > 0){
5367 ds.data.each(function(d,rowIndex){
5368 var row = this.renderRow(cm, ds, rowIndex);
5370 tbody.createChild(row);
5374 if(row.cellObjects.length){
5375 Roo.each(row.cellObjects, function(r){
5376 _this.renderCellObject(r);
5383 Roo.each(this.el.select('tbody td', true).elements, function(e){
5384 e.on('mouseover', _this.onMouseover, _this);
5387 Roo.each(this.el.select('tbody td', true).elements, function(e){
5388 e.on('mouseout', _this.onMouseout, _this);
5391 //if(this.loadMask){
5392 // this.maskEl.hide();
5397 onUpdate : function(ds,record)
5399 this.refreshRow(record);
5401 onRemove : function(ds, record, index, isUpdate){
5402 if(isUpdate !== true){
5403 this.fireEvent("beforerowremoved", this, index, record);
5405 var bt = this.mainBody.dom;
5407 bt.removeChild(bt.rows[index]);
5410 if(isUpdate !== true){
5411 //this.stripeRows(index);
5412 //this.syncRowHeights(index, index);
5414 this.fireEvent("rowremoved", this, index, record);
5419 refreshRow : function(record){
5420 var ds = this.store, index;
5421 if(typeof record == 'number'){
5423 record = ds.getAt(index);
5425 index = ds.indexOf(record);
5427 this.insertRow(ds, index, true);
5428 this.onRemove(ds, record, index+1, true);
5429 //this.syncRowHeights(index, index);
5431 this.fireEvent("rowupdated", this, index, record);
5434 insertRow : function(dm, rowIndex, isUpdate){
5437 this.fireEvent("beforerowsinserted", this, rowIndex);
5439 //var s = this.getScrollState();
5440 var row = this.renderRow(this.cm, this.store, rowIndex);
5441 // insert before rowIndex..
5442 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5446 if(row.cellObjects.length){
5447 Roo.each(row.cellObjects, function(r){
5448 _this.renderCellObject(r);
5453 this.fireEvent("rowsinserted", this, rowIndex);
5454 //this.syncRowHeights(firstRow, lastRow);
5455 //this.stripeRows(firstRow);
5462 getRowDom : function(rowIndex)
5464 // not sure if I need to check this.. but let's do it anyway..
5465 return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5466 this.mainBody.dom.rows[rowIndex] : false
5468 // returns the object tree for a tr..
5471 renderRow : function(cm, ds, rowIndex) {
5473 var d = ds.getAt(rowIndex);
5480 var cellObjects = [];
5482 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5483 var config = cm.config[i];
5485 var renderer = cm.getRenderer(i);
5489 if(typeof(renderer) !== 'undefined'){
5490 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5492 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5493 // and are rendered into the cells after the row is rendered - using the id for the element.
5495 if(typeof(value) === 'object'){
5505 rowIndex : rowIndex,
5510 this.fireEvent('rowclass', this, rowcfg);
5514 cls : rowcfg.rowClass,
5516 html: (typeof(value) === 'object') ? '' : value
5523 if(typeof(config.hidden) != 'undefined' && config.hidden){
5524 td.style += ' display:none;';
5527 if(typeof(config.align) != 'undefined' && config.align.length){
5528 td.style += ' text-align:' + config.align + ';';
5531 if(typeof(config.width) != 'undefined'){
5532 td.style += ' width:' + config.width + 'px;';
5539 row.cellObjects = cellObjects;
5547 onBeforeLoad : function()
5549 //Roo.log('ds onBeforeLoad');
5553 //if(this.loadMask){
5554 // this.maskEl.show();
5560 this.el.select('tbody', true).first().dom.innerHTML = '';
5563 getSelectionModel : function(){
5565 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5567 return this.selModel;
5570 * Render the Roo.bootstrap object from renderder
5572 renderCellObject : function(r)
5576 var t = r.cfg.render(r.container);
5579 Roo.each(r.cfg.cn, function(c){
5581 container: t.getChildContainer(),
5584 _this.renderCellObject(child);
5601 * @class Roo.bootstrap.TableCell
5602 * @extends Roo.bootstrap.Component
5603 * Bootstrap TableCell class
5604 * @cfg {String} html cell contain text
5605 * @cfg {String} cls cell class
5606 * @cfg {String} tag cell tag (td|th) default td
5607 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5608 * @cfg {String} align Aligns the content in a cell
5609 * @cfg {String} axis Categorizes cells
5610 * @cfg {String} bgcolor Specifies the background color of a cell
5611 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5612 * @cfg {Number} colspan Specifies the number of columns a cell should span
5613 * @cfg {String} headers Specifies one or more header cells a cell is related to
5614 * @cfg {Number} height Sets the height of a cell
5615 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5616 * @cfg {Number} rowspan Sets the number of rows a cell should span
5617 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5618 * @cfg {String} valign Vertical aligns the content in a cell
5619 * @cfg {Number} width Specifies the width of a cell
5622 * Create a new TableCell
5623 * @param {Object} config The config object
5626 Roo.bootstrap.TableCell = function(config){
5627 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5630 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
5650 getAutoCreate : function(){
5651 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5671 cfg.align=this.align
5677 cfg.bgcolor=this.bgcolor
5680 cfg.charoff=this.charoff
5683 cfg.colspan=this.colspan
5686 cfg.headers=this.headers
5689 cfg.height=this.height
5692 cfg.nowrap=this.nowrap
5695 cfg.rowspan=this.rowspan
5698 cfg.scope=this.scope
5701 cfg.valign=this.valign
5704 cfg.width=this.width
5723 * @class Roo.bootstrap.TableRow
5724 * @extends Roo.bootstrap.Component
5725 * Bootstrap TableRow class
5726 * @cfg {String} cls row class
5727 * @cfg {String} align Aligns the content in a table row
5728 * @cfg {String} bgcolor Specifies a background color for a table row
5729 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5730 * @cfg {String} valign Vertical aligns the content in a table row
5733 * Create a new TableRow
5734 * @param {Object} config The config object
5737 Roo.bootstrap.TableRow = function(config){
5738 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5741 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
5749 getAutoCreate : function(){
5750 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5760 cfg.align = this.align;
5763 cfg.bgcolor = this.bgcolor;
5766 cfg.charoff = this.charoff;
5769 cfg.valign = this.valign;
5787 * @class Roo.bootstrap.TableBody
5788 * @extends Roo.bootstrap.Component
5789 * Bootstrap TableBody class
5790 * @cfg {String} cls element class
5791 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5792 * @cfg {String} align Aligns the content inside the element
5793 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5794 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5797 * Create a new TableBody
5798 * @param {Object} config The config object
5801 Roo.bootstrap.TableBody = function(config){
5802 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5805 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
5813 getAutoCreate : function(){
5814 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5828 cfg.align = this.align;
5831 cfg.charoff = this.charoff;
5834 cfg.valign = this.valign;
5841 // initEvents : function()
5848 // this.store = Roo.factory(this.store, Roo.data);
5849 // this.store.on('load', this.onLoad, this);
5851 // this.store.load();
5855 // onLoad: function ()
5857 // this.fireEvent('load', this);
5867 * Ext JS Library 1.1.1
5868 * Copyright(c) 2006-2007, Ext JS, LLC.
5870 * Originally Released Under LGPL - original licence link has changed is not relivant.
5873 * <script type="text/javascript">
5876 // as we use this in bootstrap.
5877 Roo.namespace('Roo.form');
5879 * @class Roo.form.Action
5880 * Internal Class used to handle form actions
5882 * @param {Roo.form.BasicForm} el The form element or its id
5883 * @param {Object} config Configuration options
5888 // define the action interface
5889 Roo.form.Action = function(form, options){
5891 this.options = options || {};
5894 * Client Validation Failed
5897 Roo.form.Action.CLIENT_INVALID = 'client';
5899 * Server Validation Failed
5902 Roo.form.Action.SERVER_INVALID = 'server';
5904 * Connect to Server Failed
5907 Roo.form.Action.CONNECT_FAILURE = 'connect';
5909 * Reading Data from Server Failed
5912 Roo.form.Action.LOAD_FAILURE = 'load';
5914 Roo.form.Action.prototype = {
5916 failureType : undefined,
5917 response : undefined,
5921 run : function(options){
5926 success : function(response){
5931 handleResponse : function(response){
5935 // default connection failure
5936 failure : function(response){
5938 this.response = response;
5939 this.failureType = Roo.form.Action.CONNECT_FAILURE;
5940 this.form.afterAction(this, false);
5943 processResponse : function(response){
5944 this.response = response;
5945 if(!response.responseText){
5948 this.result = this.handleResponse(response);
5952 // utility functions used internally
5953 getUrl : function(appendParams){
5954 var url = this.options.url || this.form.url || this.form.el.dom.action;
5956 var p = this.getParams();
5958 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5964 getMethod : function(){
5965 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5968 getParams : function(){
5969 var bp = this.form.baseParams;
5970 var p = this.options.params;
5972 if(typeof p == "object"){
5973 p = Roo.urlEncode(Roo.applyIf(p, bp));
5974 }else if(typeof p == 'string' && bp){
5975 p += '&' + Roo.urlEncode(bp);
5978 p = Roo.urlEncode(bp);
5983 createCallback : function(){
5985 success: this.success,
5986 failure: this.failure,
5988 timeout: (this.form.timeout*1000),
5989 upload: this.form.fileUpload ? this.success : undefined
5994 Roo.form.Action.Submit = function(form, options){
5995 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5998 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6001 haveProgress : false,
6002 uploadComplete : false,
6004 // uploadProgress indicator.
6005 uploadProgress : function()
6007 if (!this.form.progressUrl) {
6011 if (!this.haveProgress) {
6012 Roo.MessageBox.progress("Uploading", "Uploading");
6014 if (this.uploadComplete) {
6015 Roo.MessageBox.hide();
6019 this.haveProgress = true;
6021 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6023 var c = new Roo.data.Connection();
6025 url : this.form.progressUrl,
6030 success : function(req){
6031 //console.log(data);
6035 rdata = Roo.decode(req.responseText)
6037 Roo.log("Invalid data from server..");
6041 if (!rdata || !rdata.success) {
6043 Roo.MessageBox.alert(Roo.encode(rdata));
6046 var data = rdata.data;
6048 if (this.uploadComplete) {
6049 Roo.MessageBox.hide();
6054 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6055 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6058 this.uploadProgress.defer(2000,this);
6061 failure: function(data) {
6062 Roo.log('progress url failed ');
6073 // run get Values on the form, so it syncs any secondary forms.
6074 this.form.getValues();
6076 var o = this.options;
6077 var method = this.getMethod();
6078 var isPost = method == 'POST';
6079 if(o.clientValidation === false || this.form.isValid()){
6081 if (this.form.progressUrl) {
6082 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6083 (new Date() * 1) + '' + Math.random());
6088 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6089 form:this.form.el.dom,
6090 url:this.getUrl(!isPost),
6092 params:isPost ? this.getParams() : null,
6093 isUpload: this.form.fileUpload
6096 this.uploadProgress();
6098 }else if (o.clientValidation !== false){ // client validation failed
6099 this.failureType = Roo.form.Action.CLIENT_INVALID;
6100 this.form.afterAction(this, false);
6104 success : function(response)
6106 this.uploadComplete= true;
6107 if (this.haveProgress) {
6108 Roo.MessageBox.hide();
6112 var result = this.processResponse(response);
6113 if(result === true || result.success){
6114 this.form.afterAction(this, true);
6118 this.form.markInvalid(result.errors);
6119 this.failureType = Roo.form.Action.SERVER_INVALID;
6121 this.form.afterAction(this, false);
6123 failure : function(response)
6125 this.uploadComplete= true;
6126 if (this.haveProgress) {
6127 Roo.MessageBox.hide();
6130 this.response = response;
6131 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6132 this.form.afterAction(this, false);
6135 handleResponse : function(response){
6136 if(this.form.errorReader){
6137 var rs = this.form.errorReader.read(response);
6140 for(var i = 0, len = rs.records.length; i < len; i++) {
6141 var r = rs.records[i];
6145 if(errors.length < 1){
6149 success : rs.success,
6155 ret = Roo.decode(response.responseText);
6159 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6169 Roo.form.Action.Load = function(form, options){
6170 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6171 this.reader = this.form.reader;
6174 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6179 Roo.Ajax.request(Roo.apply(
6180 this.createCallback(), {
6181 method:this.getMethod(),
6182 url:this.getUrl(false),
6183 params:this.getParams()
6187 success : function(response){
6189 var result = this.processResponse(response);
6190 if(result === true || !result.success || !result.data){
6191 this.failureType = Roo.form.Action.LOAD_FAILURE;
6192 this.form.afterAction(this, false);
6195 this.form.clearInvalid();
6196 this.form.setValues(result.data);
6197 this.form.afterAction(this, true);
6200 handleResponse : function(response){
6201 if(this.form.reader){
6202 var rs = this.form.reader.read(response);
6203 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6205 success : rs.success,
6209 return Roo.decode(response.responseText);
6213 Roo.form.Action.ACTION_TYPES = {
6214 'load' : Roo.form.Action.Load,
6215 'submit' : Roo.form.Action.Submit
6224 * @class Roo.bootstrap.Form
6225 * @extends Roo.bootstrap.Component
6226 * Bootstrap Form class
6227 * @cfg {String} method GET | POST (default POST)
6228 * @cfg {String} labelAlign top | left (default top)
6229 * @cfg {String} align left | right - for navbars
6230 * @cfg {Boolean} loadMask load mask when submit (default true)
6235 * @param {Object} config The config object
6239 Roo.bootstrap.Form = function(config){
6240 Roo.bootstrap.Form.superclass.constructor.call(this, config);
6243 * @event clientvalidation
6244 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6245 * @param {Form} this
6246 * @param {Boolean} valid true if the form has passed client-side validation
6248 clientvalidation: true,
6250 * @event beforeaction
6251 * Fires before any action is performed. Return false to cancel the action.
6252 * @param {Form} this
6253 * @param {Action} action The action to be performed
6257 * @event actionfailed
6258 * Fires when an action fails.
6259 * @param {Form} this
6260 * @param {Action} action The action that failed
6262 actionfailed : true,
6264 * @event actioncomplete
6265 * Fires when an action is completed.
6266 * @param {Form} this
6267 * @param {Action} action The action that completed
6269 actioncomplete : true
6274 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
6277 * @cfg {String} method
6278 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6283 * The URL to use for form actions if one isn't supplied in the action options.
6286 * @cfg {Boolean} fileUpload
6287 * Set to true if this form is a file upload.
6291 * @cfg {Object} baseParams
6292 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6296 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6300 * @cfg {Sting} align (left|right) for navbar forms
6305 activeAction : null,
6308 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6309 * element by passing it or its id or mask the form itself by passing in true.
6312 waitMsgTarget : false,
6316 getAutoCreate : function(){
6320 method : this.method || 'POST',
6321 id : this.id || Roo.id(),
6324 if (this.parent().xtype.match(/^Nav/)) {
6325 cfg.cls = 'navbar-form navbar-' + this.align;
6329 if (this.labelAlign == 'left' ) {
6330 cfg.cls += ' form-horizontal';
6336 initEvents : function()
6338 this.el.on('submit', this.onSubmit, this);
6339 // this was added as random key presses on the form where triggering form submit.
6340 this.el.on('keypress', function(e) {
6341 if (e.getCharCode() != 13) {
6344 // we might need to allow it for textareas.. and some other items.
6345 // check e.getTarget().
6347 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6351 Roo.log("keypress blocked");
6359 onSubmit : function(e){
6364 * Returns true if client-side validation on the form is successful.
6367 isValid : function(){
6368 var items = this.getItems();
6370 items.each(function(f){
6379 * Returns true if any fields in this form have changed since their original load.
6382 isDirty : function(){
6384 var items = this.getItems();
6385 items.each(function(f){
6395 * Performs a predefined action (submit or load) or custom actions you define on this form.
6396 * @param {String} actionName The name of the action type
6397 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
6398 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6399 * accept other config options):
6401 Property Type Description
6402 ---------------- --------------- ----------------------------------------------------------------------------------
6403 url String The url for the action (defaults to the form's url)
6404 method String The form method to use (defaults to the form's method, or POST if not defined)
6405 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
6406 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
6407 validate the form on the client (defaults to false)
6409 * @return {BasicForm} this
6411 doAction : function(action, options){
6412 if(typeof action == 'string'){
6413 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6415 if(this.fireEvent('beforeaction', this, action) !== false){
6416 this.beforeAction(action);
6417 action.run.defer(100, action);
6423 beforeAction : function(action){
6424 var o = action.options;
6427 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6429 // not really supported yet.. ??
6431 //if(this.waitMsgTarget === true){
6432 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6433 //}else if(this.waitMsgTarget){
6434 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6435 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6437 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6443 afterAction : function(action, success){
6444 this.activeAction = null;
6445 var o = action.options;
6447 //if(this.waitMsgTarget === true){
6449 //}else if(this.waitMsgTarget){
6450 // this.waitMsgTarget.unmask();
6452 // Roo.MessageBox.updateProgress(1);
6453 // Roo.MessageBox.hide();
6460 Roo.callback(o.success, o.scope, [this, action]);
6461 this.fireEvent('actioncomplete', this, action);
6465 // failure condition..
6466 // we have a scenario where updates need confirming.
6467 // eg. if a locking scenario exists..
6468 // we look for { errors : { needs_confirm : true }} in the response.
6470 (typeof(action.result) != 'undefined') &&
6471 (typeof(action.result.errors) != 'undefined') &&
6472 (typeof(action.result.errors.needs_confirm) != 'undefined')
6475 Roo.log("not supported yet");
6478 Roo.MessageBox.confirm(
6479 "Change requires confirmation",
6480 action.result.errorMsg,
6485 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
6495 Roo.callback(o.failure, o.scope, [this, action]);
6496 // show an error message if no failed handler is set..
6497 if (!this.hasListener('actionfailed')) {
6498 Roo.log("need to add dialog support");
6500 Roo.MessageBox.alert("Error",
6501 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6502 action.result.errorMsg :
6503 "Saving Failed, please check your entries or try again"
6508 this.fireEvent('actionfailed', this, action);
6513 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6514 * @param {String} id The value to search for
6517 findField : function(id){
6518 var items = this.getItems();
6519 var field = items.get(id);
6521 items.each(function(f){
6522 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6529 return field || null;
6532 * Mark fields in this form invalid in bulk.
6533 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6534 * @return {BasicForm} this
6536 markInvalid : function(errors){
6537 if(errors instanceof Array){
6538 for(var i = 0, len = errors.length; i < len; i++){
6539 var fieldError = errors[i];
6540 var f = this.findField(fieldError.id);
6542 f.markInvalid(fieldError.msg);
6548 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6549 field.markInvalid(errors[id]);
6553 //Roo.each(this.childForms || [], function (f) {
6554 // f.markInvalid(errors);
6561 * Set values for fields in this form in bulk.
6562 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6563 * @return {BasicForm} this
6565 setValues : function(values){
6566 if(values instanceof Array){ // array of objects
6567 for(var i = 0, len = values.length; i < len; i++){
6569 var f = this.findField(v.id);
6571 f.setValue(v.value);
6572 if(this.trackResetOnLoad){
6573 f.originalValue = f.getValue();
6577 }else{ // object hash
6580 if(typeof values[id] != 'function' && (field = this.findField(id))){
6582 if (field.setFromData &&
6584 field.displayField &&
6585 // combos' with local stores can
6586 // be queried via setValue()
6587 // to set their value..
6588 (field.store && !field.store.isLocal)
6592 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6593 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6594 field.setFromData(sd);
6597 field.setValue(values[id]);
6601 if(this.trackResetOnLoad){
6602 field.originalValue = field.getValue();
6608 //Roo.each(this.childForms || [], function (f) {
6609 // f.setValues(values);
6616 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6617 * they are returned as an array.
6618 * @param {Boolean} asString
6621 getValues : function(asString){
6622 //if (this.childForms) {
6623 // copy values from the child forms
6624 // Roo.each(this.childForms, function (f) {
6625 // this.setValues(f.getValues());
6631 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6632 if(asString === true){
6635 return Roo.urlDecode(fs);
6639 * Returns the fields in this form as an object with key/value pairs.
6640 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6643 getFieldValues : function(with_hidden)
6645 var items = this.getItems();
6647 items.each(function(f){
6651 var v = f.getValue();
6652 if (f.inputType =='radio') {
6653 if (typeof(ret[f.getName()]) == 'undefined') {
6654 ret[f.getName()] = ''; // empty..
6657 if (!f.el.dom.checked) {
6665 // not sure if this supported any more..
6666 if ((typeof(v) == 'object') && f.getRawValue) {
6667 v = f.getRawValue() ; // dates..
6669 // combo boxes where name != hiddenName...
6670 if (f.name != f.getName()) {
6671 ret[f.name] = f.getRawValue();
6673 ret[f.getName()] = v;
6680 * Clears all invalid messages in this form.
6681 * @return {BasicForm} this
6683 clearInvalid : function(){
6684 var items = this.getItems();
6686 items.each(function(f){
6697 * @return {BasicForm} this
6700 var items = this.getItems();
6701 items.each(function(f){
6705 Roo.each(this.childForms || [], function (f) {
6712 getItems : function()
6714 var r=new Roo.util.MixedCollection(false, function(o){
6715 return o.id || (o.id = Roo.id());
6717 var iter = function(el) {
6724 Roo.each(el.items,function(e) {
6743 * Ext JS Library 1.1.1
6744 * Copyright(c) 2006-2007, Ext JS, LLC.
6746 * Originally Released Under LGPL - original licence link has changed is not relivant.
6749 * <script type="text/javascript">
6752 * @class Roo.form.VTypes
6753 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6756 Roo.form.VTypes = function(){
6757 // closure these in so they are only created once.
6758 var alpha = /^[a-zA-Z_]+$/;
6759 var alphanum = /^[a-zA-Z0-9_]+$/;
6760 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6761 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6763 // All these messages and functions are configurable
6766 * The function used to validate email addresses
6767 * @param {String} value The email address
6769 'email' : function(v){
6770 return email.test(v);
6773 * The error text to display when the email validation function returns false
6776 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6778 * The keystroke filter mask to be applied on email input
6781 'emailMask' : /[a-z0-9_\.\-@]/i,
6784 * The function used to validate URLs
6785 * @param {String} value The URL
6787 'url' : function(v){
6791 * The error text to display when the url validation function returns false
6794 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6797 * The function used to validate alpha values
6798 * @param {String} value The value
6800 'alpha' : function(v){
6801 return alpha.test(v);
6804 * The error text to display when the alpha validation function returns false
6807 'alphaText' : 'This field should only contain letters and _',
6809 * The keystroke filter mask to be applied on alpha input
6812 'alphaMask' : /[a-z_]/i,
6815 * The function used to validate alphanumeric values
6816 * @param {String} value The value
6818 'alphanum' : function(v){
6819 return alphanum.test(v);
6822 * The error text to display when the alphanumeric validation function returns false
6825 'alphanumText' : 'This field should only contain letters, numbers and _',
6827 * The keystroke filter mask to be applied on alphanumeric input
6830 'alphanumMask' : /[a-z0-9_]/i
6840 * @class Roo.bootstrap.Input
6841 * @extends Roo.bootstrap.Component
6842 * Bootstrap Input class
6843 * @cfg {Boolean} disabled is it disabled
6844 * @cfg {String} fieldLabel - the label associated
6845 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6846 * @cfg {String} name name of the input
6847 * @cfg {string} fieldLabel - the label associated
6848 * @cfg {string} inputType - input / file submit ...
6849 * @cfg {string} placeholder - placeholder to put in text.
6850 * @cfg {string} before - input group add on before
6851 * @cfg {string} after - input group add on after
6852 * @cfg {string} size - (lg|sm) or leave empty..
6853 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6854 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6855 * @cfg {Number} md colspan out of 12 for computer-sized screens
6856 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6857 * @cfg {string} value default value of the input
6858 * @cfg {Number} labelWidth set the width of label (0-12)
6859 * @cfg {String} labelAlign (top|left)
6860 * @cfg {Boolean} readOnly Specifies that the field should be read-only
6861 * @cfg {String} align (left|center|right) Default left
6865 * Create a new Input
6866 * @param {Object} config The config object
6869 Roo.bootstrap.Input = function(config){
6870 Roo.bootstrap.Input.superclass.constructor.call(this, config);
6875 * Fires when this field receives input focus.
6876 * @param {Roo.form.Field} this
6881 * Fires when this field loses input focus.
6882 * @param {Roo.form.Field} this
6887 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
6888 * {@link Roo.EventObject#getKey} to determine which key was pressed.
6889 * @param {Roo.form.Field} this
6890 * @param {Roo.EventObject} e The event object
6895 * Fires just before the field blurs if the field value has changed.
6896 * @param {Roo.form.Field} this
6897 * @param {Mixed} newValue The new value
6898 * @param {Mixed} oldValue The original value
6903 * Fires after the field has been marked as invalid.
6904 * @param {Roo.form.Field} this
6905 * @param {String} msg The validation message
6910 * Fires after the field has been validated with no errors.
6911 * @param {Roo.form.Field} this
6916 * Fires after the key up
6917 * @param {Roo.form.Field} this
6918 * @param {Roo.EventObject} e The event Object
6924 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
6926 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6927 automatic validation (defaults to "keyup").
6929 validationEvent : "keyup",
6931 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6933 validateOnBlur : true,
6935 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6937 validationDelay : 250,
6939 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6941 focusClass : "x-form-focus", // not needed???
6945 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6947 invalidClass : "has-error",
6950 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6952 selectOnFocus : false,
6955 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6959 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6964 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6966 disableKeyFilter : false,
6969 * @cfg {Boolean} disabled True to disable the field (defaults to false).
6973 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6977 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6979 blankText : "This field is required",
6982 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6986 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6988 maxLength : Number.MAX_VALUE,
6990 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6992 minLengthText : "The minimum length for this field is {0}",
6994 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6996 maxLengthText : "The maximum length for this field is {0}",
7000 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7001 * If available, this function will be called only after the basic validators all return true, and will be passed the
7002 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7006 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7007 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7008 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7012 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7035 formatedValue : false,
7037 parentLabelAlign : function()
7040 while (parent.parent()) {
7041 parent = parent.parent();
7042 if (typeof(parent.labelAlign) !='undefined') {
7043 return parent.labelAlign;
7050 getAutoCreate : function(){
7052 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7058 if(this.inputType != 'hidden'){
7059 cfg.cls = 'form-group' //input-group
7065 type : this.inputType,
7067 cls : 'form-control',
7068 placeholder : this.placeholder || ''
7073 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7076 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7077 input.maxLength = this.maxLength;
7080 if (this.disabled) {
7081 input.disabled=true;
7084 if (this.readOnly) {
7085 input.readonly=true;
7089 input.name = this.name;
7092 input.cls += ' input-' + this.size;
7095 ['xs','sm','md','lg'].map(function(size){
7096 if (settings[size]) {
7097 cfg.cls += ' col-' + size + '-' + settings[size];
7101 var inputblock = input;
7103 if (this.before || this.after) {
7106 cls : 'input-group',
7109 if (this.before && typeof(this.before) == 'string') {
7111 inputblock.cn.push({
7113 cls : 'roo-input-before input-group-addon',
7117 if (this.before && typeof(this.before) == 'object') {
7118 this.before = Roo.factory(this.before);
7119 Roo.log(this.before);
7120 inputblock.cn.push({
7122 cls : 'roo-input-before input-group-' +
7123 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7127 inputblock.cn.push(input);
7129 if (this.after && typeof(this.after) == 'string') {
7130 inputblock.cn.push({
7132 cls : 'roo-input-after input-group-addon',
7136 if (this.after && typeof(this.after) == 'object') {
7137 this.after = Roo.factory(this.after);
7138 Roo.log(this.after);
7139 inputblock.cn.push({
7141 cls : 'roo-input-after input-group-' +
7142 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7147 if (align ==='left' && this.fieldLabel.length) {
7148 Roo.log("left and has label");
7154 cls : 'control-label col-sm-' + this.labelWidth,
7155 html : this.fieldLabel
7159 cls : "col-sm-" + (12 - this.labelWidth),
7166 } else if ( this.fieldLabel.length) {
7172 //cls : 'input-group-addon',
7173 html : this.fieldLabel
7183 Roo.log(" no label && no align");
7192 Roo.log('input-parentType: ' + this.parentType);
7194 if (this.parentType === 'Navbar' && this.parent().bar) {
7195 cfg.cls += ' navbar-form';
7203 * return the real input element.
7205 inputEl: function ()
7207 return this.el.select('input.form-control',true).first();
7209 setDisabled : function(v)
7211 var i = this.inputEl().dom;
7213 i.removeAttribute('disabled');
7217 i.setAttribute('disabled','true');
7219 initEvents : function()
7222 this.inputEl().on("keydown" , this.fireKey, this);
7223 this.inputEl().on("focus", this.onFocus, this);
7224 this.inputEl().on("blur", this.onBlur, this);
7226 this.inputEl().relayEvent('keyup', this);
7228 // reference to original value for reset
7229 this.originalValue = this.getValue();
7230 //Roo.form.TextField.superclass.initEvents.call(this);
7231 if(this.validationEvent == 'keyup'){
7232 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7233 this.inputEl().on('keyup', this.filterValidation, this);
7235 else if(this.validationEvent !== false){
7236 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7239 if(this.selectOnFocus){
7240 this.on("focus", this.preFocus, this);
7243 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7244 this.inputEl().on("keypress", this.filterKeys, this);
7247 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
7248 this.el.on("click", this.autoSize, this);
7251 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7252 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7255 if (typeof(this.before) == 'object') {
7256 this.before.render(this.el.select('.roo-input-before',true).first());
7258 if (typeof(this.after) == 'object') {
7259 this.after.render(this.el.select('.roo-input-after',true).first());
7264 filterValidation : function(e){
7265 if(!e.isNavKeyPress()){
7266 this.validationTask.delay(this.validationDelay);
7270 * Validates the field value
7271 * @return {Boolean} True if the value is valid, else false
7273 validate : function(){
7274 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7275 if(this.disabled || this.validateValue(this.getRawValue())){
7276 this.clearInvalid();
7284 * Validates a value according to the field's validation rules and marks the field as invalid
7285 * if the validation fails
7286 * @param {Mixed} value The value to validate
7287 * @return {Boolean} True if the value is valid, else false
7289 validateValue : function(value){
7290 if(value.length < 1) { // if it's blank
7291 if(this.allowBlank){
7292 this.clearInvalid();
7295 this.markInvalid(this.blankText);
7299 if(value.length < this.minLength){
7300 this.markInvalid(String.format(this.minLengthText, this.minLength));
7303 if(value.length > this.maxLength){
7304 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7308 var vt = Roo.form.VTypes;
7309 if(!vt[this.vtype](value, this)){
7310 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7314 if(typeof this.validator == "function"){
7315 var msg = this.validator(value);
7317 this.markInvalid(msg);
7321 if(this.regex && !this.regex.test(value)){
7322 this.markInvalid(this.regexText);
7331 fireKey : function(e){
7332 //Roo.log('field ' + e.getKey());
7333 if(e.isNavKeyPress()){
7334 this.fireEvent("specialkey", this, e);
7337 focus : function (selectText){
7339 this.inputEl().focus();
7340 if(selectText === true){
7341 this.inputEl().dom.select();
7347 onFocus : function(){
7348 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7349 // this.el.addClass(this.focusClass);
7352 this.hasFocus = true;
7353 this.startValue = this.getValue();
7354 this.fireEvent("focus", this);
7358 beforeBlur : Roo.emptyFn,
7362 onBlur : function(){
7364 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7365 //this.el.removeClass(this.focusClass);
7367 this.hasFocus = false;
7368 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7371 var v = this.getValue();
7372 if(String(v) !== String(this.startValue)){
7373 this.fireEvent('change', this, v, this.startValue);
7375 this.fireEvent("blur", this);
7379 * Resets the current field value to the originally loaded value and clears any validation messages
7382 this.setValue(this.originalValue);
7383 this.clearInvalid();
7386 * Returns the name of the field
7387 * @return {Mixed} name The name field
7389 getName: function(){
7393 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
7394 * @return {Mixed} value The field value
7396 getValue : function(){
7398 var v = this.inputEl().getValue();
7403 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
7404 * @return {Mixed} value The field value
7406 getRawValue : function(){
7407 var v = this.inputEl().getValue();
7413 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
7414 * @param {Mixed} value The value to set
7416 setRawValue : function(v){
7417 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7420 selectText : function(start, end){
7421 var v = this.getRawValue();
7423 start = start === undefined ? 0 : start;
7424 end = end === undefined ? v.length : end;
7425 var d = this.inputEl().dom;
7426 if(d.setSelectionRange){
7427 d.setSelectionRange(start, end);
7428 }else if(d.createTextRange){
7429 var range = d.createTextRange();
7430 range.moveStart("character", start);
7431 range.moveEnd("character", v.length-end);
7438 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
7439 * @param {Mixed} value The value to set
7441 setValue : function(v){
7444 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7450 processValue : function(value){
7451 if(this.stripCharsRe){
7452 var newValue = value.replace(this.stripCharsRe, '');
7453 if(newValue !== value){
7454 this.setRawValue(newValue);
7461 preFocus : function(){
7463 if(this.selectOnFocus){
7464 this.inputEl().dom.select();
7467 filterKeys : function(e){
7469 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7472 var c = e.getCharCode(), cc = String.fromCharCode(c);
7473 if(Roo.isIE && (e.isSpecialKey() || !cc)){
7476 if(!this.maskRe.test(cc)){
7481 * Clear any invalid styles/messages for this field
7483 clearInvalid : function(){
7485 if(!this.el || this.preventMark){ // not rendered
7488 this.el.removeClass(this.invalidClass);
7490 switch(this.msgTarget){
7492 this.el.dom.qtip = '';
7495 this.el.dom.title = '';
7499 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7504 this.errorIcon.dom.qtip = '';
7505 this.errorIcon.hide();
7506 this.un('resize', this.alignErrorIcon, this);
7510 var t = Roo.getDom(this.msgTarget);
7512 t.style.display = 'none';
7516 this.fireEvent('valid', this);
7519 * Mark this field as invalid
7520 * @param {String} msg The validation message
7522 markInvalid : function(msg){
7523 if(!this.el || this.preventMark){ // not rendered
7526 this.el.addClass(this.invalidClass);
7528 msg = msg || this.invalidText;
7529 switch(this.msgTarget){
7531 this.el.dom.qtip = msg;
7532 this.el.dom.qclass = 'x-form-invalid-tip';
7533 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7534 Roo.QuickTips.enable();
7538 this.el.dom.title = msg;
7542 var elp = this.el.findParent('.x-form-element', 5, true);
7543 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7544 this.errorEl.setWidth(elp.getWidth(true)-20);
7546 this.errorEl.update(msg);
7547 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7550 if(!this.errorIcon){
7551 var elp = this.el.findParent('.x-form-element', 5, true);
7552 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7554 this.alignErrorIcon();
7555 this.errorIcon.dom.qtip = msg;
7556 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7557 this.errorIcon.show();
7558 this.on('resize', this.alignErrorIcon, this);
7561 var t = Roo.getDom(this.msgTarget);
7563 t.style.display = this.msgDisplay;
7567 this.fireEvent('invalid', this, msg);
7570 SafariOnKeyDown : function(event)
7572 // this is a workaround for a password hang bug on chrome/ webkit.
7574 var isSelectAll = false;
7576 if(this.inputEl().dom.selectionEnd > 0){
7577 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7579 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7580 event.preventDefault();
7585 if(isSelectAll){ // backspace and delete key
7587 event.preventDefault();
7588 // this is very hacky as keydown always get's upper case.
7590 var cc = String.fromCharCode(event.getCharCode());
7591 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
7595 adjustWidth : function(tag, w){
7596 tag = tag.toLowerCase();
7597 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7598 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7602 if(tag == 'textarea'){
7605 }else if(Roo.isOpera){
7609 if(tag == 'textarea'){
7628 * @class Roo.bootstrap.TextArea
7629 * @extends Roo.bootstrap.Input
7630 * Bootstrap TextArea class
7631 * @cfg {Number} cols Specifies the visible width of a text area
7632 * @cfg {Number} rows Specifies the visible number of lines in a text area
7633 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7634 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7635 * @cfg {string} html text
7638 * Create a new TextArea
7639 * @param {Object} config The config object
7642 Roo.bootstrap.TextArea = function(config){
7643 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7647 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
7657 getAutoCreate : function(){
7659 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7670 value : this.value || '',
7671 html: this.html || '',
7672 cls : 'form-control',
7673 placeholder : this.placeholder || ''
7677 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7678 input.maxLength = this.maxLength;
7682 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7686 input.cols = this.cols;
7689 if (this.readOnly) {
7690 input.readonly = true;
7694 input.name = this.name;
7698 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7702 ['xs','sm','md','lg'].map(function(size){
7703 if (settings[size]) {
7704 cfg.cls += ' col-' + size + '-' + settings[size];
7708 var inputblock = input;
7710 if (this.before || this.after) {
7713 cls : 'input-group',
7717 inputblock.cn.push({
7719 cls : 'input-group-addon',
7723 inputblock.cn.push(input);
7725 inputblock.cn.push({
7727 cls : 'input-group-addon',
7734 if (align ==='left' && this.fieldLabel.length) {
7735 Roo.log("left and has label");
7741 cls : 'control-label col-sm-' + this.labelWidth,
7742 html : this.fieldLabel
7746 cls : "col-sm-" + (12 - this.labelWidth),
7753 } else if ( this.fieldLabel.length) {
7759 //cls : 'input-group-addon',
7760 html : this.fieldLabel
7770 Roo.log(" no label && no align");
7780 if (this.disabled) {
7781 input.disabled=true;
7788 * return the real textarea element.
7790 inputEl: function ()
7792 return this.el.select('textarea.form-control',true).first();
7800 * trigger field - base class for combo..
7805 * @class Roo.bootstrap.TriggerField
7806 * @extends Roo.bootstrap.Input
7807 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7808 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7809 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7810 * for which you can provide a custom implementation. For example:
7812 var trigger = new Roo.bootstrap.TriggerField();
7813 trigger.onTriggerClick = myTriggerFn;
7814 trigger.applyTo('my-field');
7817 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7818 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7819 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
7820 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7822 * Create a new TriggerField.
7823 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7824 * to the base TextField)
7826 Roo.bootstrap.TriggerField = function(config){
7827 this.mimicing = false;
7828 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7831 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
7833 * @cfg {String} triggerClass A CSS class to apply to the trigger
7836 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7840 /** @cfg {Boolean} grow @hide */
7841 /** @cfg {Number} growMin @hide */
7842 /** @cfg {Number} growMax @hide */
7848 autoSize: Roo.emptyFn,
7855 actionMode : 'wrap',
7859 getAutoCreate : function(){
7861 var align = this.labelAlign || this.parentLabelAlign();
7866 cls: 'form-group' //input-group
7873 type : this.inputType,
7874 cls : 'form-control',
7875 autocomplete: 'off',
7876 placeholder : this.placeholder || ''
7880 input.name = this.name;
7883 input.cls += ' input-' + this.size;
7886 if (this.disabled) {
7887 input.disabled=true;
7890 var inputblock = input;
7892 if (this.before || this.after) {
7895 cls : 'input-group',
7899 inputblock.cn.push({
7901 cls : 'input-group-addon',
7905 inputblock.cn.push(input);
7907 inputblock.cn.push({
7909 cls : 'input-group-addon',
7922 cls: 'form-hidden-field'
7930 Roo.log('multiple');
7938 cls: 'form-hidden-field'
7942 cls: 'select2-choices',
7946 cls: 'select2-search-field',
7959 cls: 'select2-container input-group',
7964 // cls: 'typeahead typeahead-long dropdown-menu',
7965 // style: 'display:none'
7970 if(!this.multiple && this.showToggleBtn){
7973 cls : 'input-group-addon btn dropdown-toggle',
7981 cls: 'combobox-clear',
7995 combobox.cls += ' select2-container-multi';
7998 if (align ==='left' && this.fieldLabel.length) {
8000 Roo.log("left and has label");
8006 cls : 'control-label col-sm-' + this.labelWidth,
8007 html : this.fieldLabel
8011 cls : "col-sm-" + (12 - this.labelWidth),
8018 } else if ( this.fieldLabel.length) {
8024 //cls : 'input-group-addon',
8025 html : this.fieldLabel
8035 Roo.log(" no label && no align");
8042 ['xs','sm','md','lg'].map(function(size){
8043 if (settings[size]) {
8044 cfg.cls += ' col-' + size + '-' + settings[size];
8055 onResize : function(w, h){
8056 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8057 // if(typeof w == 'number'){
8058 // var x = w - this.trigger.getWidth();
8059 // this.inputEl().setWidth(this.adjustWidth('input', x));
8060 // this.trigger.setStyle('left', x+'px');
8065 adjustSize : Roo.BoxComponent.prototype.adjustSize,
8068 getResizeEl : function(){
8069 return this.inputEl();
8073 getPositionEl : function(){
8074 return this.inputEl();
8078 alignErrorIcon : function(){
8079 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8083 initEvents : function(){
8087 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8088 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8089 if(!this.multiple && this.showToggleBtn){
8090 this.trigger = this.el.select('span.dropdown-toggle',true).first();
8091 if(this.hideTrigger){
8092 this.trigger.setDisplayed(false);
8094 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8098 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8101 //this.trigger.addClassOnOver('x-form-trigger-over');
8102 //this.trigger.addClassOnClick('x-form-trigger-click');
8105 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8109 createList : function()
8111 this.list = Roo.get(document.body).createChild({
8113 cls: 'typeahead typeahead-long dropdown-menu',
8114 style: 'display:none'
8117 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8122 initTrigger : function(){
8127 onDestroy : function(){
8129 this.trigger.removeAllListeners();
8130 // this.trigger.remove();
8133 // this.wrap.remove();
8135 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8139 onFocus : function(){
8140 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8143 this.wrap.addClass('x-trigger-wrap-focus');
8144 this.mimicing = true;
8145 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8146 if(this.monitorTab){
8147 this.el.on("keydown", this.checkTab, this);
8154 checkTab : function(e){
8155 if(e.getKey() == e.TAB){
8161 onBlur : function(){
8166 mimicBlur : function(e, t){
8168 if(!this.wrap.contains(t) && this.validateBlur()){
8175 triggerBlur : function(){
8176 this.mimicing = false;
8177 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8178 if(this.monitorTab){
8179 this.el.un("keydown", this.checkTab, this);
8181 //this.wrap.removeClass('x-trigger-wrap-focus');
8182 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8186 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8187 validateBlur : function(e, t){
8192 onDisable : function(){
8193 this.inputEl().dom.disabled = true;
8194 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8196 // this.wrap.addClass('x-item-disabled');
8201 onEnable : function(){
8202 this.inputEl().dom.disabled = false;
8203 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8205 // this.el.removeClass('x-item-disabled');
8210 onShow : function(){
8211 var ae = this.getActionEl();
8214 ae.dom.style.display = '';
8215 ae.dom.style.visibility = 'visible';
8221 onHide : function(){
8222 var ae = this.getActionEl();
8223 ae.dom.style.display = 'none';
8227 * The function that should handle the trigger's click event. This method does nothing by default until overridden
8228 * by an implementing function.
8230 * @param {EventObject} e
8232 onTriggerClick : Roo.emptyFn
8236 * Ext JS Library 1.1.1
8237 * Copyright(c) 2006-2007, Ext JS, LLC.
8239 * Originally Released Under LGPL - original licence link has changed is not relivant.
8242 * <script type="text/javascript">
8247 * @class Roo.data.SortTypes
8249 * Defines the default sorting (casting?) comparison functions used when sorting data.
8251 Roo.data.SortTypes = {
8253 * Default sort that does nothing
8254 * @param {Mixed} s The value being converted
8255 * @return {Mixed} The comparison value
8262 * The regular expression used to strip tags
8266 stripTagsRE : /<\/?[^>]+>/gi,
8269 * Strips all HTML tags to sort on text only
8270 * @param {Mixed} s The value being converted
8271 * @return {String} The comparison value
8273 asText : function(s){
8274 return String(s).replace(this.stripTagsRE, "");
8278 * Strips all HTML tags to sort on text only - Case insensitive
8279 * @param {Mixed} s The value being converted
8280 * @return {String} The comparison value
8282 asUCText : function(s){
8283 return String(s).toUpperCase().replace(this.stripTagsRE, "");
8287 * Case insensitive string
8288 * @param {Mixed} s The value being converted
8289 * @return {String} The comparison value
8291 asUCString : function(s) {
8292 return String(s).toUpperCase();
8297 * @param {Mixed} s The value being converted
8298 * @return {Number} The comparison value
8300 asDate : function(s) {
8304 if(s instanceof Date){
8307 return Date.parse(String(s));
8312 * @param {Mixed} s The value being converted
8313 * @return {Float} The comparison value
8315 asFloat : function(s) {
8316 var val = parseFloat(String(s).replace(/,/g, ""));
8317 if(isNaN(val)) val = 0;
8323 * @param {Mixed} s The value being converted
8324 * @return {Number} The comparison value
8326 asInt : function(s) {
8327 var val = parseInt(String(s).replace(/,/g, ""));
8328 if(isNaN(val)) val = 0;
8333 * Ext JS Library 1.1.1
8334 * Copyright(c) 2006-2007, Ext JS, LLC.
8336 * Originally Released Under LGPL - original licence link has changed is not relivant.
8339 * <script type="text/javascript">
8343 * @class Roo.data.Record
8344 * Instances of this class encapsulate both record <em>definition</em> information, and record
8345 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8346 * to access Records cached in an {@link Roo.data.Store} object.<br>
8348 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8349 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8352 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8354 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8355 * {@link #create}. The parameters are the same.
8356 * @param {Array} data An associative Array of data values keyed by the field name.
8357 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8358 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8359 * not specified an integer id is generated.
8361 Roo.data.Record = function(data, id){
8362 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8367 * Generate a constructor for a specific record layout.
8368 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8369 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8370 * Each field definition object may contain the following properties: <ul>
8371 * <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,
8372 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8373 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8374 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8375 * is being used, then this is a string containing the javascript expression to reference the data relative to
8376 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8377 * to the data item relative to the record element. If the mapping expression is the same as the field name,
8378 * this may be omitted.</p></li>
8379 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8380 * <ul><li>auto (Default, implies no conversion)</li>
8385 * <li>date</li></ul></p></li>
8386 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8387 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8388 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8389 * by the Reader into an object that will be stored in the Record. It is passed the
8390 * following parameters:<ul>
8391 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8393 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8395 * <br>usage:<br><pre><code>
8396 var TopicRecord = Roo.data.Record.create(
8397 {name: 'title', mapping: 'topic_title'},
8398 {name: 'author', mapping: 'username'},
8399 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8400 {name: 'lastPost', mapping: 'post_time', type: 'date'},
8401 {name: 'lastPoster', mapping: 'user2'},
8402 {name: 'excerpt', mapping: 'post_text'}
8405 var myNewRecord = new TopicRecord({
8406 title: 'Do my job please',
8409 lastPost: new Date(),
8410 lastPoster: 'Animal',
8411 excerpt: 'No way dude!'
8413 myStore.add(myNewRecord);
8418 Roo.data.Record.create = function(o){
8420 f.superclass.constructor.apply(this, arguments);
8422 Roo.extend(f, Roo.data.Record);
8423 var p = f.prototype;
8424 p.fields = new Roo.util.MixedCollection(false, function(field){
8427 for(var i = 0, len = o.length; i < len; i++){
8428 p.fields.add(new Roo.data.Field(o[i]));
8430 f.getField = function(name){
8431 return p.fields.get(name);
8436 Roo.data.Record.AUTO_ID = 1000;
8437 Roo.data.Record.EDIT = 'edit';
8438 Roo.data.Record.REJECT = 'reject';
8439 Roo.data.Record.COMMIT = 'commit';
8441 Roo.data.Record.prototype = {
8443 * Readonly flag - true if this record has been modified.
8452 join : function(store){
8457 * Set the named field to the specified value.
8458 * @param {String} name The name of the field to set.
8459 * @param {Object} value The value to set the field to.
8461 set : function(name, value){
8462 if(this.data[name] == value){
8469 if(typeof this.modified[name] == 'undefined'){
8470 this.modified[name] = this.data[name];
8472 this.data[name] = value;
8473 if(!this.editing && this.store){
8474 this.store.afterEdit(this);
8479 * Get the value of the named field.
8480 * @param {String} name The name of the field to get the value of.
8481 * @return {Object} The value of the field.
8483 get : function(name){
8484 return this.data[name];
8488 beginEdit : function(){
8489 this.editing = true;
8494 cancelEdit : function(){
8495 this.editing = false;
8496 delete this.modified;
8500 endEdit : function(){
8501 this.editing = false;
8502 if(this.dirty && this.store){
8503 this.store.afterEdit(this);
8508 * Usually called by the {@link Roo.data.Store} which owns the Record.
8509 * Rejects all changes made to the Record since either creation, or the last commit operation.
8510 * Modified fields are reverted to their original values.
8512 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8513 * of reject operations.
8515 reject : function(){
8516 var m = this.modified;
8518 if(typeof m[n] != "function"){
8519 this.data[n] = m[n];
8523 delete this.modified;
8524 this.editing = false;
8526 this.store.afterReject(this);
8531 * Usually called by the {@link Roo.data.Store} which owns the Record.
8532 * Commits all changes made to the Record since either creation, or the last commit operation.
8534 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8535 * of commit operations.
8537 commit : function(){
8539 delete this.modified;
8540 this.editing = false;
8542 this.store.afterCommit(this);
8547 hasError : function(){
8548 return this.error != null;
8552 clearError : function(){
8557 * Creates a copy of this record.
8558 * @param {String} id (optional) A new record id if you don't want to use this record's id
8561 copy : function(newId) {
8562 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8566 * Ext JS Library 1.1.1
8567 * Copyright(c) 2006-2007, Ext JS, LLC.
8569 * Originally Released Under LGPL - original licence link has changed is not relivant.
8572 * <script type="text/javascript">
8578 * @class Roo.data.Store
8579 * @extends Roo.util.Observable
8580 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8581 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8583 * 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
8584 * has no knowledge of the format of the data returned by the Proxy.<br>
8586 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8587 * instances from the data object. These records are cached and made available through accessor functions.
8589 * Creates a new Store.
8590 * @param {Object} config A config object containing the objects needed for the Store to access data,
8591 * and read the data into Records.
8593 Roo.data.Store = function(config){
8594 this.data = new Roo.util.MixedCollection(false);
8595 this.data.getKey = function(o){
8598 this.baseParams = {};
8605 "multisort" : "_multisort"
8608 if(config && config.data){
8609 this.inlineData = config.data;
8613 Roo.apply(this, config);
8615 if(this.reader){ // reader passed
8616 this.reader = Roo.factory(this.reader, Roo.data);
8617 this.reader.xmodule = this.xmodule || false;
8618 if(!this.recordType){
8619 this.recordType = this.reader.recordType;
8621 if(this.reader.onMetaChange){
8622 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8626 if(this.recordType){
8627 this.fields = this.recordType.prototype.fields;
8633 * @event datachanged
8634 * Fires when the data cache has changed, and a widget which is using this Store
8635 * as a Record cache should refresh its view.
8636 * @param {Store} this
8641 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8642 * @param {Store} this
8643 * @param {Object} meta The JSON metadata
8648 * Fires when Records have been added to the Store
8649 * @param {Store} this
8650 * @param {Roo.data.Record[]} records The array of Records added
8651 * @param {Number} index The index at which the record(s) were added
8656 * Fires when a Record has been removed from the Store
8657 * @param {Store} this
8658 * @param {Roo.data.Record} record The Record that was removed
8659 * @param {Number} index The index at which the record was removed
8664 * Fires when a Record has been updated
8665 * @param {Store} this
8666 * @param {Roo.data.Record} record The Record that was updated
8667 * @param {String} operation The update operation being performed. Value may be one of:
8669 Roo.data.Record.EDIT
8670 Roo.data.Record.REJECT
8671 Roo.data.Record.COMMIT
8677 * Fires when the data cache has been cleared.
8678 * @param {Store} this
8683 * Fires before a request is made for a new data object. If the beforeload handler returns false
8684 * the load action will be canceled.
8685 * @param {Store} this
8686 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8690 * @event beforeloadadd
8691 * Fires after a new set of Records has been loaded.
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)
8696 beforeloadadd : true,
8699 * Fires after a new set of Records has been loaded, before they are added to the store.
8700 * @param {Store} this
8701 * @param {Roo.data.Record[]} records The Records that were loaded
8702 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8703 * @params {Object} return from reader
8707 * @event loadexception
8708 * Fires if an exception occurs in the Proxy during loading.
8709 * Called with the signature of the Proxy's "loadexception" event.
8710 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8713 * @param {Object} return from JsonData.reader() - success, totalRecords, records
8714 * @param {Object} load options
8715 * @param {Object} jsonData from your request (normally this contains the Exception)
8717 loadexception : true
8721 this.proxy = Roo.factory(this.proxy, Roo.data);
8722 this.proxy.xmodule = this.xmodule || false;
8723 this.relayEvents(this.proxy, ["loadexception"]);
8725 this.sortToggle = {};
8726 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8728 Roo.data.Store.superclass.constructor.call(this);
8730 if(this.inlineData){
8731 this.loadData(this.inlineData);
8732 delete this.inlineData;
8736 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8738 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
8739 * without a remote query - used by combo/forms at present.
8743 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8746 * @cfg {Array} data Inline data to be loaded when the store is initialized.
8749 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8750 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8753 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8754 * on any HTTP request
8757 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8760 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8764 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8765 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8770 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8771 * loaded or when a record is removed. (defaults to false).
8773 pruneModifiedRecords : false,
8779 * Add Records to the Store and fires the add event.
8780 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8782 add : function(records){
8783 records = [].concat(records);
8784 for(var i = 0, len = records.length; i < len; i++){
8785 records[i].join(this);
8787 var index = this.data.length;
8788 this.data.addAll(records);
8789 this.fireEvent("add", this, records, index);
8793 * Remove a Record from the Store and fires the remove event.
8794 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8796 remove : function(record){
8797 var index = this.data.indexOf(record);
8798 this.data.removeAt(index);
8799 if(this.pruneModifiedRecords){
8800 this.modified.remove(record);
8802 this.fireEvent("remove", this, record, index);
8806 * Remove all Records from the Store and fires the clear event.
8808 removeAll : function(){
8810 if(this.pruneModifiedRecords){
8813 this.fireEvent("clear", this);
8817 * Inserts Records to the Store at the given index and fires the add event.
8818 * @param {Number} index The start index at which to insert the passed Records.
8819 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8821 insert : function(index, records){
8822 records = [].concat(records);
8823 for(var i = 0, len = records.length; i < len; i++){
8824 this.data.insert(index, records[i]);
8825 records[i].join(this);
8827 this.fireEvent("add", this, records, index);
8831 * Get the index within the cache of the passed Record.
8832 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8833 * @return {Number} The index of the passed Record. Returns -1 if not found.
8835 indexOf : function(record){
8836 return this.data.indexOf(record);
8840 * Get the index within the cache of the Record with the passed id.
8841 * @param {String} id The id of the Record to find.
8842 * @return {Number} The index of the Record. Returns -1 if not found.
8844 indexOfId : function(id){
8845 return this.data.indexOfKey(id);
8849 * Get the Record with the specified id.
8850 * @param {String} id The id of the Record to find.
8851 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8853 getById : function(id){
8854 return this.data.key(id);
8858 * Get the Record at the specified index.
8859 * @param {Number} index The index of the Record to find.
8860 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8862 getAt : function(index){
8863 return this.data.itemAt(index);
8867 * Returns a range of Records between specified indices.
8868 * @param {Number} startIndex (optional) The starting index (defaults to 0)
8869 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8870 * @return {Roo.data.Record[]} An array of Records
8872 getRange : function(start, end){
8873 return this.data.getRange(start, end);
8877 storeOptions : function(o){
8878 o = Roo.apply({}, o);
8881 this.lastOptions = o;
8885 * Loads the Record cache from the configured Proxy using the configured Reader.
8887 * If using remote paging, then the first load call must specify the <em>start</em>
8888 * and <em>limit</em> properties in the options.params property to establish the initial
8889 * position within the dataset, and the number of Records to cache on each read from the Proxy.
8891 * <strong>It is important to note that for remote data sources, loading is asynchronous,
8892 * and this call will return before the new data has been loaded. Perform any post-processing
8893 * in a callback function, or in a "load" event handler.</strong>
8895 * @param {Object} options An object containing properties which control loading options:<ul>
8896 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8897 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8898 * passed the following arguments:<ul>
8899 * <li>r : Roo.data.Record[]</li>
8900 * <li>options: Options object from the load call</li>
8901 * <li>success: Boolean success indicator</li></ul></li>
8902 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8903 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8906 load : function(options){
8907 options = options || {};
8908 if(this.fireEvent("beforeload", this, options) !== false){
8909 this.storeOptions(options);
8910 var p = Roo.apply(options.params || {}, this.baseParams);
8911 // if meta was not loaded from remote source.. try requesting it.
8912 if (!this.reader.metaFromRemote) {
8915 if(this.sortInfo && this.remoteSort){
8916 var pn = this.paramNames;
8917 p[pn["sort"]] = this.sortInfo.field;
8918 p[pn["dir"]] = this.sortInfo.direction;
8920 if (this.multiSort) {
8921 var pn = this.paramNames;
8922 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8925 this.proxy.load(p, this.reader, this.loadRecords, this, options);
8930 * Reloads the Record cache from the configured Proxy using the configured Reader and
8931 * the options from the last load operation performed.
8932 * @param {Object} options (optional) An object containing properties which may override the options
8933 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8934 * the most recently used options are reused).
8936 reload : function(options){
8937 this.load(Roo.applyIf(options||{}, this.lastOptions));
8941 // Called as a callback by the Reader during a load operation.
8942 loadRecords : function(o, options, success){
8943 if(!o || success === false){
8944 if(success !== false){
8945 this.fireEvent("load", this, [], options, o);
8947 if(options.callback){
8948 options.callback.call(options.scope || this, [], options, false);
8952 // if data returned failure - throw an exception.
8953 if (o.success === false) {
8954 // show a message if no listener is registered.
8955 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8956 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8958 // loadmask wil be hooked into this..
8959 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8962 var r = o.records, t = o.totalRecords || r.length;
8964 this.fireEvent("beforeloadadd", this, r, options, o);
8966 if(!options || options.add !== true){
8967 if(this.pruneModifiedRecords){
8970 for(var i = 0, len = r.length; i < len; i++){
8974 this.data = this.snapshot;
8975 delete this.snapshot;
8978 this.data.addAll(r);
8979 this.totalLength = t;
8981 this.fireEvent("datachanged", this);
8983 this.totalLength = Math.max(t, this.data.length+r.length);
8986 this.fireEvent("load", this, r, options, o);
8987 if(options.callback){
8988 options.callback.call(options.scope || this, r, options, true);
8994 * Loads data from a passed data block. A Reader which understands the format of the data
8995 * must have been configured in the constructor.
8996 * @param {Object} data The data block from which to read the Records. The format of the data expected
8997 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8998 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9000 loadData : function(o, append){
9001 var r = this.reader.readRecords(o);
9002 this.loadRecords(r, {add: append}, true);
9006 * Gets the number of cached records.
9008 * <em>If using paging, this may not be the total size of the dataset. If the data object
9009 * used by the Reader contains the dataset size, then the getTotalCount() function returns
9010 * the data set size</em>
9012 getCount : function(){
9013 return this.data.length || 0;
9017 * Gets the total number of records in the dataset as returned by the server.
9019 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9020 * the dataset size</em>
9022 getTotalCount : function(){
9023 return this.totalLength || 0;
9027 * Returns the sort state of the Store as an object with two properties:
9029 field {String} The name of the field by which the Records are sorted
9030 direction {String} The sort order, "ASC" or "DESC"
9033 getSortState : function(){
9034 return this.sortInfo;
9038 applySort : function(){
9039 if(this.sortInfo && !this.remoteSort){
9040 var s = this.sortInfo, f = s.field;
9041 var st = this.fields.get(f).sortType;
9042 var fn = function(r1, r2){
9043 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9044 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9046 this.data.sort(s.direction, fn);
9047 if(this.snapshot && this.snapshot != this.data){
9048 this.snapshot.sort(s.direction, fn);
9054 * Sets the default sort column and order to be used by the next load operation.
9055 * @param {String} fieldName The name of the field to sort by.
9056 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9058 setDefaultSort : function(field, dir){
9059 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9064 * If remote sorting is used, the sort is performed on the server, and the cache is
9065 * reloaded. If local sorting is used, the cache is sorted internally.
9066 * @param {String} fieldName The name of the field to sort by.
9067 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9069 sort : function(fieldName, dir){
9070 var f = this.fields.get(fieldName);
9072 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9074 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9075 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9080 this.sortToggle[f.name] = dir;
9081 this.sortInfo = {field: f.name, direction: dir};
9082 if(!this.remoteSort){
9084 this.fireEvent("datachanged", this);
9086 this.load(this.lastOptions);
9091 * Calls the specified function for each of the Records in the cache.
9092 * @param {Function} fn The function to call. The Record is passed as the first parameter.
9093 * Returning <em>false</em> aborts and exits the iteration.
9094 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9096 each : function(fn, scope){
9097 this.data.each(fn, scope);
9101 * Gets all records modified since the last commit. Modified records are persisted across load operations
9102 * (e.g., during paging).
9103 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9105 getModifiedRecords : function(){
9106 return this.modified;
9110 createFilterFn : function(property, value, anyMatch){
9111 if(!value.exec){ // not a regex
9112 value = String(value);
9113 if(value.length == 0){
9116 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9119 return value.test(r.data[property]);
9124 * Sums the value of <i>property</i> for each record between start and end and returns the result.
9125 * @param {String} property A field on your records
9126 * @param {Number} start The record index to start at (defaults to 0)
9127 * @param {Number} end The last record index to include (defaults to length - 1)
9128 * @return {Number} The sum
9130 sum : function(property, start, end){
9131 var rs = this.data.items, v = 0;
9133 end = (end || end === 0) ? end : rs.length-1;
9135 for(var i = start; i <= end; i++){
9136 v += (rs[i].data[property] || 0);
9142 * Filter the records by a specified property.
9143 * @param {String} field A field on your records
9144 * @param {String/RegExp} value Either a string that the field
9145 * should start with or a RegExp to test against the field
9146 * @param {Boolean} anyMatch True to match any part not just the beginning
9148 filter : function(property, value, anyMatch){
9149 var fn = this.createFilterFn(property, value, anyMatch);
9150 return fn ? this.filterBy(fn) : this.clearFilter();
9154 * Filter by a function. The specified function will be called with each
9155 * record in this data source. If the function returns true the record is included,
9156 * otherwise it is filtered.
9157 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9158 * @param {Object} scope (optional) The scope of the function (defaults to this)
9160 filterBy : function(fn, scope){
9161 this.snapshot = this.snapshot || this.data;
9162 this.data = this.queryBy(fn, scope||this);
9163 this.fireEvent("datachanged", this);
9167 * Query the records by a specified property.
9168 * @param {String} field A field on your records
9169 * @param {String/RegExp} value Either a string that the field
9170 * should start with or a RegExp to test against the field
9171 * @param {Boolean} anyMatch True to match any part not just the beginning
9172 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9174 query : function(property, value, anyMatch){
9175 var fn = this.createFilterFn(property, value, anyMatch);
9176 return fn ? this.queryBy(fn) : this.data.clone();
9180 * Query by a function. The specified function will be called with each
9181 * record in this data source. If the function returns true the record is included
9183 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9184 * @param {Object} scope (optional) The scope of the function (defaults to this)
9185 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9187 queryBy : function(fn, scope){
9188 var data = this.snapshot || this.data;
9189 return data.filterBy(fn, scope||this);
9193 * Collects unique values for a particular dataIndex from this store.
9194 * @param {String} dataIndex The property to collect
9195 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9196 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9197 * @return {Array} An array of the unique values
9199 collect : function(dataIndex, allowNull, bypassFilter){
9200 var d = (bypassFilter === true && this.snapshot) ?
9201 this.snapshot.items : this.data.items;
9202 var v, sv, r = [], l = {};
9203 for(var i = 0, len = d.length; i < len; i++){
9204 v = d[i].data[dataIndex];
9206 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9215 * Revert to a view of the Record cache with no filtering applied.
9216 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9218 clearFilter : function(suppressEvent){
9219 if(this.snapshot && this.snapshot != this.data){
9220 this.data = this.snapshot;
9221 delete this.snapshot;
9222 if(suppressEvent !== true){
9223 this.fireEvent("datachanged", this);
9229 afterEdit : function(record){
9230 if(this.modified.indexOf(record) == -1){
9231 this.modified.push(record);
9233 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9237 afterReject : function(record){
9238 this.modified.remove(record);
9239 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9243 afterCommit : function(record){
9244 this.modified.remove(record);
9245 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9249 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9250 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9252 commitChanges : function(){
9253 var m = this.modified.slice(0);
9255 for(var i = 0, len = m.length; i < len; i++){
9261 * Cancel outstanding changes on all changed records.
9263 rejectChanges : function(){
9264 var m = this.modified.slice(0);
9266 for(var i = 0, len = m.length; i < len; i++){
9271 onMetaChange : function(meta, rtype, o){
9272 this.recordType = rtype;
9273 this.fields = rtype.prototype.fields;
9274 delete this.snapshot;
9275 this.sortInfo = meta.sortInfo || this.sortInfo;
9277 this.fireEvent('metachange', this, this.reader.meta);
9280 moveIndex : function(data, type)
9282 var index = this.indexOf(data);
9284 var newIndex = index + type;
9288 this.insert(newIndex, data);
9293 * Ext JS Library 1.1.1
9294 * Copyright(c) 2006-2007, Ext JS, LLC.
9296 * Originally Released Under LGPL - original licence link has changed is not relivant.
9299 * <script type="text/javascript">
9303 * @class Roo.data.SimpleStore
9304 * @extends Roo.data.Store
9305 * Small helper class to make creating Stores from Array data easier.
9306 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9307 * @cfg {Array} fields An array of field definition objects, or field name strings.
9308 * @cfg {Array} data The multi-dimensional array of data
9310 * @param {Object} config
9312 Roo.data.SimpleStore = function(config){
9313 Roo.data.SimpleStore.superclass.constructor.call(this, {
9315 reader: new Roo.data.ArrayReader({
9318 Roo.data.Record.create(config.fields)
9320 proxy : new Roo.data.MemoryProxy(config.data)
9324 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9326 * Ext JS Library 1.1.1
9327 * Copyright(c) 2006-2007, Ext JS, LLC.
9329 * Originally Released Under LGPL - original licence link has changed is not relivant.
9332 * <script type="text/javascript">
9337 * @extends Roo.data.Store
9338 * @class Roo.data.JsonStore
9339 * Small helper class to make creating Stores for JSON data easier. <br/>
9341 var store = new Roo.data.JsonStore({
9342 url: 'get-images.php',
9344 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9347 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9348 * JsonReader and HttpProxy (unless inline data is provided).</b>
9349 * @cfg {Array} fields An array of field definition objects, or field name strings.
9351 * @param {Object} config
9353 Roo.data.JsonStore = function(c){
9354 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9355 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9356 reader: new Roo.data.JsonReader(c, c.fields)
9359 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9361 * Ext JS Library 1.1.1
9362 * Copyright(c) 2006-2007, Ext JS, LLC.
9364 * Originally Released Under LGPL - original licence link has changed is not relivant.
9367 * <script type="text/javascript">
9371 Roo.data.Field = function(config){
9372 if(typeof config == "string"){
9373 config = {name: config};
9375 Roo.apply(this, config);
9381 var st = Roo.data.SortTypes;
9382 // named sortTypes are supported, here we look them up
9383 if(typeof this.sortType == "string"){
9384 this.sortType = st[this.sortType];
9387 // set default sortType for strings and dates
9391 this.sortType = st.asUCString;
9394 this.sortType = st.asDate;
9397 this.sortType = st.none;
9402 var stripRe = /[\$,%]/g;
9404 // prebuilt conversion function for this field, instead of
9405 // switching every time we're reading a value
9407 var cv, dateFormat = this.dateFormat;
9412 cv = function(v){ return v; };
9415 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9419 return v !== undefined && v !== null && v !== '' ?
9420 parseInt(String(v).replace(stripRe, ""), 10) : '';
9425 return v !== undefined && v !== null && v !== '' ?
9426 parseFloat(String(v).replace(stripRe, ""), 10) : '';
9431 cv = function(v){ return v === true || v === "true" || v == 1; };
9438 if(v instanceof Date){
9442 if(dateFormat == "timestamp"){
9443 return new Date(v*1000);
9445 return Date.parseDate(v, dateFormat);
9447 var parsed = Date.parse(v);
9448 return parsed ? new Date(parsed) : null;
9457 Roo.data.Field.prototype = {
9465 * Ext JS Library 1.1.1
9466 * Copyright(c) 2006-2007, Ext JS, LLC.
9468 * Originally Released Under LGPL - original licence link has changed is not relivant.
9471 * <script type="text/javascript">
9474 // Base class for reading structured data from a data source. This class is intended to be
9475 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9478 * @class Roo.data.DataReader
9479 * Base class for reading structured data from a data source. This class is intended to be
9480 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9483 Roo.data.DataReader = function(meta, recordType){
9487 this.recordType = recordType instanceof Array ?
9488 Roo.data.Record.create(recordType) : recordType;
9491 Roo.data.DataReader.prototype = {
9493 * Create an empty record
9494 * @param {Object} data (optional) - overlay some values
9495 * @return {Roo.data.Record} record created.
9497 newRow : function(d) {
9499 this.recordType.prototype.fields.each(function(c) {
9501 case 'int' : da[c.name] = 0; break;
9502 case 'date' : da[c.name] = new Date(); break;
9503 case 'float' : da[c.name] = 0.0; break;
9504 case 'boolean' : da[c.name] = false; break;
9505 default : da[c.name] = ""; break;
9509 return new this.recordType(Roo.apply(da, d));
9514 * Ext JS Library 1.1.1
9515 * Copyright(c) 2006-2007, Ext JS, LLC.
9517 * Originally Released Under LGPL - original licence link has changed is not relivant.
9520 * <script type="text/javascript">
9524 * @class Roo.data.DataProxy
9525 * @extends Roo.data.Observable
9526 * This class is an abstract base class for implementations which provide retrieval of
9527 * unformatted data objects.<br>
9529 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9530 * (of the appropriate type which knows how to parse the data object) to provide a block of
9531 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9533 * Custom implementations must implement the load method as described in
9534 * {@link Roo.data.HttpProxy#load}.
9536 Roo.data.DataProxy = function(){
9540 * Fires before a network request is made to retrieve a data object.
9541 * @param {Object} This DataProxy object.
9542 * @param {Object} params The params parameter to the load function.
9547 * Fires before the load method's callback is called.
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.
9554 * @event loadexception
9555 * Fires if an Exception occurs during data retrieval.
9556 * @param {Object} This DataProxy object.
9557 * @param {Object} o The data object.
9558 * @param {Object} arg The callback argument object passed to the load function.
9559 * @param {Object} e The Exception.
9561 loadexception : true
9563 Roo.data.DataProxy.superclass.constructor.call(this);
9566 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9569 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9573 * Ext JS Library 1.1.1
9574 * Copyright(c) 2006-2007, Ext JS, LLC.
9576 * Originally Released Under LGPL - original licence link has changed is not relivant.
9579 * <script type="text/javascript">
9582 * @class Roo.data.MemoryProxy
9583 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9584 * to the Reader when its load method is called.
9586 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9588 Roo.data.MemoryProxy = function(data){
9592 Roo.data.MemoryProxy.superclass.constructor.call(this);
9596 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9598 * Load data from the requested source (in this case an in-memory
9599 * data object passed to the constructor), read the data object into
9600 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9601 * process that block using the passed callback.
9602 * @param {Object} params This parameter is not used by the MemoryProxy class.
9603 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9604 * object into a block of Roo.data.Records.
9605 * @param {Function} callback The function into which to pass the block of Roo.data.records.
9606 * The function must be passed <ul>
9607 * <li>The Record block object</li>
9608 * <li>The "arg" argument from the load function</li>
9609 * <li>A boolean success indicator</li>
9611 * @param {Object} scope The scope in which to call the callback
9612 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9614 load : function(params, reader, callback, scope, arg){
9615 params = params || {};
9618 result = reader.readRecords(this.data);
9620 this.fireEvent("loadexception", this, arg, null, e);
9621 callback.call(scope, null, arg, false);
9624 callback.call(scope, result, arg, true);
9628 update : function(params, records){
9633 * Ext JS Library 1.1.1
9634 * Copyright(c) 2006-2007, Ext JS, LLC.
9636 * Originally Released Under LGPL - original licence link has changed is not relivant.
9639 * <script type="text/javascript">
9642 * @class Roo.data.HttpProxy
9643 * @extends Roo.data.DataProxy
9644 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9645 * configured to reference a certain URL.<br><br>
9647 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9648 * from which the running page was served.<br><br>
9650 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9652 * Be aware that to enable the browser to parse an XML document, the server must set
9653 * the Content-Type header in the HTTP response to "text/xml".
9655 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9656 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
9657 * will be used to make the request.
9659 Roo.data.HttpProxy = function(conn){
9660 Roo.data.HttpProxy.superclass.constructor.call(this);
9661 // is conn a conn config or a real conn?
9663 this.useAjax = !conn || !conn.events;
9667 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9668 // thse are take from connection...
9671 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9674 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9675 * extra parameters to each request made by this object. (defaults to undefined)
9678 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9679 * to each request made by this object. (defaults to undefined)
9682 * @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)
9685 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9688 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9694 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9698 * Return the {@link Roo.data.Connection} object being used by this Proxy.
9699 * @return {Connection} The Connection object. This object may be used to subscribe to events on
9700 * a finer-grained basis than the DataProxy events.
9702 getConnection : function(){
9703 return this.useAjax ? Roo.Ajax : this.conn;
9707 * Load data from the configured {@link Roo.data.Connection}, read the data object into
9708 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9709 * process that block using the passed callback.
9710 * @param {Object} params An object containing properties which are to be used as HTTP parameters
9711 * for the request to the remote server.
9712 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9713 * object into a block of Roo.data.Records.
9714 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9715 * The function must be passed <ul>
9716 * <li>The Record block object</li>
9717 * <li>The "arg" argument from the load function</li>
9718 * <li>A boolean success indicator</li>
9720 * @param {Object} scope The scope in which to call the callback
9721 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9723 load : function(params, reader, callback, scope, arg){
9724 if(this.fireEvent("beforeload", this, params) !== false){
9726 params : params || {},
9728 callback : callback,
9733 callback : this.loadResponse,
9737 Roo.applyIf(o, this.conn);
9738 if(this.activeRequest){
9739 Roo.Ajax.abort(this.activeRequest);
9741 this.activeRequest = Roo.Ajax.request(o);
9743 this.conn.request(o);
9746 callback.call(scope||this, null, arg, false);
9751 loadResponse : function(o, success, response){
9752 delete this.activeRequest;
9754 this.fireEvent("loadexception", this, o, response);
9755 o.request.callback.call(o.request.scope, null, o.request.arg, false);
9760 result = o.reader.read(response);
9762 this.fireEvent("loadexception", this, o, response, e);
9763 o.request.callback.call(o.request.scope, null, o.request.arg, false);
9767 this.fireEvent("load", this, o, o.request.arg);
9768 o.request.callback.call(o.request.scope, result, o.request.arg, true);
9772 update : function(dataSet){
9777 updateResponse : function(dataSet){
9782 * Ext JS Library 1.1.1
9783 * Copyright(c) 2006-2007, Ext JS, LLC.
9785 * Originally Released Under LGPL - original licence link has changed is not relivant.
9788 * <script type="text/javascript">
9792 * @class Roo.data.ScriptTagProxy
9793 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9794 * other than the originating domain of the running page.<br><br>
9796 * <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
9797 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9799 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9800 * source code that is used as the source inside a <script> tag.<br><br>
9802 * In order for the browser to process the returned data, the server must wrap the data object
9803 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9804 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9805 * depending on whether the callback name was passed:
9808 boolean scriptTag = false;
9809 String cb = request.getParameter("callback");
9812 response.setContentType("text/javascript");
9814 response.setContentType("application/x-json");
9816 Writer out = response.getWriter();
9818 out.write(cb + "(");
9820 out.print(dataBlock.toJsonString());
9827 * @param {Object} config A configuration object.
9829 Roo.data.ScriptTagProxy = function(config){
9830 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9831 Roo.apply(this, config);
9832 this.head = document.getElementsByTagName("head")[0];
9835 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9837 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9839 * @cfg {String} url The URL from which to request the data object.
9842 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9846 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9847 * the server the name of the callback function set up by the load call to process the returned data object.
9848 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9849 * javascript output which calls this named function passing the data object as its only parameter.
9851 callbackParam : "callback",
9853 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9854 * name to the request.
9859 * Load data from the configured URL, read the data object into
9860 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9861 * process that block using the passed callback.
9862 * @param {Object} params An object containing properties which are to be used as HTTP parameters
9863 * for the request to the remote server.
9864 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9865 * object into a block of Roo.data.Records.
9866 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9867 * The function must be passed <ul>
9868 * <li>The Record block object</li>
9869 * <li>The "arg" argument from the load function</li>
9870 * <li>A boolean success indicator</li>
9872 * @param {Object} scope The scope in which to call the callback
9873 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9875 load : function(params, reader, callback, scope, arg){
9876 if(this.fireEvent("beforeload", this, params) !== false){
9878 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9881 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9883 url += "&_dc=" + (new Date().getTime());
9885 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9888 cb : "stcCallback"+transId,
9889 scriptId : "stcScript"+transId,
9893 callback : callback,
9899 window[trans.cb] = function(o){
9900 conn.handleResponse(o, trans);
9903 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9905 if(this.autoAbort !== false){
9909 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9911 var script = document.createElement("script");
9912 script.setAttribute("src", url);
9913 script.setAttribute("type", "text/javascript");
9914 script.setAttribute("id", trans.scriptId);
9915 this.head.appendChild(script);
9919 callback.call(scope||this, null, arg, false);
9924 isLoading : function(){
9925 return this.trans ? true : false;
9929 * Abort the current server request.
9932 if(this.isLoading()){
9933 this.destroyTrans(this.trans);
9938 destroyTrans : function(trans, isLoaded){
9939 this.head.removeChild(document.getElementById(trans.scriptId));
9940 clearTimeout(trans.timeoutId);
9942 window[trans.cb] = undefined;
9944 delete window[trans.cb];
9947 // if hasn't been loaded, wait for load to remove it to prevent script error
9948 window[trans.cb] = function(){
9949 window[trans.cb] = undefined;
9951 delete window[trans.cb];
9958 handleResponse : function(o, trans){
9960 this.destroyTrans(trans, true);
9963 result = trans.reader.readRecords(o);
9965 this.fireEvent("loadexception", this, o, trans.arg, e);
9966 trans.callback.call(trans.scope||window, null, trans.arg, false);
9969 this.fireEvent("load", this, o, trans.arg);
9970 trans.callback.call(trans.scope||window, result, trans.arg, true);
9974 handleFailure : function(trans){
9976 this.destroyTrans(trans, false);
9977 this.fireEvent("loadexception", this, null, trans.arg);
9978 trans.callback.call(trans.scope||window, null, trans.arg, false);
9982 * Ext JS Library 1.1.1
9983 * Copyright(c) 2006-2007, Ext JS, LLC.
9985 * Originally Released Under LGPL - original licence link has changed is not relivant.
9988 * <script type="text/javascript">
9992 * @class Roo.data.JsonReader
9993 * @extends Roo.data.DataReader
9994 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9995 * based on mappings in a provided Roo.data.Record constructor.
9997 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9998 * in the reply previously.
10003 var RecordDef = Roo.data.Record.create([
10004 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
10005 {name: 'occupation'} // This field will use "occupation" as the mapping.
10007 var myReader = new Roo.data.JsonReader({
10008 totalProperty: "results", // The property which contains the total dataset size (optional)
10009 root: "rows", // The property which contains an Array of row objects
10010 id: "id" // The property within each row object that provides an ID for the record (optional)
10014 * This would consume a JSON file like this:
10016 { 'results': 2, 'rows': [
10017 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10018 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10021 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10022 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10023 * paged from the remote server.
10024 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10025 * @cfg {String} root name of the property which contains the Array of row objects.
10026 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10028 * Create a new JsonReader
10029 * @param {Object} meta Metadata configuration options
10030 * @param {Object} recordType Either an Array of field definition objects,
10031 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10033 Roo.data.JsonReader = function(meta, recordType){
10036 // set some defaults:
10037 Roo.applyIf(meta, {
10038 totalProperty: 'total',
10039 successProperty : 'success',
10044 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10046 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10049 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
10050 * Used by Store query builder to append _requestMeta to params.
10053 metaFromRemote : false,
10055 * This method is only used by a DataProxy which has retrieved data from a remote server.
10056 * @param {Object} response The XHR object which contains the JSON data in its responseText.
10057 * @return {Object} data A data block which is used by an Roo.data.Store object as
10058 * a cache of Roo.data.Records.
10060 read : function(response){
10061 var json = response.responseText;
10063 var o = /* eval:var:o */ eval("("+json+")");
10065 throw {message: "JsonReader.read: Json object not found"};
10071 this.metaFromRemote = true;
10072 this.meta = o.metaData;
10073 this.recordType = Roo.data.Record.create(o.metaData.fields);
10074 this.onMetaChange(this.meta, this.recordType, o);
10076 return this.readRecords(o);
10079 // private function a store will implement
10080 onMetaChange : function(meta, recordType, o){
10087 simpleAccess: function(obj, subsc) {
10094 getJsonAccessor: function(){
10096 return function(expr) {
10098 return(re.test(expr))
10099 ? new Function("obj", "return obj." + expr)
10104 return Roo.emptyFn;
10109 * Create a data block containing Roo.data.Records from an XML document.
10110 * @param {Object} o An object which contains an Array of row objects in the property specified
10111 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10112 * which contains the total size of the dataset.
10113 * @return {Object} data A data block which is used by an Roo.data.Store object as
10114 * a cache of Roo.data.Records.
10116 readRecords : function(o){
10118 * After any data loads, the raw JSON data is available for further custom processing.
10122 var s = this.meta, Record = this.recordType,
10123 f = Record.prototype.fields, fi = f.items, fl = f.length;
10125 // Generate extraction functions for the totalProperty, the root, the id, and for each field
10127 if(s.totalProperty) {
10128 this.getTotal = this.getJsonAccessor(s.totalProperty);
10130 if(s.successProperty) {
10131 this.getSuccess = this.getJsonAccessor(s.successProperty);
10133 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10135 var g = this.getJsonAccessor(s.id);
10136 this.getId = function(rec) {
10138 return (r === undefined || r === "") ? null : r;
10141 this.getId = function(){return null;};
10144 for(var jj = 0; jj < fl; jj++){
10146 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10147 this.ef[jj] = this.getJsonAccessor(map);
10151 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10152 if(s.totalProperty){
10153 var vt = parseInt(this.getTotal(o), 10);
10158 if(s.successProperty){
10159 var vs = this.getSuccess(o);
10160 if(vs === false || vs === 'false'){
10165 for(var i = 0; i < c; i++){
10168 var id = this.getId(n);
10169 for(var j = 0; j < fl; j++){
10171 var v = this.ef[j](n);
10173 Roo.log('missing convert for ' + f.name);
10177 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10179 var record = new Record(values, id);
10181 records[i] = record;
10187 totalRecords : totalRecords
10192 * Ext JS Library 1.1.1
10193 * Copyright(c) 2006-2007, Ext JS, LLC.
10195 * Originally Released Under LGPL - original licence link has changed is not relivant.
10198 * <script type="text/javascript">
10202 * @class Roo.data.ArrayReader
10203 * @extends Roo.data.DataReader
10204 * Data reader class to create an Array of Roo.data.Record objects from an Array.
10205 * Each element of that Array represents a row of data fields. The
10206 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10207 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10211 var RecordDef = Roo.data.Record.create([
10212 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
10213 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
10215 var myReader = new Roo.data.ArrayReader({
10216 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
10220 * This would consume an Array like this:
10222 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10224 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10226 * Create a new JsonReader
10227 * @param {Object} meta Metadata configuration options.
10228 * @param {Object} recordType Either an Array of field definition objects
10229 * as specified to {@link Roo.data.Record#create},
10230 * or an {@link Roo.data.Record} object
10231 * created using {@link Roo.data.Record#create}.
10233 Roo.data.ArrayReader = function(meta, recordType){
10234 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10237 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10239 * Create a data block containing Roo.data.Records from an XML document.
10240 * @param {Object} o An Array of row objects which represents the dataset.
10241 * @return {Object} data A data block which is used by an Roo.data.Store object as
10242 * a cache of Roo.data.Records.
10244 readRecords : function(o){
10245 var sid = this.meta ? this.meta.id : null;
10246 var recordType = this.recordType, fields = recordType.prototype.fields;
10249 for(var i = 0; i < root.length; i++){
10252 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10253 for(var j = 0, jlen = fields.length; j < jlen; j++){
10254 var f = fields.items[j];
10255 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10256 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10258 values[f.name] = v;
10260 var record = new recordType(values, id);
10262 records[records.length] = record;
10266 totalRecords : records.length
10275 * @class Roo.bootstrap.ComboBox
10276 * @extends Roo.bootstrap.TriggerField
10277 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10278 * @cfg {Boolean} append (true|false) default false
10279 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10280 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10281 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10282 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10283 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10285 * Create a new ComboBox.
10286 * @param {Object} config Configuration options
10288 Roo.bootstrap.ComboBox = function(config){
10289 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10293 * Fires when the dropdown list is expanded
10294 * @param {Roo.bootstrap.ComboBox} combo This combo box
10299 * Fires when the dropdown list is collapsed
10300 * @param {Roo.bootstrap.ComboBox} combo This combo box
10304 * @event beforeselect
10305 * Fires before a list item is selected. Return false to cancel the selection.
10306 * @param {Roo.bootstrap.ComboBox} combo This combo box
10307 * @param {Roo.data.Record} record The data record returned from the underlying store
10308 * @param {Number} index The index of the selected item in the dropdown list
10310 'beforeselect' : true,
10313 * Fires when a list item is selected
10314 * @param {Roo.bootstrap.ComboBox} combo This combo box
10315 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10316 * @param {Number} index The index of the selected item in the dropdown list
10320 * @event beforequery
10321 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10322 * The event object passed has these properties:
10323 * @param {Roo.bootstrap.ComboBox} combo This combo box
10324 * @param {String} query The query
10325 * @param {Boolean} forceAll true to force "all" query
10326 * @param {Boolean} cancel true to cancel the query
10327 * @param {Object} e The query event object
10329 'beforequery': true,
10332 * Fires when the 'add' icon is pressed (add a listener to enable add button)
10333 * @param {Roo.bootstrap.ComboBox} combo This combo box
10338 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10339 * @param {Roo.bootstrap.ComboBox} combo This combo box
10340 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10345 * Fires when the remove value from the combobox array
10346 * @param {Roo.bootstrap.ComboBox} combo This combo box
10353 this.tickItems = [];
10355 this.selectedIndex = -1;
10356 if(this.mode == 'local'){
10357 if(config.queryDelay === undefined){
10358 this.queryDelay = 10;
10360 if(config.minChars === undefined){
10366 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10369 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10370 * rendering into an Roo.Editor, defaults to false)
10373 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10374 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10377 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10380 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10381 * the dropdown list (defaults to undefined, with no header element)
10385 * @cfg {String/Roo.Template} tpl The template to use to render the output
10389 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10391 listWidth: undefined,
10393 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10394 * mode = 'remote' or 'text' if mode = 'local')
10396 displayField: undefined,
10398 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10399 * mode = 'remote' or 'value' if mode = 'local').
10400 * Note: use of a valueField requires the user make a selection
10401 * in order for a value to be mapped.
10403 valueField: undefined,
10407 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10408 * field's data value (defaults to the underlying DOM element's name)
10410 hiddenName: undefined,
10412 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10416 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10418 selectedClass: 'active',
10421 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10425 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10426 * anchor positions (defaults to 'tl-bl')
10428 listAlign: 'tl-bl?',
10430 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10434 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
10435 * query specified by the allQuery config option (defaults to 'query')
10437 triggerAction: 'query',
10439 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10440 * (defaults to 4, does not apply if editable = false)
10444 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10445 * delay (typeAheadDelay) if it matches a known value (defaults to false)
10449 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10450 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10454 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10455 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
10459 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
10460 * when editable = true (defaults to false)
10462 selectOnFocus:false,
10464 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10466 queryParam: 'query',
10468 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
10469 * when mode = 'remote' (defaults to 'Loading...')
10471 loadingText: 'Loading...',
10473 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10477 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10481 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10482 * traditional select (defaults to true)
10486 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10490 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10494 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10495 * listWidth has a higher value)
10499 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10500 * allow the user to set arbitrary text into the field (defaults to false)
10502 forceSelection:false,
10504 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10505 * if typeAhead = true (defaults to 250)
10507 typeAheadDelay : 250,
10509 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10510 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10512 valueNotFoundText : undefined,
10514 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10516 blockFocus : false,
10519 * @cfg {Boolean} disableClear Disable showing of clear button.
10521 disableClear : false,
10523 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
10525 alwaysQuery : false,
10528 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
10542 btnPosition : 'right',
10543 triggerList : true,
10544 showToggleBtn : true,
10545 // element that contains real text value.. (when hidden is used..)
10547 getAutoCreate : function()
10554 if(!this.tickable){
10555 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10560 * ComboBox with tickable selections
10563 var align = this.labelAlign || this.parentLabelAlign();
10566 cls : 'form-group roo-combobox-tickable' //input-group
10572 cls : 'tickable-buttons',
10577 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10584 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10591 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10598 Roo.each(buttons.cn, function(c){
10600 c.cls += ' btn-' + _this.size;
10603 if (_this.disabled) {
10614 cls: 'form-hidden-field'
10618 cls: 'select2-choices',
10622 cls: 'select2-search-field',
10634 cls: 'select2-container input-group select2-container-multi',
10639 // cls: 'typeahead typeahead-long dropdown-menu',
10640 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
10645 if (align ==='left' && this.fieldLabel.length) {
10647 Roo.log("left and has label");
10653 cls : 'control-label col-sm-' + this.labelWidth,
10654 html : this.fieldLabel
10658 cls : "col-sm-" + (12 - this.labelWidth),
10665 } else if ( this.fieldLabel.length) {
10671 //cls : 'input-group-addon',
10672 html : this.fieldLabel
10682 Roo.log(" no label && no align");
10689 ['xs','sm','md','lg'].map(function(size){
10690 if (settings[size]) {
10691 cfg.cls += ' col-' + size + '-' + settings[size];
10700 initEvents: function()
10704 throw "can not find store for combo";
10706 this.store = Roo.factory(this.store, Roo.data);
10709 this.initTickableEvents();
10713 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10715 if(this.hiddenName){
10717 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10719 this.hiddenField.dom.value =
10720 this.hiddenValue !== undefined ? this.hiddenValue :
10721 this.value !== undefined ? this.value : '';
10723 // prevent input submission
10724 this.el.dom.removeAttribute('name');
10725 this.hiddenField.dom.setAttribute('name', this.hiddenName);
10730 // this.el.dom.setAttribute('autocomplete', 'off');
10733 var cls = 'x-combo-list';
10735 //this.list = new Roo.Layer({
10736 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10742 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10743 _this.list.setWidth(lw);
10746 this.list.on('mouseover', this.onViewOver, this);
10747 this.list.on('mousemove', this.onViewMove, this);
10749 this.list.on('scroll', this.onViewScroll, this);
10752 this.list.swallowEvent('mousewheel');
10753 this.assetHeight = 0;
10756 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10757 this.assetHeight += this.header.getHeight();
10760 this.innerList = this.list.createChild({cls:cls+'-inner'});
10761 this.innerList.on('mouseover', this.onViewOver, this);
10762 this.innerList.on('mousemove', this.onViewMove, this);
10763 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10765 if(this.allowBlank && !this.pageSize && !this.disableClear){
10766 this.footer = this.list.createChild({cls:cls+'-ft'});
10767 this.pageTb = new Roo.Toolbar(this.footer);
10771 this.footer = this.list.createChild({cls:cls+'-ft'});
10772 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10773 {pageSize: this.pageSize});
10777 if (this.pageTb && this.allowBlank && !this.disableClear) {
10779 this.pageTb.add(new Roo.Toolbar.Fill(), {
10780 cls: 'x-btn-icon x-btn-clear',
10782 handler: function()
10785 _this.clearValue();
10786 _this.onSelect(false, -1);
10791 this.assetHeight += this.footer.getHeight();
10796 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10799 this.view = new Roo.View(this.list, this.tpl, {
10800 singleSelect:true, store: this.store, selectedClass: this.selectedClass
10802 //this.view.wrapEl.setDisplayed(false);
10803 this.view.on('click', this.onViewClick, this);
10807 this.store.on('beforeload', this.onBeforeLoad, this);
10808 this.store.on('load', this.onLoad, this);
10809 this.store.on('loadexception', this.onLoadException, this);
10811 if(this.resizable){
10812 this.resizer = new Roo.Resizable(this.list, {
10813 pinned:true, handles:'se'
10815 this.resizer.on('resize', function(r, w, h){
10816 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10817 this.listWidth = w;
10818 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10819 this.restrictHeight();
10821 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10824 if(!this.editable){
10825 this.editable = true;
10826 this.setEditable(false);
10831 if (typeof(this.events.add.listeners) != 'undefined') {
10833 this.addicon = this.wrap.createChild(
10834 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
10836 this.addicon.on('click', function(e) {
10837 this.fireEvent('add', this);
10840 if (typeof(this.events.edit.listeners) != 'undefined') {
10842 this.editicon = this.wrap.createChild(
10843 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
10844 if (this.addicon) {
10845 this.editicon.setStyle('margin-left', '40px');
10847 this.editicon.on('click', function(e) {
10849 // we fire even if inothing is selected..
10850 this.fireEvent('edit', this, this.lastData );
10856 this.keyNav = new Roo.KeyNav(this.inputEl(), {
10857 "up" : function(e){
10858 this.inKeyMode = true;
10862 "down" : function(e){
10863 if(!this.isExpanded()){
10864 this.onTriggerClick();
10866 this.inKeyMode = true;
10871 "enter" : function(e){
10872 // this.onViewClick();
10876 if(this.fireEvent("specialkey", this, e)){
10877 this.onViewClick(false);
10883 "esc" : function(e){
10887 "tab" : function(e){
10890 if(this.fireEvent("specialkey", this, e)){
10891 this.onViewClick(false);
10899 doRelay : function(foo, bar, hname){
10900 if(hname == 'down' || this.scope.isExpanded()){
10901 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10910 this.queryDelay = Math.max(this.queryDelay || 10,
10911 this.mode == 'local' ? 10 : 250);
10914 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10916 if(this.typeAhead){
10917 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10919 if(this.editable !== false){
10920 this.inputEl().on("keyup", this.onKeyUp, this);
10922 if(this.forceSelection){
10923 this.inputEl().on('blur', this.doForce, this);
10927 this.choices = this.el.select('ul.select2-choices', true).first();
10928 this.searchField = this.el.select('ul li.select2-search-field', true).first();
10932 initTickableEvents: function()
10936 if(this.hiddenName){
10938 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10940 this.hiddenField.dom.value =
10941 this.hiddenValue !== undefined ? this.hiddenValue :
10942 this.value !== undefined ? this.value : '';
10944 // prevent input submission
10945 this.el.dom.removeAttribute('name');
10946 this.hiddenField.dom.setAttribute('name', this.hiddenName);
10951 // this.list = this.el.select('ul.dropdown-menu',true).first();
10953 this.choices = this.el.select('ul.select2-choices', true).first();
10954 this.searchField = this.el.select('ul li.select2-search-field', true).first();
10955 if(this.triggerList){
10956 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10959 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10960 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10962 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10963 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10965 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10966 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10968 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10969 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10970 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10973 this.cancelBtn.hide();
10978 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10979 _this.list.setWidth(lw);
10982 this.list.on('mouseover', this.onViewOver, this);
10983 this.list.on('mousemove', this.onViewMove, this);
10985 this.list.on('scroll', this.onViewScroll, this);
10988 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>';
10991 this.view = new Roo.View(this.list, this.tpl, {
10992 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10995 //this.view.wrapEl.setDisplayed(false);
10996 this.view.on('click', this.onViewClick, this);
11000 this.store.on('beforeload', this.onBeforeLoad, this);
11001 this.store.on('load', this.onLoad, this);
11002 this.store.on('loadexception', this.onLoadException, this);
11004 // this.keyNav = new Roo.KeyNav(this.inputEl(), {
11005 // "up" : function(e){
11006 // this.inKeyMode = true;
11007 // this.selectPrev();
11010 // "down" : function(e){
11011 // if(!this.isExpanded()){
11012 // this.onTriggerClick();
11014 // this.inKeyMode = true;
11015 // this.selectNext();
11019 // "enter" : function(e){
11020 //// this.onViewClick();
11022 // this.collapse();
11024 // if(this.fireEvent("specialkey", this, e)){
11025 // this.onViewClick(false);
11031 // "esc" : function(e){
11032 // this.collapse();
11035 // "tab" : function(e){
11036 // this.collapse();
11038 // if(this.fireEvent("specialkey", this, e)){
11039 // this.onViewClick(false);
11047 // doRelay : function(foo, bar, hname){
11048 // if(hname == 'down' || this.scope.isExpanded()){
11049 // return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11054 // forceKeyDown: true
11058 this.queryDelay = Math.max(this.queryDelay || 10,
11059 this.mode == 'local' ? 10 : 250);
11062 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11064 if(this.typeAhead){
11065 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11069 onDestroy : function(){
11071 this.view.setStore(null);
11072 this.view.el.removeAllListeners();
11073 this.view.el.remove();
11074 this.view.purgeListeners();
11077 this.list.dom.innerHTML = '';
11081 this.store.un('beforeload', this.onBeforeLoad, this);
11082 this.store.un('load', this.onLoad, this);
11083 this.store.un('loadexception', this.onLoadException, this);
11085 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11089 fireKey : function(e){
11090 if(e.isNavKeyPress() && !this.list.isVisible()){
11091 this.fireEvent("specialkey", this, e);
11096 onResize: function(w, h){
11097 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11099 // if(typeof w != 'number'){
11100 // // we do not handle it!?!?
11103 // var tw = this.trigger.getWidth();
11104 // // tw += this.addicon ? this.addicon.getWidth() : 0;
11105 // // tw += this.editicon ? this.editicon.getWidth() : 0;
11107 // this.inputEl().setWidth( this.adjustWidth('input', x));
11109 // //this.trigger.setStyle('left', x+'px');
11111 // if(this.list && this.listWidth === undefined){
11112 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11113 // this.list.setWidth(lw);
11114 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11122 * Allow or prevent the user from directly editing the field text. If false is passed,
11123 * the user will only be able to select from the items defined in the dropdown list. This method
11124 * is the runtime equivalent of setting the 'editable' config option at config time.
11125 * @param {Boolean} value True to allow the user to directly edit the field text
11127 setEditable : function(value){
11128 if(value == this.editable){
11131 this.editable = value;
11133 this.inputEl().dom.setAttribute('readOnly', true);
11134 this.inputEl().on('mousedown', this.onTriggerClick, this);
11135 this.inputEl().addClass('x-combo-noedit');
11137 this.inputEl().dom.setAttribute('readOnly', false);
11138 this.inputEl().un('mousedown', this.onTriggerClick, this);
11139 this.inputEl().removeClass('x-combo-noedit');
11145 onBeforeLoad : function(combo,opts){
11146 if(!this.hasFocus){
11150 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11152 this.restrictHeight();
11153 this.selectedIndex = -1;
11157 onLoad : function(){
11159 this.hasQuery = false;
11161 if(!this.hasFocus){
11165 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11166 this.loading.hide();
11169 if(this.store.getCount() > 0){
11171 // this.restrictHeight();
11172 if(this.lastQuery == this.allQuery){
11173 if(this.editable && !this.tickable){
11174 this.inputEl().dom.select();
11178 !this.selectByValue(this.value, true) &&
11179 this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' ||
11180 this.store.lastOptions.add != true)
11182 this.select(0, true);
11185 if(this.autoFocus){
11188 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11189 this.taTask.delay(this.typeAheadDelay);
11193 this.onEmptyResults();
11199 onLoadException : function()
11201 this.hasQuery = false;
11203 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11204 this.loading.hide();
11208 Roo.log(this.store.reader.jsonData);
11209 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11211 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11217 onTypeAhead : function(){
11218 if(this.store.getCount() > 0){
11219 var r = this.store.getAt(0);
11220 var newValue = r.data[this.displayField];
11221 var len = newValue.length;
11222 var selStart = this.getRawValue().length;
11224 if(selStart != len){
11225 this.setRawValue(newValue);
11226 this.selectText(selStart, newValue.length);
11232 onSelect : function(record, index){
11234 if(this.fireEvent('beforeselect', this, record, index) !== false){
11236 this.setFromData(index > -1 ? record.data : false);
11239 this.fireEvent('select', this, record, index);
11244 * Returns the currently selected field value or empty string if no value is set.
11245 * @return {String} value The selected value
11247 getValue : function(){
11250 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11253 if(this.valueField){
11254 return typeof this.value != 'undefined' ? this.value : '';
11256 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11261 * Clears any text/value currently set in the field
11263 clearValue : function(){
11264 if(this.hiddenField){
11265 this.hiddenField.dom.value = '';
11268 this.setRawValue('');
11269 this.lastSelectionText = '';
11274 * Sets the specified value into the field. If the value finds a match, the corresponding record text
11275 * will be displayed in the field. If the value does not match the data value of an existing item,
11276 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11277 * Otherwise the field will be blank (although the value will still be set).
11278 * @param {String} value The value to match
11280 setValue : function(v){
11287 if(this.valueField){
11288 var r = this.findRecord(this.valueField, v);
11290 text = r.data[this.displayField];
11291 }else if(this.valueNotFoundText !== undefined){
11292 text = this.valueNotFoundText;
11295 this.lastSelectionText = text;
11296 if(this.hiddenField){
11297 this.hiddenField.dom.value = v;
11299 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11303 * @property {Object} the last set data for the element
11308 * Sets the value of the field based on a object which is related to the record format for the store.
11309 * @param {Object} value the value to set as. or false on reset?
11311 setFromData : function(o){
11318 var dv = ''; // display value
11319 var vv = ''; // value value..
11321 if (this.displayField) {
11322 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11324 // this is an error condition!!!
11325 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
11328 if(this.valueField){
11329 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11332 if(this.hiddenField){
11333 this.hiddenField.dom.value = vv;
11335 this.lastSelectionText = dv;
11336 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11340 // no hidden field.. - we store the value in 'value', but still display
11341 // display field!!!!
11342 this.lastSelectionText = dv;
11343 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11349 reset : function(){
11350 // overridden so that last data is reset..
11351 this.setValue(this.originalValue);
11352 this.clearInvalid();
11353 this.lastData = false;
11355 this.view.clearSelections();
11359 findRecord : function(prop, value){
11361 if(this.store.getCount() > 0){
11362 this.store.each(function(r){
11363 if(r.data[prop] == value){
11373 getName: function()
11375 // returns hidden if it's set..
11376 if (!this.rendered) {return ''};
11377 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
11381 onViewMove : function(e, t){
11382 this.inKeyMode = false;
11386 onViewOver : function(e, t){
11387 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11390 var item = this.view.findItemFromChild(t);
11393 var index = this.view.indexOf(item);
11394 this.select(index, false);
11399 onViewClick : function(view, doFocus, el, e)
11401 var index = this.view.getSelectedIndexes()[0];
11403 var r = this.store.getAt(index);
11407 if(e.getTarget().nodeName.toLowerCase() != 'input'){
11414 Roo.each(this.tickItems, function(v,k){
11416 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11417 _this.tickItems.splice(k, 1);
11427 this.tickItems.push(r.data);
11432 this.onSelect(r, index);
11434 if(doFocus !== false && !this.blockFocus){
11435 this.inputEl().focus();
11440 restrictHeight : function(){
11441 //this.innerList.dom.style.height = '';
11442 //var inner = this.innerList.dom;
11443 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11444 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11445 //this.list.beginUpdate();
11446 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11447 this.list.alignTo(this.inputEl(), this.listAlign);
11448 this.list.alignTo(this.inputEl(), this.listAlign);
11449 //this.list.endUpdate();
11453 onEmptyResults : function(){
11458 * Returns true if the dropdown list is expanded, else false.
11460 isExpanded : function(){
11461 return this.list.isVisible();
11465 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11466 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11467 * @param {String} value The data value of the item to select
11468 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11469 * selected item if it is not currently in view (defaults to true)
11470 * @return {Boolean} True if the value matched an item in the list, else false
11472 selectByValue : function(v, scrollIntoView){
11473 if(v !== undefined && v !== null){
11474 var r = this.findRecord(this.valueField || this.displayField, v);
11476 this.select(this.store.indexOf(r), scrollIntoView);
11484 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11485 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11486 * @param {Number} index The zero-based index of the list item to select
11487 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11488 * selected item if it is not currently in view (defaults to true)
11490 select : function(index, scrollIntoView){
11491 this.selectedIndex = index;
11492 this.view.select(index);
11493 if(scrollIntoView !== false){
11494 var el = this.view.getNode(index);
11495 if(el && !this.multiple && !this.tickable){
11496 this.list.scrollChildIntoView(el, false);
11502 selectNext : function(){
11503 var ct = this.store.getCount();
11505 if(this.selectedIndex == -1){
11507 }else if(this.selectedIndex < ct-1){
11508 this.select(this.selectedIndex+1);
11514 selectPrev : function(){
11515 var ct = this.store.getCount();
11517 if(this.selectedIndex == -1){
11519 }else if(this.selectedIndex != 0){
11520 this.select(this.selectedIndex-1);
11526 onKeyUp : function(e){
11527 if(this.editable !== false && !e.isSpecialKey()){
11528 this.lastKey = e.getKey();
11529 this.dqTask.delay(this.queryDelay);
11534 validateBlur : function(){
11535 return !this.list || !this.list.isVisible();
11539 initQuery : function(){
11540 this.doQuery(this.getRawValue());
11544 doForce : function(){
11545 if(this.inputEl().dom.value.length > 0){
11546 this.inputEl().dom.value =
11547 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11553 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
11554 * query allowing the query action to be canceled if needed.
11555 * @param {String} query The SQL query to execute
11556 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11557 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
11558 * saved in the current store (defaults to false)
11560 doQuery : function(q, forceAll){
11562 if(q === undefined || q === null){
11567 forceAll: forceAll,
11571 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11576 forceAll = qe.forceAll;
11577 if(forceAll === true || (q.length >= this.minChars)){
11579 this.hasQuery = true;
11581 if(this.lastQuery != q || this.alwaysQuery){
11582 this.lastQuery = q;
11583 if(this.mode == 'local'){
11584 this.selectedIndex = -1;
11586 this.store.clearFilter();
11588 this.store.filter(this.displayField, q);
11592 this.store.baseParams[this.queryParam] = q;
11594 var options = {params : this.getParams(q)};
11597 options.add = true;
11598 options.params.start = this.page * this.pageSize;
11601 this.store.load(options);
11603 * this code will make the page width larger, at the beginning, the list not align correctly,
11604 * we should expand the list on onLoad
11605 * so command out it
11610 this.selectedIndex = -1;
11615 this.loadNext = false;
11619 getParams : function(q){
11621 //p[this.queryParam] = q;
11625 p.limit = this.pageSize;
11631 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11633 collapse : function(){
11634 if(!this.isExpanded()){
11642 this.cancelBtn.hide();
11643 this.trigger.show();
11646 Roo.get(document).un('mousedown', this.collapseIf, this);
11647 Roo.get(document).un('mousewheel', this.collapseIf, this);
11648 if (!this.editable) {
11649 Roo.get(document).un('keydown', this.listKeyPress, this);
11651 this.fireEvent('collapse', this);
11655 collapseIf : function(e){
11656 var in_combo = e.within(this.el);
11657 var in_list = e.within(this.list);
11658 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11660 if (in_combo || in_list || is_list) {
11661 //e.stopPropagation();
11666 this.onTickableFooterButtonClick(e, false, false);
11674 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11676 expand : function(){
11678 if(this.isExpanded() || !this.hasFocus){
11682 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11683 this.list.setWidth(lw);
11690 this.restrictHeight();
11694 this.tickItems = Roo.apply([], this.item);
11697 this.cancelBtn.show();
11698 this.trigger.hide();
11702 Roo.get(document).on('mousedown', this.collapseIf, this);
11703 Roo.get(document).on('mousewheel', this.collapseIf, this);
11704 if (!this.editable) {
11705 Roo.get(document).on('keydown', this.listKeyPress, this);
11708 this.fireEvent('expand', this);
11712 // Implements the default empty TriggerField.onTriggerClick function
11713 onTriggerClick : function(e)
11715 Roo.log('trigger click');
11717 if(this.disabled || !this.triggerList){
11722 this.loadNext = false;
11724 if(this.isExpanded()){
11726 if (!this.blockFocus) {
11727 this.inputEl().focus();
11731 this.hasFocus = true;
11732 if(this.triggerAction == 'all') {
11733 this.doQuery(this.allQuery, true);
11735 this.doQuery(this.getRawValue());
11737 if (!this.blockFocus) {
11738 this.inputEl().focus();
11743 onTickableTriggerClick : function(e)
11750 this.loadNext = false;
11751 this.hasFocus = true;
11753 if(this.triggerAction == 'all') {
11754 this.doQuery(this.allQuery, true);
11756 this.doQuery(this.getRawValue());
11760 onSearchFieldClick : function(e)
11762 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11767 this.loadNext = false;
11768 this.hasFocus = true;
11770 if(this.triggerAction == 'all') {
11771 this.doQuery(this.allQuery, true);
11773 this.doQuery(this.getRawValue());
11777 listKeyPress : function(e)
11779 //Roo.log('listkeypress');
11780 // scroll to first matching element based on key pres..
11781 if (e.isSpecialKey()) {
11784 var k = String.fromCharCode(e.getKey()).toUpperCase();
11787 var csel = this.view.getSelectedNodes();
11788 var cselitem = false;
11790 var ix = this.view.indexOf(csel[0]);
11791 cselitem = this.store.getAt(ix);
11792 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11798 this.store.each(function(v) {
11800 // start at existing selection.
11801 if (cselitem.id == v.id) {
11807 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11808 match = this.store.indexOf(v);
11814 if (match === false) {
11815 return true; // no more action?
11818 this.view.select(match);
11819 var sn = Roo.get(this.view.getSelectedNodes()[0])
11820 //sn.scrollIntoView(sn.dom.parentNode, false);
11823 onViewScroll : function(e, t){
11825 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){
11829 this.hasQuery = true;
11831 this.loading = this.list.select('.loading', true).first();
11833 if(this.loading === null){
11834 this.list.createChild({
11836 cls: 'loading select2-more-results select2-active',
11837 html: 'Loading more results...'
11840 this.loading = this.list.select('.loading', true).first();
11842 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11844 this.loading.hide();
11847 this.loading.show();
11852 this.loadNext = true;
11854 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11859 addItem : function(o)
11861 var dv = ''; // display value
11863 if (this.displayField) {
11864 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11866 // this is an error condition!!!
11867 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
11874 var choice = this.choices.createChild({
11876 cls: 'select2-search-choice',
11885 cls: 'select2-search-choice-close',
11890 }, this.searchField);
11892 var close = choice.select('a.select2-search-choice-close', true).first()
11894 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11902 this.inputEl().dom.value = '';
11906 onRemoveItem : function(e, _self, o)
11908 e.preventDefault();
11909 var index = this.item.indexOf(o.data) * 1;
11912 Roo.log('not this item?!');
11916 this.item.splice(index, 1);
11921 this.fireEvent('remove', this, e);
11925 syncValue : function()
11927 if(!this.item.length){
11934 Roo.each(this.item, function(i){
11935 if(_this.valueField){
11936 value.push(i[_this.valueField]);
11943 this.value = value.join(',');
11945 if(this.hiddenField){
11946 this.hiddenField.dom.value = this.value;
11950 clearItem : function()
11952 if(!this.multiple){
11958 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11965 inputEl: function ()
11968 return this.searchField;
11970 return this.el.select('input.form-control',true).first();
11974 onTickableFooterButtonClick : function(e, btn, el)
11976 e.preventDefault();
11978 if(btn && btn.name == 'cancel'){
11979 this.tickItems = Roo.apply([], this.item);
11988 Roo.each(this.tickItems, function(o){
11999 * @cfg {Boolean} grow
12003 * @cfg {Number} growMin
12007 * @cfg {Number} growMax
12017 * Ext JS Library 1.1.1
12018 * Copyright(c) 2006-2007, Ext JS, LLC.
12020 * Originally Released Under LGPL - original licence link has changed is not relivant.
12023 * <script type="text/javascript">
12028 * @extends Roo.util.Observable
12029 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
12030 * This class also supports single and multi selection modes. <br>
12031 * Create a data model bound view:
12033 var store = new Roo.data.Store(...);
12035 var view = new Roo.View({
12037 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
12039 singleSelect: true,
12040 selectedClass: "ydataview-selected",
12044 // listen for node click?
12045 view.on("click", function(vw, index, node, e){
12046 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12050 dataModel.load("foobar.xml");
12052 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12054 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12055 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12057 * Note: old style constructor is still suported (container, template, config)
12060 * Create a new View
12061 * @param {Object} config The config object
12064 Roo.View = function(config, depreciated_tpl, depreciated_config){
12066 this.parent = false;
12068 if (typeof(depreciated_tpl) == 'undefined') {
12069 // new way.. - universal constructor.
12070 Roo.apply(this, config);
12071 this.el = Roo.get(this.el);
12074 this.el = Roo.get(config);
12075 this.tpl = depreciated_tpl;
12076 Roo.apply(this, depreciated_config);
12078 this.wrapEl = this.el.wrap().wrap();
12079 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12082 if(typeof(this.tpl) == "string"){
12083 this.tpl = new Roo.Template(this.tpl);
12085 // support xtype ctors..
12086 this.tpl = new Roo.factory(this.tpl, Roo);
12090 this.tpl.compile();
12095 * @event beforeclick
12096 * Fires before a click is processed. Returns false to cancel the default action.
12097 * @param {Roo.View} this
12098 * @param {Number} index The index of the target node
12099 * @param {HTMLElement} node The target node
12100 * @param {Roo.EventObject} e The raw event object
12102 "beforeclick" : true,
12105 * Fires when a template node is clicked.
12106 * @param {Roo.View} this
12107 * @param {Number} index The index of the target node
12108 * @param {HTMLElement} node The target node
12109 * @param {Roo.EventObject} e The raw event object
12114 * Fires when a template node is double clicked.
12115 * @param {Roo.View} this
12116 * @param {Number} index The index of the target node
12117 * @param {HTMLElement} node The target node
12118 * @param {Roo.EventObject} e The raw event object
12122 * @event contextmenu
12123 * Fires when a template node is right clicked.
12124 * @param {Roo.View} this
12125 * @param {Number} index The index of the target node
12126 * @param {HTMLElement} node The target node
12127 * @param {Roo.EventObject} e The raw event object
12129 "contextmenu" : true,
12131 * @event selectionchange
12132 * Fires when the selected nodes change.
12133 * @param {Roo.View} this
12134 * @param {Array} selections Array of the selected nodes
12136 "selectionchange" : true,
12139 * @event beforeselect
12140 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12141 * @param {Roo.View} this
12142 * @param {HTMLElement} node The node to be selected
12143 * @param {Array} selections Array of currently selected nodes
12145 "beforeselect" : true,
12147 * @event preparedata
12148 * Fires on every row to render, to allow you to change the data.
12149 * @param {Roo.View} this
12150 * @param {Object} data to be rendered (change this)
12152 "preparedata" : true
12160 "click": this.onClick,
12161 "dblclick": this.onDblClick,
12162 "contextmenu": this.onContextMenu,
12166 this.selections = [];
12168 this.cmp = new Roo.CompositeElementLite([]);
12170 this.store = Roo.factory(this.store, Roo.data);
12171 this.setStore(this.store, true);
12174 if ( this.footer && this.footer.xtype) {
12176 var fctr = this.wrapEl.appendChild(document.createElement("div"));
12178 this.footer.dataSource = this.store
12179 this.footer.container = fctr;
12180 this.footer = Roo.factory(this.footer, Roo);
12181 fctr.insertFirst(this.el);
12183 // this is a bit insane - as the paging toolbar seems to detach the el..
12184 // dom.parentNode.parentNode.parentNode
12185 // they get detached?
12189 Roo.View.superclass.constructor.call(this);
12194 Roo.extend(Roo.View, Roo.util.Observable, {
12197 * @cfg {Roo.data.Store} store Data store to load data from.
12202 * @cfg {String|Roo.Element} el The container element.
12207 * @cfg {String|Roo.Template} tpl The template used by this View
12211 * @cfg {String} dataName the named area of the template to use as the data area
12212 * Works with domtemplates roo-name="name"
12216 * @cfg {String} selectedClass The css class to add to selected nodes
12218 selectedClass : "x-view-selected",
12220 * @cfg {String} emptyText The empty text to show when nothing is loaded.
12225 * @cfg {String} text to display on mask (default Loading)
12229 * @cfg {Boolean} multiSelect Allow multiple selection
12231 multiSelect : false,
12233 * @cfg {Boolean} singleSelect Allow single selection
12235 singleSelect: false,
12238 * @cfg {Boolean} toggleSelect - selecting
12240 toggleSelect : false,
12243 * @cfg {Boolean} tickable - selecting
12248 * Returns the element this view is bound to.
12249 * @return {Roo.Element}
12251 getEl : function(){
12252 return this.wrapEl;
12258 * Refreshes the view. - called by datachanged on the store. - do not call directly.
12260 refresh : function(){
12261 Roo.log('refresh');
12264 // if we are using something like 'domtemplate', then
12265 // the what gets used is:
12266 // t.applySubtemplate(NAME, data, wrapping data..)
12267 // the outer template then get' applied with
12268 // the store 'extra data'
12269 // and the body get's added to the
12270 // roo-name="data" node?
12271 // <span class='roo-tpl-{name}'></span> ?????
12275 this.clearSelections();
12276 this.el.update("");
12278 var records = this.store.getRange();
12279 if(records.length < 1) {
12281 // is this valid?? = should it render a template??
12283 this.el.update(this.emptyText);
12287 if (this.dataName) {
12288 this.el.update(t.apply(this.store.meta)); //????
12289 el = this.el.child('.roo-tpl-' + this.dataName);
12292 for(var i = 0, len = records.length; i < len; i++){
12293 var data = this.prepareData(records[i].data, i, records[i]);
12294 this.fireEvent("preparedata", this, data, i, records[i]);
12296 var d = Roo.apply({}, data);
12299 Roo.apply(d, {'roo-id' : Roo.id()});
12303 Roo.each(this.parent.item, function(item){
12304 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12307 Roo.apply(d, {'roo-data-checked' : 'checked'});
12311 html[html.length] = Roo.util.Format.trim(
12313 t.applySubtemplate(this.dataName, d, this.store.meta) :
12320 el.update(html.join(""));
12321 this.nodes = el.dom.childNodes;
12322 this.updateIndexes(0);
12327 * Function to override to reformat the data that is sent to
12328 * the template for each node.
12329 * DEPRICATED - use the preparedata event handler.
12330 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12331 * a JSON object for an UpdateManager bound view).
12333 prepareData : function(data, index, record)
12335 this.fireEvent("preparedata", this, data, index, record);
12339 onUpdate : function(ds, record){
12340 Roo.log('on update');
12341 this.clearSelections();
12342 var index = this.store.indexOf(record);
12343 var n = this.nodes[index];
12344 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12345 n.parentNode.removeChild(n);
12346 this.updateIndexes(index, index);
12352 onAdd : function(ds, records, index)
12354 Roo.log(['on Add', ds, records, index] );
12355 this.clearSelections();
12356 if(this.nodes.length == 0){
12360 var n = this.nodes[index];
12361 for(var i = 0, len = records.length; i < len; i++){
12362 var d = this.prepareData(records[i].data, i, records[i]);
12364 this.tpl.insertBefore(n, d);
12367 this.tpl.append(this.el, d);
12370 this.updateIndexes(index);
12373 onRemove : function(ds, record, index){
12374 Roo.log('onRemove');
12375 this.clearSelections();
12376 var el = this.dataName ?
12377 this.el.child('.roo-tpl-' + this.dataName) :
12380 el.dom.removeChild(this.nodes[index]);
12381 this.updateIndexes(index);
12385 * Refresh an individual node.
12386 * @param {Number} index
12388 refreshNode : function(index){
12389 this.onUpdate(this.store, this.store.getAt(index));
12392 updateIndexes : function(startIndex, endIndex){
12393 var ns = this.nodes;
12394 startIndex = startIndex || 0;
12395 endIndex = endIndex || ns.length - 1;
12396 for(var i = startIndex; i <= endIndex; i++){
12397 ns[i].nodeIndex = i;
12402 * Changes the data store this view uses and refresh the view.
12403 * @param {Store} store
12405 setStore : function(store, initial){
12406 if(!initial && this.store){
12407 this.store.un("datachanged", this.refresh);
12408 this.store.un("add", this.onAdd);
12409 this.store.un("remove", this.onRemove);
12410 this.store.un("update", this.onUpdate);
12411 this.store.un("clear", this.refresh);
12412 this.store.un("beforeload", this.onBeforeLoad);
12413 this.store.un("load", this.onLoad);
12414 this.store.un("loadexception", this.onLoad);
12418 store.on("datachanged", this.refresh, this);
12419 store.on("add", this.onAdd, this);
12420 store.on("remove", this.onRemove, this);
12421 store.on("update", this.onUpdate, this);
12422 store.on("clear", this.refresh, this);
12423 store.on("beforeload", this.onBeforeLoad, this);
12424 store.on("load", this.onLoad, this);
12425 store.on("loadexception", this.onLoad, this);
12433 * onbeforeLoad - masks the loading area.
12436 onBeforeLoad : function(store,opts)
12438 Roo.log('onBeforeLoad');
12440 this.el.update("");
12442 this.el.mask(this.mask ? this.mask : "Loading" );
12444 onLoad : function ()
12451 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12452 * @param {HTMLElement} node
12453 * @return {HTMLElement} The template node
12455 findItemFromChild : function(node){
12456 var el = this.dataName ?
12457 this.el.child('.roo-tpl-' + this.dataName,true) :
12460 if(!node || node.parentNode == el){
12463 var p = node.parentNode;
12464 while(p && p != el){
12465 if(p.parentNode == el){
12474 onClick : function(e){
12475 var item = this.findItemFromChild(e.getTarget());
12477 var index = this.indexOf(item);
12478 if(this.onItemClick(item, index, e) !== false){
12479 this.fireEvent("click", this, index, item, e);
12482 this.clearSelections();
12487 onContextMenu : function(e){
12488 var item = this.findItemFromChild(e.getTarget());
12490 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12495 onDblClick : function(e){
12496 var item = this.findItemFromChild(e.getTarget());
12498 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12502 onItemClick : function(item, index, e)
12504 if(this.fireEvent("beforeclick", this, index, item, e) === false){
12507 if (this.toggleSelect) {
12508 var m = this.isSelected(item) ? 'unselect' : 'select';
12511 _t[m](item, true, false);
12514 if(this.multiSelect || this.singleSelect){
12515 if(this.multiSelect && e.shiftKey && this.lastSelection){
12516 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12518 this.select(item, this.multiSelect && e.ctrlKey);
12519 this.lastSelection = item;
12522 if(!this.tickable){
12523 e.preventDefault();
12531 * Get the number of selected nodes.
12534 getSelectionCount : function(){
12535 return this.selections.length;
12539 * Get the currently selected nodes.
12540 * @return {Array} An array of HTMLElements
12542 getSelectedNodes : function(){
12543 return this.selections;
12547 * Get the indexes of the selected nodes.
12550 getSelectedIndexes : function(){
12551 var indexes = [], s = this.selections;
12552 for(var i = 0, len = s.length; i < len; i++){
12553 indexes.push(s[i].nodeIndex);
12559 * Clear all selections
12560 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12562 clearSelections : function(suppressEvent){
12563 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12564 this.cmp.elements = this.selections;
12565 this.cmp.removeClass(this.selectedClass);
12566 this.selections = [];
12567 if(!suppressEvent){
12568 this.fireEvent("selectionchange", this, this.selections);
12574 * Returns true if the passed node is selected
12575 * @param {HTMLElement/Number} node The node or node index
12576 * @return {Boolean}
12578 isSelected : function(node){
12579 var s = this.selections;
12583 node = this.getNode(node);
12584 return s.indexOf(node) !== -1;
12589 * @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
12590 * @param {Boolean} keepExisting (optional) true to keep existing selections
12591 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12593 select : function(nodeInfo, keepExisting, suppressEvent){
12594 if(nodeInfo instanceof Array){
12596 this.clearSelections(true);
12598 for(var i = 0, len = nodeInfo.length; i < len; i++){
12599 this.select(nodeInfo[i], true, true);
12603 var node = this.getNode(nodeInfo);
12604 if(!node || this.isSelected(node)){
12605 return; // already selected.
12608 this.clearSelections(true);
12610 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12611 Roo.fly(node).addClass(this.selectedClass);
12612 this.selections.push(node);
12613 if(!suppressEvent){
12614 this.fireEvent("selectionchange", this, this.selections);
12622 * @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
12623 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12624 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12626 unselect : function(nodeInfo, keepExisting, suppressEvent)
12628 if(nodeInfo instanceof Array){
12629 Roo.each(this.selections, function(s) {
12630 this.unselect(s, nodeInfo);
12634 var node = this.getNode(nodeInfo);
12635 if(!node || !this.isSelected(node)){
12636 Roo.log("not selected");
12637 return; // not selected.
12641 Roo.each(this.selections, function(s) {
12643 Roo.fly(node).removeClass(this.selectedClass);
12650 this.selections= ns;
12651 this.fireEvent("selectionchange", this, this.selections);
12655 * Gets a template node.
12656 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12657 * @return {HTMLElement} The node or null if it wasn't found
12659 getNode : function(nodeInfo){
12660 if(typeof nodeInfo == "string"){
12661 return document.getElementById(nodeInfo);
12662 }else if(typeof nodeInfo == "number"){
12663 return this.nodes[nodeInfo];
12669 * Gets a range template nodes.
12670 * @param {Number} startIndex
12671 * @param {Number} endIndex
12672 * @return {Array} An array of nodes
12674 getNodes : function(start, end){
12675 var ns = this.nodes;
12676 start = start || 0;
12677 end = typeof end == "undefined" ? ns.length - 1 : end;
12680 for(var i = start; i <= end; i++){
12684 for(var i = start; i >= end; i--){
12692 * Finds the index of the passed node
12693 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12694 * @return {Number} The index of the node or -1
12696 indexOf : function(node){
12697 node = this.getNode(node);
12698 if(typeof node.nodeIndex == "number"){
12699 return node.nodeIndex;
12701 var ns = this.nodes;
12702 for(var i = 0, len = ns.length; i < len; i++){
12713 * based on jquery fullcalendar
12717 Roo.bootstrap = Roo.bootstrap || {};
12719 * @class Roo.bootstrap.Calendar
12720 * @extends Roo.bootstrap.Component
12721 * Bootstrap Calendar class
12722 * @cfg {Boolean} loadMask (true|false) default false
12723 * @cfg {Object} header generate the user specific header of the calendar, default false
12726 * Create a new Container
12727 * @param {Object} config The config object
12732 Roo.bootstrap.Calendar = function(config){
12733 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12737 * Fires when a date is selected
12738 * @param {DatePicker} this
12739 * @param {Date} date The selected date
12743 * @event monthchange
12744 * Fires when the displayed month changes
12745 * @param {DatePicker} this
12746 * @param {Date} date The selected month
12748 'monthchange': true,
12750 * @event evententer
12751 * Fires when mouse over an event
12752 * @param {Calendar} this
12753 * @param {event} Event
12755 'evententer': true,
12757 * @event eventleave
12758 * Fires when the mouse leaves an
12759 * @param {Calendar} this
12762 'eventleave': true,
12764 * @event eventclick
12765 * Fires when the mouse click an
12766 * @param {Calendar} this
12775 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
12778 * @cfg {Number} startDay
12779 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12787 getAutoCreate : function(){
12790 var fc_button = function(name, corner, style, content ) {
12791 return Roo.apply({},{
12793 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
12795 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12798 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12809 style : 'width:100%',
12816 cls : 'fc-header-left',
12818 fc_button('prev', 'left', 'arrow', '‹' ),
12819 fc_button('next', 'right', 'arrow', '›' ),
12820 { tag: 'span', cls: 'fc-header-space' },
12821 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
12829 cls : 'fc-header-center',
12833 cls: 'fc-header-title',
12836 html : 'month / year'
12844 cls : 'fc-header-right',
12846 /* fc_button('month', 'left', '', 'month' ),
12847 fc_button('week', '', '', 'week' ),
12848 fc_button('day', 'right', '', 'day' )
12860 header = this.header;
12863 var cal_heads = function() {
12865 // fixme - handle this.
12867 for (var i =0; i < Date.dayNames.length; i++) {
12868 var d = Date.dayNames[i];
12871 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12872 html : d.substring(0,3)
12876 ret[0].cls += ' fc-first';
12877 ret[6].cls += ' fc-last';
12880 var cal_cell = function(n) {
12883 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12888 cls: 'fc-day-number',
12892 cls: 'fc-day-content',
12896 style: 'position: relative;' // height: 17px;
12908 var cal_rows = function() {
12911 for (var r = 0; r < 6; r++) {
12918 for (var i =0; i < Date.dayNames.length; i++) {
12919 var d = Date.dayNames[i];
12920 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12923 row.cn[0].cls+=' fc-first';
12924 row.cn[0].cn[0].style = 'min-height:90px';
12925 row.cn[6].cls+=' fc-last';
12929 ret[0].cls += ' fc-first';
12930 ret[4].cls += ' fc-prev-last';
12931 ret[5].cls += ' fc-last';
12938 cls: 'fc-border-separate',
12939 style : 'width:100%',
12947 cls : 'fc-first fc-last',
12965 cls : 'fc-content',
12966 style : "position: relative;",
12969 cls : 'fc-view fc-view-month fc-grid',
12970 style : 'position: relative',
12971 unselectable : 'on',
12974 cls : 'fc-event-container',
12975 style : 'position:absolute;z-index:8;top:0;left:0;'
12993 initEvents : function()
12996 throw "can not find store for calendar";
13002 style: "text-align:center",
13006 style: "background-color:white;width:50%;margin:250 auto",
13010 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
13021 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13023 var size = this.el.select('.fc-content', true).first().getSize();
13024 this.maskEl.setSize(size.width, size.height);
13025 this.maskEl.enableDisplayMode("block");
13026 if(!this.loadMask){
13027 this.maskEl.hide();
13030 this.store = Roo.factory(this.store, Roo.data);
13031 this.store.on('load', this.onLoad, this);
13032 this.store.on('beforeload', this.onBeforeLoad, this);
13036 this.cells = this.el.select('.fc-day',true);
13037 //Roo.log(this.cells);
13038 this.textNodes = this.el.query('.fc-day-number');
13039 this.cells.addClassOnOver('fc-state-hover');
13041 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13042 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13043 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13044 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13046 this.on('monthchange', this.onMonthChange, this);
13048 this.update(new Date().clearTime());
13051 resize : function() {
13052 var sz = this.el.getSize();
13054 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13055 this.el.select('.fc-day-content div',true).setHeight(34);
13060 showPrevMonth : function(e){
13061 this.update(this.activeDate.add("mo", -1));
13063 showToday : function(e){
13064 this.update(new Date().clearTime());
13067 showNextMonth : function(e){
13068 this.update(this.activeDate.add("mo", 1));
13072 showPrevYear : function(){
13073 this.update(this.activeDate.add("y", -1));
13077 showNextYear : function(){
13078 this.update(this.activeDate.add("y", 1));
13083 update : function(date)
13085 var vd = this.activeDate;
13086 this.activeDate = date;
13087 // if(vd && this.el){
13088 // var t = date.getTime();
13089 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13090 // Roo.log('using add remove');
13092 // this.fireEvent('monthchange', this, date);
13094 // this.cells.removeClass("fc-state-highlight");
13095 // this.cells.each(function(c){
13096 // if(c.dateValue == t){
13097 // c.addClass("fc-state-highlight");
13098 // setTimeout(function(){
13099 // try{c.dom.firstChild.focus();}catch(e){}
13109 var days = date.getDaysInMonth();
13111 var firstOfMonth = date.getFirstDateOfMonth();
13112 var startingPos = firstOfMonth.getDay()-this.startDay;
13114 if(startingPos < this.startDay){
13118 var pm = date.add(Date.MONTH, -1);
13119 var prevStart = pm.getDaysInMonth()-startingPos;
13121 this.cells = this.el.select('.fc-day',true);
13122 this.textNodes = this.el.query('.fc-day-number');
13123 this.cells.addClassOnOver('fc-state-hover');
13125 var cells = this.cells.elements;
13126 var textEls = this.textNodes;
13128 Roo.each(cells, function(cell){
13129 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13132 days += startingPos;
13134 // convert everything to numbers so it's fast
13135 var day = 86400000;
13136 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13139 //Roo.log(prevStart);
13141 var today = new Date().clearTime().getTime();
13142 var sel = date.clearTime().getTime();
13143 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13144 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13145 var ddMatch = this.disabledDatesRE;
13146 var ddText = this.disabledDatesText;
13147 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13148 var ddaysText = this.disabledDaysText;
13149 var format = this.format;
13151 var setCellClass = function(cal, cell){
13155 //Roo.log('set Cell Class');
13157 var t = d.getTime();
13161 cell.dateValue = t;
13163 cell.className += " fc-today";
13164 cell.className += " fc-state-highlight";
13165 cell.title = cal.todayText;
13168 // disable highlight in other month..
13169 //cell.className += " fc-state-highlight";
13174 cell.className = " fc-state-disabled";
13175 cell.title = cal.minText;
13179 cell.className = " fc-state-disabled";
13180 cell.title = cal.maxText;
13184 if(ddays.indexOf(d.getDay()) != -1){
13185 cell.title = ddaysText;
13186 cell.className = " fc-state-disabled";
13189 if(ddMatch && format){
13190 var fvalue = d.dateFormat(format);
13191 if(ddMatch.test(fvalue)){
13192 cell.title = ddText.replace("%0", fvalue);
13193 cell.className = " fc-state-disabled";
13197 if (!cell.initialClassName) {
13198 cell.initialClassName = cell.dom.className;
13201 cell.dom.className = cell.initialClassName + ' ' + cell.className;
13206 for(; i < startingPos; i++) {
13207 textEls[i].innerHTML = (++prevStart);
13208 d.setDate(d.getDate()+1);
13210 cells[i].className = "fc-past fc-other-month";
13211 setCellClass(this, cells[i]);
13216 for(; i < days; i++){
13217 intDay = i - startingPos + 1;
13218 textEls[i].innerHTML = (intDay);
13219 d.setDate(d.getDate()+1);
13221 cells[i].className = ''; // "x-date-active";
13222 setCellClass(this, cells[i]);
13226 for(; i < 42; i++) {
13227 textEls[i].innerHTML = (++extraDays);
13228 d.setDate(d.getDate()+1);
13230 cells[i].className = "fc-future fc-other-month";
13231 setCellClass(this, cells[i]);
13234 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13236 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13238 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13239 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13241 if(totalRows != 6){
13242 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13243 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13246 this.fireEvent('monthchange', this, date);
13250 if(!this.internalRender){
13251 var main = this.el.dom.firstChild;
13252 var w = main.offsetWidth;
13253 this.el.setWidth(w + this.el.getBorderWidth("lr"));
13254 Roo.fly(main).setWidth(w);
13255 this.internalRender = true;
13256 // opera does not respect the auto grow header center column
13257 // then, after it gets a width opera refuses to recalculate
13258 // without a second pass
13259 if(Roo.isOpera && !this.secondPass){
13260 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13261 this.secondPass = true;
13262 this.update.defer(10, this, [date]);
13269 findCell : function(dt) {
13270 dt = dt.clearTime().getTime();
13272 this.cells.each(function(c){
13273 //Roo.log("check " +c.dateValue + '?=' + dt);
13274 if(c.dateValue == dt){
13284 findCells : function(ev) {
13285 var s = ev.start.clone().clearTime().getTime();
13287 var e= ev.end.clone().clearTime().getTime();
13290 this.cells.each(function(c){
13291 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13293 if(c.dateValue > e){
13296 if(c.dateValue < s){
13305 // findBestRow: function(cells)
13309 // for (var i =0 ; i < cells.length;i++) {
13310 // ret = Math.max(cells[i].rows || 0,ret);
13317 addItem : function(ev)
13319 // look for vertical location slot in
13320 var cells = this.findCells(ev);
13322 // ev.row = this.findBestRow(cells);
13324 // work out the location.
13328 for(var i =0; i < cells.length; i++) {
13330 cells[i].row = cells[0].row;
13333 cells[i].row = cells[i].row + 1;
13343 if (crow.start.getY() == cells[i].getY()) {
13345 crow.end = cells[i];
13362 cells[0].events.push(ev);
13364 this.calevents.push(ev);
13367 clearEvents: function() {
13369 if(!this.calevents){
13373 Roo.each(this.cells.elements, function(c){
13379 Roo.each(this.calevents, function(e) {
13380 Roo.each(e.els, function(el) {
13381 el.un('mouseenter' ,this.onEventEnter, this);
13382 el.un('mouseleave' ,this.onEventLeave, this);
13387 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13393 renderEvents: function()
13397 this.cells.each(function(c) {
13406 if(c.row != c.events.length){
13407 r = 4 - (4 - (c.row - c.events.length));
13410 c.events = ev.slice(0, r);
13411 c.more = ev.slice(r);
13413 if(c.more.length && c.more.length == 1){
13414 c.events.push(c.more.pop());
13417 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13421 this.cells.each(function(c) {
13423 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13426 for (var e = 0; e < c.events.length; e++){
13427 var ev = c.events[e];
13428 var rows = ev.rows;
13430 for(var i = 0; i < rows.length; i++) {
13432 // how many rows should it span..
13435 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13436 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13438 unselectable : "on",
13441 cls: 'fc-event-inner',
13445 // cls: 'fc-event-time',
13446 // html : cells.length > 1 ? '' : ev.time
13450 cls: 'fc-event-title',
13451 html : String.format('{0}', ev.title)
13458 cls: 'ui-resizable-handle ui-resizable-e',
13459 html : '  '
13466 cfg.cls += ' fc-event-start';
13468 if ((i+1) == rows.length) {
13469 cfg.cls += ' fc-event-end';
13472 var ctr = _this.el.select('.fc-event-container',true).first();
13473 var cg = ctr.createChild(cfg);
13475 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13476 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13478 var r = (c.more.length) ? 1 : 0;
13479 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
13480 cg.setWidth(ebox.right - sbox.x -2);
13482 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13483 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13484 cg.on('click', _this.onEventClick, _this, ev);
13495 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13496 style : 'position: absolute',
13497 unselectable : "on",
13500 cls: 'fc-event-inner',
13504 cls: 'fc-event-title',
13512 cls: 'ui-resizable-handle ui-resizable-e',
13513 html : '  '
13519 var ctr = _this.el.select('.fc-event-container',true).first();
13520 var cg = ctr.createChild(cfg);
13522 var sbox = c.select('.fc-day-content',true).first().getBox();
13523 var ebox = c.select('.fc-day-content',true).first().getBox();
13525 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
13526 cg.setWidth(ebox.right - sbox.x -2);
13528 cg.on('click', _this.onMoreEventClick, _this, c.more);
13538 onEventEnter: function (e, el,event,d) {
13539 this.fireEvent('evententer', this, el, event);
13542 onEventLeave: function (e, el,event,d) {
13543 this.fireEvent('eventleave', this, el, event);
13546 onEventClick: function (e, el,event,d) {
13547 this.fireEvent('eventclick', this, el, event);
13550 onMonthChange: function () {
13554 onMoreEventClick: function(e, el, more)
13558 this.calpopover.placement = 'right';
13559 this.calpopover.setTitle('More');
13561 this.calpopover.setContent('');
13563 var ctr = this.calpopover.el.select('.popover-content', true).first();
13565 Roo.each(more, function(m){
13567 cls : 'fc-event-hori fc-event-draggable',
13570 var cg = ctr.createChild(cfg);
13572 cg.on('click', _this.onEventClick, _this, m);
13575 this.calpopover.show(el);
13580 onLoad: function ()
13582 this.calevents = [];
13585 if(this.store.getCount() > 0){
13586 this.store.data.each(function(d){
13589 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13590 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13591 time : d.data.start_time,
13592 title : d.data.title,
13593 description : d.data.description,
13594 venue : d.data.venue
13599 this.renderEvents();
13601 if(this.calevents.length && this.loadMask){
13602 this.maskEl.hide();
13606 onBeforeLoad: function()
13608 this.clearEvents();
13610 this.maskEl.show();
13624 * @class Roo.bootstrap.Popover
13625 * @extends Roo.bootstrap.Component
13626 * Bootstrap Popover class
13627 * @cfg {String} html contents of the popover (or false to use children..)
13628 * @cfg {String} title of popover (or false to hide)
13629 * @cfg {String} placement how it is placed
13630 * @cfg {String} trigger click || hover (or false to trigger manually)
13631 * @cfg {String} over what (parent or false to trigger manually.)
13634 * Create a new Popover
13635 * @param {Object} config The config object
13638 Roo.bootstrap.Popover = function(config){
13639 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13642 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
13644 title: 'Fill in a title',
13647 placement : 'right',
13648 trigger : 'hover', // hover
13652 can_build_overlaid : false,
13654 getChildContainer : function()
13656 return this.el.select('.popover-content',true).first();
13659 getAutoCreate : function(){
13660 Roo.log('make popover?');
13662 cls : 'popover roo-dynamic',
13663 style: 'display:block',
13669 cls : 'popover-inner',
13673 cls: 'popover-title',
13677 cls : 'popover-content',
13688 setTitle: function(str)
13690 this.el.select('.popover-title',true).first().dom.innerHTML = str;
13692 setContent: function(str)
13694 this.el.select('.popover-content',true).first().dom.innerHTML = str;
13696 // as it get's added to the bottom of the page.
13697 onRender : function(ct, position)
13699 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13701 var cfg = Roo.apply({}, this.getAutoCreate());
13705 cfg.cls += ' ' + this.cls;
13708 cfg.style = this.style;
13710 Roo.log("adding to ")
13711 this.el = Roo.get(document.body).createChild(cfg, position);
13717 initEvents : function()
13719 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13720 this.el.enableDisplayMode('block');
13722 if (this.over === false) {
13725 if (this.triggers === false) {
13728 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13729 var triggers = this.trigger ? this.trigger.split(' ') : [];
13730 Roo.each(triggers, function(trigger) {
13732 if (trigger == 'click') {
13733 on_el.on('click', this.toggle, this);
13734 } else if (trigger != 'manual') {
13735 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
13736 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13738 on_el.on(eventIn ,this.enter, this);
13739 on_el.on(eventOut, this.leave, this);
13750 toggle : function () {
13751 this.hoverState == 'in' ? this.leave() : this.enter();
13754 enter : function () {
13757 clearTimeout(this.timeout);
13759 this.hoverState = 'in'
13761 if (!this.delay || !this.delay.show) {
13766 this.timeout = setTimeout(function () {
13767 if (_t.hoverState == 'in') {
13770 }, this.delay.show)
13772 leave : function() {
13773 clearTimeout(this.timeout);
13775 this.hoverState = 'out'
13777 if (!this.delay || !this.delay.hide) {
13782 this.timeout = setTimeout(function () {
13783 if (_t.hoverState == 'out') {
13786 }, this.delay.hide)
13789 show : function (on_el)
13792 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13795 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13796 if (this.html !== false) {
13797 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13799 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13800 if (!this.title.length) {
13801 this.el.select('.popover-title',true).hide();
13804 var placement = typeof this.placement == 'function' ?
13805 this.placement.call(this, this.el, on_el) :
13808 var autoToken = /\s?auto?\s?/i;
13809 var autoPlace = autoToken.test(placement);
13811 placement = placement.replace(autoToken, '') || 'top';
13815 //this.el.setXY([0,0]);
13817 this.el.dom.style.display='block';
13818 this.el.addClass(placement);
13820 //this.el.appendTo(on_el);
13822 var p = this.getPosition();
13823 var box = this.el.getBox();
13828 var align = Roo.bootstrap.Popover.alignment[placement]
13829 this.el.alignTo(on_el, align[0],align[1]);
13830 //var arrow = this.el.select('.arrow',true).first();
13831 //arrow.set(align[2],
13833 this.el.addClass('in');
13834 this.hoverState = null;
13836 if (this.el.hasClass('fade')) {
13843 this.el.setXY([0,0]);
13844 this.el.removeClass('in');
13851 Roo.bootstrap.Popover.alignment = {
13852 'left' : ['r-l', [-10,0], 'right'],
13853 'right' : ['l-r', [10,0], 'left'],
13854 'bottom' : ['t-b', [0,10], 'top'],
13855 'top' : [ 'b-t', [0,-10], 'bottom']
13866 * @class Roo.bootstrap.Progress
13867 * @extends Roo.bootstrap.Component
13868 * Bootstrap Progress class
13869 * @cfg {Boolean} striped striped of the progress bar
13870 * @cfg {Boolean} active animated of the progress bar
13874 * Create a new Progress
13875 * @param {Object} config The config object
13878 Roo.bootstrap.Progress = function(config){
13879 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13882 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
13887 getAutoCreate : function(){
13895 cfg.cls += ' progress-striped';
13899 cfg.cls += ' active';
13918 * @class Roo.bootstrap.ProgressBar
13919 * @extends Roo.bootstrap.Component
13920 * Bootstrap ProgressBar class
13921 * @cfg {Number} aria_valuenow aria-value now
13922 * @cfg {Number} aria_valuemin aria-value min
13923 * @cfg {Number} aria_valuemax aria-value max
13924 * @cfg {String} label label for the progress bar
13925 * @cfg {String} panel (success | info | warning | danger )
13926 * @cfg {String} role role of the progress bar
13927 * @cfg {String} sr_only text
13931 * Create a new ProgressBar
13932 * @param {Object} config The config object
13935 Roo.bootstrap.ProgressBar = function(config){
13936 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13939 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
13943 aria_valuemax : 100,
13949 getAutoCreate : function()
13954 cls: 'progress-bar',
13955 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13967 cfg.role = this.role;
13970 if(this.aria_valuenow){
13971 cfg['aria-valuenow'] = this.aria_valuenow;
13974 if(this.aria_valuemin){
13975 cfg['aria-valuemin'] = this.aria_valuemin;
13978 if(this.aria_valuemax){
13979 cfg['aria-valuemax'] = this.aria_valuemax;
13982 if(this.label && !this.sr_only){
13983 cfg.html = this.label;
13987 cfg.cls += ' progress-bar-' + this.panel;
13993 update : function(aria_valuenow)
13995 this.aria_valuenow = aria_valuenow;
13997 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14012 * @class Roo.bootstrap.TabGroup
14013 * @extends Roo.bootstrap.Column
14014 * Bootstrap Column class
14015 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14016 * @cfg {Boolean} carousel true to make the group behave like a carousel
14019 * Create a new TabGroup
14020 * @param {Object} config The config object
14023 Roo.bootstrap.TabGroup = function(config){
14024 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14026 this.navId = Roo.id();
14029 Roo.bootstrap.TabGroup.register(this);
14033 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
14036 transition : false,
14038 getAutoCreate : function()
14040 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14042 cfg.cls += ' tab-content';
14044 if (this.carousel) {
14045 cfg.cls += ' carousel slide';
14047 cls : 'carousel-inner'
14054 getChildContainer : function()
14056 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14060 * register a Navigation item
14061 * @param {Roo.bootstrap.NavItem} the navitem to add
14063 register : function(item)
14065 this.tabs.push( item);
14066 item.navId = this.navId; // not really needed..
14070 getActivePanel : function()
14073 Roo.each(this.tabs, function(t) {
14083 getPanelByName : function(n)
14086 Roo.each(this.tabs, function(t) {
14087 if (t.tabId == n) {
14095 indexOfPanel : function(p)
14098 Roo.each(this.tabs, function(t,i) {
14099 if (t.tabId == p.tabId) {
14108 * show a specific panel
14109 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14110 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14112 showPanel : function (pan)
14115 if (typeof(pan) == 'number') {
14116 pan = this.tabs[pan];
14118 if (typeof(pan) == 'string') {
14119 pan = this.getPanelByName(pan);
14121 if (pan.tabId == this.getActivePanel().tabId) {
14124 var cur = this.getActivePanel();
14126 if (false === cur.fireEvent('beforedeactivate')) {
14130 if (this.carousel) {
14131 this.transition = true;
14132 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
14133 var lr = dir == 'next' ? 'left' : 'right';
14134 pan.el.addClass(dir); // or prev
14135 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14136 cur.el.addClass(lr); // or right
14137 pan.el.addClass(lr);
14140 cur.el.on('transitionend', function() {
14141 Roo.log("trans end?");
14143 pan.el.removeClass([lr,dir]);
14144 pan.setActive(true);
14146 cur.el.removeClass([lr]);
14147 cur.setActive(false);
14149 _this.transition = false;
14151 }, this, { single: true } );
14155 cur.setActive(false);
14156 pan.setActive(true);
14160 showPanelNext : function()
14162 var i = this.indexOfPanel(this.getActivePanel());
14163 if (i > this.tabs.length) {
14166 this.showPanel(this.tabs[i+1]);
14168 showPanelPrev : function()
14170 var i = this.indexOfPanel(this.getActivePanel());
14174 this.showPanel(this.tabs[i-1]);
14185 Roo.apply(Roo.bootstrap.TabGroup, {
14189 * register a Navigation Group
14190 * @param {Roo.bootstrap.NavGroup} the navgroup to add
14192 register : function(navgrp)
14194 this.groups[navgrp.navId] = navgrp;
14198 * fetch a Navigation Group based on the navigation ID
14199 * if one does not exist , it will get created.
14200 * @param {string} the navgroup to add
14201 * @returns {Roo.bootstrap.NavGroup} the navgroup
14203 get: function(navId) {
14204 if (typeof(this.groups[navId]) == 'undefined') {
14205 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14207 return this.groups[navId] ;
14222 * @class Roo.bootstrap.TabPanel
14223 * @extends Roo.bootstrap.Component
14224 * Bootstrap TabPanel class
14225 * @cfg {Boolean} active panel active
14226 * @cfg {String} html panel content
14227 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14228 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14232 * Create a new TabPanel
14233 * @param {Object} config The config object
14236 Roo.bootstrap.TabPanel = function(config){
14237 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14241 * Fires when the active status changes
14242 * @param {Roo.bootstrap.TabPanel} this
14243 * @param {Boolean} state the new state
14248 * @event beforedeactivate
14249 * Fires before a tab is de-activated - can be used to do validation on a form.
14250 * @param {Roo.bootstrap.TabPanel} this
14251 * @return {Boolean} false if there is an error
14254 'beforedeactivate': true
14257 this.tabId = this.tabId || Roo.id();
14261 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
14268 getAutoCreate : function(){
14271 // item is needed for carousel - not sure if it has any effect otherwise
14272 cls: 'tab-pane item',
14273 html: this.html || ''
14277 cfg.cls += ' active';
14281 cfg.tabId = this.tabId;
14288 initEvents: function()
14290 Roo.log('-------- init events on tab panel ---------');
14292 var p = this.parent();
14293 this.navId = this.navId || p.navId;
14295 if (typeof(this.navId) != 'undefined') {
14296 // not really needed.. but just in case.. parent should be a NavGroup.
14297 var tg = Roo.bootstrap.TabGroup.get(this.navId);
14298 Roo.log(['register', tg, this]);
14304 onRender : function(ct, position)
14306 // Roo.log("Call onRender: " + this.xtype);
14308 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14316 setActive: function(state)
14318 Roo.log("panel - set active " + this.tabId + "=" + state);
14320 this.active = state;
14322 this.el.removeClass('active');
14324 } else if (!this.el.hasClass('active')) {
14325 this.el.addClass('active');
14327 this.fireEvent('changed', this, state);
14344 * @class Roo.bootstrap.DateField
14345 * @extends Roo.bootstrap.Input
14346 * Bootstrap DateField class
14347 * @cfg {Number} weekStart default 0
14348 * @cfg {Number} weekStart default 0
14349 * @cfg {Number} viewMode default empty, (months|years)
14350 * @cfg {Number} minViewMode default empty, (months|years)
14351 * @cfg {Number} startDate default -Infinity
14352 * @cfg {Number} endDate default Infinity
14353 * @cfg {Boolean} todayHighlight default false
14354 * @cfg {Boolean} todayBtn default false
14355 * @cfg {Boolean} calendarWeeks default false
14356 * @cfg {Object} daysOfWeekDisabled default empty
14358 * @cfg {Boolean} keyboardNavigation default true
14359 * @cfg {String} language default en
14362 * Create a new DateField
14363 * @param {Object} config The config object
14366 Roo.bootstrap.DateField = function(config){
14367 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14371 * Fires when this field show.
14372 * @param {Roo.bootstrap.DateField} this
14373 * @param {Mixed} date The date value
14378 * Fires when this field hide.
14379 * @param {Roo.bootstrap.DateField} this
14380 * @param {Mixed} date The date value
14385 * Fires when select a date.
14386 * @param {Roo.bootstrap.DateField} this
14387 * @param {Mixed} date The date value
14393 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
14396 * @cfg {String} format
14397 * The default date format string which can be overriden for localization support. The format must be
14398 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14402 * @cfg {String} altFormats
14403 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14404 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14406 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14414 todayHighlight : false,
14420 keyboardNavigation: true,
14422 calendarWeeks: false,
14424 startDate: -Infinity,
14428 daysOfWeekDisabled: [],
14432 UTCDate: function()
14434 return new Date(Date.UTC.apply(Date, arguments));
14437 UTCToday: function()
14439 var today = new Date();
14440 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14443 getDate: function() {
14444 var d = this.getUTCDate();
14445 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14448 getUTCDate: function() {
14452 setDate: function(d) {
14453 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14456 setUTCDate: function(d) {
14458 this.setValue(this.formatDate(this.date));
14461 onRender: function(ct, position)
14464 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14466 this.language = this.language || 'en';
14467 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14468 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14470 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14471 this.format = this.format || 'm/d/y';
14472 this.isInline = false;
14473 this.isInput = true;
14474 this.component = this.el.select('.add-on', true).first() || false;
14475 this.component = (this.component && this.component.length === 0) ? false : this.component;
14476 this.hasInput = this.component && this.inputEL().length;
14478 if (typeof(this.minViewMode === 'string')) {
14479 switch (this.minViewMode) {
14481 this.minViewMode = 1;
14484 this.minViewMode = 2;
14487 this.minViewMode = 0;
14492 if (typeof(this.viewMode === 'string')) {
14493 switch (this.viewMode) {
14506 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14508 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14510 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14512 this.picker().on('mousedown', this.onMousedown, this);
14513 this.picker().on('click', this.onClick, this);
14515 this.picker().addClass('datepicker-dropdown');
14517 this.startViewMode = this.viewMode;
14520 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14521 if(!this.calendarWeeks){
14526 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14527 v.attr('colspan', function(i, val){
14528 return parseInt(val) + 1;
14533 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14535 this.setStartDate(this.startDate);
14536 this.setEndDate(this.endDate);
14538 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14545 if(this.isInline) {
14550 picker : function()
14552 return this.pickerEl;
14553 // return this.el.select('.datepicker', true).first();
14556 fillDow: function()
14558 var dowCnt = this.weekStart;
14567 if(this.calendarWeeks){
14575 while (dowCnt < this.weekStart + 7) {
14579 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14583 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14586 fillMonths: function()
14589 var months = this.picker().select('>.datepicker-months td', true).first();
14591 months.dom.innerHTML = '';
14597 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14600 months.createChild(month);
14607 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;
14609 if (this.date < this.startDate) {
14610 this.viewDate = new Date(this.startDate);
14611 } else if (this.date > this.endDate) {
14612 this.viewDate = new Date(this.endDate);
14614 this.viewDate = new Date(this.date);
14622 var d = new Date(this.viewDate),
14623 year = d.getUTCFullYear(),
14624 month = d.getUTCMonth(),
14625 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14626 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14627 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14628 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14629 currentDate = this.date && this.date.valueOf(),
14630 today = this.UTCToday();
14632 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14634 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14636 // this.picker.select('>tfoot th.today').
14637 // .text(dates[this.language].today)
14638 // .toggle(this.todayBtn !== false);
14640 this.updateNavArrows();
14643 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14645 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14647 prevMonth.setUTCDate(day);
14649 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14651 var nextMonth = new Date(prevMonth);
14653 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14655 nextMonth = nextMonth.valueOf();
14657 var fillMonths = false;
14659 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14661 while(prevMonth.valueOf() < nextMonth) {
14664 if (prevMonth.getUTCDay() === this.weekStart) {
14666 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14674 if(this.calendarWeeks){
14675 // ISO 8601: First week contains first thursday.
14676 // ISO also states week starts on Monday, but we can be more abstract here.
14678 // Start of current week: based on weekstart/current date
14679 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14680 // Thursday of this week
14681 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14682 // First Thursday of year, year from thursday
14683 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14684 // Calendar week: ms between thursdays, div ms per day, div 7 days
14685 calWeek = (th - yth) / 864e5 / 7 + 1;
14687 fillMonths.cn.push({
14695 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14697 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14700 if (this.todayHighlight &&
14701 prevMonth.getUTCFullYear() == today.getFullYear() &&
14702 prevMonth.getUTCMonth() == today.getMonth() &&
14703 prevMonth.getUTCDate() == today.getDate()) {
14704 clsName += ' today';
14707 if (currentDate && prevMonth.valueOf() === currentDate) {
14708 clsName += ' active';
14711 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14712 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14713 clsName += ' disabled';
14716 fillMonths.cn.push({
14718 cls: 'day ' + clsName,
14719 html: prevMonth.getDate()
14722 prevMonth.setDate(prevMonth.getDate()+1);
14725 var currentYear = this.date && this.date.getUTCFullYear();
14726 var currentMonth = this.date && this.date.getUTCMonth();
14728 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14730 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14731 v.removeClass('active');
14733 if(currentYear === year && k === currentMonth){
14734 v.addClass('active');
14737 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14738 v.addClass('disabled');
14744 year = parseInt(year/10, 10) * 10;
14746 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14748 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14751 for (var i = -1; i < 11; i++) {
14752 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14754 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14762 showMode: function(dir)
14765 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14767 Roo.each(this.picker().select('>div',true).elements, function(v){
14768 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14771 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14776 if(this.isInline) return;
14778 this.picker().removeClass(['bottom', 'top']);
14780 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14782 * place to the top of element!
14786 this.picker().addClass('top');
14787 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14792 this.picker().addClass('bottom');
14794 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14797 parseDate : function(value)
14799 if(!value || value instanceof Date){
14802 var v = Date.parseDate(value, this.format);
14803 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
14804 v = Date.parseDate(value, 'Y-m-d');
14806 if(!v && this.altFormats){
14807 if(!this.altFormatsArray){
14808 this.altFormatsArray = this.altFormats.split("|");
14810 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14811 v = Date.parseDate(value, this.altFormatsArray[i]);
14817 formatDate : function(date, fmt)
14819 return (!date || !(date instanceof Date)) ?
14820 date : date.dateFormat(fmt || this.format);
14823 onFocus : function()
14825 Roo.bootstrap.DateField.superclass.onFocus.call(this);
14829 onBlur : function()
14831 Roo.bootstrap.DateField.superclass.onBlur.call(this);
14833 var d = this.inputEl().getValue();
14842 this.picker().show();
14846 this.fireEvent('show', this, this.date);
14851 if(this.isInline) return;
14852 this.picker().hide();
14853 this.viewMode = this.startViewMode;
14856 this.fireEvent('hide', this, this.date);
14860 onMousedown: function(e)
14862 e.stopPropagation();
14863 e.preventDefault();
14868 Roo.bootstrap.DateField.superclass.keyup.call(this);
14872 setValue: function(v)
14875 // v can be a string or a date..
14878 var d = new Date(this.parseDate(v) ).clearTime();
14882 if(isNaN(d.getTime())){
14883 this.date = this.viewDate = '';
14884 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14888 v = this.formatDate(d);
14890 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14892 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14896 this.fireEvent('select', this, this.date);
14900 getValue: function()
14902 return this.formatDate(this.date);
14905 fireKey: function(e)
14907 if (!this.picker().isVisible()){
14908 if (e.keyCode == 27) // allow escape to hide and re-show picker
14913 var dateChanged = false,
14915 newDate, newViewDate;
14920 e.preventDefault();
14924 if (!this.keyboardNavigation) break;
14925 dir = e.keyCode == 37 ? -1 : 1;
14928 newDate = this.moveYear(this.date, dir);
14929 newViewDate = this.moveYear(this.viewDate, dir);
14930 } else if (e.shiftKey){
14931 newDate = this.moveMonth(this.date, dir);
14932 newViewDate = this.moveMonth(this.viewDate, dir);
14934 newDate = new Date(this.date);
14935 newDate.setUTCDate(this.date.getUTCDate() + dir);
14936 newViewDate = new Date(this.viewDate);
14937 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14939 if (this.dateWithinRange(newDate)){
14940 this.date = newDate;
14941 this.viewDate = newViewDate;
14942 this.setValue(this.formatDate(this.date));
14944 e.preventDefault();
14945 dateChanged = true;
14950 if (!this.keyboardNavigation) break;
14951 dir = e.keyCode == 38 ? -1 : 1;
14953 newDate = this.moveYear(this.date, dir);
14954 newViewDate = this.moveYear(this.viewDate, dir);
14955 } else if (e.shiftKey){
14956 newDate = this.moveMonth(this.date, dir);
14957 newViewDate = this.moveMonth(this.viewDate, dir);
14959 newDate = new Date(this.date);
14960 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14961 newViewDate = new Date(this.viewDate);
14962 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14964 if (this.dateWithinRange(newDate)){
14965 this.date = newDate;
14966 this.viewDate = newViewDate;
14967 this.setValue(this.formatDate(this.date));
14969 e.preventDefault();
14970 dateChanged = true;
14974 this.setValue(this.formatDate(this.date));
14976 e.preventDefault();
14979 this.setValue(this.formatDate(this.date));
14993 onClick: function(e)
14995 e.stopPropagation();
14996 e.preventDefault();
14998 var target = e.getTarget();
15000 if(target.nodeName.toLowerCase() === 'i'){
15001 target = Roo.get(target).dom.parentNode;
15004 var nodeName = target.nodeName;
15005 var className = target.className;
15006 var html = target.innerHTML;
15007 //Roo.log(nodeName);
15009 switch(nodeName.toLowerCase()) {
15011 switch(className) {
15017 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15018 switch(this.viewMode){
15020 this.viewDate = this.moveMonth(this.viewDate, dir);
15024 this.viewDate = this.moveYear(this.viewDate, dir);
15030 var date = new Date();
15031 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15033 this.setValue(this.formatDate(this.date));
15040 if (className.indexOf('disabled') < 0) {
15041 this.viewDate.setUTCDate(1);
15042 if (className.indexOf('month') > -1) {
15043 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15045 var year = parseInt(html, 10) || 0;
15046 this.viewDate.setUTCFullYear(year);
15055 //Roo.log(className);
15056 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15057 var day = parseInt(html, 10) || 1;
15058 var year = this.viewDate.getUTCFullYear(),
15059 month = this.viewDate.getUTCMonth();
15061 if (className.indexOf('old') > -1) {
15068 } else if (className.indexOf('new') > -1) {
15076 //Roo.log([year,month,day]);
15077 this.date = this.UTCDate(year, month, day,0,0,0,0);
15078 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15080 //Roo.log(this.formatDate(this.date));
15081 this.setValue(this.formatDate(this.date));
15088 setStartDate: function(startDate)
15090 this.startDate = startDate || -Infinity;
15091 if (this.startDate !== -Infinity) {
15092 this.startDate = this.parseDate(this.startDate);
15095 this.updateNavArrows();
15098 setEndDate: function(endDate)
15100 this.endDate = endDate || Infinity;
15101 if (this.endDate !== Infinity) {
15102 this.endDate = this.parseDate(this.endDate);
15105 this.updateNavArrows();
15108 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15110 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15111 if (typeof(this.daysOfWeekDisabled) !== 'object') {
15112 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15114 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15115 return parseInt(d, 10);
15118 this.updateNavArrows();
15121 updateNavArrows: function()
15123 var d = new Date(this.viewDate),
15124 year = d.getUTCFullYear(),
15125 month = d.getUTCMonth();
15127 Roo.each(this.picker().select('.prev', true).elements, function(v){
15129 switch (this.viewMode) {
15132 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15138 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15145 Roo.each(this.picker().select('.next', true).elements, function(v){
15147 switch (this.viewMode) {
15150 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15156 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15164 moveMonth: function(date, dir)
15166 if (!dir) return date;
15167 var new_date = new Date(date.valueOf()),
15168 day = new_date.getUTCDate(),
15169 month = new_date.getUTCMonth(),
15170 mag = Math.abs(dir),
15172 dir = dir > 0 ? 1 : -1;
15175 // If going back one month, make sure month is not current month
15176 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15178 return new_date.getUTCMonth() == month;
15180 // If going forward one month, make sure month is as expected
15181 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15183 return new_date.getUTCMonth() != new_month;
15185 new_month = month + dir;
15186 new_date.setUTCMonth(new_month);
15187 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15188 if (new_month < 0 || new_month > 11)
15189 new_month = (new_month + 12) % 12;
15191 // For magnitudes >1, move one month at a time...
15192 for (var i=0; i<mag; i++)
15193 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15194 new_date = this.moveMonth(new_date, dir);
15195 // ...then reset the day, keeping it in the new month
15196 new_month = new_date.getUTCMonth();
15197 new_date.setUTCDate(day);
15199 return new_month != new_date.getUTCMonth();
15202 // Common date-resetting loop -- if date is beyond end of month, make it
15205 new_date.setUTCDate(--day);
15206 new_date.setUTCMonth(new_month);
15211 moveYear: function(date, dir)
15213 return this.moveMonth(date, dir*12);
15216 dateWithinRange: function(date)
15218 return date >= this.startDate && date <= this.endDate;
15224 this.picker().remove();
15229 Roo.apply(Roo.bootstrap.DateField, {
15240 html: '<i class="fa fa-arrow-left"/>'
15250 html: '<i class="fa fa-arrow-right"/>'
15292 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15293 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15294 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15295 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15296 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15309 navFnc: 'FullYear',
15314 navFnc: 'FullYear',
15319 Roo.apply(Roo.bootstrap.DateField, {
15323 cls: 'datepicker dropdown-menu',
15327 cls: 'datepicker-days',
15331 cls: 'table-condensed',
15333 Roo.bootstrap.DateField.head,
15337 Roo.bootstrap.DateField.footer
15344 cls: 'datepicker-months',
15348 cls: 'table-condensed',
15350 Roo.bootstrap.DateField.head,
15351 Roo.bootstrap.DateField.content,
15352 Roo.bootstrap.DateField.footer
15359 cls: 'datepicker-years',
15363 cls: 'table-condensed',
15365 Roo.bootstrap.DateField.head,
15366 Roo.bootstrap.DateField.content,
15367 Roo.bootstrap.DateField.footer
15386 * @class Roo.bootstrap.TimeField
15387 * @extends Roo.bootstrap.Input
15388 * Bootstrap DateField class
15392 * Create a new TimeField
15393 * @param {Object} config The config object
15396 Roo.bootstrap.TimeField = function(config){
15397 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15401 * Fires when this field show.
15402 * @param {Roo.bootstrap.DateField} this
15403 * @param {Mixed} date The date value
15408 * Fires when this field hide.
15409 * @param {Roo.bootstrap.DateField} this
15410 * @param {Mixed} date The date value
15415 * Fires when select a date.
15416 * @param {Roo.bootstrap.DateField} this
15417 * @param {Mixed} date The date value
15423 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
15426 * @cfg {String} format
15427 * The default time format string which can be overriden for localization support. The format must be
15428 * valid according to {@link Date#parseDate} (defaults to 'H:i').
15432 onRender: function(ct, position)
15435 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15437 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15439 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15441 this.pop = this.picker().select('>.datepicker-time',true).first();
15442 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
15444 this.picker().on('mousedown', this.onMousedown, this);
15445 this.picker().on('click', this.onClick, this);
15447 this.picker().addClass('datepicker-dropdown');
15452 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15453 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15454 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15455 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15456 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15457 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15461 fireKey: function(e){
15462 if (!this.picker().isVisible()){
15463 if (e.keyCode == 27) // allow escape to hide and re-show picker
15468 e.preventDefault();
15476 this.onTogglePeriod();
15479 this.onIncrementMinutes();
15482 this.onDecrementMinutes();
15491 onClick: function(e) {
15492 e.stopPropagation();
15493 e.preventDefault();
15496 picker : function()
15498 return this.el.select('.datepicker', true).first();
15501 fillTime: function()
15503 var time = this.pop.select('tbody', true).first();
15505 time.dom.innerHTML = '';
15520 cls: 'hours-up glyphicon glyphicon-chevron-up'
15540 cls: 'minutes-up glyphicon glyphicon-chevron-up'
15561 cls: 'timepicker-hour',
15576 cls: 'timepicker-minute',
15591 cls: 'btn btn-primary period',
15613 cls: 'hours-down glyphicon glyphicon-chevron-down'
15633 cls: 'minutes-down glyphicon glyphicon-chevron-down'
15651 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15658 var hours = this.time.getHours();
15659 var minutes = this.time.getMinutes();
15672 hours = hours - 12;
15676 hours = '0' + hours;
15680 minutes = '0' + minutes;
15683 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15684 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15685 this.pop.select('button', true).first().dom.innerHTML = period;
15691 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15693 var cls = ['bottom'];
15695 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15702 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15707 this.picker().addClass(cls.join('-'));
15711 Roo.each(cls, function(c){
15713 _this.picker().setTop(_this.inputEl().getHeight());
15717 _this.picker().setTop(0 - _this.picker().getHeight());
15722 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15726 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15733 onFocus : function()
15735 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15739 onBlur : function()
15741 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15747 this.picker().show();
15752 this.fireEvent('show', this, this.date);
15757 this.picker().hide();
15760 this.fireEvent('hide', this, this.date);
15763 setTime : function()
15766 this.setValue(this.time.format(this.format));
15768 this.fireEvent('select', this, this.date);
15773 onMousedown: function(e){
15774 e.stopPropagation();
15775 e.preventDefault();
15778 onIncrementHours: function()
15780 Roo.log('onIncrementHours');
15781 this.time = this.time.add(Date.HOUR, 1);
15786 onDecrementHours: function()
15788 Roo.log('onDecrementHours');
15789 this.time = this.time.add(Date.HOUR, -1);
15793 onIncrementMinutes: function()
15795 Roo.log('onIncrementMinutes');
15796 this.time = this.time.add(Date.MINUTE, 1);
15800 onDecrementMinutes: function()
15802 Roo.log('onDecrementMinutes');
15803 this.time = this.time.add(Date.MINUTE, -1);
15807 onTogglePeriod: function()
15809 Roo.log('onTogglePeriod');
15810 this.time = this.time.add(Date.HOUR, 12);
15817 Roo.apply(Roo.bootstrap.TimeField, {
15847 cls: 'btn btn-info ok',
15859 Roo.apply(Roo.bootstrap.TimeField, {
15863 cls: 'datepicker dropdown-menu',
15867 cls: 'datepicker-time',
15871 cls: 'table-condensed',
15873 Roo.bootstrap.TimeField.content,
15874 Roo.bootstrap.TimeField.footer
15893 * @class Roo.bootstrap.CheckBox
15894 * @extends Roo.bootstrap.Input
15895 * Bootstrap CheckBox class
15897 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15898 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15899 * @cfg {String} boxLabel The text that appears beside the checkbox
15900 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15901 * @cfg {Boolean} checked initnal the element
15905 * Create a new CheckBox
15906 * @param {Object} config The config object
15909 Roo.bootstrap.CheckBox = function(config){
15910 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15915 * Fires when the element is checked or unchecked.
15916 * @param {Roo.bootstrap.CheckBox} this This input
15917 * @param {Boolean} checked The new checked value
15923 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
15925 inputType: 'checkbox',
15932 getAutoCreate : function()
15934 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15940 cfg.cls = 'form-group checkbox' //input-group
15948 type : this.inputType,
15949 value : (!this.checked) ? this.valueOff : this.inputValue,
15950 cls : 'roo-checkbox', //'form-box',
15951 placeholder : this.placeholder || ''
15955 if (this.weight) { // Validity check?
15956 cfg.cls += " checkbox-" + this.weight;
15959 if (this.disabled) {
15960 input.disabled=true;
15964 input.checked = this.checked;
15968 input.name = this.name;
15972 input.cls += ' input-' + this.size;
15976 ['xs','sm','md','lg'].map(function(size){
15977 if (settings[size]) {
15978 cfg.cls += ' col-' + size + '-' + settings[size];
15984 var inputblock = input;
15989 if (this.before || this.after) {
15992 cls : 'input-group',
15996 inputblock.cn.push({
15998 cls : 'input-group-addon',
16002 inputblock.cn.push(input);
16004 inputblock.cn.push({
16006 cls : 'input-group-addon',
16013 if (align ==='left' && this.fieldLabel.length) {
16014 Roo.log("left and has label");
16020 cls : 'control-label col-md-' + this.labelWidth,
16021 html : this.fieldLabel
16025 cls : "col-md-" + (12 - this.labelWidth),
16032 } else if ( this.fieldLabel.length) {
16037 tag: this.boxLabel ? 'span' : 'label',
16039 cls: 'control-label box-input-label',
16040 //cls : 'input-group-addon',
16041 html : this.fieldLabel
16051 Roo.log(" no label && no align");
16052 cfg.cn = [ inputblock ] ;
16061 html: this.boxLabel
16073 * return the real input element.
16075 inputEl: function ()
16077 return this.el.select('input.roo-checkbox',true).first();
16082 return this.el.select('label.control-label',true).first();
16085 initEvents : function()
16087 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16089 this.inputEl().on('click', this.onClick, this);
16093 onClick : function()
16095 this.setChecked(!this.checked);
16098 setChecked : function(state,suppressEvent)
16100 this.checked = state;
16102 this.inputEl().dom.checked = state;
16104 if(suppressEvent !== true){
16105 this.fireEvent('check', this, state);
16108 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16112 setValue : function(v,suppressEvent)
16114 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16128 * @class Roo.bootstrap.Radio
16129 * @extends Roo.bootstrap.CheckBox
16130 * Bootstrap Radio class
16133 * Create a new Radio
16134 * @param {Object} config The config object
16137 Roo.bootstrap.Radio = function(config){
16138 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16142 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
16144 inputType: 'radio',
16148 getAutoCreate : function()
16150 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16156 cfg.cls = 'form-group radio' //input-group
16161 type : this.inputType,
16162 value : (!this.checked) ? this.valueOff : this.inputValue,
16164 placeholder : this.placeholder || ''
16167 if (this.weight) { // Validity check?
16168 cfg.cls += " radio-" + this.weight;
16170 if (this.disabled) {
16171 input.disabled=true;
16175 input.checked = this.checked;
16179 input.name = this.name;
16183 input.cls += ' input-' + this.size;
16187 ['xs','sm','md','lg'].map(function(size){
16188 if (settings[size]) {
16189 cfg.cls += ' col-' + size + '-' + settings[size];
16193 var inputblock = input;
16195 if (this.before || this.after) {
16198 cls : 'input-group',
16202 inputblock.cn.push({
16204 cls : 'input-group-addon',
16208 inputblock.cn.push(input);
16210 inputblock.cn.push({
16212 cls : 'input-group-addon',
16219 if (align ==='left' && this.fieldLabel.length) {
16220 Roo.log("left and has label");
16226 cls : 'control-label col-md-' + this.labelWidth,
16227 html : this.fieldLabel
16231 cls : "col-md-" + (12 - this.labelWidth),
16238 } else if ( this.fieldLabel.length) {
16245 cls: 'control-label box-input-label',
16246 //cls : 'input-group-addon',
16247 html : this.fieldLabel
16257 Roo.log(" no label && no align");
16272 html: this.boxLabel
16279 inputEl: function ()
16281 return this.el.select('input.roo-radio',true).first();
16283 onClick : function()
16285 this.setChecked(true);
16288 setChecked : function(state,suppressEvent)
16291 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16292 v.dom.checked = false;
16296 this.checked = state;
16297 this.inputEl().dom.checked = state;
16299 if(suppressEvent !== true){
16300 this.fireEvent('check', this, state);
16303 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16307 getGroupValue : function()
16310 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16311 if(v.dom.checked == true){
16312 value = v.dom.value;
16320 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
16321 * @return {Mixed} value The field value
16323 getValue : function(){
16324 return this.getGroupValue();
16330 //<script type="text/javascript">
16333 * Based Ext JS Library 1.1.1
16334 * Copyright(c) 2006-2007, Ext JS, LLC.
16340 * @class Roo.HtmlEditorCore
16341 * @extends Roo.Component
16342 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16344 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16347 Roo.HtmlEditorCore = function(config){
16350 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16355 * @event initialize
16356 * Fires when the editor is fully initialized (including the iframe)
16357 * @param {Roo.HtmlEditorCore} this
16362 * Fires when the editor is first receives the focus. Any insertion must wait
16363 * until after this event.
16364 * @param {Roo.HtmlEditorCore} this
16368 * @event beforesync
16369 * Fires before the textarea is updated with content from the editor iframe. Return false
16370 * to cancel the sync.
16371 * @param {Roo.HtmlEditorCore} this
16372 * @param {String} html
16376 * @event beforepush
16377 * Fires before the iframe editor is updated with content from the textarea. Return false
16378 * to cancel the push.
16379 * @param {Roo.HtmlEditorCore} this
16380 * @param {String} html
16385 * Fires when the textarea is updated with content from the editor iframe.
16386 * @param {Roo.HtmlEditorCore} this
16387 * @param {String} html
16392 * Fires when the iframe editor is updated with content from the textarea.
16393 * @param {Roo.HtmlEditorCore} this
16394 * @param {String} html
16399 * @event editorevent
16400 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16401 * @param {Roo.HtmlEditorCore} this
16406 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16408 // defaults : white / black...
16409 this.applyBlacklists();
16416 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
16420 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
16426 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
16431 * @cfg {Number} height (in pixels)
16435 * @cfg {Number} width (in pixels)
16440 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16443 stylesheets: false,
16448 // private properties
16449 validationEvent : false,
16451 initialized : false,
16453 sourceEditMode : false,
16454 onFocus : Roo.emptyFn,
16456 hideMode:'offsets',
16460 // blacklist + whitelisted elements..
16467 * Protected method that will not generally be called directly. It
16468 * is called when the editor initializes the iframe with HTML contents. Override this method if you
16469 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16471 getDocMarkup : function(){
16474 Roo.log(this.stylesheets);
16476 // inherit styels from page...??
16477 if (this.stylesheets === false) {
16479 Roo.get(document.head).select('style').each(function(node) {
16480 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16483 Roo.get(document.head).select('link').each(function(node) {
16484 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16487 } else if (!this.stylesheets.length) {
16489 st = '<style type="text/css">' +
16490 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16493 Roo.each(this.stylesheets, function(s) {
16494 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16499 st += '<style type="text/css">' +
16500 'IMG { cursor: pointer } ' +
16504 return '<html><head>' + st +
16505 //<style type="text/css">' +
16506 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16508 ' </head><body class="roo-htmleditor-body"></body></html>';
16512 onRender : function(ct, position)
16515 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16516 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16519 this.el.dom.style.border = '0 none';
16520 this.el.dom.setAttribute('tabIndex', -1);
16521 this.el.addClass('x-hidden hide');
16525 if(Roo.isIE){ // fix IE 1px bogus margin
16526 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16530 this.frameId = Roo.id();
16534 var iframe = this.owner.wrap.createChild({
16536 cls: 'form-control', // bootstrap..
16538 name: this.frameId,
16539 frameBorder : 'no',
16540 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
16545 this.iframe = iframe.dom;
16547 this.assignDocWin();
16549 this.doc.designMode = 'on';
16552 this.doc.write(this.getDocMarkup());
16556 var task = { // must defer to wait for browser to be ready
16558 //console.log("run task?" + this.doc.readyState);
16559 this.assignDocWin();
16560 if(this.doc.body || this.doc.readyState == 'complete'){
16562 this.doc.designMode="on";
16566 Roo.TaskMgr.stop(task);
16567 this.initEditor.defer(10, this);
16574 Roo.TaskMgr.start(task);
16581 onResize : function(w, h)
16583 Roo.log('resize: ' +w + ',' + h );
16584 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16588 if(typeof w == 'number'){
16590 this.iframe.style.width = w + 'px';
16592 if(typeof h == 'number'){
16594 this.iframe.style.height = h + 'px';
16596 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16603 * Toggles the editor between standard and source edit mode.
16604 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16606 toggleSourceEdit : function(sourceEditMode){
16608 this.sourceEditMode = sourceEditMode === true;
16610 if(this.sourceEditMode){
16612 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
16615 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16616 //this.iframe.className = '';
16619 //this.setSize(this.owner.wrap.getSize());
16620 //this.fireEvent('editmodechange', this, this.sourceEditMode);
16627 * Protected method that will not generally be called directly. If you need/want
16628 * custom HTML cleanup, this is the method you should override.
16629 * @param {String} html The HTML to be cleaned
16630 * return {String} The cleaned HTML
16632 cleanHtml : function(html){
16633 html = String(html);
16634 if(html.length > 5){
16635 if(Roo.isSafari){ // strip safari nonsense
16636 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16639 if(html == ' '){
16646 * HTML Editor -> Textarea
16647 * Protected method that will not generally be called directly. Syncs the contents
16648 * of the editor iframe with the textarea.
16650 syncValue : function(){
16651 if(this.initialized){
16652 var bd = (this.doc.body || this.doc.documentElement);
16653 //this.cleanUpPaste(); -- this is done else where and causes havoc..
16654 var html = bd.innerHTML;
16656 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16657 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16659 html = '<div style="'+m[0]+'">' + html + '</div>';
16662 html = this.cleanHtml(html);
16663 // fix up the special chars.. normaly like back quotes in word...
16664 // however we do not want to do this with chinese..
16665 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16666 var cc = b.charCodeAt();
16668 (cc >= 0x4E00 && cc < 0xA000 ) ||
16669 (cc >= 0x3400 && cc < 0x4E00 ) ||
16670 (cc >= 0xf900 && cc < 0xfb00 )
16676 if(this.owner.fireEvent('beforesync', this, html) !== false){
16677 this.el.dom.value = html;
16678 this.owner.fireEvent('sync', this, html);
16684 * Protected method that will not generally be called directly. Pushes the value of the textarea
16685 * into the iframe editor.
16687 pushValue : function(){
16688 if(this.initialized){
16689 var v = this.el.dom.value.trim();
16691 // if(v.length < 1){
16695 if(this.owner.fireEvent('beforepush', this, v) !== false){
16696 var d = (this.doc.body || this.doc.documentElement);
16698 this.cleanUpPaste();
16699 this.el.dom.value = d.innerHTML;
16700 this.owner.fireEvent('push', this, v);
16706 deferFocus : function(){
16707 this.focus.defer(10, this);
16711 focus : function(){
16712 if(this.win && !this.sourceEditMode){
16719 assignDocWin: function()
16721 var iframe = this.iframe;
16724 this.doc = iframe.contentWindow.document;
16725 this.win = iframe.contentWindow;
16727 // if (!Roo.get(this.frameId)) {
16730 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16731 // this.win = Roo.get(this.frameId).dom.contentWindow;
16733 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16737 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16738 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16743 initEditor : function(){
16744 //console.log("INIT EDITOR");
16745 this.assignDocWin();
16749 this.doc.designMode="on";
16751 this.doc.write(this.getDocMarkup());
16754 var dbody = (this.doc.body || this.doc.documentElement);
16755 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16756 // this copies styles from the containing element into thsi one..
16757 // not sure why we need all of this..
16758 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16760 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16761 //ss['background-attachment'] = 'fixed'; // w3c
16762 dbody.bgProperties = 'fixed'; // ie
16763 //Roo.DomHelper.applyStyles(dbody, ss);
16764 Roo.EventManager.on(this.doc, {
16765 //'mousedown': this.onEditorEvent,
16766 'mouseup': this.onEditorEvent,
16767 'dblclick': this.onEditorEvent,
16768 'click': this.onEditorEvent,
16769 'keyup': this.onEditorEvent,
16774 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16776 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16777 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16779 this.initialized = true;
16781 this.owner.fireEvent('initialize', this);
16786 onDestroy : function(){
16792 //for (var i =0; i < this.toolbars.length;i++) {
16793 // // fixme - ask toolbars for heights?
16794 // this.toolbars[i].onDestroy();
16797 //this.wrap.dom.innerHTML = '';
16798 //this.wrap.remove();
16803 onFirstFocus : function(){
16805 this.assignDocWin();
16808 this.activated = true;
16811 if(Roo.isGecko){ // prevent silly gecko errors
16813 var s = this.win.getSelection();
16814 if(!s.focusNode || s.focusNode.nodeType != 3){
16815 var r = s.getRangeAt(0);
16816 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16821 this.execCmd('useCSS', true);
16822 this.execCmd('styleWithCSS', false);
16825 this.owner.fireEvent('activate', this);
16829 adjustFont: function(btn){
16830 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16831 //if(Roo.isSafari){ // safari
16834 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16835 if(Roo.isSafari){ // safari
16836 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16837 v = (v < 10) ? 10 : v;
16838 v = (v > 48) ? 48 : v;
16839 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16844 v = Math.max(1, v+adjust);
16846 this.execCmd('FontSize', v );
16849 onEditorEvent : function(e){
16850 this.owner.fireEvent('editorevent', this, e);
16851 // this.updateToolbar();
16852 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16855 insertTag : function(tg)
16857 // could be a bit smarter... -> wrap the current selected tRoo..
16858 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16860 range = this.createRange(this.getSelection());
16861 var wrappingNode = this.doc.createElement(tg.toLowerCase());
16862 wrappingNode.appendChild(range.extractContents());
16863 range.insertNode(wrappingNode);
16870 this.execCmd("formatblock", tg);
16874 insertText : function(txt)
16878 var range = this.createRange();
16879 range.deleteContents();
16880 //alert(Sender.getAttribute('label'));
16882 range.insertNode(this.doc.createTextNode(txt));
16888 * Executes a Midas editor command on the editor document and performs necessary focus and
16889 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16890 * @param {String} cmd The Midas command
16891 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16893 relayCmd : function(cmd, value){
16895 this.execCmd(cmd, value);
16896 this.owner.fireEvent('editorevent', this);
16897 //this.updateToolbar();
16898 this.owner.deferFocus();
16902 * Executes a Midas editor command directly on the editor document.
16903 * For visual commands, you should use {@link #relayCmd} instead.
16904 * <b>This should only be called after the editor is initialized.</b>
16905 * @param {String} cmd The Midas command
16906 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16908 execCmd : function(cmd, value){
16909 this.doc.execCommand(cmd, false, value === undefined ? null : value);
16916 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16918 * @param {String} text | dom node..
16920 insertAtCursor : function(text)
16925 if(!this.activated){
16931 var r = this.doc.selection.createRange();
16942 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16946 // from jquery ui (MIT licenced)
16948 var win = this.win;
16950 if (win.getSelection && win.getSelection().getRangeAt) {
16951 range = win.getSelection().getRangeAt(0);
16952 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16953 range.insertNode(node);
16954 } else if (win.document.selection && win.document.selection.createRange) {
16955 // no firefox support
16956 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16957 win.document.selection.createRange().pasteHTML(txt);
16959 // no firefox support
16960 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16961 this.execCmd('InsertHTML', txt);
16970 mozKeyPress : function(e){
16972 var c = e.getCharCode(), cmd;
16975 c = String.fromCharCode(c).toLowerCase();
16989 this.cleanUpPaste.defer(100, this);
16997 e.preventDefault();
17005 fixKeys : function(){ // load time branching for fastest keydown performance
17007 return function(e){
17008 var k = e.getKey(), r;
17011 r = this.doc.selection.createRange();
17014 r.pasteHTML('    ');
17021 r = this.doc.selection.createRange();
17023 var target = r.parentElement();
17024 if(!target || target.tagName.toLowerCase() != 'li'){
17026 r.pasteHTML('<br />');
17032 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17033 this.cleanUpPaste.defer(100, this);
17039 }else if(Roo.isOpera){
17040 return function(e){
17041 var k = e.getKey();
17045 this.execCmd('InsertHTML','    ');
17048 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17049 this.cleanUpPaste.defer(100, this);
17054 }else if(Roo.isSafari){
17055 return function(e){
17056 var k = e.getKey();
17060 this.execCmd('InsertText','\t');
17064 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17065 this.cleanUpPaste.defer(100, this);
17073 getAllAncestors: function()
17075 var p = this.getSelectedNode();
17078 a.push(p); // push blank onto stack..
17079 p = this.getParentElement();
17083 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17087 a.push(this.doc.body);
17091 lastSelNode : false,
17094 getSelection : function()
17096 this.assignDocWin();
17097 return Roo.isIE ? this.doc.selection : this.win.getSelection();
17100 getSelectedNode: function()
17102 // this may only work on Gecko!!!
17104 // should we cache this!!!!
17109 var range = this.createRange(this.getSelection()).cloneRange();
17112 var parent = range.parentElement();
17114 var testRange = range.duplicate();
17115 testRange.moveToElementText(parent);
17116 if (testRange.inRange(range)) {
17119 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17122 parent = parent.parentElement;
17127 // is ancestor a text element.
17128 var ac = range.commonAncestorContainer;
17129 if (ac.nodeType == 3) {
17130 ac = ac.parentNode;
17133 var ar = ac.childNodes;
17136 var other_nodes = [];
17137 var has_other_nodes = false;
17138 for (var i=0;i<ar.length;i++) {
17139 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
17142 // fullly contained node.
17144 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17149 // probably selected..
17150 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17151 other_nodes.push(ar[i]);
17155 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
17160 has_other_nodes = true;
17162 if (!nodes.length && other_nodes.length) {
17163 nodes= other_nodes;
17165 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17171 createRange: function(sel)
17173 // this has strange effects when using with
17174 // top toolbar - not sure if it's a great idea.
17175 //this.editor.contentWindow.focus();
17176 if (typeof sel != "undefined") {
17178 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17180 return this.doc.createRange();
17183 return this.doc.createRange();
17186 getParentElement: function()
17189 this.assignDocWin();
17190 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17192 var range = this.createRange(sel);
17195 var p = range.commonAncestorContainer;
17196 while (p.nodeType == 3) { // text node
17207 * Range intersection.. the hard stuff...
17211 * [ -- selected range --- ]
17215 * if end is before start or hits it. fail.
17216 * if start is after end or hits it fail.
17218 * if either hits (but other is outside. - then it's not
17224 // @see http://www.thismuchiknow.co.uk/?p=64.
17225 rangeIntersectsNode : function(range, node)
17227 var nodeRange = node.ownerDocument.createRange();
17229 nodeRange.selectNode(node);
17231 nodeRange.selectNodeContents(node);
17234 var rangeStartRange = range.cloneRange();
17235 rangeStartRange.collapse(true);
17237 var rangeEndRange = range.cloneRange();
17238 rangeEndRange.collapse(false);
17240 var nodeStartRange = nodeRange.cloneRange();
17241 nodeStartRange.collapse(true);
17243 var nodeEndRange = nodeRange.cloneRange();
17244 nodeEndRange.collapse(false);
17246 return rangeStartRange.compareBoundaryPoints(
17247 Range.START_TO_START, nodeEndRange) == -1 &&
17248 rangeEndRange.compareBoundaryPoints(
17249 Range.START_TO_START, nodeStartRange) == 1;
17253 rangeCompareNode : function(range, node)
17255 var nodeRange = node.ownerDocument.createRange();
17257 nodeRange.selectNode(node);
17259 nodeRange.selectNodeContents(node);
17263 range.collapse(true);
17265 nodeRange.collapse(true);
17267 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17268 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
17270 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17272 var nodeIsBefore = ss == 1;
17273 var nodeIsAfter = ee == -1;
17275 if (nodeIsBefore && nodeIsAfter)
17277 if (!nodeIsBefore && nodeIsAfter)
17278 return 1; //right trailed.
17280 if (nodeIsBefore && !nodeIsAfter)
17281 return 2; // left trailed.
17286 // private? - in a new class?
17287 cleanUpPaste : function()
17289 // cleans up the whole document..
17290 Roo.log('cleanuppaste');
17292 this.cleanUpChildren(this.doc.body);
17293 var clean = this.cleanWordChars(this.doc.body.innerHTML);
17294 if (clean != this.doc.body.innerHTML) {
17295 this.doc.body.innerHTML = clean;
17300 cleanWordChars : function(input) {// change the chars to hex code
17301 var he = Roo.HtmlEditorCore;
17303 var output = input;
17304 Roo.each(he.swapCodes, function(sw) {
17305 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17307 output = output.replace(swapper, sw[1]);
17314 cleanUpChildren : function (n)
17316 if (!n.childNodes.length) {
17319 for (var i = n.childNodes.length-1; i > -1 ; i--) {
17320 this.cleanUpChild(n.childNodes[i]);
17327 cleanUpChild : function (node)
17330 //console.log(node);
17331 if (node.nodeName == "#text") {
17332 // clean up silly Windows -- stuff?
17335 if (node.nodeName == "#comment") {
17336 node.parentNode.removeChild(node);
17337 // clean up silly Windows -- stuff?
17340 var lcname = node.tagName.toLowerCase();
17341 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17342 // whitelist of tags..
17344 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17346 node.parentNode.removeChild(node);
17351 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17353 // remove <a name=....> as rendering on yahoo mailer is borked with this.
17354 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17356 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17357 // remove_keep_children = true;
17360 if (remove_keep_children) {
17361 this.cleanUpChildren(node);
17362 // inserts everything just before this node...
17363 while (node.childNodes.length) {
17364 var cn = node.childNodes[0];
17365 node.removeChild(cn);
17366 node.parentNode.insertBefore(cn, node);
17368 node.parentNode.removeChild(node);
17372 if (!node.attributes || !node.attributes.length) {
17373 this.cleanUpChildren(node);
17377 function cleanAttr(n,v)
17380 if (v.match(/^\./) || v.match(/^\//)) {
17383 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17386 if (v.match(/^#/)) {
17389 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17390 node.removeAttribute(n);
17394 var cwhite = this.cwhite;
17395 var cblack = this.cblack;
17397 function cleanStyle(n,v)
17399 if (v.match(/expression/)) { //XSS?? should we even bother..
17400 node.removeAttribute(n);
17404 var parts = v.split(/;/);
17407 Roo.each(parts, function(p) {
17408 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17412 var l = p.split(':').shift().replace(/\s+/g,'');
17413 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17415 if ( cwhite.length && cblack.indexOf(l) > -1) {
17416 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17417 //node.removeAttribute(n);
17421 // only allow 'c whitelisted system attributes'
17422 if ( cwhite.length && cwhite.indexOf(l) < 0) {
17423 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17424 //node.removeAttribute(n);
17434 if (clean.length) {
17435 node.setAttribute(n, clean.join(';'));
17437 node.removeAttribute(n);
17443 for (var i = node.attributes.length-1; i > -1 ; i--) {
17444 var a = node.attributes[i];
17447 if (a.name.toLowerCase().substr(0,2)=='on') {
17448 node.removeAttribute(a.name);
17451 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17452 node.removeAttribute(a.name);
17455 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17456 cleanAttr(a.name,a.value); // fixme..
17459 if (a.name == 'style') {
17460 cleanStyle(a.name,a.value);
17463 /// clean up MS crap..
17464 // tecnically this should be a list of valid class'es..
17467 if (a.name == 'class') {
17468 if (a.value.match(/^Mso/)) {
17469 node.className = '';
17472 if (a.value.match(/body/)) {
17473 node.className = '';
17484 this.cleanUpChildren(node);
17489 * Clean up MS wordisms...
17491 cleanWord : function(node)
17494 var cleanWordChildren = function()
17496 if (!node.childNodes.length) {
17499 for (var i = node.childNodes.length-1; i > -1 ; i--) {
17500 _t.cleanWord(node.childNodes[i]);
17506 this.cleanWord(this.doc.body);
17509 if (node.nodeName == "#text") {
17510 // clean up silly Windows -- stuff?
17513 if (node.nodeName == "#comment") {
17514 node.parentNode.removeChild(node);
17515 // clean up silly Windows -- stuff?
17519 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17520 node.parentNode.removeChild(node);
17524 // remove - but keep children..
17525 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17526 while (node.childNodes.length) {
17527 var cn = node.childNodes[0];
17528 node.removeChild(cn);
17529 node.parentNode.insertBefore(cn, node);
17531 node.parentNode.removeChild(node);
17532 cleanWordChildren();
17536 if (node.className.length) {
17538 var cn = node.className.split(/\W+/);
17540 Roo.each(cn, function(cls) {
17541 if (cls.match(/Mso[a-zA-Z]+/)) {
17546 node.className = cna.length ? cna.join(' ') : '';
17548 node.removeAttribute("class");
17552 if (node.hasAttribute("lang")) {
17553 node.removeAttribute("lang");
17556 if (node.hasAttribute("style")) {
17558 var styles = node.getAttribute("style").split(";");
17560 Roo.each(styles, function(s) {
17561 if (!s.match(/:/)) {
17564 var kv = s.split(":");
17565 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17568 // what ever is left... we allow.
17571 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17572 if (!nstyle.length) {
17573 node.removeAttribute('style');
17577 cleanWordChildren();
17581 domToHTML : function(currentElement, depth, nopadtext) {
17583 depth = depth || 0;
17584 nopadtext = nopadtext || false;
17586 if (!currentElement) {
17587 return this.domToHTML(this.doc.body);
17590 //Roo.log(currentElement);
17592 var allText = false;
17593 var nodeName = currentElement.nodeName;
17594 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17596 if (nodeName == '#text') {
17597 return currentElement.nodeValue;
17602 if (nodeName != 'BODY') {
17605 // Prints the node tagName, such as <A>, <IMG>, etc
17608 for(i = 0; i < currentElement.attributes.length;i++) {
17610 var aname = currentElement.attributes.item(i).name;
17611 if (!currentElement.attributes.item(i).value.length) {
17614 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17617 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17626 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17629 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17634 // Traverse the tree
17636 var currentElementChild = currentElement.childNodes.item(i);
17637 var allText = true;
17638 var innerHTML = '';
17640 while (currentElementChild) {
17641 // Formatting code (indent the tree so it looks nice on the screen)
17642 var nopad = nopadtext;
17643 if (lastnode == 'SPAN') {
17647 if (currentElementChild.nodeName == '#text') {
17648 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17649 if (!nopad && toadd.length > 80) {
17650 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
17652 innerHTML += toadd;
17655 currentElementChild = currentElement.childNodes.item(i);
17661 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
17663 // Recursively traverse the tree structure of the child node
17664 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
17665 lastnode = currentElementChild.nodeName;
17667 currentElementChild=currentElement.childNodes.item(i);
17673 // The remaining code is mostly for formatting the tree
17674 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
17679 ret+= "</"+tagName+">";
17685 applyBlacklists : function()
17687 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
17688 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
17692 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17693 if (b.indexOf(tag) > -1) {
17696 this.white.push(tag);
17700 Roo.each(w, function(tag) {
17701 if (b.indexOf(tag) > -1) {
17704 if (this.white.indexOf(tag) > -1) {
17707 this.white.push(tag);
17712 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17713 if (w.indexOf(tag) > -1) {
17716 this.black.push(tag);
17720 Roo.each(b, function(tag) {
17721 if (w.indexOf(tag) > -1) {
17724 if (this.black.indexOf(tag) > -1) {
17727 this.black.push(tag);
17732 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
17733 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
17737 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17738 if (b.indexOf(tag) > -1) {
17741 this.cwhite.push(tag);
17745 Roo.each(w, function(tag) {
17746 if (b.indexOf(tag) > -1) {
17749 if (this.cwhite.indexOf(tag) > -1) {
17752 this.cwhite.push(tag);
17757 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17758 if (w.indexOf(tag) > -1) {
17761 this.cblack.push(tag);
17765 Roo.each(b, function(tag) {
17766 if (w.indexOf(tag) > -1) {
17769 if (this.cblack.indexOf(tag) > -1) {
17772 this.cblack.push(tag);
17777 // hide stuff that is not compatible
17791 * @event specialkey
17795 * @cfg {String} fieldClass @hide
17798 * @cfg {String} focusClass @hide
17801 * @cfg {String} autoCreate @hide
17804 * @cfg {String} inputType @hide
17807 * @cfg {String} invalidClass @hide
17810 * @cfg {String} invalidText @hide
17813 * @cfg {String} msgFx @hide
17816 * @cfg {String} validateOnBlur @hide
17820 Roo.HtmlEditorCore.white = [
17821 'area', 'br', 'img', 'input', 'hr', 'wbr',
17823 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
17824 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
17825 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
17826 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
17827 'table', 'ul', 'xmp',
17829 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
17832 'dir', 'menu', 'ol', 'ul', 'dl',
17838 Roo.HtmlEditorCore.black = [
17839 // 'embed', 'object', // enable - backend responsiblity to clean thiese
17841 'base', 'basefont', 'bgsound', 'blink', 'body',
17842 'frame', 'frameset', 'head', 'html', 'ilayer',
17843 'iframe', 'layer', 'link', 'meta', 'object',
17844 'script', 'style' ,'title', 'xml' // clean later..
17846 Roo.HtmlEditorCore.clean = [
17847 'script', 'style', 'title', 'xml'
17849 Roo.HtmlEditorCore.remove = [
17854 Roo.HtmlEditorCore.ablack = [
17858 Roo.HtmlEditorCore.aclean = [
17859 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
17863 Roo.HtmlEditorCore.pwhite= [
17864 'http', 'https', 'mailto'
17867 // white listed style attributes.
17868 Roo.HtmlEditorCore.cwhite= [
17869 // 'text-align', /// default is to allow most things..
17875 // black listed style attributes.
17876 Roo.HtmlEditorCore.cblack= [
17877 // 'font-size' -- this can be set by the project
17881 Roo.HtmlEditorCore.swapCodes =[
17900 * @class Roo.bootstrap.HtmlEditor
17901 * @extends Roo.bootstrap.TextArea
17902 * Bootstrap HtmlEditor class
17905 * Create a new HtmlEditor
17906 * @param {Object} config The config object
17909 Roo.bootstrap.HtmlEditor = function(config){
17910 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17911 if (!this.toolbars) {
17912 this.toolbars = [];
17914 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17917 * @event initialize
17918 * Fires when the editor is fully initialized (including the iframe)
17919 * @param {HtmlEditor} this
17924 * Fires when the editor is first receives the focus. Any insertion must wait
17925 * until after this event.
17926 * @param {HtmlEditor} this
17930 * @event beforesync
17931 * Fires before the textarea is updated with content from the editor iframe. Return false
17932 * to cancel the sync.
17933 * @param {HtmlEditor} this
17934 * @param {String} html
17938 * @event beforepush
17939 * Fires before the iframe editor is updated with content from the textarea. Return false
17940 * to cancel the push.
17941 * @param {HtmlEditor} this
17942 * @param {String} html
17947 * Fires when the textarea is updated with content from the editor iframe.
17948 * @param {HtmlEditor} this
17949 * @param {String} html
17954 * Fires when the iframe editor is updated with content from the textarea.
17955 * @param {HtmlEditor} this
17956 * @param {String} html
17960 * @event editmodechange
17961 * Fires when the editor switches edit modes
17962 * @param {HtmlEditor} this
17963 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17965 editmodechange: true,
17967 * @event editorevent
17968 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17969 * @param {HtmlEditor} this
17973 * @event firstfocus
17974 * Fires when on first focus - needed by toolbars..
17975 * @param {HtmlEditor} this
17980 * Auto save the htmlEditor value as a file into Events
17981 * @param {HtmlEditor} this
17985 * @event savedpreview
17986 * preview the saved version of htmlEditor
17987 * @param {HtmlEditor} this
17994 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
17998 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
18003 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
18008 * @cfg {Number} height (in pixels)
18012 * @cfg {Number} width (in pixels)
18017 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18020 stylesheets: false,
18025 // private properties
18026 validationEvent : false,
18028 initialized : false,
18031 onFocus : Roo.emptyFn,
18033 hideMode:'offsets',
18036 tbContainer : false,
18038 toolbarContainer :function() {
18039 return this.wrap.select('.x-html-editor-tb',true).first();
18043 * Protected method that will not generally be called directly. It
18044 * is called when the editor creates its toolbar. Override this method if you need to
18045 * add custom toolbar buttons.
18046 * @param {HtmlEditor} editor
18048 createToolbar : function(){
18050 Roo.log("create toolbars");
18052 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18053 this.toolbars[0].render(this.toolbarContainer());
18057 // if (!editor.toolbars || !editor.toolbars.length) {
18058 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18061 // for (var i =0 ; i < editor.toolbars.length;i++) {
18062 // editor.toolbars[i] = Roo.factory(
18063 // typeof(editor.toolbars[i]) == 'string' ?
18064 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
18065 // Roo.bootstrap.HtmlEditor);
18066 // editor.toolbars[i].init(editor);
18072 onRender : function(ct, position)
18074 // Roo.log("Call onRender: " + this.xtype);
18076 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18078 this.wrap = this.inputEl().wrap({
18079 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18082 this.editorcore.onRender(ct, position);
18084 if (this.resizable) {
18085 this.resizeEl = new Roo.Resizable(this.wrap, {
18089 minHeight : this.height,
18090 height: this.height,
18091 handles : this.resizable,
18094 resize : function(r, w, h) {
18095 _t.onResize(w,h); // -something
18101 this.createToolbar(this);
18104 if(!this.width && this.resizable){
18105 this.setSize(this.wrap.getSize());
18107 if (this.resizeEl) {
18108 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18109 // should trigger onReize..
18115 onResize : function(w, h)
18117 Roo.log('resize: ' +w + ',' + h );
18118 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18122 if(this.inputEl() ){
18123 if(typeof w == 'number'){
18124 var aw = w - this.wrap.getFrameWidth('lr');
18125 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18128 if(typeof h == 'number'){
18129 var tbh = -11; // fixme it needs to tool bar size!
18130 for (var i =0; i < this.toolbars.length;i++) {
18131 // fixme - ask toolbars for heights?
18132 tbh += this.toolbars[i].el.getHeight();
18133 //if (this.toolbars[i].footer) {
18134 // tbh += this.toolbars[i].footer.el.getHeight();
18142 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18143 ah -= 5; // knock a few pixes off for look..
18144 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18148 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18149 this.editorcore.onResize(ew,eh);
18154 * Toggles the editor between standard and source edit mode.
18155 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18157 toggleSourceEdit : function(sourceEditMode)
18159 this.editorcore.toggleSourceEdit(sourceEditMode);
18161 if(this.editorcore.sourceEditMode){
18162 Roo.log('editor - showing textarea');
18165 // Roo.log(this.syncValue());
18167 this.inputEl().removeClass(['hide', 'x-hidden']);
18168 this.inputEl().dom.removeAttribute('tabIndex');
18169 this.inputEl().focus();
18171 Roo.log('editor - hiding textarea');
18173 // Roo.log(this.pushValue());
18176 this.inputEl().addClass(['hide', 'x-hidden']);
18177 this.inputEl().dom.setAttribute('tabIndex', -1);
18178 //this.deferFocus();
18181 if(this.resizable){
18182 this.setSize(this.wrap.getSize());
18185 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18188 // private (for BoxComponent)
18189 adjustSize : Roo.BoxComponent.prototype.adjustSize,
18191 // private (for BoxComponent)
18192 getResizeEl : function(){
18196 // private (for BoxComponent)
18197 getPositionEl : function(){
18202 initEvents : function(){
18203 this.originalValue = this.getValue();
18207 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18210 // markInvalid : Roo.emptyFn,
18212 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18215 // clearInvalid : Roo.emptyFn,
18217 setValue : function(v){
18218 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18219 this.editorcore.pushValue();
18224 deferFocus : function(){
18225 this.focus.defer(10, this);
18229 focus : function(){
18230 this.editorcore.focus();
18236 onDestroy : function(){
18242 for (var i =0; i < this.toolbars.length;i++) {
18243 // fixme - ask toolbars for heights?
18244 this.toolbars[i].onDestroy();
18247 this.wrap.dom.innerHTML = '';
18248 this.wrap.remove();
18253 onFirstFocus : function(){
18254 //Roo.log("onFirstFocus");
18255 this.editorcore.onFirstFocus();
18256 for (var i =0; i < this.toolbars.length;i++) {
18257 this.toolbars[i].onFirstFocus();
18263 syncValue : function()
18265 this.editorcore.syncValue();
18268 pushValue : function()
18270 this.editorcore.pushValue();
18274 // hide stuff that is not compatible
18288 * @event specialkey
18292 * @cfg {String} fieldClass @hide
18295 * @cfg {String} focusClass @hide
18298 * @cfg {String} autoCreate @hide
18301 * @cfg {String} inputType @hide
18304 * @cfg {String} invalidClass @hide
18307 * @cfg {String} invalidText @hide
18310 * @cfg {String} msgFx @hide
18313 * @cfg {String} validateOnBlur @hide
18322 Roo.namespace('Roo.bootstrap.htmleditor');
18324 * @class Roo.bootstrap.HtmlEditorToolbar1
18329 new Roo.bootstrap.HtmlEditor({
18332 new Roo.bootstrap.HtmlEditorToolbar1({
18333 disable : { fonts: 1 , format: 1, ..., ... , ...],
18339 * @cfg {Object} disable List of elements to disable..
18340 * @cfg {Array} btns List of additional buttons.
18344 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18347 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18350 Roo.apply(this, config);
18352 // default disabled, based on 'good practice'..
18353 this.disable = this.disable || {};
18354 Roo.applyIf(this.disable, {
18357 specialElements : true
18359 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18361 this.editor = config.editor;
18362 this.editorcore = config.editor.editorcore;
18364 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18366 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18367 // dont call parent... till later.
18369 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
18374 editorcore : false,
18379 "h1","h2","h3","h4","h5","h6",
18381 "abbr", "acronym", "address", "cite", "samp", "var",
18385 onRender : function(ct, position)
18387 // Roo.log("Call onRender: " + this.xtype);
18389 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18391 this.el.dom.style.marginBottom = '0';
18393 var editorcore = this.editorcore;
18394 var editor= this.editor;
18397 var btn = function(id,cmd , toggle, handler){
18399 var event = toggle ? 'toggle' : 'click';
18404 xns: Roo.bootstrap,
18407 enableToggle:toggle !== false,
18409 pressed : toggle ? false : null,
18412 a.listeners[toggle ? 'toggle' : 'click'] = function() {
18413 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
18422 xns: Roo.bootstrap,
18423 glyphicon : 'font',
18427 xns: Roo.bootstrap,
18431 Roo.each(this.formats, function(f) {
18432 style.menu.items.push({
18434 xns: Roo.bootstrap,
18435 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18440 editorcore.insertTag(this.tagname);
18447 children.push(style);
18450 btn('bold',false,true);
18451 btn('italic',false,true);
18452 btn('align-left', 'justifyleft',true);
18453 btn('align-center', 'justifycenter',true);
18454 btn('align-right' , 'justifyright',true);
18455 btn('link', false, false, function(btn) {
18456 //Roo.log("create link?");
18457 var url = prompt(this.createLinkText, this.defaultLinkValue);
18458 if(url && url != 'http:/'+'/'){
18459 this.editorcore.relayCmd('createlink', url);
18462 btn('list','insertunorderedlist',true);
18463 btn('pencil', false,true, function(btn){
18466 this.toggleSourceEdit(btn.pressed);
18472 xns: Roo.bootstrap,
18477 xns: Roo.bootstrap,
18482 cog.menu.items.push({
18484 xns: Roo.bootstrap,
18485 html : Clean styles,
18490 editorcore.insertTag(this.tagname);
18499 this.xtype = 'NavSimplebar';
18501 for(var i=0;i< children.length;i++) {
18503 this.buttons.add(this.addxtypeChild(children[i]));
18507 editor.on('editorevent', this.updateToolbar, this);
18509 onBtnClick : function(id)
18511 this.editorcore.relayCmd(id);
18512 this.editorcore.focus();
18516 * Protected method that will not generally be called directly. It triggers
18517 * a toolbar update by reading the markup state of the current selection in the editor.
18519 updateToolbar: function(){
18521 if(!this.editorcore.activated){
18522 this.editor.onFirstFocus(); // is this neeed?
18526 var btns = this.buttons;
18527 var doc = this.editorcore.doc;
18528 btns.get('bold').setActive(doc.queryCommandState('bold'));
18529 btns.get('italic').setActive(doc.queryCommandState('italic'));
18530 //btns.get('underline').setActive(doc.queryCommandState('underline'));
18532 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18533 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18534 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18536 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18537 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18540 var ans = this.editorcore.getAllAncestors();
18541 if (this.formatCombo) {
18544 var store = this.formatCombo.store;
18545 this.formatCombo.setValue("");
18546 for (var i =0; i < ans.length;i++) {
18547 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18549 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18557 // hides menus... - so this cant be on a menu...
18558 Roo.bootstrap.MenuMgr.hideAll();
18560 Roo.bootstrap.MenuMgr.hideAll();
18561 //this.editorsyncValue();
18563 onFirstFocus: function() {
18564 this.buttons.each(function(item){
18568 toggleSourceEdit : function(sourceEditMode){
18571 if(sourceEditMode){
18572 Roo.log("disabling buttons");
18573 this.buttons.each( function(item){
18574 if(item.cmd != 'pencil'){
18580 Roo.log("enabling buttons");
18581 if(this.editorcore.initialized){
18582 this.buttons.each( function(item){
18588 Roo.log("calling toggole on editor");
18589 // tell the editor that it's been pressed..
18590 this.editor.toggleSourceEdit(sourceEditMode);
18600 * @class Roo.bootstrap.Table.AbstractSelectionModel
18601 * @extends Roo.util.Observable
18602 * Abstract base class for grid SelectionModels. It provides the interface that should be
18603 * implemented by descendant classes. This class should not be directly instantiated.
18606 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18607 this.locked = false;
18608 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18612 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
18613 /** @ignore Called by the grid automatically. Do not call directly. */
18614 init : function(grid){
18620 * Locks the selections.
18623 this.locked = true;
18627 * Unlocks the selections.
18629 unlock : function(){
18630 this.locked = false;
18634 * Returns true if the selections are locked.
18635 * @return {Boolean}
18637 isLocked : function(){
18638 return this.locked;
18642 * @extends Roo.bootstrap.Table.AbstractSelectionModel
18643 * @class Roo.bootstrap.Table.RowSelectionModel
18644 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18645 * It supports multiple selections and keyboard selection/navigation.
18647 * @param {Object} config
18650 Roo.bootstrap.Table.RowSelectionModel = function(config){
18651 Roo.apply(this, config);
18652 this.selections = new Roo.util.MixedCollection(false, function(o){
18657 this.lastActive = false;
18661 * @event selectionchange
18662 * Fires when the selection changes
18663 * @param {SelectionModel} this
18665 "selectionchange" : true,
18667 * @event afterselectionchange
18668 * Fires after the selection changes (eg. by key press or clicking)
18669 * @param {SelectionModel} this
18671 "afterselectionchange" : true,
18673 * @event beforerowselect
18674 * Fires when a row is selected being selected, return false to cancel.
18675 * @param {SelectionModel} this
18676 * @param {Number} rowIndex The selected index
18677 * @param {Boolean} keepExisting False if other selections will be cleared
18679 "beforerowselect" : true,
18682 * Fires when a row is selected.
18683 * @param {SelectionModel} this
18684 * @param {Number} rowIndex The selected index
18685 * @param {Roo.data.Record} r The record
18687 "rowselect" : true,
18689 * @event rowdeselect
18690 * Fires when a row is deselected.
18691 * @param {SelectionModel} this
18692 * @param {Number} rowIndex The selected index
18694 "rowdeselect" : true
18696 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18697 this.locked = false;
18700 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
18702 * @cfg {Boolean} singleSelect
18703 * True to allow selection of only one row at a time (defaults to false)
18705 singleSelect : false,
18708 initEvents : function(){
18710 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18711 this.grid.on("mousedown", this.handleMouseDown, this);
18712 }else{ // allow click to work like normal
18713 this.grid.on("rowclick", this.handleDragableRowClick, this);
18716 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18717 "up" : function(e){
18719 this.selectPrevious(e.shiftKey);
18720 }else if(this.last !== false && this.lastActive !== false){
18721 var last = this.last;
18722 this.selectRange(this.last, this.lastActive-1);
18723 this.grid.getView().focusRow(this.lastActive);
18724 if(last !== false){
18728 this.selectFirstRow();
18730 this.fireEvent("afterselectionchange", this);
18732 "down" : function(e){
18734 this.selectNext(e.shiftKey);
18735 }else if(this.last !== false && this.lastActive !== false){
18736 var last = this.last;
18737 this.selectRange(this.last, this.lastActive+1);
18738 this.grid.getView().focusRow(this.lastActive);
18739 if(last !== false){
18743 this.selectFirstRow();
18745 this.fireEvent("afterselectionchange", this);
18750 var view = this.grid.view;
18751 view.on("refresh", this.onRefresh, this);
18752 view.on("rowupdated", this.onRowUpdated, this);
18753 view.on("rowremoved", this.onRemove, this);
18757 onRefresh : function(){
18758 var ds = this.grid.dataSource, i, v = this.grid.view;
18759 var s = this.selections;
18760 s.each(function(r){
18761 if((i = ds.indexOfId(r.id)) != -1){
18770 onRemove : function(v, index, r){
18771 this.selections.remove(r);
18775 onRowUpdated : function(v, index, r){
18776 if(this.isSelected(r)){
18777 v.onRowSelect(index);
18783 * @param {Array} records The records to select
18784 * @param {Boolean} keepExisting (optional) True to keep existing selections
18786 selectRecords : function(records, keepExisting){
18788 this.clearSelections();
18790 var ds = this.grid.dataSource;
18791 for(var i = 0, len = records.length; i < len; i++){
18792 this.selectRow(ds.indexOf(records[i]), true);
18797 * Gets the number of selected rows.
18800 getCount : function(){
18801 return this.selections.length;
18805 * Selects the first row in the grid.
18807 selectFirstRow : function(){
18812 * Select the last row.
18813 * @param {Boolean} keepExisting (optional) True to keep existing selections
18815 selectLastRow : function(keepExisting){
18816 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18820 * Selects the row immediately following the last selected row.
18821 * @param {Boolean} keepExisting (optional) True to keep existing selections
18823 selectNext : function(keepExisting){
18824 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18825 this.selectRow(this.last+1, keepExisting);
18826 this.grid.getView().focusRow(this.last);
18831 * Selects the row that precedes the last selected row.
18832 * @param {Boolean} keepExisting (optional) True to keep existing selections
18834 selectPrevious : function(keepExisting){
18836 this.selectRow(this.last-1, keepExisting);
18837 this.grid.getView().focusRow(this.last);
18842 * Returns the selected records
18843 * @return {Array} Array of selected records
18845 getSelections : function(){
18846 return [].concat(this.selections.items);
18850 * Returns the first selected record.
18853 getSelected : function(){
18854 return this.selections.itemAt(0);
18859 * Clears all selections.
18861 clearSelections : function(fast){
18862 if(this.locked) return;
18864 var ds = this.grid.dataSource;
18865 var s = this.selections;
18866 s.each(function(r){
18867 this.deselectRow(ds.indexOfId(r.id));
18871 this.selections.clear();
18878 * Selects all rows.
18880 selectAll : function(){
18881 if(this.locked) return;
18882 this.selections.clear();
18883 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18884 this.selectRow(i, true);
18889 * Returns True if there is a selection.
18890 * @return {Boolean}
18892 hasSelection : function(){
18893 return this.selections.length > 0;
18897 * Returns True if the specified row is selected.
18898 * @param {Number/Record} record The record or index of the record to check
18899 * @return {Boolean}
18901 isSelected : function(index){
18902 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18903 return (r && this.selections.key(r.id) ? true : false);
18907 * Returns True if the specified record id is selected.
18908 * @param {String} id The id of record to check
18909 * @return {Boolean}
18911 isIdSelected : function(id){
18912 return (this.selections.key(id) ? true : false);
18916 handleMouseDown : function(e, t){
18917 var view = this.grid.getView(), rowIndex;
18918 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18921 if(e.shiftKey && this.last !== false){
18922 var last = this.last;
18923 this.selectRange(last, rowIndex, e.ctrlKey);
18924 this.last = last; // reset the last
18925 view.focusRow(rowIndex);
18927 var isSelected = this.isSelected(rowIndex);
18928 if(e.button !== 0 && isSelected){
18929 view.focusRow(rowIndex);
18930 }else if(e.ctrlKey && isSelected){
18931 this.deselectRow(rowIndex);
18932 }else if(!isSelected){
18933 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18934 view.focusRow(rowIndex);
18937 this.fireEvent("afterselectionchange", this);
18940 handleDragableRowClick : function(grid, rowIndex, e)
18942 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18943 this.selectRow(rowIndex, false);
18944 grid.view.focusRow(rowIndex);
18945 this.fireEvent("afterselectionchange", this);
18950 * Selects multiple rows.
18951 * @param {Array} rows Array of the indexes of the row to select
18952 * @param {Boolean} keepExisting (optional) True to keep existing selections
18954 selectRows : function(rows, keepExisting){
18956 this.clearSelections();
18958 for(var i = 0, len = rows.length; i < len; i++){
18959 this.selectRow(rows[i], true);
18964 * Selects a range of rows. All rows in between startRow and endRow are also selected.
18965 * @param {Number} startRow The index of the first row in the range
18966 * @param {Number} endRow The index of the last row in the range
18967 * @param {Boolean} keepExisting (optional) True to retain existing selections
18969 selectRange : function(startRow, endRow, keepExisting){
18970 if(this.locked) return;
18972 this.clearSelections();
18974 if(startRow <= endRow){
18975 for(var i = startRow; i <= endRow; i++){
18976 this.selectRow(i, true);
18979 for(var i = startRow; i >= endRow; i--){
18980 this.selectRow(i, true);
18986 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18987 * @param {Number} startRow The index of the first row in the range
18988 * @param {Number} endRow The index of the last row in the range
18990 deselectRange : function(startRow, endRow, preventViewNotify){
18991 if(this.locked) return;
18992 for(var i = startRow; i <= endRow; i++){
18993 this.deselectRow(i, preventViewNotify);
18999 * @param {Number} row The index of the row to select
19000 * @param {Boolean} keepExisting (optional) True to keep existing selections
19002 selectRow : function(index, keepExisting, preventViewNotify){
19003 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
19004 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
19005 if(!keepExisting || this.singleSelect){
19006 this.clearSelections();
19008 var r = this.grid.dataSource.getAt(index);
19009 this.selections.add(r);
19010 this.last = this.lastActive = index;
19011 if(!preventViewNotify){
19012 this.grid.getView().onRowSelect(index);
19014 this.fireEvent("rowselect", this, index, r);
19015 this.fireEvent("selectionchange", this);
19021 * @param {Number} row The index of the row to deselect
19023 deselectRow : function(index, preventViewNotify){
19024 if(this.locked) return;
19025 if(this.last == index){
19028 if(this.lastActive == index){
19029 this.lastActive = false;
19031 var r = this.grid.dataSource.getAt(index);
19032 this.selections.remove(r);
19033 if(!preventViewNotify){
19034 this.grid.getView().onRowDeselect(index);
19036 this.fireEvent("rowdeselect", this, index);
19037 this.fireEvent("selectionchange", this);
19041 restoreLast : function(){
19043 this.last = this._last;
19048 acceptsNav : function(row, col, cm){
19049 return !cm.isHidden(col) && cm.isCellEditable(col, row);
19053 onEditorKey : function(field, e){
19054 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19059 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19061 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19063 }else if(k == e.ENTER && !e.ctrlKey){
19067 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19069 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19071 }else if(k == e.ESC){
19075 g.startEditing(newCell[0], newCell[1]);
19080 * Ext JS Library 1.1.1
19081 * Copyright(c) 2006-2007, Ext JS, LLC.
19083 * Originally Released Under LGPL - original licence link has changed is not relivant.
19086 * <script type="text/javascript">
19090 * @class Roo.bootstrap.PagingToolbar
19092 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19094 * Create a new PagingToolbar
19095 * @param {Object} config The config object
19097 Roo.bootstrap.PagingToolbar = function(config)
19099 // old args format still supported... - xtype is prefered..
19100 // created from xtype...
19101 var ds = config.dataSource;
19102 this.toolbarItems = [];
19103 if (config.items) {
19104 this.toolbarItems = config.items;
19105 // config.items = [];
19108 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19115 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19119 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19121 * @cfg {Roo.data.Store} dataSource
19122 * The underlying data store providing the paged data
19125 * @cfg {String/HTMLElement/Element} container
19126 * container The id or element that will contain the toolbar
19129 * @cfg {Boolean} displayInfo
19130 * True to display the displayMsg (defaults to false)
19133 * @cfg {Number} pageSize
19134 * The number of records to display per page (defaults to 20)
19138 * @cfg {String} displayMsg
19139 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19141 displayMsg : 'Displaying {0} - {1} of {2}',
19143 * @cfg {String} emptyMsg
19144 * The message to display when no records are found (defaults to "No data to display")
19146 emptyMsg : 'No data to display',
19148 * Customizable piece of the default paging text (defaults to "Page")
19151 beforePageText : "Page",
19153 * Customizable piece of the default paging text (defaults to "of %0")
19156 afterPageText : "of {0}",
19158 * Customizable piece of the default paging text (defaults to "First Page")
19161 firstText : "First Page",
19163 * Customizable piece of the default paging text (defaults to "Previous Page")
19166 prevText : "Previous Page",
19168 * Customizable piece of the default paging text (defaults to "Next Page")
19171 nextText : "Next Page",
19173 * Customizable piece of the default paging text (defaults to "Last Page")
19176 lastText : "Last Page",
19178 * Customizable piece of the default paging text (defaults to "Refresh")
19181 refreshText : "Refresh",
19185 onRender : function(ct, position)
19187 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19188 this.navgroup.parentId = this.id;
19189 this.navgroup.onRender(this.el, null);
19190 // add the buttons to the navgroup
19192 if(this.displayInfo){
19193 Roo.log(this.el.select('ul.navbar-nav',true).first());
19194 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19195 this.displayEl = this.el.select('.x-paging-info', true).first();
19196 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19197 // this.displayEl = navel.el.select('span',true).first();
19203 Roo.each(_this.buttons, function(e){
19204 Roo.factory(e).onRender(_this.el, null);
19208 Roo.each(_this.toolbarItems, function(e) {
19209 _this.navgroup.addItem(e);
19212 this.first = this.navgroup.addItem({
19213 tooltip: this.firstText,
19215 icon : 'fa fa-backward',
19217 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19220 this.prev = this.navgroup.addItem({
19221 tooltip: this.prevText,
19223 icon : 'fa fa-step-backward',
19225 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
19227 //this.addSeparator();
19230 var field = this.navgroup.addItem( {
19232 cls : 'x-paging-position',
19234 html : this.beforePageText +
19235 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19236 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
19239 this.field = field.el.select('input', true).first();
19240 this.field.on("keydown", this.onPagingKeydown, this);
19241 this.field.on("focus", function(){this.dom.select();});
19244 this.afterTextEl = field.el.select('.x-paging-after',true).first();
19245 //this.field.setHeight(18);
19246 //this.addSeparator();
19247 this.next = this.navgroup.addItem({
19248 tooltip: this.nextText,
19250 html : ' <i class="fa fa-step-forward">',
19252 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
19254 this.last = this.navgroup.addItem({
19255 tooltip: this.lastText,
19256 icon : 'fa fa-forward',
19259 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
19261 //this.addSeparator();
19262 this.loading = this.navgroup.addItem({
19263 tooltip: this.refreshText,
19264 icon: 'fa fa-refresh',
19266 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19272 updateInfo : function(){
19273 if(this.displayEl){
19274 var count = this.ds.getCount();
19275 var msg = count == 0 ?
19279 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
19281 this.displayEl.update(msg);
19286 onLoad : function(ds, r, o){
19287 this.cursor = o.params ? o.params.start : 0;
19288 var d = this.getPageData(),
19292 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19293 this.field.dom.value = ap;
19294 this.first.setDisabled(ap == 1);
19295 this.prev.setDisabled(ap == 1);
19296 this.next.setDisabled(ap == ps);
19297 this.last.setDisabled(ap == ps);
19298 this.loading.enable();
19303 getPageData : function(){
19304 var total = this.ds.getTotalCount();
19307 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19308 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19313 onLoadError : function(){
19314 this.loading.enable();
19318 onPagingKeydown : function(e){
19319 var k = e.getKey();
19320 var d = this.getPageData();
19322 var v = this.field.dom.value, pageNum;
19323 if(!v || isNaN(pageNum = parseInt(v, 10))){
19324 this.field.dom.value = d.activePage;
19327 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19328 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19331 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))
19333 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19334 this.field.dom.value = pageNum;
19335 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19338 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19340 var v = this.field.dom.value, pageNum;
19341 var increment = (e.shiftKey) ? 10 : 1;
19342 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19344 if(!v || isNaN(pageNum = parseInt(v, 10))) {
19345 this.field.dom.value = d.activePage;
19348 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19350 this.field.dom.value = parseInt(v, 10) + increment;
19351 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19352 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19359 beforeLoad : function(){
19361 this.loading.disable();
19366 onClick : function(which){
19373 ds.load({params:{start: 0, limit: this.pageSize}});
19376 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19379 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19382 var total = ds.getTotalCount();
19383 var extra = total % this.pageSize;
19384 var lastStart = extra ? (total - extra) : total-this.pageSize;
19385 ds.load({params:{start: lastStart, limit: this.pageSize}});
19388 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19394 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19395 * @param {Roo.data.Store} store The data store to unbind
19397 unbind : function(ds){
19398 ds.un("beforeload", this.beforeLoad, this);
19399 ds.un("load", this.onLoad, this);
19400 ds.un("loadexception", this.onLoadError, this);
19401 ds.un("remove", this.updateInfo, this);
19402 ds.un("add", this.updateInfo, this);
19403 this.ds = undefined;
19407 * Binds the paging toolbar to the specified {@link Roo.data.Store}
19408 * @param {Roo.data.Store} store The data store to bind
19410 bind : function(ds){
19411 ds.on("beforeload", this.beforeLoad, this);
19412 ds.on("load", this.onLoad, this);
19413 ds.on("loadexception", this.onLoadError, this);
19414 ds.on("remove", this.updateInfo, this);
19415 ds.on("add", this.updateInfo, this);
19426 * @class Roo.bootstrap.MessageBar
19427 * @extends Roo.bootstrap.Component
19428 * Bootstrap MessageBar class
19429 * @cfg {String} html contents of the MessageBar
19430 * @cfg {String} weight (info | success | warning | danger) default info
19431 * @cfg {String} beforeClass insert the bar before the given class
19432 * @cfg {Boolean} closable (true | false) default false
19433 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19436 * Create a new Element
19437 * @param {Object} config The config object
19440 Roo.bootstrap.MessageBar = function(config){
19441 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19444 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
19450 beforeClass: 'bootstrap-sticky-wrap',
19452 getAutoCreate : function(){
19456 cls: 'alert alert-dismissable alert-' + this.weight,
19461 html: this.html || ''
19467 cfg.cls += ' alert-messages-fixed';
19481 onRender : function(ct, position)
19483 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19486 var cfg = Roo.apply({}, this.getAutoCreate());
19490 cfg.cls += ' ' + this.cls;
19493 cfg.style = this.style;
19495 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19497 this.el.setVisibilityMode(Roo.Element.DISPLAY);
19500 this.el.select('>button.close').on('click', this.hide, this);
19506 if (!this.rendered) {
19512 this.fireEvent('show', this);
19518 if (!this.rendered) {
19524 this.fireEvent('hide', this);
19527 update : function()
19529 // var e = this.el.dom.firstChild;
19531 // if(this.closable){
19532 // e = e.nextSibling;
19535 // e.data = this.html || '';
19537 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19553 * @class Roo.bootstrap.Graph
19554 * @extends Roo.bootstrap.Component
19555 * Bootstrap Graph class
19559 @cfg {String} graphtype bar | vbar | pie
19560 @cfg {number} g_x coodinator | centre x (pie)
19561 @cfg {number} g_y coodinator | centre y (pie)
19562 @cfg {number} g_r radius (pie)
19563 @cfg {number} g_height height of the chart (respected by all elements in the set)
19564 @cfg {number} g_width width of the chart (respected by all elements in the set)
19565 @cfg {Object} title The title of the chart
19568 -opts (object) options for the chart
19570 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19571 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19573 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.
19574 o stacked (boolean) whether or not to tread values as in a stacked bar chart
19576 o stretch (boolean)
19578 -opts (object) options for the pie
19581 o startAngle (number)
19582 o endAngle (number)
19586 * Create a new Input
19587 * @param {Object} config The config object
19590 Roo.bootstrap.Graph = function(config){
19591 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19597 * The img click event for the img.
19598 * @param {Roo.EventObject} e
19604 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
19615 //g_colors: this.colors,
19622 getAutoCreate : function(){
19633 onRender : function(ct,position){
19634 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19635 this.raphael = Raphael(this.el.dom);
19637 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19638 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19639 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19640 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19642 r.text(160, 10, "Single Series Chart").attr(txtattr);
19643 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19644 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19645 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19647 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19648 r.barchart(330, 10, 300, 220, data1);
19649 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19650 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19653 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19654 // r.barchart(30, 30, 560, 250, xdata, {
19655 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19656 // axis : "0 0 1 1",
19657 // axisxlabels : xdata
19658 // //yvalues : cols,
19661 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19663 // this.load(null,xdata,{
19664 // axis : "0 0 1 1",
19665 // axisxlabels : xdata
19670 load : function(graphtype,xdata,opts){
19671 this.raphael.clear();
19673 graphtype = this.graphtype;
19678 var r = this.raphael,
19679 fin = function () {
19680 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19682 fout = function () {
19683 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19685 pfin = function() {
19686 this.sector.stop();
19687 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19690 this.label[0].stop();
19691 this.label[0].attr({ r: 7.5 });
19692 this.label[1].attr({ "font-weight": 800 });
19695 pfout = function() {
19696 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19699 this.label[0].animate({ r: 5 }, 500, "bounce");
19700 this.label[1].attr({ "font-weight": 400 });
19706 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19709 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19712 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
19713 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19715 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19722 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19727 setTitle: function(o)
19732 initEvents: function() {
19735 this.el.on('click', this.onClick, this);
19739 onClick : function(e)
19741 Roo.log('img onclick');
19742 this.fireEvent('click', this, e);
19754 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19757 * @class Roo.bootstrap.dash.NumberBox
19758 * @extends Roo.bootstrap.Component
19759 * Bootstrap NumberBox class
19760 * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19761 * @cfg {String} headline Box headline
19762 * @cfg {String} content Box content
19763 * @cfg {String} icon Box icon
19764 * @cfg {String} footer Footer text
19765 * @cfg {String} fhref Footer href
19768 * Create a new NumberBox
19769 * @param {Object} config The config object
19773 Roo.bootstrap.dash.NumberBox = function(config){
19774 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19778 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
19788 getAutoCreate : function(){
19792 cls : 'small-box bg-' + this.bgcolor,
19800 cls : 'roo-headline',
19801 html : this.headline
19805 cls : 'roo-content',
19806 html : this.content
19820 cls : 'ion ' + this.icon
19829 cls : 'small-box-footer',
19830 href : this.fhref || '#',
19834 cfg.cn.push(footer);
19841 onRender : function(ct,position){
19842 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19849 setHeadline: function (value)
19851 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19854 setFooter: function (value, href)
19856 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19859 this.el.select('a.small-box-footer',true).first().attr('href', href);
19864 setContent: function (value)
19866 this.el.select('.roo-content',true).first().dom.innerHTML = value;
19869 initEvents: function()
19883 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19886 * @class Roo.bootstrap.dash.TabBox
19887 * @extends Roo.bootstrap.Component
19888 * Bootstrap TabBox class
19889 * @cfg {String} title Title of the TabBox
19890 * @cfg {String} icon Icon of the TabBox
19891 * @cfg {Boolean} showtabs (true|false) show the tabs default true
19892 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
19895 * Create a new TabBox
19896 * @param {Object} config The config object
19900 Roo.bootstrap.dash.TabBox = function(config){
19901 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19906 * When a pane is added
19907 * @param {Roo.bootstrap.dash.TabPane} pane
19911 * @event activatepane
19912 * When a pane is activated
19913 * @param {Roo.bootstrap.dash.TabPane} pane
19915 "activatepane" : true
19923 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
19928 tabScrollable : false,
19930 getChildContainer : function()
19932 return this.el.select('.tab-content', true).first();
19935 getAutoCreate : function(){
19939 cls: 'pull-left header',
19947 cls: 'fa ' + this.icon
19953 cls: 'nav nav-tabs pull-right',
19959 if(this.tabScrollable){
19966 cls: 'nav nav-tabs pull-right',
19977 cls: 'nav-tabs-custom',
19982 cls: 'tab-content no-padding',
19990 initEvents : function()
19992 //Roo.log('add add pane handler');
19993 this.on('addpane', this.onAddPane, this);
19996 * Updates the box title
19997 * @param {String} html to set the title to.
19999 setTitle : function(value)
20001 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
20003 onAddPane : function(pane)
20005 this.panes.push(pane);
20006 //Roo.log('addpane');
20008 // tabs are rendere left to right..
20009 if(!this.showtabs){
20013 var ctr = this.el.select('.nav-tabs', true).first();
20016 var existing = ctr.select('.nav-tab',true);
20017 var qty = existing.getCount();;
20020 var tab = ctr.createChild({
20022 cls : 'nav-tab' + (qty ? '' : ' active'),
20030 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
20033 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
20035 pane.el.addClass('active');
20040 onTabClick : function(ev,un,ob,pane)
20042 //Roo.log('tab - prev default');
20043 ev.preventDefault();
20046 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20047 pane.tab.addClass('active');
20048 //Roo.log(pane.title);
20049 this.getChildContainer().select('.tab-pane',true).removeClass('active');
20050 // technically we should have a deactivate event.. but maybe add later.
20051 // and it should not de-activate the selected tab...
20052 this.fireEvent('activatepane', pane);
20053 pane.el.addClass('active');
20054 pane.fireEvent('activate');
20059 getActivePane : function()
20062 Roo.each(this.panes, function(p) {
20063 if(p.el.hasClass('active')){
20084 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20086 * @class Roo.bootstrap.TabPane
20087 * @extends Roo.bootstrap.Component
20088 * Bootstrap TabPane class
20089 * @cfg {Boolean} active (false | true) Default false
20090 * @cfg {String} title title of panel
20094 * Create a new TabPane
20095 * @param {Object} config The config object
20098 Roo.bootstrap.dash.TabPane = function(config){
20099 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20105 * When a pane is activated
20106 * @param {Roo.bootstrap.dash.TabPane} pane
20113 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
20118 // the tabBox that this is attached to.
20121 getAutoCreate : function()
20129 cfg.cls += ' active';
20134 initEvents : function()
20136 //Roo.log('trigger add pane handler');
20137 this.parent().fireEvent('addpane', this)
20141 * Updates the tab title
20142 * @param {String} html to set the title to.
20144 setTitle: function(str)
20150 this.tab.select('a', true).first().dom.innerHTML = str;
20167 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20170 * @class Roo.bootstrap.menu.Menu
20171 * @extends Roo.bootstrap.Component
20172 * Bootstrap Menu class - container for Menu
20173 * @cfg {String} html Text of the menu
20174 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20175 * @cfg {String} icon Font awesome icon
20176 * @cfg {String} pos Menu align to (top | bottom) default bottom
20180 * Create a new Menu
20181 * @param {Object} config The config object
20185 Roo.bootstrap.menu.Menu = function(config){
20186 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20190 * @event beforeshow
20191 * Fires before this menu is displayed
20192 * @param {Roo.bootstrap.menu.Menu} this
20196 * @event beforehide
20197 * Fires before this menu is hidden
20198 * @param {Roo.bootstrap.menu.Menu} this
20203 * Fires after this menu is displayed
20204 * @param {Roo.bootstrap.menu.Menu} this
20209 * Fires after this menu is hidden
20210 * @param {Roo.bootstrap.menu.Menu} this
20215 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20216 * @param {Roo.bootstrap.menu.Menu} this
20217 * @param {Roo.EventObject} e
20224 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
20228 weight : 'default',
20233 getChildContainer : function() {
20234 if(this.isSubMenu){
20238 return this.el.select('ul.dropdown-menu', true).first();
20241 getAutoCreate : function()
20246 cls : 'roo-menu-text',
20254 cls : 'fa ' + this.icon
20265 cls : 'dropdown-button btn btn-' + this.weight,
20270 cls : 'dropdown-toggle btn btn-' + this.weight,
20280 cls : 'dropdown-menu'
20286 if(this.pos == 'top'){
20287 cfg.cls += ' dropup';
20290 if(this.isSubMenu){
20293 cls : 'dropdown-menu'
20300 onRender : function(ct, position)
20302 this.isSubMenu = ct.hasClass('dropdown-submenu');
20304 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20307 initEvents : function()
20309 if(this.isSubMenu){
20313 this.hidden = true;
20315 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20316 this.triggerEl.on('click', this.onTriggerPress, this);
20318 this.buttonEl = this.el.select('button.dropdown-button', true).first();
20319 this.buttonEl.on('click', this.onClick, this);
20325 if(this.isSubMenu){
20329 return this.el.select('ul.dropdown-menu', true).first();
20332 onClick : function(e)
20334 this.fireEvent("click", this, e);
20337 onTriggerPress : function(e)
20339 if (this.isVisible()) {
20346 isVisible : function(){
20347 return !this.hidden;
20352 this.fireEvent("beforeshow", this);
20354 this.hidden = false;
20355 this.el.addClass('open');
20357 Roo.get(document).on("mouseup", this.onMouseUp, this);
20359 this.fireEvent("show", this);
20366 this.fireEvent("beforehide", this);
20368 this.hidden = true;
20369 this.el.removeClass('open');
20371 Roo.get(document).un("mouseup", this.onMouseUp);
20373 this.fireEvent("hide", this);
20376 onMouseUp : function()
20390 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20393 * @class Roo.bootstrap.menu.Item
20394 * @extends Roo.bootstrap.Component
20395 * Bootstrap MenuItem class
20396 * @cfg {Boolean} submenu (true | false) default false
20397 * @cfg {String} html text of the item
20398 * @cfg {String} href the link
20399 * @cfg {Boolean} disable (true | false) default false
20400 * @cfg {Boolean} preventDefault (true | false) default true
20401 * @cfg {String} icon Font awesome icon
20402 * @cfg {String} pos Submenu align to (left | right) default right
20406 * Create a new Item
20407 * @param {Object} config The config object
20411 Roo.bootstrap.menu.Item = function(config){
20412 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20416 * Fires when the mouse is hovering over this menu
20417 * @param {Roo.bootstrap.menu.Item} this
20418 * @param {Roo.EventObject} e
20423 * Fires when the mouse exits this menu
20424 * @param {Roo.bootstrap.menu.Item} this
20425 * @param {Roo.EventObject} e
20431 * The raw click event for the entire grid.
20432 * @param {Roo.EventObject} e
20438 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
20443 preventDefault: true,
20448 getAutoCreate : function()
20453 cls : 'roo-menu-item-text',
20461 cls : 'fa ' + this.icon
20470 href : this.href || '#',
20477 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20481 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20483 if(this.pos == 'left'){
20484 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20491 initEvents : function()
20493 this.el.on('mouseover', this.onMouseOver, this);
20494 this.el.on('mouseout', this.onMouseOut, this);
20496 this.el.select('a', true).first().on('click', this.onClick, this);
20500 onClick : function(e)
20502 if(this.preventDefault){
20503 e.preventDefault();
20506 this.fireEvent("click", this, e);
20509 onMouseOver : function(e)
20511 if(this.submenu && this.pos == 'left'){
20512 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20515 this.fireEvent("mouseover", this, e);
20518 onMouseOut : function(e)
20520 this.fireEvent("mouseout", this, e);
20532 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20535 * @class Roo.bootstrap.menu.Separator
20536 * @extends Roo.bootstrap.Component
20537 * Bootstrap Separator class
20540 * Create a new Separator
20541 * @param {Object} config The config object
20545 Roo.bootstrap.menu.Separator = function(config){
20546 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20549 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
20551 getAutoCreate : function(){