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...
44 * Initialize Events for the element
46 initEvents : function() { },
52 can_build_overlaid : true,
59 // returns the parent component..
60 return Roo.ComponentMgr.get(this.parentId)
66 onRender : function(ct, position)
68 // Roo.log("Call onRender: " + this.xtype);
70 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
73 if (this.el.attr('xtype')) {
74 this.el.attr('xtypex', this.el.attr('xtype'));
75 this.el.dom.removeAttribute('xtype');
85 var cfg = Roo.apply({}, this.getAutoCreate());
88 // fill in the extra attributes
89 if (this.xattr && typeof(this.xattr) =='object') {
90 for (var i in this.xattr) {
91 cfg[i] = this.xattr[i];
96 cfg.dataId = this.dataId;
100 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
103 if (this.style) { // fixme needs to support more complex style data.
104 cfg.style = this.style;
108 cfg.name = this.name;
112 this.tooltipEl().attr('tooltip', this.tooltip);
115 this.el = ct.createChild(cfg, position);
117 if(this.tabIndex !== undefined){
118 this.el.dom.setAttribute('tabIndex', this.tabIndex);
125 * Fetch the element to add children to
126 * @return {Roo.Element} defaults to this.el
128 getChildContainer : function()
133 * Fetch the element to display the tooltip on.
134 * @return {Roo.Element} defaults to this.el
136 getTooltipEl : function()
141 addxtype : function(tree,cntr)
145 cn = Roo.factory(tree);
147 cn.parentType = this.xtype; //??
148 cn.parentId = this.id;
150 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
152 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
154 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
156 var build_from_html = Roo.XComponent.build_from_html;
158 var is_body = (tree.xtype == 'Body') ;
160 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
162 var self_cntr_el = Roo.get(this[cntr](false));
164 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
165 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
166 return this.addxtypeChild(tree,cntr);
169 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
172 return this.addxtypeChild(Roo.apply({}, tree),cntr);
175 Roo.log('skipping render');
183 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
189 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
193 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
198 addxtypeChild : function (tree, cntr)
200 Roo.log('addxtypeChild:' + cntr);
202 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
205 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
206 (typeof(tree['flexy:foreach']) != 'undefined');
210 skip_children = false;
211 // render the element if it's not BODY.
212 if (tree.xtype != 'Body') {
214 cn = Roo.factory(tree);
216 cn.parentType = this.xtype; //??
217 cn.parentId = this.id;
219 var build_from_html = Roo.XComponent.build_from_html;
222 // does the container contain child eleemnts with 'xtype' attributes.
223 // that match this xtype..
224 // note - when we render we create these as well..
225 // so we should check to see if body has xtype set.
226 if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
228 var self_cntr_el = Roo.get(this[cntr](false));
229 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
232 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
233 // and are not displayed -this causes this to use up the wrong element when matching.
234 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
237 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
238 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
244 //echild.dom.removeAttribute('xtype');
246 Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
247 Roo.log(self_cntr_el);
255 // if object has flexy:if - then it may or may not be rendered.
256 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
257 // skip a flexy if element.
258 Roo.log('skipping render');
261 Roo.log('skipping all children');
262 skip_children = true;
267 // actually if flexy:foreach is found, we really want to create
268 // multiple copies here...
270 //Roo.log(this[cntr]());
271 cn.render(this[cntr](true));
273 // then add the element..
281 if (typeof (tree.menu) != 'undefined') {
282 tree.menu.parentType = cn.xtype;
283 tree.menu.triggerEl = cn.el;
284 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
288 if (!tree.items || !tree.items.length) {
292 var items = tree.items;
295 //Roo.log(items.length);
297 if (!skip_children) {
298 for(var i =0;i < items.length;i++) {
299 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
321 * @class Roo.bootstrap.Body
322 * @extends Roo.bootstrap.Component
323 * Bootstrap Body class
327 * @param {Object} config The config object
330 Roo.bootstrap.Body = function(config){
331 Roo.bootstrap.Body.superclass.constructor.call(this, config);
332 this.el = Roo.get(document.body);
333 if (this.cls && this.cls.length) {
334 Roo.get(document.body).addClass(this.cls);
338 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
343 onRender : function(ct, position)
345 /* Roo.log("Roo.bootstrap.Body - onRender");
346 if (this.cls && this.cls.length) {
347 Roo.get(document.body).addClass(this.cls);
367 * @class Roo.bootstrap.ButtonGroup
368 * @extends Roo.bootstrap.Component
369 * Bootstrap ButtonGroup class
370 * @cfg {String} size lg | sm | xs (default empty normal)
371 * @cfg {String} align vertical | justified (default none)
372 * @cfg {String} direction up | down (default down)
373 * @cfg {Boolean} toolbar false | true
374 * @cfg {Boolean} btn true | false
379 * @param {Object} config The config object
382 Roo.bootstrap.ButtonGroup = function(config){
383 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
386 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
394 getAutoCreate : function(){
400 cfg.html = this.html || cfg.html;
411 if (['vertical','justified'].indexOf(this.align)!==-1) {
412 cfg.cls = 'btn-group-' + this.align;
414 if (this.align == 'justified') {
415 console.log(this.items);
419 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
420 cfg.cls += ' btn-group-' + this.size;
423 if (this.direction == 'up') {
424 cfg.cls += ' dropup' ;
440 * @class Roo.bootstrap.Button
441 * @extends Roo.bootstrap.Component
442 * Bootstrap Button class
443 * @cfg {String} html The button content
444 * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
445 * @cfg {String} size empty | lg | sm | xs
446 * @cfg {String} tag empty | a | input | submit
447 * @cfg {String} href empty or href
448 * @cfg {Boolean} disabled false | true
449 * @cfg {Boolean} isClose false | true
450 * @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
451 * @cfg {String} badge text for badge
452 * @cfg {String} theme default (or empty) | glow
453 * @cfg {Boolean} inverse false | true
454 * @cfg {Boolean} toggle false | true
455 * @cfg {String} ontext text for on toggle state
456 * @cfg {String} offtext text for off toggle state
457 * @cfg {Boolean} defaulton true | false
458 * @cfg {Boolean} preventDefault (true | false) default true
459 * @cfg {Boolean} removeClass true | false remove the standard class..
460 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
463 * Create a new button
464 * @param {Object} config The config object
468 Roo.bootstrap.Button = function(config){
469 Roo.bootstrap.Button.superclass.constructor.call(this, config);
474 * When a butotn is pressed
475 * @param {Roo.EventObject} e
480 * After the button has been toggles
481 * @param {Roo.EventObject} e
482 * @param {boolean} pressed (also available as button.pressed)
488 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
506 preventDefault: true,
515 getAutoCreate : function(){
523 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
524 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
529 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
531 if (this.toggle == true) {
534 cls: 'slider-frame roo-button',
539 'data-off-text':'OFF',
540 cls: 'slider-button',
546 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
547 cfg.cls += ' '+this.weight;
556 cfg["aria-hidden"] = true;
558 cfg.html = "×";
564 if (this.theme==='default') {
565 cfg.cls = 'btn roo-button';
567 //if (this.parentType != 'Navbar') {
568 this.weight = this.weight.length ? this.weight : 'default';
570 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
572 cfg.cls += ' btn-' + this.weight;
574 } else if (this.theme==='glow') {
577 cfg.cls = 'btn-glow roo-button';
579 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
581 cfg.cls += ' ' + this.weight;
587 this.cls += ' inverse';
592 cfg.cls += ' active';
596 cfg.disabled = 'disabled';
600 Roo.log('changing to ul' );
602 this.glyphicon = 'caret';
605 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
607 //gsRoo.log(this.parentType);
608 if (this.parentType === 'Navbar' && !this.parent().bar) {
609 Roo.log('changing to li?');
618 href : this.href || '#'
621 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
622 cfg.cls += ' dropdown';
629 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
631 if (this.glyphicon) {
632 cfg.html = ' ' + cfg.html;
637 cls: 'glyphicon glyphicon-' + this.glyphicon
647 // cfg.cls='btn roo-button';
651 var value = cfg.html;
656 cls: 'glyphicon glyphicon-' + this.glyphicon,
675 cfg.cls += ' dropdown';
676 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
679 if (cfg.tag !== 'a' && this.href !== '') {
680 throw "Tag must be a to set href.";
681 } else if (this.href.length > 0) {
682 cfg.href = this.href;
685 if(this.removeClass){
690 cfg.target = this.target;
695 initEvents: function() {
696 // Roo.log('init events?');
697 // Roo.log(this.el.dom);
700 if (typeof (this.menu) != 'undefined') {
701 this.menu.parentType = this.xtype;
702 this.menu.triggerEl = this.el;
703 this.addxtype(Roo.apply({}, this.menu));
707 if (this.el.hasClass('roo-button')) {
708 this.el.on('click', this.onClick, this);
710 this.el.select('.roo-button').on('click', this.onClick, this);
713 if(this.removeClass){
714 this.el.on('click', this.onClick, this);
717 this.el.enableDisplayMode();
720 onClick : function(e)
726 Roo.log('button on click ');
727 if(this.preventDefault){
730 if (this.pressed === true || this.pressed === false) {
731 this.pressed = !this.pressed;
732 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
733 this.fireEvent('toggle', this, e, this.pressed);
737 this.fireEvent('click', this, e);
741 * Enables this button
745 this.disabled = false;
746 this.el.removeClass('disabled');
750 * Disable this button
754 this.disabled = true;
755 this.el.addClass('disabled');
758 * sets the active state on/off,
759 * @param {Boolean} state (optional) Force a particular state
761 setActive : function(v) {
763 this.el[v ? 'addClass' : 'removeClass']('active');
766 * toggles the current active state
768 toggleActive : function()
770 var active = this.el.hasClass('active');
771 this.setActive(!active);
775 setText : function(str)
777 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
781 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
804 * @class Roo.bootstrap.Column
805 * @extends Roo.bootstrap.Component
806 * Bootstrap Column class
807 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
808 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
809 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
810 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
811 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
812 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
813 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
814 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
817 * @cfg {Boolean} hidden (true|false) hide the element
818 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
819 * @cfg {String} fa (ban|check|...) font awesome icon
820 * @cfg {Number} fasize (1|2|....) font awsome size
822 * @cfg {String} icon (info-sign|check|...) glyphicon name
824 * @cfg {String} html content of column.
827 * Create a new Column
828 * @param {Object} config The config object
831 Roo.bootstrap.Column = function(config){
832 Roo.bootstrap.Column.superclass.constructor.call(this, config);
835 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
853 getAutoCreate : function(){
854 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
862 ['xs','sm','md','lg'].map(function(size){
863 //Roo.log( size + ':' + settings[size]);
865 if (settings[size+'off'] !== false) {
866 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
869 if (settings[size] === false) {
872 Roo.log(settings[size]);
873 if (!settings[size]) { // 0 = hidden
874 cfg.cls += ' hidden-' + size;
877 cfg.cls += ' col-' + size + '-' + settings[size];
882 cfg.cls += ' hidden';
885 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
886 cfg.cls +=' alert alert-' + this.alert;
890 if (this.html.length) {
891 cfg.html = this.html;
895 if (this.fasize > 1) {
896 fasize = ' fa-' + this.fasize + 'x';
898 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
903 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
922 * @class Roo.bootstrap.Container
923 * @extends Roo.bootstrap.Component
924 * Bootstrap Container class
925 * @cfg {Boolean} jumbotron is it a jumbotron element
926 * @cfg {String} html content of element
927 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
928 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
929 * @cfg {String} header content of header (for panel)
930 * @cfg {String} footer content of footer (for panel)
931 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
932 * @cfg {String} tag (header|aside|section) type of HTML tag.
933 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
934 * @cfg {String} fa (ban|check|...) font awesome icon
935 * @cfg {String} icon (info-sign|check|...) glyphicon name
936 * @cfg {Boolean} hidden (true|false) hide the element
940 * Create a new Container
941 * @param {Object} config The config object
944 Roo.bootstrap.Container = function(config){
945 Roo.bootstrap.Container.superclass.constructor.call(this, config);
948 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
962 getChildContainer : function() {
968 if (this.panel.length) {
969 return this.el.select('.panel-body',true).first();
976 getAutoCreate : function(){
979 tag : this.tag || 'div',
983 if (this.jumbotron) {
984 cfg.cls = 'jumbotron';
989 // - this is applied by the parent..
991 // cfg.cls = this.cls + '';
994 if (this.sticky.length) {
996 var bd = Roo.get(document.body);
997 if (!bd.hasClass('bootstrap-sticky')) {
998 bd.addClass('bootstrap-sticky');
999 Roo.select('html',true).setStyle('height', '100%');
1002 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1006 if (this.well.length) {
1007 switch (this.well) {
1010 cfg.cls +=' well well-' +this.well;
1019 cfg.cls += ' hidden';
1023 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1024 cfg.cls +=' alert alert-' + this.alert;
1029 if (this.panel.length) {
1030 cfg.cls += ' panel panel-' + this.panel;
1032 if (this.header.length) {
1035 cls : 'panel-heading',
1038 cls : 'panel-title',
1051 if (this.footer.length) {
1053 cls : 'panel-footer',
1062 body.html = this.html || cfg.html;
1063 // prefix with the icons..
1065 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1068 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1073 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1074 cfg.cls = 'container';
1080 titleEl : function()
1082 if(!this.el || !this.panel.length || !this.header.length){
1086 return this.el.select('.panel-title',true).first();
1089 setTitle : function(v)
1091 var titleEl = this.titleEl();
1097 titleEl.dom.innerHTML = v;
1100 getTitle : function()
1103 var titleEl = this.titleEl();
1109 return titleEl.dom.innerHTML;
1123 * @class Roo.bootstrap.Img
1124 * @extends Roo.bootstrap.Component
1125 * Bootstrap Img class
1126 * @cfg {Boolean} imgResponsive false | true
1127 * @cfg {String} border rounded | circle | thumbnail
1128 * @cfg {String} src image source
1129 * @cfg {String} alt image alternative text
1130 * @cfg {String} href a tag href
1131 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1134 * Create a new Input
1135 * @param {Object} config The config object
1138 Roo.bootstrap.Img = function(config){
1139 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1145 * The img click event for the img.
1146 * @param {Roo.EventObject} e
1152 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1154 imgResponsive: true,
1160 getAutoCreate : function(){
1164 cls: (this.imgResponsive) ? 'img-responsive' : '',
1168 cfg.html = this.html || cfg.html;
1170 cfg.src = this.src || cfg.src;
1172 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1173 cfg.cls += ' img-' + this.border;
1190 a.target = this.target;
1196 return (this.href) ? a : cfg;
1199 initEvents: function() {
1202 this.el.on('click', this.onClick, this);
1206 onClick : function(e)
1208 Roo.log('img onclick');
1209 this.fireEvent('click', this, e);
1223 * @class Roo.bootstrap.Link
1224 * @extends Roo.bootstrap.Component
1225 * Bootstrap Link Class
1226 * @cfg {String} alt image alternative text
1227 * @cfg {String} href a tag href
1228 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1229 * @cfg {String} html the content of the link.
1230 * @cfg {String} anchor name for the anchor link
1232 * @cfg {Boolean} preventDefault (true | false) default false
1236 * Create a new Input
1237 * @param {Object} config The config object
1240 Roo.bootstrap.Link = function(config){
1241 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1247 * The img click event for the img.
1248 * @param {Roo.EventObject} e
1254 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1258 preventDefault: false,
1262 getAutoCreate : function()
1268 // anchor's do not require html/href...
1269 if (this.anchor === false) {
1270 cfg.html = this.html || 'html-missing';
1271 cfg.href = this.href || '#';
1273 cfg.name = this.anchor;
1274 if (this.html !== false) {
1275 cfg.html = this.html;
1277 if (this.href !== false) {
1278 cfg.href = this.href;
1282 if(this.alt !== false){
1287 if(this.target !== false) {
1288 cfg.target = this.target;
1294 initEvents: function() {
1296 if(!this.href || this.preventDefault){
1297 this.el.on('click', this.onClick, this);
1301 onClick : function(e)
1303 if(this.preventDefault){
1306 //Roo.log('img onclick');
1307 this.fireEvent('click', this, e);
1320 * @class Roo.bootstrap.Header
1321 * @extends Roo.bootstrap.Component
1322 * Bootstrap Header class
1323 * @cfg {String} html content of header
1324 * @cfg {Number} level (1|2|3|4|5|6) default 1
1327 * Create a new Header
1328 * @param {Object} config The config object
1332 Roo.bootstrap.Header = function(config){
1333 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1336 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1344 getAutoCreate : function(){
1347 tag: 'h' + (1 *this.level),
1348 html: this.html || 'fill in html'
1360 * Ext JS Library 1.1.1
1361 * Copyright(c) 2006-2007, Ext JS, LLC.
1363 * Originally Released Under LGPL - original licence link has changed is not relivant.
1366 * <script type="text/javascript">
1370 * @class Roo.bootstrap.MenuMgr
1371 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1374 Roo.bootstrap.MenuMgr = function(){
1375 var menus, active, groups = {}, attached = false, lastShow = new Date();
1377 // private - called when first menu is created
1380 active = new Roo.util.MixedCollection();
1381 Roo.get(document).addKeyListener(27, function(){
1382 if(active.length > 0){
1390 if(active && active.length > 0){
1391 var c = active.clone();
1401 if(active.length < 1){
1402 Roo.get(document).un("mouseup", onMouseDown);
1410 var last = active.last();
1411 lastShow = new Date();
1414 Roo.get(document).on("mouseup", onMouseDown);
1419 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1420 m.parentMenu.activeChild = m;
1421 }else if(last && last.isVisible()){
1422 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1427 function onBeforeHide(m){
1429 m.activeChild.hide();
1431 if(m.autoHideTimer){
1432 clearTimeout(m.autoHideTimer);
1433 delete m.autoHideTimer;
1438 function onBeforeShow(m){
1439 var pm = m.parentMenu;
1440 if(!pm && !m.allowOtherMenus){
1442 }else if(pm && pm.activeChild && active != m){
1443 pm.activeChild.hide();
1448 function onMouseDown(e){
1449 Roo.log("on MouseDown");
1450 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1458 function onBeforeCheck(mi, state){
1460 var g = groups[mi.group];
1461 for(var i = 0, l = g.length; i < l; i++){
1463 g[i].setChecked(false);
1472 * Hides all menus that are currently visible
1474 hideAll : function(){
1479 register : function(menu){
1483 menus[menu.id] = menu;
1484 menu.on("beforehide", onBeforeHide);
1485 menu.on("hide", onHide);
1486 menu.on("beforeshow", onBeforeShow);
1487 menu.on("show", onShow);
1489 if(g && menu.events["checkchange"]){
1493 groups[g].push(menu);
1494 menu.on("checkchange", onCheck);
1499 * Returns a {@link Roo.menu.Menu} object
1500 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1501 * be used to generate and return a new Menu instance.
1503 get : function(menu){
1504 if(typeof menu == "string"){ // menu id
1506 }else if(menu.events){ // menu instance
1509 /*else if(typeof menu.length == 'number'){ // array of menu items?
1510 return new Roo.bootstrap.Menu({items:menu});
1511 }else{ // otherwise, must be a config
1512 return new Roo.bootstrap.Menu(menu);
1519 unregister : function(menu){
1520 delete menus[menu.id];
1521 menu.un("beforehide", onBeforeHide);
1522 menu.un("hide", onHide);
1523 menu.un("beforeshow", onBeforeShow);
1524 menu.un("show", onShow);
1526 if(g && menu.events["checkchange"]){
1527 groups[g].remove(menu);
1528 menu.un("checkchange", onCheck);
1533 registerCheckable : function(menuItem){
1534 var g = menuItem.group;
1539 groups[g].push(menuItem);
1540 menuItem.on("beforecheckchange", onBeforeCheck);
1545 unregisterCheckable : function(menuItem){
1546 var g = menuItem.group;
1548 groups[g].remove(menuItem);
1549 menuItem.un("beforecheckchange", onBeforeCheck);
1561 * @class Roo.bootstrap.Menu
1562 * @extends Roo.bootstrap.Component
1563 * Bootstrap Menu class - container for MenuItems
1564 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1568 * @param {Object} config The config object
1572 Roo.bootstrap.Menu = function(config){
1573 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1574 if (this.registerMenu) {
1575 Roo.bootstrap.MenuMgr.register(this);
1580 * Fires before this menu is displayed
1581 * @param {Roo.menu.Menu} this
1586 * Fires before this menu is hidden
1587 * @param {Roo.menu.Menu} this
1592 * Fires after this menu is displayed
1593 * @param {Roo.menu.Menu} this
1598 * Fires after this menu is hidden
1599 * @param {Roo.menu.Menu} this
1604 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1605 * @param {Roo.menu.Menu} this
1606 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1607 * @param {Roo.EventObject} e
1612 * Fires when the mouse is hovering over this menu
1613 * @param {Roo.menu.Menu} this
1614 * @param {Roo.EventObject} e
1615 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1620 * Fires when the mouse exits this menu
1621 * @param {Roo.menu.Menu} this
1622 * @param {Roo.EventObject} e
1623 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1628 * Fires when a menu item contained in this menu is clicked
1629 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1630 * @param {Roo.EventObject} e
1634 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1637 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1641 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1644 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1646 registerMenu : true,
1648 menuItems :false, // stores the menu items..
1654 getChildContainer : function() {
1658 getAutoCreate : function(){
1660 //if (['right'].indexOf(this.align)!==-1) {
1661 // cfg.cn[1].cls += ' pull-right'
1667 cls : 'dropdown-menu' ,
1668 style : 'z-index:1000'
1672 if (this.type === 'submenu') {
1673 cfg.cls = 'submenu active';
1675 if (this.type === 'treeview') {
1676 cfg.cls = 'treeview-menu';
1681 initEvents : function() {
1683 // Roo.log("ADD event");
1684 // Roo.log(this.triggerEl.dom);
1685 this.triggerEl.on('click', this.onTriggerPress, this);
1686 this.triggerEl.addClass('dropdown-toggle');
1687 this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1689 this.el.on("mouseover", this.onMouseOver, this);
1690 this.el.on("mouseout", this.onMouseOut, this);
1694 findTargetItem : function(e){
1695 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1699 //Roo.log(t); Roo.log(t.id);
1701 //Roo.log(this.menuitems);
1702 return this.menuitems.get(t.id);
1704 //return this.items.get(t.menuItemId);
1709 onClick : function(e){
1710 Roo.log("menu.onClick");
1711 var t = this.findTargetItem(e);
1712 if(!t || t.isContainer){
1717 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
1718 if(t == this.activeItem && t.shouldDeactivate(e)){
1719 this.activeItem.deactivate();
1720 delete this.activeItem;
1724 this.setActiveItem(t, true);
1732 Roo.log('pass click event');
1736 this.fireEvent("click", this, t, e);
1740 onMouseOver : function(e){
1741 var t = this.findTargetItem(e);
1744 // if(t.canActivate && !t.disabled){
1745 // this.setActiveItem(t, true);
1749 this.fireEvent("mouseover", this, e, t);
1751 isVisible : function(){
1752 return !this.hidden;
1754 onMouseOut : function(e){
1755 var t = this.findTargetItem(e);
1758 // if(t == this.activeItem && t.shouldDeactivate(e)){
1759 // this.activeItem.deactivate();
1760 // delete this.activeItem;
1763 this.fireEvent("mouseout", this, e, t);
1768 * Displays this menu relative to another element
1769 * @param {String/HTMLElement/Roo.Element} element The element to align to
1770 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1771 * the element (defaults to this.defaultAlign)
1772 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1774 show : function(el, pos, parentMenu){
1775 this.parentMenu = parentMenu;
1779 this.fireEvent("beforeshow", this);
1780 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1783 * Displays this menu at a specific xy position
1784 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1785 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1787 showAt : function(xy, parentMenu, /* private: */_e){
1788 this.parentMenu = parentMenu;
1793 this.fireEvent("beforeshow", this);
1795 //xy = this.el.adjustForConstraints(xy);
1797 //this.el.setXY(xy);
1799 this.hideMenuItems();
1800 this.hidden = false;
1801 this.triggerEl.addClass('open');
1803 this.fireEvent("show", this);
1809 this.doFocus.defer(50, this);
1813 doFocus : function(){
1815 this.focusEl.focus();
1820 * Hides this menu and optionally all parent menus
1821 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1823 hide : function(deep){
1825 this.hideMenuItems();
1826 if(this.el && this.isVisible()){
1827 this.fireEvent("beforehide", this);
1828 if(this.activeItem){
1829 this.activeItem.deactivate();
1830 this.activeItem = null;
1832 this.triggerEl.removeClass('open');;
1834 this.fireEvent("hide", this);
1836 if(deep === true && this.parentMenu){
1837 this.parentMenu.hide(true);
1841 onTriggerPress : function(e)
1844 Roo.log('trigger press');
1845 //Roo.log(e.getTarget());
1846 // Roo.log(this.triggerEl.dom);
1847 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1850 if (this.isVisible()) {
1854 this.show(this.triggerEl, false, false);
1863 hideMenuItems : function()
1865 //$(backdrop).remove()
1866 Roo.select('.open',true).each(function(aa) {
1868 aa.removeClass('open');
1869 //var parent = getParent($(this))
1870 //var relatedTarget = { relatedTarget: this }
1872 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1873 //if (e.isDefaultPrevented()) return
1874 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1877 addxtypeChild : function (tree, cntr) {
1878 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1880 this.menuitems.add(comp);
1901 * @class Roo.bootstrap.MenuItem
1902 * @extends Roo.bootstrap.Component
1903 * Bootstrap MenuItem class
1904 * @cfg {String} html the menu label
1905 * @cfg {String} href the link
1906 * @cfg {Boolean} preventDefault (true | false) default true
1907 * @cfg {Boolean} isContainer (true | false) default false
1911 * Create a new MenuItem
1912 * @param {Object} config The config object
1916 Roo.bootstrap.MenuItem = function(config){
1917 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1922 * The raw click event for the entire grid.
1923 * @param {Roo.EventObject} e
1929 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
1933 preventDefault: true,
1934 isContainer : false,
1936 getAutoCreate : function(){
1938 if(this.isContainer){
1941 cls: 'dropdown-menu-item'
1947 cls: 'dropdown-menu-item',
1956 if (this.parent().type == 'treeview') {
1957 cfg.cls = 'treeview-menu';
1960 cfg.cn[0].href = this.href || cfg.cn[0].href ;
1961 cfg.cn[0].html = this.html || cfg.cn[0].html ;
1965 initEvents: function() {
1967 //this.el.select('a').on('click', this.onClick, this);
1970 onClick : function(e)
1972 Roo.log('item on click ');
1973 //if(this.preventDefault){
1974 // e.preventDefault();
1976 //this.parent().hideMenuItems();
1978 this.fireEvent('click', this, e);
1997 * @class Roo.bootstrap.MenuSeparator
1998 * @extends Roo.bootstrap.Component
1999 * Bootstrap MenuSeparator class
2002 * Create a new MenuItem
2003 * @param {Object} config The config object
2007 Roo.bootstrap.MenuSeparator = function(config){
2008 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2011 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2013 getAutoCreate : function(){
2028 <div class="modal fade">
2029 <div class="modal-dialog">
2030 <div class="modal-content">
2031 <div class="modal-header">
2032 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
2033 <h4 class="modal-title">Modal title</h4>
2035 <div class="modal-body">
2036 <p>One fine body…</p>
2038 <div class="modal-footer">
2039 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2040 <button type="button" class="btn btn-primary">Save changes</button>
2042 </div><!-- /.modal-content -->
2043 </div><!-- /.modal-dialog -->
2044 </div><!-- /.modal -->
2054 * @class Roo.bootstrap.Modal
2055 * @extends Roo.bootstrap.Component
2056 * Bootstrap Modal class
2057 * @cfg {String} title Title of dialog
2058 * @cfg {Boolean} specificTitle (true|false) default false
2059 * @cfg {Array} buttons Array of buttons or standard button set..
2060 * @cfg {String} buttonPosition (left|right|center) default right
2061 * @cfg {Boolean} animate (true | false) default true
2064 * Create a new Modal Dialog
2065 * @param {Object} config The config object
2068 Roo.bootstrap.Modal = function(config){
2069 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2074 * The raw btnclick event for the button
2075 * @param {Roo.EventObject} e
2079 this.buttons = this.buttons || [];
2082 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2084 title : 'test dialog',
2091 specificTitle: false,
2093 buttonPosition: 'right',
2097 onRender : function(ct, position)
2099 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2102 var cfg = Roo.apply({}, this.getAutoCreate());
2105 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2107 //if (!cfg.name.length) {
2111 cfg.cls += ' ' + this.cls;
2114 cfg.style = this.style;
2116 this.el = Roo.get(document.body).createChild(cfg, position);
2118 //var type = this.el.dom.type;
2120 if(this.tabIndex !== undefined){
2121 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2126 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2127 this.maskEl.enableDisplayMode("block");
2129 //this.el.addClass("x-dlg-modal");
2131 if (this.buttons.length) {
2132 Roo.each(this.buttons, function(bb) {
2133 b = Roo.apply({}, bb);
2134 b.xns = b.xns || Roo.bootstrap;
2135 b.xtype = b.xtype || 'Button';
2136 if (typeof(b.listeners) == 'undefined') {
2137 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2140 var btn = Roo.factory(b);
2142 btn.onRender(this.el.select('.modal-footer div').first());
2146 // render the children.
2149 if(typeof(this.items) != 'undefined'){
2150 var items = this.items;
2153 for(var i =0;i < items.length;i++) {
2154 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2158 this.items = nitems;
2160 this.body = this.el.select('.modal-body',true).first();
2161 this.close = this.el.select('.modal-header .close', true).first();
2162 this.footer = this.el.select('.modal-footer',true).first();
2164 //this.el.addClass([this.fieldClass, this.cls]);
2167 getAutoCreate : function(){
2172 html : this.html || ''
2177 cls : 'modal-title',
2181 if(this.specificTitle){
2187 style : 'display: none',
2190 cls: "modal-dialog",
2193 cls : "modal-content",
2196 cls : 'modal-header',
2208 cls : 'modal-footer',
2212 cls: 'btn-' + this.buttonPosition
2229 modal.cls += ' fade';
2235 getChildContainer : function() {
2237 return this.el.select('.modal-body',true).first();
2240 getButtonContainer : function() {
2241 return this.el.select('.modal-footer div',true).first();
2244 initEvents : function()
2246 this.el.select('.modal-header .close').on('click', this.hide, this);
2248 // this.addxtype(this);
2252 if (!this.rendered) {
2256 this.el.setStyle('display', 'block');
2260 (function(){ _this.el.addClass('in'); }).defer(50);
2262 this.el.addClass('in');
2265 Roo.get(document.body).addClass("x-body-masked");
2266 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2268 this.el.setStyle('zIndex', '10001');
2269 this.fireEvent('show', this);
2276 Roo.get(document.body).removeClass("x-body-masked");
2277 this.el.removeClass('in');
2281 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2283 this.el.setStyle('display', 'none');
2286 this.fireEvent('hide', this);
2289 addButton : function(str, cb)
2293 var b = Roo.apply({}, { html : str } );
2294 b.xns = b.xns || Roo.bootstrap;
2295 b.xtype = b.xtype || 'Button';
2296 if (typeof(b.listeners) == 'undefined') {
2297 b.listeners = { click : cb.createDelegate(this) };
2300 var btn = Roo.factory(b);
2302 btn.onRender(this.el.select('.modal-footer div').first());
2308 setDefaultButton : function(btn)
2310 //this.el.select('.modal-footer').()
2312 resizeTo: function(w,h)
2316 setContentSize : function(w, h)
2320 onButtonClick: function(btn,e)
2323 this.fireEvent('btnclick', btn.name, e);
2325 setTitle: function(str) {
2326 this.el.select('.modal-title',true).first().dom.innerHTML = str;
2332 Roo.apply(Roo.bootstrap.Modal, {
2334 * Button config that displays a single OK button
2343 * Button config that displays Yes and No buttons
2359 * Button config that displays OK and Cancel buttons
2374 * Button config that displays Yes, No and Cancel buttons
2396 * messagebox - can be used as a replace
2400 * @class Roo.MessageBox
2401 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2405 Roo.Msg.alert('Status', 'Changes saved successfully.');
2407 // Prompt for user data:
2408 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2410 // process text value...
2414 // Show a dialog using config options:
2416 title:'Save Changes?',
2417 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2418 buttons: Roo.Msg.YESNOCANCEL,
2425 Roo.bootstrap.MessageBox = function(){
2426 var dlg, opt, mask, waitTimer;
2427 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2428 var buttons, activeTextEl, bwidth;
2432 var handleButton = function(button){
2434 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2438 var handleHide = function(){
2440 dlg.el.removeClass(opt.cls);
2443 // Roo.TaskMgr.stop(waitTimer);
2444 // waitTimer = null;
2449 var updateButtons = function(b){
2452 buttons["ok"].hide();
2453 buttons["cancel"].hide();
2454 buttons["yes"].hide();
2455 buttons["no"].hide();
2456 //dlg.footer.dom.style.display = 'none';
2459 dlg.footer.dom.style.display = '';
2460 for(var k in buttons){
2461 if(typeof buttons[k] != "function"){
2464 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2465 width += buttons[k].el.getWidth()+15;
2475 var handleEsc = function(d, k, e){
2476 if(opt && opt.closable !== false){
2486 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2487 * @return {Roo.BasicDialog} The BasicDialog element
2489 getDialog : function(){
2491 dlg = new Roo.bootstrap.Modal( {
2494 //constraintoviewport:false,
2496 //collapsible : false,
2501 //buttonAlign:"center",
2502 closeClick : function(){
2503 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2506 handleButton("cancel");
2511 dlg.on("hide", handleHide);
2513 //dlg.addKeyListener(27, handleEsc);
2515 this.buttons = buttons;
2516 var bt = this.buttonText;
2517 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2518 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2519 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2520 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2522 bodyEl = dlg.body.createChild({
2524 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2525 '<textarea class="roo-mb-textarea"></textarea>' +
2526 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2528 msgEl = bodyEl.dom.firstChild;
2529 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2530 textboxEl.enableDisplayMode();
2531 textboxEl.addKeyListener([10,13], function(){
2532 if(dlg.isVisible() && opt && opt.buttons){
2535 }else if(opt.buttons.yes){
2536 handleButton("yes");
2540 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2541 textareaEl.enableDisplayMode();
2542 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2543 progressEl.enableDisplayMode();
2544 var pf = progressEl.dom.firstChild;
2546 pp = Roo.get(pf.firstChild);
2547 pp.setHeight(pf.offsetHeight);
2555 * Updates the message box body text
2556 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2557 * the XHTML-compliant non-breaking space character '&#160;')
2558 * @return {Roo.MessageBox} This message box
2560 updateText : function(text){
2561 if(!dlg.isVisible() && !opt.width){
2562 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2564 msgEl.innerHTML = text || ' ';
2566 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2567 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2569 Math.min(opt.width || cw , this.maxWidth),
2570 Math.max(opt.minWidth || this.minWidth, bwidth)
2573 activeTextEl.setWidth(w);
2575 if(dlg.isVisible()){
2576 dlg.fixedcenter = false;
2578 // to big, make it scroll. = But as usual stupid IE does not support
2581 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2582 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2583 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2585 bodyEl.dom.style.height = '';
2586 bodyEl.dom.style.overflowY = '';
2589 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2591 bodyEl.dom.style.overflowX = '';
2594 dlg.setContentSize(w, bodyEl.getHeight());
2595 if(dlg.isVisible()){
2596 dlg.fixedcenter = true;
2602 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2603 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2604 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2605 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2606 * @return {Roo.MessageBox} This message box
2608 updateProgress : function(value, text){
2610 this.updateText(text);
2612 if (pp) { // weird bug on my firefox - for some reason this is not defined
2613 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2619 * Returns true if the message box is currently displayed
2620 * @return {Boolean} True if the message box is visible, else false
2622 isVisible : function(){
2623 return dlg && dlg.isVisible();
2627 * Hides the message box if it is displayed
2630 if(this.isVisible()){
2636 * Displays a new message box, or reinitializes an existing message box, based on the config options
2637 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2638 * The following config object properties are supported:
2640 Property Type Description
2641 ---------- --------------- ------------------------------------------------------------------------------------
2642 animEl String/Element An id or Element from which the message box should animate as it opens and
2643 closes (defaults to undefined)
2644 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2645 cancel:'Bar'}), or false to not show any buttons (defaults to false)
2646 closable Boolean False to hide the top-right close button (defaults to true). Note that
2647 progress and wait dialogs will ignore this property and always hide the
2648 close button as they can only be closed programmatically.
2649 cls String A custom CSS class to apply to the message box element
2650 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
2651 displayed (defaults to 75)
2652 fn Function A callback function to execute after closing the dialog. The arguments to the
2653 function will be btn (the name of the button that was clicked, if applicable,
2654 e.g. "ok"), and text (the value of the active text field, if applicable).
2655 Progress and wait dialogs will ignore this option since they do not respond to
2656 user actions and can only be closed programmatically, so any required function
2657 should be called by the same code after it closes the dialog.
2658 icon String A CSS class that provides a background image to be used as an icon for
2659 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2660 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
2661 minWidth Number The minimum width in pixels of the message box (defaults to 100)
2662 modal Boolean False to allow user interaction with the page while the message box is
2663 displayed (defaults to true)
2664 msg String A string that will replace the existing message box body text (defaults
2665 to the XHTML-compliant non-breaking space character ' ')
2666 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
2667 progress Boolean True to display a progress bar (defaults to false)
2668 progressText String The text to display inside the progress bar if progress = true (defaults to '')
2669 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
2670 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
2671 title String The title text
2672 value String The string value to set into the active textbox element if displayed
2673 wait Boolean True to display a progress bar (defaults to false)
2674 width Number The width of the dialog in pixels
2681 msg: 'Please enter your address:',
2683 buttons: Roo.MessageBox.OKCANCEL,
2686 animEl: 'addAddressBtn'
2689 * @param {Object} config Configuration options
2690 * @return {Roo.MessageBox} This message box
2692 show : function(options)
2695 // this causes nightmares if you show one dialog after another
2696 // especially on callbacks..
2698 if(this.isVisible()){
2701 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2702 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
2703 Roo.log("New Dialog Message:" + options.msg )
2704 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2705 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2708 var d = this.getDialog();
2710 d.setTitle(opt.title || " ");
2711 d.close.setDisplayed(opt.closable !== false);
2712 activeTextEl = textboxEl;
2713 opt.prompt = opt.prompt || (opt.multiline ? true : false);
2718 textareaEl.setHeight(typeof opt.multiline == "number" ?
2719 opt.multiline : this.defaultTextHeight);
2720 activeTextEl = textareaEl;
2729 progressEl.setDisplayed(opt.progress === true);
2730 this.updateProgress(0);
2731 activeTextEl.dom.value = opt.value || "";
2733 dlg.setDefaultButton(activeTextEl);
2735 var bs = opt.buttons;
2739 }else if(bs && bs.yes){
2740 db = buttons["yes"];
2742 dlg.setDefaultButton(db);
2744 bwidth = updateButtons(opt.buttons);
2745 this.updateText(opt.msg);
2747 d.el.addClass(opt.cls);
2749 d.proxyDrag = opt.proxyDrag === true;
2750 d.modal = opt.modal !== false;
2751 d.mask = opt.modal !== false ? mask : false;
2753 // force it to the end of the z-index stack so it gets a cursor in FF
2754 document.body.appendChild(dlg.el.dom);
2755 d.animateTarget = null;
2756 d.show(options.animEl);
2762 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
2763 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2764 * and closing the message box when the process is complete.
2765 * @param {String} title The title bar text
2766 * @param {String} msg The message box body text
2767 * @return {Roo.MessageBox} This message box
2769 progress : function(title, msg){
2776 minWidth: this.minProgressWidth,
2783 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2784 * If a callback function is passed it will be called after the user clicks the button, and the
2785 * id of the button that was clicked will be passed as the only parameter to the callback
2786 * (could also be the top-right close button).
2787 * @param {String} title The title bar text
2788 * @param {String} msg The message box body text
2789 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2790 * @param {Object} scope (optional) The scope of the callback function
2791 * @return {Roo.MessageBox} This message box
2793 alert : function(title, msg, fn, scope){
2806 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
2807 * interaction while waiting for a long-running process to complete that does not have defined intervals.
2808 * You are responsible for closing the message box when the process is complete.
2809 * @param {String} msg The message box body text
2810 * @param {String} title (optional) The title bar text
2811 * @return {Roo.MessageBox} This message box
2813 wait : function(msg, title){
2824 waitTimer = Roo.TaskMgr.start({
2826 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2834 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2835 * If a callback function is passed it will be called after the user clicks either button, and the id of the
2836 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2837 * @param {String} title The title bar text
2838 * @param {String} msg The message box body text
2839 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2840 * @param {Object} scope (optional) The scope of the callback function
2841 * @return {Roo.MessageBox} This message box
2843 confirm : function(title, msg, fn, scope){
2847 buttons: this.YESNO,
2856 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2857 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
2858 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2859 * (could also be the top-right close button) and the text that was entered will be passed as the two
2860 * parameters to the callback.
2861 * @param {String} title The title bar text
2862 * @param {String} msg The message box body text
2863 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2864 * @param {Object} scope (optional) The scope of the callback function
2865 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2866 * property, or the height in pixels to create the textbox (defaults to false / single-line)
2867 * @return {Roo.MessageBox} This message box
2869 prompt : function(title, msg, fn, scope, multiline){
2873 buttons: this.OKCANCEL,
2878 multiline: multiline,
2885 * Button config that displays a single OK button
2890 * Button config that displays Yes and No buttons
2893 YESNO : {yes:true, no:true},
2895 * Button config that displays OK and Cancel buttons
2898 OKCANCEL : {ok:true, cancel:true},
2900 * Button config that displays Yes, No and Cancel buttons
2903 YESNOCANCEL : {yes:true, no:true, cancel:true},
2906 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2909 defaultTextHeight : 75,
2911 * The maximum width in pixels of the message box (defaults to 600)
2916 * The minimum width in pixels of the message box (defaults to 100)
2921 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
2922 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2925 minProgressWidth : 250,
2927 * An object containing the default button text strings that can be overriden for localized language support.
2928 * Supported properties are: ok, cancel, yes and no.
2929 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2942 * Shorthand for {@link Roo.MessageBox}
2944 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox
2945 Roo.Msg = Roo.Msg || Roo.MessageBox;
2954 * @class Roo.bootstrap.Navbar
2955 * @extends Roo.bootstrap.Component
2956 * Bootstrap Navbar class
2959 * Create a new Navbar
2960 * @param {Object} config The config object
2964 Roo.bootstrap.Navbar = function(config){
2965 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2969 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
2978 getAutoCreate : function(){
2981 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2985 initEvents :function ()
2987 //Roo.log(this.el.select('.navbar-toggle',true));
2988 this.el.select('.navbar-toggle',true).on('click', function() {
2989 // Roo.log('click');
2990 this.el.select('.navbar-collapse',true).toggleClass('in');
2998 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3000 var size = this.el.getSize();
3001 this.maskEl.setSize(size.width, size.height);
3002 this.maskEl.enableDisplayMode("block");
3011 getChildContainer : function()
3013 if (this.el.select('.collapse').getCount()) {
3014 return this.el.select('.collapse',true).first();
3047 * @class Roo.bootstrap.NavSimplebar
3048 * @extends Roo.bootstrap.Navbar
3049 * Bootstrap Sidebar class
3051 * @cfg {Boolean} inverse is inverted color
3053 * @cfg {String} type (nav | pills | tabs)
3054 * @cfg {Boolean} arrangement stacked | justified
3055 * @cfg {String} align (left | right) alignment
3057 * @cfg {Boolean} main (true|false) main nav bar? default false
3058 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3060 * @cfg {String} tag (header|footer|nav|div) default is nav
3066 * Create a new Sidebar
3067 * @param {Object} config The config object
3071 Roo.bootstrap.NavSimplebar = function(config){
3072 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3075 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3091 getAutoCreate : function(){
3095 tag : this.tag || 'div',
3108 this.type = this.type || 'nav';
3109 if (['tabs','pills'].indexOf(this.type)!==-1) {
3110 cfg.cn[0].cls += ' nav-' + this.type
3114 if (this.type!=='nav') {
3115 Roo.log('nav type must be nav/tabs/pills')
3117 cfg.cn[0].cls += ' navbar-nav'
3123 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3124 cfg.cn[0].cls += ' nav-' + this.arrangement;
3128 if (this.align === 'right') {
3129 cfg.cn[0].cls += ' navbar-right';
3133 cfg.cls += ' navbar-inverse';
3160 * @class Roo.bootstrap.NavHeaderbar
3161 * @extends Roo.bootstrap.NavSimplebar
3162 * Bootstrap Sidebar class
3164 * @cfg {String} brand what is brand
3165 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3166 * @cfg {String} brand_href href of the brand
3167 * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3168 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3171 * Create a new Sidebar
3172 * @param {Object} config The config object
3176 Roo.bootstrap.NavHeaderbar = function(config){
3177 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3180 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3188 getAutoCreate : function(){
3191 tag: this.nav || 'nav',
3200 cls: 'navbar-header',
3205 cls: 'navbar-toggle',
3206 'data-toggle': 'collapse',
3211 html: 'Toggle navigation'
3233 cls: 'collapse navbar-collapse',
3237 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3239 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3240 cfg.cls += ' navbar-' + this.position;
3242 // tag can override this..
3244 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3247 if (this.brand !== '') {
3250 href: this.brand_href ? this.brand_href : '#',
3251 cls: 'navbar-brand',
3259 cfg.cls += ' main-nav';
3267 initEvents : function()
3269 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3271 if (this.autohide) {
3276 Roo.get(document).on('scroll',function(e) {
3277 var ns = Roo.get(document).getScroll().top;
3278 var os = prevScroll;
3282 ft.removeClass('slideDown');
3283 ft.addClass('slideUp');
3286 ft.removeClass('slideUp');
3287 ft.addClass('slideDown');
3311 * @class Roo.bootstrap.NavSidebar
3312 * @extends Roo.bootstrap.Navbar
3313 * Bootstrap Sidebar class
3316 * Create a new Sidebar
3317 * @param {Object} config The config object
3321 Roo.bootstrap.NavSidebar = function(config){
3322 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3325 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3327 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3329 getAutoCreate : function(){
3334 cls: 'sidebar sidebar-nav'
3356 * @class Roo.bootstrap.NavGroup
3357 * @extends Roo.bootstrap.Component
3358 * Bootstrap NavGroup class
3359 * @cfg {String} align left | right
3360 * @cfg {Boolean} inverse false | true
3361 * @cfg {String} type (nav|pills|tab) default nav
3362 * @cfg {String} navId - reference Id for navbar.
3366 * Create a new nav group
3367 * @param {Object} config The config object
3370 Roo.bootstrap.NavGroup = function(config){
3371 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3374 Roo.bootstrap.NavGroup.register(this);
3378 * Fires when the active item changes
3379 * @param {Roo.bootstrap.NavGroup} this
3380 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3381 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3388 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3399 getAutoCreate : function()
3401 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3408 if (['tabs','pills'].indexOf(this.type)!==-1) {
3409 cfg.cls += ' nav-' + this.type
3411 if (this.type!=='nav') {
3412 Roo.log('nav type must be nav/tabs/pills')
3414 cfg.cls += ' navbar-nav'
3417 if (this.parent().sidebar) {
3420 cls: 'dashboard-menu sidebar-menu'
3426 if (this.form === true) {
3432 if (this.align === 'right') {
3433 cfg.cls += ' navbar-right';
3435 cfg.cls += ' navbar-left';
3439 if (this.align === 'right') {
3440 cfg.cls += ' navbar-right';
3444 cfg.cls += ' navbar-inverse';
3452 * sets the active Navigation item
3453 * @param {Roo.bootstrap.NavItem} the new current navitem
3455 setActiveItem : function(item)
3458 Roo.each(this.navItems, function(v){
3463 v.setActive(false, true);
3470 item.setActive(true, true);
3471 this.fireEvent('changed', this, item, prev);
3476 * gets the active Navigation item
3477 * @return {Roo.bootstrap.NavItem} the current navitem
3479 getActive : function()
3483 Roo.each(this.navItems, function(v){
3494 indexOfNav : function()
3498 Roo.each(this.navItems, function(v,i){
3509 * adds a Navigation item
3510 * @param {Roo.bootstrap.NavItem} the navitem to add
3512 addItem : function(cfg)
3514 var cn = new Roo.bootstrap.NavItem(cfg);
3516 cn.parentId = this.id;
3517 cn.onRender(this.el, null);
3521 * register a Navigation item
3522 * @param {Roo.bootstrap.NavItem} the navitem to add
3524 register : function(item)
3526 this.navItems.push( item);
3527 item.navId = this.navId;
3532 * clear all the Navigation item
3535 clearAll : function()
3538 this.el.dom.innerHTML = '';
3541 getNavItem: function(tabId)
3544 Roo.each(this.navItems, function(e) {
3545 if (e.tabId == tabId) {
3555 setActiveNext : function()
3557 var i = this.indexOfNav(this.getActive());
3558 if (i > this.navItems.length) {
3561 this.setActiveItem(this.navItems[i+1]);
3563 setActivePrev : function()
3565 var i = this.indexOfNav(this.getActive());
3569 this.setActiveItem(this.navItems[i-1]);
3571 clearWasActive : function(except) {
3572 Roo.each(this.navItems, function(e) {
3573 if (e.tabId != except.tabId && e.was_active) {
3574 e.was_active = false;
3581 getWasActive : function ()
3584 Roo.each(this.navItems, function(e) {
3599 Roo.apply(Roo.bootstrap.NavGroup, {
3603 * register a Navigation Group
3604 * @param {Roo.bootstrap.NavGroup} the navgroup to add
3606 register : function(navgrp)
3608 this.groups[navgrp.navId] = navgrp;
3612 * fetch a Navigation Group based on the navigation ID
3613 * @param {string} the navgroup to add
3614 * @returns {Roo.bootstrap.NavGroup} the navgroup
3616 get: function(navId) {
3617 if (typeof(this.groups[navId]) == 'undefined') {
3619 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3621 return this.groups[navId] ;
3636 * @class Roo.bootstrap.NavItem
3637 * @extends Roo.bootstrap.Component
3638 * Bootstrap Navbar.NavItem class
3639 * @cfg {String} href link to
3640 * @cfg {String} html content of button
3641 * @cfg {String} badge text inside badge
3642 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3643 * @cfg {String} glyphicon name of glyphicon
3644 * @cfg {String} icon name of font awesome icon
3645 * @cfg {Boolean} active Is item active
3646 * @cfg {Boolean} disabled Is item disabled
3648 * @cfg {Boolean} preventDefault (true | false) default false
3649 * @cfg {String} tabId the tab that this item activates.
3650 * @cfg {String} tagtype (a|span) render as a href or span?
3653 * Create a new Navbar Item
3654 * @param {Object} config The config object
3656 Roo.bootstrap.NavItem = function(config){
3657 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3662 * The raw click event for the entire grid.
3663 * @param {Roo.EventObject} e
3668 * Fires when the active item active state changes
3669 * @param {Roo.bootstrap.NavItem} this
3670 * @param {boolean} state the new state
3678 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
3686 preventDefault : false,
3693 getAutoCreate : function(){
3701 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3703 if (this.disabled) {
3704 cfg.cls += ' disabled';
3707 if (this.href || this.html || this.glyphicon || this.icon) {
3711 href : this.href || "#",
3712 html: this.html || ''
3717 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3720 if(this.glyphicon) {
3721 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
3726 cfg.cn[0].html += " <span class='caret'></span>";
3730 if (this.badge !== '') {
3732 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3740 initEvents: function()
3742 if (typeof (this.menu) != 'undefined') {
3743 this.menu.parentType = this.xtype;
3744 this.menu.triggerEl = this.el;
3745 this.addxtype(Roo.apply({}, this.menu));
3748 this.el.select('a',true).on('click', this.onClick, this);
3750 if(this.tagtype == 'span'){
3751 this.el.select('span',true).on('click', this.onClick, this);
3754 // at this point parent should be available..
3755 this.parent().register(this);
3758 onClick : function(e)
3761 if(this.preventDefault){
3764 if (this.disabled) {
3768 var tg = Roo.bootstrap.TabGroup.get(this.navId);
3769 if (tg && tg.transition) {
3770 Roo.log("waiting for the transitionend");
3774 Roo.log("fire event clicked");
3775 if(this.fireEvent('click', this, e) === false){
3779 if(this.tagtype == 'span'){
3783 var p = this.parent();
3784 if (['tabs','pills'].indexOf(p.type)!==-1) {
3785 if (typeof(p.setActiveItem) !== 'undefined') {
3786 p.setActiveItem(this);
3789 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3790 if (p.parentType == 'NavHeaderbar' && !this.menu) {
3791 // remove the collapsed menu expand...
3792 p.parent().el.select('.navbar-collapse',true).removeClass('in');
3797 isActive: function () {
3800 setActive : function(state, fire, is_was_active)
3802 if (this.active && !state & this.navId) {
3803 this.was_active = true;
3804 var nv = Roo.bootstrap.NavGroup.get(this.navId);
3806 nv.clearWasActive(this);
3810 this.active = state;
3813 this.el.removeClass('active');
3814 } else if (!this.el.hasClass('active')) {
3815 this.el.addClass('active');
3818 this.fireEvent('changed', this, state);
3821 // show a panel if it's registered and related..
3823 if (!this.navId || !this.tabId || !state || is_was_active) {
3827 var tg = Roo.bootstrap.TabGroup.get(this.navId);
3831 var pan = tg.getPanelByName(this.tabId);
3835 // if we can not flip to new panel - go back to old nav highlight..
3836 if (false == tg.showPanel(pan)) {
3837 var nv = Roo.bootstrap.NavGroup.get(this.navId);
3839 var onav = nv.getWasActive();
3841 onav.setActive(true, false, true);
3850 // this should not be here...
3851 setDisabled : function(state)
3853 this.disabled = state;
3855 this.el.removeClass('disabled');
3856 } else if (!this.el.hasClass('disabled')) {
3857 this.el.addClass('disabled');
3870 * <span> icon </span>
3871 * <span> text </span>
3872 * <span>badge </span>
3876 * @class Roo.bootstrap.NavSidebarItem
3877 * @extends Roo.bootstrap.NavItem
3878 * Bootstrap Navbar.NavSidebarItem class
3880 * Create a new Navbar Button
3881 * @param {Object} config The config object
3883 Roo.bootstrap.NavSidebarItem = function(config){
3884 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3889 * The raw click event for the entire grid.
3890 * @param {Roo.EventObject} e
3895 * Fires when the active item active state changes
3896 * @param {Roo.bootstrap.NavSidebarItem} this
3897 * @param {boolean} state the new state
3905 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
3908 getAutoCreate : function(){
3913 href : this.href || '#',
3925 html : this.html || ''
3930 cfg.cls += ' active';
3934 if (this.glyphicon || this.icon) {
3935 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
3936 a.cn.push({ tag : 'i', cls : c }) ;
3941 if (this.badge !== '') {
3942 a.cn.push({ tag: 'span', cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge });
3946 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3947 a.cls += 'dropdown-toggle treeview' ;
3971 * @class Roo.bootstrap.Row
3972 * @extends Roo.bootstrap.Component
3973 * Bootstrap Row class (contains columns...)
3977 * @param {Object} config The config object
3980 Roo.bootstrap.Row = function(config){
3981 Roo.bootstrap.Row.superclass.constructor.call(this, config);
3984 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
3986 getAutoCreate : function(){
4005 * @class Roo.bootstrap.Element
4006 * @extends Roo.bootstrap.Component
4007 * Bootstrap Element class
4008 * @cfg {String} html contents of the element
4009 * @cfg {String} tag tag of the element
4010 * @cfg {String} cls class of the element
4013 * Create a new Element
4014 * @param {Object} config The config object
4017 Roo.bootstrap.Element = function(config){
4018 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4021 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4028 getAutoCreate : function(){
4053 * @class Roo.bootstrap.Pagination
4054 * @extends Roo.bootstrap.Component
4055 * Bootstrap Pagination class
4056 * @cfg {String} size xs | sm | md | lg
4057 * @cfg {Boolean} inverse false | true
4060 * Create a new Pagination
4061 * @param {Object} config The config object
4064 Roo.bootstrap.Pagination = function(config){
4065 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4068 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4074 getAutoCreate : function(){
4080 cfg.cls += ' inverse';
4086 cfg.cls += " " + this.cls;
4104 * @class Roo.bootstrap.PaginationItem
4105 * @extends Roo.bootstrap.Component
4106 * Bootstrap PaginationItem class
4107 * @cfg {String} html text
4108 * @cfg {String} href the link
4109 * @cfg {Boolean} preventDefault (true | false) default true
4110 * @cfg {Boolean} active (true | false) default false
4114 * Create a new PaginationItem
4115 * @param {Object} config The config object
4119 Roo.bootstrap.PaginationItem = function(config){
4120 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4125 * The raw click event for the entire grid.
4126 * @param {Roo.EventObject} e
4132 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4136 preventDefault: true,
4140 getAutoCreate : function(){
4146 href : this.href ? this.href : '#',
4147 html : this.html ? this.html : ''
4157 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4163 initEvents: function() {
4165 this.el.on('click', this.onClick, this);
4168 onClick : function(e)
4170 Roo.log('PaginationItem on click ');
4171 if(this.preventDefault){
4175 this.fireEvent('click', this, e);
4191 * @class Roo.bootstrap.Slider
4192 * @extends Roo.bootstrap.Component
4193 * Bootstrap Slider class
4196 * Create a new Slider
4197 * @param {Object} config The config object
4200 Roo.bootstrap.Slider = function(config){
4201 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4204 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4206 getAutoCreate : function(){
4210 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4214 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4226 * Ext JS Library 1.1.1
4227 * Copyright(c) 2006-2007, Ext JS, LLC.
4229 * Originally Released Under LGPL - original licence link has changed is not relivant.
4232 * <script type="text/javascript">
4237 * @class Roo.grid.ColumnModel
4238 * @extends Roo.util.Observable
4239 * This is the default implementation of a ColumnModel used by the Grid. It defines
4240 * the columns in the grid.
4243 var colModel = new Roo.grid.ColumnModel([
4244 {header: "Ticker", width: 60, sortable: true, locked: true},
4245 {header: "Company Name", width: 150, sortable: true},
4246 {header: "Market Cap.", width: 100, sortable: true},
4247 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4248 {header: "Employees", width: 100, sortable: true, resizable: false}
4253 * The config options listed for this class are options which may appear in each
4254 * individual column definition.
4255 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4257 * @param {Object} config An Array of column config objects. See this class's
4258 * config objects for details.
4260 Roo.grid.ColumnModel = function(config){
4262 * The config passed into the constructor
4264 this.config = config;
4267 // if no id, create one
4268 // if the column does not have a dataIndex mapping,
4269 // map it to the order it is in the config
4270 for(var i = 0, len = config.length; i < len; i++){
4272 if(typeof c.dataIndex == "undefined"){
4275 if(typeof c.renderer == "string"){
4276 c.renderer = Roo.util.Format[c.renderer];
4278 if(typeof c.id == "undefined"){
4281 if(c.editor && c.editor.xtype){
4282 c.editor = Roo.factory(c.editor, Roo.grid);
4284 if(c.editor && c.editor.isFormField){
4285 c.editor = new Roo.grid.GridEditor(c.editor);
4287 this.lookup[c.id] = c;
4291 * The width of columns which have no width specified (defaults to 100)
4294 this.defaultWidth = 100;
4297 * Default sortable of columns which have no sortable specified (defaults to false)
4300 this.defaultSortable = false;
4304 * @event widthchange
4305 * Fires when the width of a column changes.
4306 * @param {ColumnModel} this
4307 * @param {Number} columnIndex The column index
4308 * @param {Number} newWidth The new width
4310 "widthchange": true,
4312 * @event headerchange
4313 * Fires when the text of a header changes.
4314 * @param {ColumnModel} this
4315 * @param {Number} columnIndex The column index
4316 * @param {Number} newText The new header text
4318 "headerchange": true,
4320 * @event hiddenchange
4321 * Fires when a column is hidden or "unhidden".
4322 * @param {ColumnModel} this
4323 * @param {Number} columnIndex The column index
4324 * @param {Boolean} hidden true if hidden, false otherwise
4326 "hiddenchange": true,
4328 * @event columnmoved
4329 * Fires when a column is moved.
4330 * @param {ColumnModel} this
4331 * @param {Number} oldIndex
4332 * @param {Number} newIndex
4334 "columnmoved" : true,
4336 * @event columlockchange
4337 * Fires when a column's locked state is changed
4338 * @param {ColumnModel} this
4339 * @param {Number} colIndex
4340 * @param {Boolean} locked true if locked
4342 "columnlockchange" : true
4344 Roo.grid.ColumnModel.superclass.constructor.call(this);
4346 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4348 * @cfg {String} header The header text to display in the Grid view.
4351 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4352 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4353 * specified, the column's index is used as an index into the Record's data Array.
4356 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4357 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4360 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4361 * Defaults to the value of the {@link #defaultSortable} property.
4362 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4365 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4368 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4371 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4374 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4377 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4378 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4379 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4380 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4383 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4386 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4390 * Returns the id of the column at the specified index.
4391 * @param {Number} index The column index
4392 * @return {String} the id
4394 getColumnId : function(index){
4395 return this.config[index].id;
4399 * Returns the column for a specified id.
4400 * @param {String} id The column id
4401 * @return {Object} the column
4403 getColumnById : function(id){
4404 return this.lookup[id];
4409 * Returns the column for a specified dataIndex.
4410 * @param {String} dataIndex The column dataIndex
4411 * @return {Object|Boolean} the column or false if not found
4413 getColumnByDataIndex: function(dataIndex){
4414 var index = this.findColumnIndex(dataIndex);
4415 return index > -1 ? this.config[index] : false;
4419 * Returns the index for a specified column id.
4420 * @param {String} id The column id
4421 * @return {Number} the index, or -1 if not found
4423 getIndexById : function(id){
4424 for(var i = 0, len = this.config.length; i < len; i++){
4425 if(this.config[i].id == id){
4433 * Returns the index for a specified column dataIndex.
4434 * @param {String} dataIndex The column dataIndex
4435 * @return {Number} the index, or -1 if not found
4438 findColumnIndex : function(dataIndex){
4439 for(var i = 0, len = this.config.length; i < len; i++){
4440 if(this.config[i].dataIndex == dataIndex){
4448 moveColumn : function(oldIndex, newIndex){
4449 var c = this.config[oldIndex];
4450 this.config.splice(oldIndex, 1);
4451 this.config.splice(newIndex, 0, c);
4452 this.dataMap = null;
4453 this.fireEvent("columnmoved", this, oldIndex, newIndex);
4456 isLocked : function(colIndex){
4457 return this.config[colIndex].locked === true;
4460 setLocked : function(colIndex, value, suppressEvent){
4461 if(this.isLocked(colIndex) == value){
4464 this.config[colIndex].locked = value;
4466 this.fireEvent("columnlockchange", this, colIndex, value);
4470 getTotalLockedWidth : function(){
4472 for(var i = 0; i < this.config.length; i++){
4473 if(this.isLocked(i) && !this.isHidden(i)){
4474 this.totalWidth += this.getColumnWidth(i);
4480 getLockedCount : function(){
4481 for(var i = 0, len = this.config.length; i < len; i++){
4482 if(!this.isLocked(i)){
4489 * Returns the number of columns.
4492 getColumnCount : function(visibleOnly){
4493 if(visibleOnly === true){
4495 for(var i = 0, len = this.config.length; i < len; i++){
4496 if(!this.isHidden(i)){
4502 return this.config.length;
4506 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4507 * @param {Function} fn
4508 * @param {Object} scope (optional)
4509 * @return {Array} result
4511 getColumnsBy : function(fn, scope){
4513 for(var i = 0, len = this.config.length; i < len; i++){
4514 var c = this.config[i];
4515 if(fn.call(scope||this, c, i) === true){
4523 * Returns true if the specified column is sortable.
4524 * @param {Number} col The column index
4527 isSortable : function(col){
4528 if(typeof this.config[col].sortable == "undefined"){
4529 return this.defaultSortable;
4531 return this.config[col].sortable;
4535 * Returns the rendering (formatting) function defined for the column.
4536 * @param {Number} col The column index.
4537 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4539 getRenderer : function(col){
4540 if(!this.config[col].renderer){
4541 return Roo.grid.ColumnModel.defaultRenderer;
4543 return this.config[col].renderer;
4547 * Sets the rendering (formatting) function for a column.
4548 * @param {Number} col The column index
4549 * @param {Function} fn The function to use to process the cell's raw data
4550 * to return HTML markup for the grid view. The render function is called with
4551 * the following parameters:<ul>
4552 * <li>Data value.</li>
4553 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4554 * <li>css A CSS style string to apply to the table cell.</li>
4555 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4556 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4557 * <li>Row index</li>
4558 * <li>Column index</li>
4559 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4561 setRenderer : function(col, fn){
4562 this.config[col].renderer = fn;
4566 * Returns the width for the specified column.
4567 * @param {Number} col The column index
4570 getColumnWidth : function(col){
4571 return this.config[col].width * 1 || this.defaultWidth;
4575 * Sets the width for a column.
4576 * @param {Number} col The column index
4577 * @param {Number} width The new width
4579 setColumnWidth : function(col, width, suppressEvent){
4580 this.config[col].width = width;
4581 this.totalWidth = null;
4583 this.fireEvent("widthchange", this, col, width);
4588 * Returns the total width of all columns.
4589 * @param {Boolean} includeHidden True to include hidden column widths
4592 getTotalWidth : function(includeHidden){
4593 if(!this.totalWidth){
4594 this.totalWidth = 0;
4595 for(var i = 0, len = this.config.length; i < len; i++){
4596 if(includeHidden || !this.isHidden(i)){
4597 this.totalWidth += this.getColumnWidth(i);
4601 return this.totalWidth;
4605 * Returns the header for the specified column.
4606 * @param {Number} col The column index
4609 getColumnHeader : function(col){
4610 return this.config[col].header;
4614 * Sets the header for a column.
4615 * @param {Number} col The column index
4616 * @param {String} header The new header
4618 setColumnHeader : function(col, header){
4619 this.config[col].header = header;
4620 this.fireEvent("headerchange", this, col, header);
4624 * Returns the tooltip for the specified column.
4625 * @param {Number} col The column index
4628 getColumnTooltip : function(col){
4629 return this.config[col].tooltip;
4632 * Sets the tooltip for a column.
4633 * @param {Number} col The column index
4634 * @param {String} tooltip The new tooltip
4636 setColumnTooltip : function(col, tooltip){
4637 this.config[col].tooltip = tooltip;
4641 * Returns the dataIndex for the specified column.
4642 * @param {Number} col The column index
4645 getDataIndex : function(col){
4646 return this.config[col].dataIndex;
4650 * Sets the dataIndex for a column.
4651 * @param {Number} col The column index
4652 * @param {Number} dataIndex The new dataIndex
4654 setDataIndex : function(col, dataIndex){
4655 this.config[col].dataIndex = dataIndex;
4661 * Returns true if the cell is editable.
4662 * @param {Number} colIndex The column index
4663 * @param {Number} rowIndex The row index
4666 isCellEditable : function(colIndex, rowIndex){
4667 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4671 * Returns the editor defined for the cell/column.
4672 * return false or null to disable editing.
4673 * @param {Number} colIndex The column index
4674 * @param {Number} rowIndex The row index
4677 getCellEditor : function(colIndex, rowIndex){
4678 return this.config[colIndex].editor;
4682 * Sets if a column is editable.
4683 * @param {Number} col The column index
4684 * @param {Boolean} editable True if the column is editable
4686 setEditable : function(col, editable){
4687 this.config[col].editable = editable;
4692 * Returns true if the column is hidden.
4693 * @param {Number} colIndex The column index
4696 isHidden : function(colIndex){
4697 return this.config[colIndex].hidden;
4702 * Returns true if the column width cannot be changed
4704 isFixed : function(colIndex){
4705 return this.config[colIndex].fixed;
4709 * Returns true if the column can be resized
4712 isResizable : function(colIndex){
4713 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4716 * Sets if a column is hidden.
4717 * @param {Number} colIndex The column index
4718 * @param {Boolean} hidden True if the column is hidden
4720 setHidden : function(colIndex, hidden){
4721 this.config[colIndex].hidden = hidden;
4722 this.totalWidth = null;
4723 this.fireEvent("hiddenchange", this, colIndex, hidden);
4727 * Sets the editor for a column.
4728 * @param {Number} col The column index
4729 * @param {Object} editor The editor object
4731 setEditor : function(col, editor){
4732 this.config[col].editor = editor;
4736 Roo.grid.ColumnModel.defaultRenderer = function(value){
4737 if(typeof value == "string" && value.length < 1){
4743 // Alias for backwards compatibility
4744 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4747 * Ext JS Library 1.1.1
4748 * Copyright(c) 2006-2007, Ext JS, LLC.
4750 * Originally Released Under LGPL - original licence link has changed is not relivant.
4753 * <script type="text/javascript">
4757 * @class Roo.LoadMask
4758 * A simple utility class for generically masking elements while loading data. If the element being masked has
4759 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4760 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
4761 * element's UpdateManager load indicator and will be destroyed after the initial load.
4763 * Create a new LoadMask
4764 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4765 * @param {Object} config The config object
4767 Roo.LoadMask = function(el, config){
4768 this.el = Roo.get(el);
4769 Roo.apply(this, config);
4771 this.store.on('beforeload', this.onBeforeLoad, this);
4772 this.store.on('load', this.onLoad, this);
4773 this.store.on('loadexception', this.onLoadException, this);
4774 this.removeMask = false;
4776 var um = this.el.getUpdateManager();
4777 um.showLoadIndicator = false; // disable the default indicator
4778 um.on('beforeupdate', this.onBeforeLoad, this);
4779 um.on('update', this.onLoad, this);
4780 um.on('failure', this.onLoad, this);
4781 this.removeMask = true;
4785 Roo.LoadMask.prototype = {
4787 * @cfg {Boolean} removeMask
4788 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4789 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
4793 * The text to display in a centered loading message box (defaults to 'Loading...')
4797 * @cfg {String} msgCls
4798 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4800 msgCls : 'x-mask-loading',
4803 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4809 * Disables the mask to prevent it from being displayed
4811 disable : function(){
4812 this.disabled = true;
4816 * Enables the mask so that it can be displayed
4818 enable : function(){
4819 this.disabled = false;
4822 onLoadException : function()
4826 if (typeof(arguments[3]) != 'undefined') {
4827 Roo.MessageBox.alert("Error loading",arguments[3]);
4831 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4832 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4841 this.el.unmask(this.removeMask);
4846 this.el.unmask(this.removeMask);
4850 onBeforeLoad : function(){
4852 this.el.mask(this.msg, this.msgCls);
4857 destroy : function(){
4859 this.store.un('beforeload', this.onBeforeLoad, this);
4860 this.store.un('load', this.onLoad, this);
4861 this.store.un('loadexception', this.onLoadException, this);
4863 var um = this.el.getUpdateManager();
4864 um.un('beforeupdate', this.onBeforeLoad, this);
4865 um.un('update', this.onLoad, this);
4866 um.un('failure', this.onLoad, this);
4877 * @class Roo.bootstrap.Table
4878 * @extends Roo.bootstrap.Component
4879 * Bootstrap Table class
4880 * @cfg {String} cls table class
4881 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4882 * @cfg {String} bgcolor Specifies the background color for a table
4883 * @cfg {Number} border Specifies whether the table cells should have borders or not
4884 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4885 * @cfg {Number} cellspacing Specifies the space between cells
4886 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4887 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4888 * @cfg {String} sortable Specifies that the table should be sortable
4889 * @cfg {String} summary Specifies a summary of the content of a table
4890 * @cfg {Number} width Specifies the width of a table
4891 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4893 * @cfg {boolean} striped Should the rows be alternative striped
4894 * @cfg {boolean} bordered Add borders to the table
4895 * @cfg {boolean} hover Add hover highlighting
4896 * @cfg {boolean} condensed Format condensed
4897 * @cfg {boolean} responsive Format condensed
4898 * @cfg {Boolean} loadMask (true|false) default false
4899 * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4900 * @cfg {Boolean} thead (true|false) generate thead, default true
4901 * @cfg {Boolean} RowSelection (true|false) default false
4902 * @cfg {Boolean} CellSelection (true|false) default false
4904 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
4908 * Create a new Table
4909 * @param {Object} config The config object
4912 Roo.bootstrap.Table = function(config){
4913 Roo.bootstrap.Table.superclass.constructor.call(this, config);
4916 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4917 this.sm = this.selModel;
4918 this.sm.xmodule = this.xmodule || false;
4920 if (this.cm && typeof(this.cm.config) == 'undefined') {
4921 this.colModel = new Roo.grid.ColumnModel(this.cm);
4922 this.cm = this.colModel;
4923 this.cm.xmodule = this.xmodule || false;
4926 this.store= Roo.factory(this.store, Roo.data);
4927 this.ds = this.store;
4928 this.ds.xmodule = this.xmodule || false;
4931 if (this.footer && this.store) {
4932 this.footer.dataSource = this.ds;
4933 this.footer = Roo.factory(this.footer);
4940 * Fires when a cell is clicked
4941 * @param {Roo.bootstrap.Table} this
4942 * @param {Roo.Element} el
4943 * @param {Number} rowIndex
4944 * @param {Number} columnIndex
4945 * @param {Roo.EventObject} e
4949 * @event celldblclick
4950 * Fires when a cell is double clicked
4951 * @param {Roo.bootstrap.Table} this
4952 * @param {Roo.Element} el
4953 * @param {Number} rowIndex
4954 * @param {Number} columnIndex
4955 * @param {Roo.EventObject} e
4957 "celldblclick" : true,
4960 * Fires when a row is clicked
4961 * @param {Roo.bootstrap.Table} this
4962 * @param {Roo.Element} el
4963 * @param {Number} rowIndex
4964 * @param {Roo.EventObject} e
4968 * @event rowdblclick
4969 * Fires when a row is double clicked
4970 * @param {Roo.bootstrap.Table} this
4971 * @param {Roo.Element} el
4972 * @param {Number} rowIndex
4973 * @param {Roo.EventObject} e
4975 "rowdblclick" : true,
4978 * Fires when a mouseover occur
4979 * @param {Roo.bootstrap.Table} this
4980 * @param {Roo.Element} el
4981 * @param {Number} rowIndex
4982 * @param {Number} columnIndex
4983 * @param {Roo.EventObject} e
4988 * Fires when a mouseout occur
4989 * @param {Roo.bootstrap.Table} this
4990 * @param {Roo.Element} el
4991 * @param {Number} rowIndex
4992 * @param {Number} columnIndex
4993 * @param {Roo.EventObject} e
4998 * Fires when a row is rendered, so you can change add a style to it.
4999 * @param {Roo.bootstrap.Table} this
5000 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5007 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5031 RowSelection : false,
5032 CellSelection : false,
5035 // Roo.Element - the tbody
5038 getAutoCreate : function(){
5039 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5048 cfg.cls += ' table-striped';
5052 cfg.cls += ' table-hover';
5054 if (this.bordered) {
5055 cfg.cls += ' table-bordered';
5057 if (this.condensed) {
5058 cfg.cls += ' table-condensed';
5060 if (this.responsive) {
5061 cfg.cls += ' table-responsive';
5065 cfg.cls+= ' ' +this.cls;
5068 // this lot should be simplifed...
5071 cfg.align=this.align;
5074 cfg.bgcolor=this.bgcolor;
5077 cfg.border=this.border;
5079 if (this.cellpadding) {
5080 cfg.cellpadding=this.cellpadding;
5082 if (this.cellspacing) {
5083 cfg.cellspacing=this.cellspacing;
5086 cfg.frame=this.frame;
5089 cfg.rules=this.rules;
5091 if (this.sortable) {
5092 cfg.sortable=this.sortable;
5095 cfg.summary=this.summary;
5098 cfg.width=this.width;
5101 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5104 if(this.store || this.cm){
5106 cfg.cn.push(this.renderHeader());
5109 cfg.cn.push(this.renderBody());
5112 cfg.cn.push(this.renderFooter());
5115 cfg.cls+= ' TableGrid';
5118 return { cn : [ cfg ] };
5121 initEvents : function()
5123 if(!this.store || !this.cm){
5127 //Roo.log('initEvents with ds!!!!');
5129 this.mainBody = this.el.select('tbody', true).first();
5134 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5135 e.on('click', _this.sort, _this);
5138 this.el.on("click", this.onClick, this);
5139 this.el.on("dblclick", this.onDblClick, this);
5141 this.parent().el.setStyle('position', 'relative');
5143 this.footer.parentId = this.id;
5144 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5147 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5149 this.store.on('load', this.onLoad, this);
5150 this.store.on('beforeload', this.onBeforeLoad, this);
5151 this.store.on('update', this.onUpdate, this);
5155 onMouseover : function(e, el)
5157 var cell = Roo.get(el);
5163 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5164 cell = cell.findParent('td', false, true);
5167 var row = cell.findParent('tr', false, true);
5168 var cellIndex = cell.dom.cellIndex;
5169 var rowIndex = row.dom.rowIndex - 1; // start from 0
5171 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5175 onMouseout : function(e, el)
5177 var cell = Roo.get(el);
5183 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5184 cell = cell.findParent('td', false, true);
5187 var row = cell.findParent('tr', false, true);
5188 var cellIndex = cell.dom.cellIndex;
5189 var rowIndex = row.dom.rowIndex - 1; // start from 0
5191 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5195 onClick : function(e, el)
5197 var cell = Roo.get(el);
5199 if(!cell || (!this.CellSelection && !this.RowSelection)){
5204 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5205 cell = cell.findParent('td', false, true);
5208 var row = cell.findParent('tr', false, true);
5209 var cellIndex = cell.dom.cellIndex;
5210 var rowIndex = row.dom.rowIndex - 1;
5212 if(this.CellSelection){
5213 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5216 if(this.RowSelection){
5217 this.fireEvent('rowclick', this, row, rowIndex, e);
5223 onDblClick : function(e,el)
5225 var cell = Roo.get(el);
5227 if(!cell || (!this.CellSelection && !this.RowSelection)){
5231 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5232 cell = cell.findParent('td', false, true);
5235 var row = cell.findParent('tr', false, true);
5236 var cellIndex = cell.dom.cellIndex;
5237 var rowIndex = row.dom.rowIndex - 1;
5239 if(this.CellSelection){
5240 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5243 if(this.RowSelection){
5244 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5248 sort : function(e,el)
5250 var col = Roo.get(el)
5252 if(!col.hasClass('sortable')){
5256 var sort = col.attr('sort');
5259 if(col.hasClass('glyphicon-arrow-up')){
5263 this.store.sortInfo = {field : sort, direction : dir};
5266 Roo.log("calling footer first");
5267 this.footer.onClick('first');
5270 this.store.load({ params : { start : 0 } });
5274 renderHeader : function()
5283 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5285 var config = cm.config[i];
5290 html: cm.getColumnHeader(i)
5293 if(typeof(config.hidden) != 'undefined' && config.hidden){
5294 c.style += ' display:none;';
5297 if(typeof(config.dataIndex) != 'undefined'){
5298 c.sort = config.dataIndex;
5301 if(typeof(config.sortable) != 'undefined' && config.sortable){
5305 if(typeof(config.align) != 'undefined' && config.align.length){
5306 c.style += ' text-align:' + config.align + ';';
5309 if(typeof(config.width) != 'undefined'){
5310 c.style += ' width:' + config.width + 'px;';
5319 renderBody : function()
5329 colspan : this.cm.getColumnCount()
5339 renderFooter : function()
5349 colspan : this.cm.getColumnCount()
5363 Roo.log('ds onload');
5368 var ds = this.store;
5370 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5371 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5373 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5374 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5377 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5378 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5382 var tbody = this.mainBody;
5384 if(ds.getCount() > 0){
5385 ds.data.each(function(d,rowIndex){
5386 var row = this.renderRow(cm, ds, rowIndex);
5388 tbody.createChild(row);
5392 if(row.cellObjects.length){
5393 Roo.each(row.cellObjects, function(r){
5394 _this.renderCellObject(r);
5401 Roo.each(this.el.select('tbody td', true).elements, function(e){
5402 e.on('mouseover', _this.onMouseover, _this);
5405 Roo.each(this.el.select('tbody td', true).elements, function(e){
5406 e.on('mouseout', _this.onMouseout, _this);
5409 //if(this.loadMask){
5410 // this.maskEl.hide();
5415 onUpdate : function(ds,record)
5417 this.refreshRow(record);
5419 onRemove : function(ds, record, index, isUpdate){
5420 if(isUpdate !== true){
5421 this.fireEvent("beforerowremoved", this, index, record);
5423 var bt = this.mainBody.dom;
5425 bt.removeChild(bt.rows[index]);
5428 if(isUpdate !== true){
5429 //this.stripeRows(index);
5430 //this.syncRowHeights(index, index);
5432 this.fireEvent("rowremoved", this, index, record);
5437 refreshRow : function(record){
5438 var ds = this.store, index;
5439 if(typeof record == 'number'){
5441 record = ds.getAt(index);
5443 index = ds.indexOf(record);
5445 this.insertRow(ds, index, true);
5446 this.onRemove(ds, record, index+1, true);
5447 //this.syncRowHeights(index, index);
5449 this.fireEvent("rowupdated", this, index, record);
5452 insertRow : function(dm, rowIndex, isUpdate){
5455 this.fireEvent("beforerowsinserted", this, rowIndex);
5457 //var s = this.getScrollState();
5458 var row = this.renderRow(this.cm, this.store, rowIndex);
5459 // insert before rowIndex..
5460 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5464 if(row.cellObjects.length){
5465 Roo.each(row.cellObjects, function(r){
5466 _this.renderCellObject(r);
5471 this.fireEvent("rowsinserted", this, rowIndex);
5472 //this.syncRowHeights(firstRow, lastRow);
5473 //this.stripeRows(firstRow);
5480 getRowDom : function(rowIndex)
5482 // not sure if I need to check this.. but let's do it anyway..
5483 return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5484 this.mainBody.dom.rows[rowIndex] : false
5486 // returns the object tree for a tr..
5489 renderRow : function(cm, ds, rowIndex) {
5491 var d = ds.getAt(rowIndex);
5498 var cellObjects = [];
5500 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5501 var config = cm.config[i];
5503 var renderer = cm.getRenderer(i);
5507 if(typeof(renderer) !== 'undefined'){
5508 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5510 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5511 // and are rendered into the cells after the row is rendered - using the id for the element.
5513 if(typeof(value) === 'object'){
5523 rowIndex : rowIndex,
5528 this.fireEvent('rowclass', this, rowcfg);
5532 cls : rowcfg.rowClass,
5534 html: (typeof(value) === 'object') ? '' : value
5541 if(typeof(config.hidden) != 'undefined' && config.hidden){
5542 td.style += ' display:none;';
5545 if(typeof(config.align) != 'undefined' && config.align.length){
5546 td.style += ' text-align:' + config.align + ';';
5549 if(typeof(config.width) != 'undefined'){
5550 td.style += ' width:' + config.width + 'px;';
5557 row.cellObjects = cellObjects;
5565 onBeforeLoad : function()
5567 //Roo.log('ds onBeforeLoad');
5571 //if(this.loadMask){
5572 // this.maskEl.show();
5578 this.el.select('tbody', true).first().dom.innerHTML = '';
5581 getSelectionModel : function(){
5583 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5585 return this.selModel;
5588 * Render the Roo.bootstrap object from renderder
5590 renderCellObject : function(r)
5594 var t = r.cfg.render(r.container);
5597 Roo.each(r.cfg.cn, function(c){
5599 container: t.getChildContainer(),
5602 _this.renderCellObject(child);
5619 * @class Roo.bootstrap.TableCell
5620 * @extends Roo.bootstrap.Component
5621 * Bootstrap TableCell class
5622 * @cfg {String} html cell contain text
5623 * @cfg {String} cls cell class
5624 * @cfg {String} tag cell tag (td|th) default td
5625 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5626 * @cfg {String} align Aligns the content in a cell
5627 * @cfg {String} axis Categorizes cells
5628 * @cfg {String} bgcolor Specifies the background color of a cell
5629 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5630 * @cfg {Number} colspan Specifies the number of columns a cell should span
5631 * @cfg {String} headers Specifies one or more header cells a cell is related to
5632 * @cfg {Number} height Sets the height of a cell
5633 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5634 * @cfg {Number} rowspan Sets the number of rows a cell should span
5635 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5636 * @cfg {String} valign Vertical aligns the content in a cell
5637 * @cfg {Number} width Specifies the width of a cell
5640 * Create a new TableCell
5641 * @param {Object} config The config object
5644 Roo.bootstrap.TableCell = function(config){
5645 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5648 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
5668 getAutoCreate : function(){
5669 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5689 cfg.align=this.align
5695 cfg.bgcolor=this.bgcolor
5698 cfg.charoff=this.charoff
5701 cfg.colspan=this.colspan
5704 cfg.headers=this.headers
5707 cfg.height=this.height
5710 cfg.nowrap=this.nowrap
5713 cfg.rowspan=this.rowspan
5716 cfg.scope=this.scope
5719 cfg.valign=this.valign
5722 cfg.width=this.width
5741 * @class Roo.bootstrap.TableRow
5742 * @extends Roo.bootstrap.Component
5743 * Bootstrap TableRow class
5744 * @cfg {String} cls row class
5745 * @cfg {String} align Aligns the content in a table row
5746 * @cfg {String} bgcolor Specifies a background color for a table row
5747 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5748 * @cfg {String} valign Vertical aligns the content in a table row
5751 * Create a new TableRow
5752 * @param {Object} config The config object
5755 Roo.bootstrap.TableRow = function(config){
5756 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5759 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
5767 getAutoCreate : function(){
5768 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5778 cfg.align = this.align;
5781 cfg.bgcolor = this.bgcolor;
5784 cfg.charoff = this.charoff;
5787 cfg.valign = this.valign;
5805 * @class Roo.bootstrap.TableBody
5806 * @extends Roo.bootstrap.Component
5807 * Bootstrap TableBody class
5808 * @cfg {String} cls element class
5809 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5810 * @cfg {String} align Aligns the content inside the element
5811 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5812 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5815 * Create a new TableBody
5816 * @param {Object} config The config object
5819 Roo.bootstrap.TableBody = function(config){
5820 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5823 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
5831 getAutoCreate : function(){
5832 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5846 cfg.align = this.align;
5849 cfg.charoff = this.charoff;
5852 cfg.valign = this.valign;
5859 // initEvents : function()
5866 // this.store = Roo.factory(this.store, Roo.data);
5867 // this.store.on('load', this.onLoad, this);
5869 // this.store.load();
5873 // onLoad: function ()
5875 // this.fireEvent('load', this);
5885 * Ext JS Library 1.1.1
5886 * Copyright(c) 2006-2007, Ext JS, LLC.
5888 * Originally Released Under LGPL - original licence link has changed is not relivant.
5891 * <script type="text/javascript">
5894 // as we use this in bootstrap.
5895 Roo.namespace('Roo.form');
5897 * @class Roo.form.Action
5898 * Internal Class used to handle form actions
5900 * @param {Roo.form.BasicForm} el The form element or its id
5901 * @param {Object} config Configuration options
5906 // define the action interface
5907 Roo.form.Action = function(form, options){
5909 this.options = options || {};
5912 * Client Validation Failed
5915 Roo.form.Action.CLIENT_INVALID = 'client';
5917 * Server Validation Failed
5920 Roo.form.Action.SERVER_INVALID = 'server';
5922 * Connect to Server Failed
5925 Roo.form.Action.CONNECT_FAILURE = 'connect';
5927 * Reading Data from Server Failed
5930 Roo.form.Action.LOAD_FAILURE = 'load';
5932 Roo.form.Action.prototype = {
5934 failureType : undefined,
5935 response : undefined,
5939 run : function(options){
5944 success : function(response){
5949 handleResponse : function(response){
5953 // default connection failure
5954 failure : function(response){
5956 this.response = response;
5957 this.failureType = Roo.form.Action.CONNECT_FAILURE;
5958 this.form.afterAction(this, false);
5961 processResponse : function(response){
5962 this.response = response;
5963 if(!response.responseText){
5966 this.result = this.handleResponse(response);
5970 // utility functions used internally
5971 getUrl : function(appendParams){
5972 var url = this.options.url || this.form.url || this.form.el.dom.action;
5974 var p = this.getParams();
5976 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5982 getMethod : function(){
5983 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5986 getParams : function(){
5987 var bp = this.form.baseParams;
5988 var p = this.options.params;
5990 if(typeof p == "object"){
5991 p = Roo.urlEncode(Roo.applyIf(p, bp));
5992 }else if(typeof p == 'string' && bp){
5993 p += '&' + Roo.urlEncode(bp);
5996 p = Roo.urlEncode(bp);
6001 createCallback : function(){
6003 success: this.success,
6004 failure: this.failure,
6006 timeout: (this.form.timeout*1000),
6007 upload: this.form.fileUpload ? this.success : undefined
6012 Roo.form.Action.Submit = function(form, options){
6013 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6016 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6019 haveProgress : false,
6020 uploadComplete : false,
6022 // uploadProgress indicator.
6023 uploadProgress : function()
6025 if (!this.form.progressUrl) {
6029 if (!this.haveProgress) {
6030 Roo.MessageBox.progress("Uploading", "Uploading");
6032 if (this.uploadComplete) {
6033 Roo.MessageBox.hide();
6037 this.haveProgress = true;
6039 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6041 var c = new Roo.data.Connection();
6043 url : this.form.progressUrl,
6048 success : function(req){
6049 //console.log(data);
6053 rdata = Roo.decode(req.responseText)
6055 Roo.log("Invalid data from server..");
6059 if (!rdata || !rdata.success) {
6061 Roo.MessageBox.alert(Roo.encode(rdata));
6064 var data = rdata.data;
6066 if (this.uploadComplete) {
6067 Roo.MessageBox.hide();
6072 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6073 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6076 this.uploadProgress.defer(2000,this);
6079 failure: function(data) {
6080 Roo.log('progress url failed ');
6091 // run get Values on the form, so it syncs any secondary forms.
6092 this.form.getValues();
6094 var o = this.options;
6095 var method = this.getMethod();
6096 var isPost = method == 'POST';
6097 if(o.clientValidation === false || this.form.isValid()){
6099 if (this.form.progressUrl) {
6100 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6101 (new Date() * 1) + '' + Math.random());
6106 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6107 form:this.form.el.dom,
6108 url:this.getUrl(!isPost),
6110 params:isPost ? this.getParams() : null,
6111 isUpload: this.form.fileUpload
6114 this.uploadProgress();
6116 }else if (o.clientValidation !== false){ // client validation failed
6117 this.failureType = Roo.form.Action.CLIENT_INVALID;
6118 this.form.afterAction(this, false);
6122 success : function(response)
6124 this.uploadComplete= true;
6125 if (this.haveProgress) {
6126 Roo.MessageBox.hide();
6130 var result = this.processResponse(response);
6131 if(result === true || result.success){
6132 this.form.afterAction(this, true);
6136 this.form.markInvalid(result.errors);
6137 this.failureType = Roo.form.Action.SERVER_INVALID;
6139 this.form.afterAction(this, false);
6141 failure : function(response)
6143 this.uploadComplete= true;
6144 if (this.haveProgress) {
6145 Roo.MessageBox.hide();
6148 this.response = response;
6149 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6150 this.form.afterAction(this, false);
6153 handleResponse : function(response){
6154 if(this.form.errorReader){
6155 var rs = this.form.errorReader.read(response);
6158 for(var i = 0, len = rs.records.length; i < len; i++) {
6159 var r = rs.records[i];
6163 if(errors.length < 1){
6167 success : rs.success,
6173 ret = Roo.decode(response.responseText);
6177 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6187 Roo.form.Action.Load = function(form, options){
6188 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6189 this.reader = this.form.reader;
6192 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6197 Roo.Ajax.request(Roo.apply(
6198 this.createCallback(), {
6199 method:this.getMethod(),
6200 url:this.getUrl(false),
6201 params:this.getParams()
6205 success : function(response){
6207 var result = this.processResponse(response);
6208 if(result === true || !result.success || !result.data){
6209 this.failureType = Roo.form.Action.LOAD_FAILURE;
6210 this.form.afterAction(this, false);
6213 this.form.clearInvalid();
6214 this.form.setValues(result.data);
6215 this.form.afterAction(this, true);
6218 handleResponse : function(response){
6219 if(this.form.reader){
6220 var rs = this.form.reader.read(response);
6221 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6223 success : rs.success,
6227 return Roo.decode(response.responseText);
6231 Roo.form.Action.ACTION_TYPES = {
6232 'load' : Roo.form.Action.Load,
6233 'submit' : Roo.form.Action.Submit
6242 * @class Roo.bootstrap.Form
6243 * @extends Roo.bootstrap.Component
6244 * Bootstrap Form class
6245 * @cfg {String} method GET | POST (default POST)
6246 * @cfg {String} labelAlign top | left (default top)
6247 * @cfg {String} align left | right - for navbars
6248 * @cfg {Boolean} loadMask load mask when submit (default true)
6253 * @param {Object} config The config object
6257 Roo.bootstrap.Form = function(config){
6258 Roo.bootstrap.Form.superclass.constructor.call(this, config);
6261 * @event clientvalidation
6262 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6263 * @param {Form} this
6264 * @param {Boolean} valid true if the form has passed client-side validation
6266 clientvalidation: true,
6268 * @event beforeaction
6269 * Fires before any action is performed. Return false to cancel the action.
6270 * @param {Form} this
6271 * @param {Action} action The action to be performed
6275 * @event actionfailed
6276 * Fires when an action fails.
6277 * @param {Form} this
6278 * @param {Action} action The action that failed
6280 actionfailed : true,
6282 * @event actioncomplete
6283 * Fires when an action is completed.
6284 * @param {Form} this
6285 * @param {Action} action The action that completed
6287 actioncomplete : true
6292 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
6295 * @cfg {String} method
6296 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6301 * The URL to use for form actions if one isn't supplied in the action options.
6304 * @cfg {Boolean} fileUpload
6305 * Set to true if this form is a file upload.
6309 * @cfg {Object} baseParams
6310 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6314 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6318 * @cfg {Sting} align (left|right) for navbar forms
6323 activeAction : null,
6326 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6327 * element by passing it or its id or mask the form itself by passing in true.
6330 waitMsgTarget : false,
6334 getAutoCreate : function(){
6338 method : this.method || 'POST',
6339 id : this.id || Roo.id(),
6342 if (this.parent().xtype.match(/^Nav/)) {
6343 cfg.cls = 'navbar-form navbar-' + this.align;
6347 if (this.labelAlign == 'left' ) {
6348 cfg.cls += ' form-horizontal';
6354 initEvents : function()
6356 this.el.on('submit', this.onSubmit, this);
6357 // this was added as random key presses on the form where triggering form submit.
6358 this.el.on('keypress', function(e) {
6359 if (e.getCharCode() != 13) {
6362 // we might need to allow it for textareas.. and some other items.
6363 // check e.getTarget().
6365 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6369 Roo.log("keypress blocked");
6377 onSubmit : function(e){
6382 * Returns true if client-side validation on the form is successful.
6385 isValid : function(){
6386 var items = this.getItems();
6388 items.each(function(f){
6397 * Returns true if any fields in this form have changed since their original load.
6400 isDirty : function(){
6402 var items = this.getItems();
6403 items.each(function(f){
6413 * Performs a predefined action (submit or load) or custom actions you define on this form.
6414 * @param {String} actionName The name of the action type
6415 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
6416 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6417 * accept other config options):
6419 Property Type Description
6420 ---------------- --------------- ----------------------------------------------------------------------------------
6421 url String The url for the action (defaults to the form's url)
6422 method String The form method to use (defaults to the form's method, or POST if not defined)
6423 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
6424 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
6425 validate the form on the client (defaults to false)
6427 * @return {BasicForm} this
6429 doAction : function(action, options){
6430 if(typeof action == 'string'){
6431 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6433 if(this.fireEvent('beforeaction', this, action) !== false){
6434 this.beforeAction(action);
6435 action.run.defer(100, action);
6441 beforeAction : function(action){
6442 var o = action.options;
6445 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6447 // not really supported yet.. ??
6449 //if(this.waitMsgTarget === true){
6450 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6451 //}else if(this.waitMsgTarget){
6452 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6453 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6455 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6461 afterAction : function(action, success){
6462 this.activeAction = null;
6463 var o = action.options;
6465 //if(this.waitMsgTarget === true){
6467 //}else if(this.waitMsgTarget){
6468 // this.waitMsgTarget.unmask();
6470 // Roo.MessageBox.updateProgress(1);
6471 // Roo.MessageBox.hide();
6478 Roo.callback(o.success, o.scope, [this, action]);
6479 this.fireEvent('actioncomplete', this, action);
6483 // failure condition..
6484 // we have a scenario where updates need confirming.
6485 // eg. if a locking scenario exists..
6486 // we look for { errors : { needs_confirm : true }} in the response.
6488 (typeof(action.result) != 'undefined') &&
6489 (typeof(action.result.errors) != 'undefined') &&
6490 (typeof(action.result.errors.needs_confirm) != 'undefined')
6493 Roo.log("not supported yet");
6496 Roo.MessageBox.confirm(
6497 "Change requires confirmation",
6498 action.result.errorMsg,
6503 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
6513 Roo.callback(o.failure, o.scope, [this, action]);
6514 // show an error message if no failed handler is set..
6515 if (!this.hasListener('actionfailed')) {
6516 Roo.log("need to add dialog support");
6518 Roo.MessageBox.alert("Error",
6519 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6520 action.result.errorMsg :
6521 "Saving Failed, please check your entries or try again"
6526 this.fireEvent('actionfailed', this, action);
6531 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6532 * @param {String} id The value to search for
6535 findField : function(id){
6536 var items = this.getItems();
6537 var field = items.get(id);
6539 items.each(function(f){
6540 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6547 return field || null;
6550 * Mark fields in this form invalid in bulk.
6551 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6552 * @return {BasicForm} this
6554 markInvalid : function(errors){
6555 if(errors instanceof Array){
6556 for(var i = 0, len = errors.length; i < len; i++){
6557 var fieldError = errors[i];
6558 var f = this.findField(fieldError.id);
6560 f.markInvalid(fieldError.msg);
6566 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6567 field.markInvalid(errors[id]);
6571 //Roo.each(this.childForms || [], function (f) {
6572 // f.markInvalid(errors);
6579 * Set values for fields in this form in bulk.
6580 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6581 * @return {BasicForm} this
6583 setValues : function(values){
6584 if(values instanceof Array){ // array of objects
6585 for(var i = 0, len = values.length; i < len; i++){
6587 var f = this.findField(v.id);
6589 f.setValue(v.value);
6590 if(this.trackResetOnLoad){
6591 f.originalValue = f.getValue();
6595 }else{ // object hash
6598 if(typeof values[id] != 'function' && (field = this.findField(id))){
6600 if (field.setFromData &&
6602 field.displayField &&
6603 // combos' with local stores can
6604 // be queried via setValue()
6605 // to set their value..
6606 (field.store && !field.store.isLocal)
6610 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6611 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6612 field.setFromData(sd);
6615 field.setValue(values[id]);
6619 if(this.trackResetOnLoad){
6620 field.originalValue = field.getValue();
6626 //Roo.each(this.childForms || [], function (f) {
6627 // f.setValues(values);
6634 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6635 * they are returned as an array.
6636 * @param {Boolean} asString
6639 getValues : function(asString){
6640 //if (this.childForms) {
6641 // copy values from the child forms
6642 // Roo.each(this.childForms, function (f) {
6643 // this.setValues(f.getValues());
6649 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6650 if(asString === true){
6653 return Roo.urlDecode(fs);
6657 * Returns the fields in this form as an object with key/value pairs.
6658 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6661 getFieldValues : function(with_hidden)
6663 var items = this.getItems();
6665 items.each(function(f){
6669 var v = f.getValue();
6670 if (f.inputType =='radio') {
6671 if (typeof(ret[f.getName()]) == 'undefined') {
6672 ret[f.getName()] = ''; // empty..
6675 if (!f.el.dom.checked) {
6683 // not sure if this supported any more..
6684 if ((typeof(v) == 'object') && f.getRawValue) {
6685 v = f.getRawValue() ; // dates..
6687 // combo boxes where name != hiddenName...
6688 if (f.name != f.getName()) {
6689 ret[f.name] = f.getRawValue();
6691 ret[f.getName()] = v;
6698 * Clears all invalid messages in this form.
6699 * @return {BasicForm} this
6701 clearInvalid : function(){
6702 var items = this.getItems();
6704 items.each(function(f){
6715 * @return {BasicForm} this
6718 var items = this.getItems();
6719 items.each(function(f){
6723 Roo.each(this.childForms || [], function (f) {
6730 getItems : function()
6732 var r=new Roo.util.MixedCollection(false, function(o){
6733 return o.id || (o.id = Roo.id());
6735 var iter = function(el) {
6742 Roo.each(el.items,function(e) {
6761 * Ext JS Library 1.1.1
6762 * Copyright(c) 2006-2007, Ext JS, LLC.
6764 * Originally Released Under LGPL - original licence link has changed is not relivant.
6767 * <script type="text/javascript">
6770 * @class Roo.form.VTypes
6771 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6774 Roo.form.VTypes = function(){
6775 // closure these in so they are only created once.
6776 var alpha = /^[a-zA-Z_]+$/;
6777 var alphanum = /^[a-zA-Z0-9_]+$/;
6778 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6779 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6781 // All these messages and functions are configurable
6784 * The function used to validate email addresses
6785 * @param {String} value The email address
6787 'email' : function(v){
6788 return email.test(v);
6791 * The error text to display when the email validation function returns false
6794 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6796 * The keystroke filter mask to be applied on email input
6799 'emailMask' : /[a-z0-9_\.\-@]/i,
6802 * The function used to validate URLs
6803 * @param {String} value The URL
6805 'url' : function(v){
6809 * The error text to display when the url validation function returns false
6812 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6815 * The function used to validate alpha values
6816 * @param {String} value The value
6818 'alpha' : function(v){
6819 return alpha.test(v);
6822 * The error text to display when the alpha validation function returns false
6825 'alphaText' : 'This field should only contain letters and _',
6827 * The keystroke filter mask to be applied on alpha input
6830 'alphaMask' : /[a-z_]/i,
6833 * The function used to validate alphanumeric values
6834 * @param {String} value The value
6836 'alphanum' : function(v){
6837 return alphanum.test(v);
6840 * The error text to display when the alphanumeric validation function returns false
6843 'alphanumText' : 'This field should only contain letters, numbers and _',
6845 * The keystroke filter mask to be applied on alphanumeric input
6848 'alphanumMask' : /[a-z0-9_]/i
6858 * @class Roo.bootstrap.Input
6859 * @extends Roo.bootstrap.Component
6860 * Bootstrap Input class
6861 * @cfg {Boolean} disabled is it disabled
6862 * @cfg {String} fieldLabel - the label associated
6863 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6864 * @cfg {String} name name of the input
6865 * @cfg {string} fieldLabel - the label associated
6866 * @cfg {string} inputType - input / file submit ...
6867 * @cfg {string} placeholder - placeholder to put in text.
6868 * @cfg {string} before - input group add on before
6869 * @cfg {string} after - input group add on after
6870 * @cfg {string} size - (lg|sm) or leave empty..
6871 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6872 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6873 * @cfg {Number} md colspan out of 12 for computer-sized screens
6874 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6875 * @cfg {string} value default value of the input
6876 * @cfg {Number} labelWidth set the width of label (0-12)
6877 * @cfg {String} labelAlign (top|left)
6878 * @cfg {Boolean} readOnly Specifies that the field should be read-only
6879 * @cfg {String} align (left|center|right) Default left
6883 * Create a new Input
6884 * @param {Object} config The config object
6887 Roo.bootstrap.Input = function(config){
6888 Roo.bootstrap.Input.superclass.constructor.call(this, config);
6893 * Fires when this field receives input focus.
6894 * @param {Roo.form.Field} this
6899 * Fires when this field loses input focus.
6900 * @param {Roo.form.Field} this
6905 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
6906 * {@link Roo.EventObject#getKey} to determine which key was pressed.
6907 * @param {Roo.form.Field} this
6908 * @param {Roo.EventObject} e The event object
6913 * Fires just before the field blurs if the field value has changed.
6914 * @param {Roo.form.Field} this
6915 * @param {Mixed} newValue The new value
6916 * @param {Mixed} oldValue The original value
6921 * Fires after the field has been marked as invalid.
6922 * @param {Roo.form.Field} this
6923 * @param {String} msg The validation message
6928 * Fires after the field has been validated with no errors.
6929 * @param {Roo.form.Field} this
6934 * Fires after the key up
6935 * @param {Roo.form.Field} this
6936 * @param {Roo.EventObject} e The event Object
6942 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
6944 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6945 automatic validation (defaults to "keyup").
6947 validationEvent : "keyup",
6949 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6951 validateOnBlur : true,
6953 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6955 validationDelay : 250,
6957 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6959 focusClass : "x-form-focus", // not needed???
6963 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6965 invalidClass : "has-error",
6968 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6970 selectOnFocus : false,
6973 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6977 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6982 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6984 disableKeyFilter : false,
6987 * @cfg {Boolean} disabled True to disable the field (defaults to false).
6991 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6995 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6997 blankText : "This field is required",
7000 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7004 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7006 maxLength : Number.MAX_VALUE,
7008 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7010 minLengthText : "The minimum length for this field is {0}",
7012 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7014 maxLengthText : "The maximum length for this field is {0}",
7018 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7019 * If available, this function will be called only after the basic validators all return true, and will be passed the
7020 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7024 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7025 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7026 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7030 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7053 formatedValue : false,
7055 parentLabelAlign : function()
7058 while (parent.parent()) {
7059 parent = parent.parent();
7060 if (typeof(parent.labelAlign) !='undefined') {
7061 return parent.labelAlign;
7068 getAutoCreate : function(){
7070 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7076 if(this.inputType != 'hidden'){
7077 cfg.cls = 'form-group' //input-group
7083 type : this.inputType,
7085 cls : 'form-control',
7086 placeholder : this.placeholder || ''
7091 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7094 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7095 input.maxLength = this.maxLength;
7098 if (this.disabled) {
7099 input.disabled=true;
7102 if (this.readOnly) {
7103 input.readonly=true;
7107 input.name = this.name;
7110 input.cls += ' input-' + this.size;
7113 ['xs','sm','md','lg'].map(function(size){
7114 if (settings[size]) {
7115 cfg.cls += ' col-' + size + '-' + settings[size];
7119 var inputblock = input;
7121 if (this.before || this.after) {
7124 cls : 'input-group',
7127 if (this.before && typeof(this.before) == 'string') {
7129 inputblock.cn.push({
7131 cls : 'roo-input-before input-group-addon',
7135 if (this.before && typeof(this.before) == 'object') {
7136 this.before = Roo.factory(this.before);
7137 Roo.log(this.before);
7138 inputblock.cn.push({
7140 cls : 'roo-input-before input-group-' +
7141 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7145 inputblock.cn.push(input);
7147 if (this.after && typeof(this.after) == 'string') {
7148 inputblock.cn.push({
7150 cls : 'roo-input-after input-group-addon',
7154 if (this.after && typeof(this.after) == 'object') {
7155 this.after = Roo.factory(this.after);
7156 Roo.log(this.after);
7157 inputblock.cn.push({
7159 cls : 'roo-input-after input-group-' +
7160 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7165 if (align ==='left' && this.fieldLabel.length) {
7166 Roo.log("left and has label");
7172 cls : 'control-label col-sm-' + this.labelWidth,
7173 html : this.fieldLabel
7177 cls : "col-sm-" + (12 - this.labelWidth),
7184 } else if ( this.fieldLabel.length) {
7190 //cls : 'input-group-addon',
7191 html : this.fieldLabel
7201 Roo.log(" no label && no align");
7210 Roo.log('input-parentType: ' + this.parentType);
7212 if (this.parentType === 'Navbar' && this.parent().bar) {
7213 cfg.cls += ' navbar-form';
7221 * return the real input element.
7223 inputEl: function ()
7225 return this.el.select('input.form-control',true).first();
7228 tooltipEl : function()
7230 return this.inputEl();
7233 setDisabled : function(v)
7235 var i = this.inputEl().dom;
7237 i.removeAttribute('disabled');
7241 i.setAttribute('disabled','true');
7243 initEvents : function()
7246 this.inputEl().on("keydown" , this.fireKey, this);
7247 this.inputEl().on("focus", this.onFocus, this);
7248 this.inputEl().on("blur", this.onBlur, this);
7250 this.inputEl().relayEvent('keyup', this);
7252 // reference to original value for reset
7253 this.originalValue = this.getValue();
7254 //Roo.form.TextField.superclass.initEvents.call(this);
7255 if(this.validationEvent == 'keyup'){
7256 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7257 this.inputEl().on('keyup', this.filterValidation, this);
7259 else if(this.validationEvent !== false){
7260 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7263 if(this.selectOnFocus){
7264 this.on("focus", this.preFocus, this);
7267 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7268 this.inputEl().on("keypress", this.filterKeys, this);
7271 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
7272 this.el.on("click", this.autoSize, this);
7275 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7276 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7279 if (typeof(this.before) == 'object') {
7280 this.before.render(this.el.select('.roo-input-before',true).first());
7282 if (typeof(this.after) == 'object') {
7283 this.after.render(this.el.select('.roo-input-after',true).first());
7288 filterValidation : function(e){
7289 if(!e.isNavKeyPress()){
7290 this.validationTask.delay(this.validationDelay);
7294 * Validates the field value
7295 * @return {Boolean} True if the value is valid, else false
7297 validate : function(){
7298 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7299 if(this.disabled || this.validateValue(this.getRawValue())){
7300 this.clearInvalid();
7308 * Validates a value according to the field's validation rules and marks the field as invalid
7309 * if the validation fails
7310 * @param {Mixed} value The value to validate
7311 * @return {Boolean} True if the value is valid, else false
7313 validateValue : function(value){
7314 if(value.length < 1) { // if it's blank
7315 if(this.allowBlank){
7316 this.clearInvalid();
7319 this.markInvalid(this.blankText);
7323 if(value.length < this.minLength){
7324 this.markInvalid(String.format(this.minLengthText, this.minLength));
7327 if(value.length > this.maxLength){
7328 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7332 var vt = Roo.form.VTypes;
7333 if(!vt[this.vtype](value, this)){
7334 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7338 if(typeof this.validator == "function"){
7339 var msg = this.validator(value);
7341 this.markInvalid(msg);
7345 if(this.regex && !this.regex.test(value)){
7346 this.markInvalid(this.regexText);
7355 fireKey : function(e){
7356 //Roo.log('field ' + e.getKey());
7357 if(e.isNavKeyPress()){
7358 this.fireEvent("specialkey", this, e);
7361 focus : function (selectText){
7363 this.inputEl().focus();
7364 if(selectText === true){
7365 this.inputEl().dom.select();
7371 onFocus : function(){
7372 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7373 // this.el.addClass(this.focusClass);
7376 this.hasFocus = true;
7377 this.startValue = this.getValue();
7378 this.fireEvent("focus", this);
7382 beforeBlur : Roo.emptyFn,
7386 onBlur : function(){
7388 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7389 //this.el.removeClass(this.focusClass);
7391 this.hasFocus = false;
7392 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7395 var v = this.getValue();
7396 if(String(v) !== String(this.startValue)){
7397 this.fireEvent('change', this, v, this.startValue);
7399 this.fireEvent("blur", this);
7403 * Resets the current field value to the originally loaded value and clears any validation messages
7406 this.setValue(this.originalValue);
7407 this.clearInvalid();
7410 * Returns the name of the field
7411 * @return {Mixed} name The name field
7413 getName: function(){
7417 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
7418 * @return {Mixed} value The field value
7420 getValue : function(){
7422 var v = this.inputEl().getValue();
7427 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
7428 * @return {Mixed} value The field value
7430 getRawValue : function(){
7431 var v = this.inputEl().getValue();
7437 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
7438 * @param {Mixed} value The value to set
7440 setRawValue : function(v){
7441 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7444 selectText : function(start, end){
7445 var v = this.getRawValue();
7447 start = start === undefined ? 0 : start;
7448 end = end === undefined ? v.length : end;
7449 var d = this.inputEl().dom;
7450 if(d.setSelectionRange){
7451 d.setSelectionRange(start, end);
7452 }else if(d.createTextRange){
7453 var range = d.createTextRange();
7454 range.moveStart("character", start);
7455 range.moveEnd("character", v.length-end);
7462 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
7463 * @param {Mixed} value The value to set
7465 setValue : function(v){
7468 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7474 processValue : function(value){
7475 if(this.stripCharsRe){
7476 var newValue = value.replace(this.stripCharsRe, '');
7477 if(newValue !== value){
7478 this.setRawValue(newValue);
7485 preFocus : function(){
7487 if(this.selectOnFocus){
7488 this.inputEl().dom.select();
7491 filterKeys : function(e){
7493 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7496 var c = e.getCharCode(), cc = String.fromCharCode(c);
7497 if(Roo.isIE && (e.isSpecialKey() || !cc)){
7500 if(!this.maskRe.test(cc)){
7505 * Clear any invalid styles/messages for this field
7507 clearInvalid : function(){
7509 if(!this.el || this.preventMark){ // not rendered
7512 this.el.removeClass(this.invalidClass);
7514 switch(this.msgTarget){
7516 this.el.dom.qtip = '';
7519 this.el.dom.title = '';
7523 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7528 this.errorIcon.dom.qtip = '';
7529 this.errorIcon.hide();
7530 this.un('resize', this.alignErrorIcon, this);
7534 var t = Roo.getDom(this.msgTarget);
7536 t.style.display = 'none';
7540 this.fireEvent('valid', this);
7543 * Mark this field as invalid
7544 * @param {String} msg The validation message
7546 markInvalid : function(msg){
7547 if(!this.el || this.preventMark){ // not rendered
7550 this.el.addClass(this.invalidClass);
7552 msg = msg || this.invalidText;
7553 switch(this.msgTarget){
7555 this.el.dom.qtip = msg;
7556 this.el.dom.qclass = 'x-form-invalid-tip';
7557 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7558 Roo.QuickTips.enable();
7562 this.el.dom.title = msg;
7566 var elp = this.el.findParent('.x-form-element', 5, true);
7567 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7568 this.errorEl.setWidth(elp.getWidth(true)-20);
7570 this.errorEl.update(msg);
7571 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7574 if(!this.errorIcon){
7575 var elp = this.el.findParent('.x-form-element', 5, true);
7576 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7578 this.alignErrorIcon();
7579 this.errorIcon.dom.qtip = msg;
7580 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7581 this.errorIcon.show();
7582 this.on('resize', this.alignErrorIcon, this);
7585 var t = Roo.getDom(this.msgTarget);
7587 t.style.display = this.msgDisplay;
7591 this.fireEvent('invalid', this, msg);
7594 SafariOnKeyDown : function(event)
7596 // this is a workaround for a password hang bug on chrome/ webkit.
7598 var isSelectAll = false;
7600 if(this.inputEl().dom.selectionEnd > 0){
7601 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7603 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7604 event.preventDefault();
7609 if(isSelectAll){ // backspace and delete key
7611 event.preventDefault();
7612 // this is very hacky as keydown always get's upper case.
7614 var cc = String.fromCharCode(event.getCharCode());
7615 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
7619 adjustWidth : function(tag, w){
7620 tag = tag.toLowerCase();
7621 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7622 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7626 if(tag == 'textarea'){
7629 }else if(Roo.isOpera){
7633 if(tag == 'textarea'){
7652 * @class Roo.bootstrap.TextArea
7653 * @extends Roo.bootstrap.Input
7654 * Bootstrap TextArea class
7655 * @cfg {Number} cols Specifies the visible width of a text area
7656 * @cfg {Number} rows Specifies the visible number of lines in a text area
7657 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7658 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7659 * @cfg {string} html text
7662 * Create a new TextArea
7663 * @param {Object} config The config object
7666 Roo.bootstrap.TextArea = function(config){
7667 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7671 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
7681 getAutoCreate : function(){
7683 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7694 value : this.value || '',
7695 html: this.html || '',
7696 cls : 'form-control',
7697 placeholder : this.placeholder || ''
7701 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7702 input.maxLength = this.maxLength;
7706 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7710 input.cols = this.cols;
7713 if (this.readOnly) {
7714 input.readonly = true;
7718 input.name = this.name;
7722 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7726 ['xs','sm','md','lg'].map(function(size){
7727 if (settings[size]) {
7728 cfg.cls += ' col-' + size + '-' + settings[size];
7732 var inputblock = input;
7734 if (this.before || this.after) {
7737 cls : 'input-group',
7741 inputblock.cn.push({
7743 cls : 'input-group-addon',
7747 inputblock.cn.push(input);
7749 inputblock.cn.push({
7751 cls : 'input-group-addon',
7758 if (align ==='left' && this.fieldLabel.length) {
7759 Roo.log("left and has label");
7765 cls : 'control-label col-sm-' + this.labelWidth,
7766 html : this.fieldLabel
7770 cls : "col-sm-" + (12 - this.labelWidth),
7777 } else if ( this.fieldLabel.length) {
7783 //cls : 'input-group-addon',
7784 html : this.fieldLabel
7794 Roo.log(" no label && no align");
7804 if (this.disabled) {
7805 input.disabled=true;
7812 * return the real textarea element.
7814 inputEl: function ()
7816 return this.el.select('textarea.form-control',true).first();
7824 * trigger field - base class for combo..
7829 * @class Roo.bootstrap.TriggerField
7830 * @extends Roo.bootstrap.Input
7831 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7832 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7833 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7834 * for which you can provide a custom implementation. For example:
7836 var trigger = new Roo.bootstrap.TriggerField();
7837 trigger.onTriggerClick = myTriggerFn;
7838 trigger.applyTo('my-field');
7841 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7842 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7843 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
7844 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7846 * Create a new TriggerField.
7847 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7848 * to the base TextField)
7850 Roo.bootstrap.TriggerField = function(config){
7851 this.mimicing = false;
7852 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7855 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
7857 * @cfg {String} triggerClass A CSS class to apply to the trigger
7860 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7864 /** @cfg {Boolean} grow @hide */
7865 /** @cfg {Number} growMin @hide */
7866 /** @cfg {Number} growMax @hide */
7872 autoSize: Roo.emptyFn,
7879 actionMode : 'wrap',
7883 getAutoCreate : function(){
7885 var align = this.labelAlign || this.parentLabelAlign();
7890 cls: 'form-group' //input-group
7897 type : this.inputType,
7898 cls : 'form-control',
7899 autocomplete: 'off',
7900 placeholder : this.placeholder || ''
7904 input.name = this.name;
7907 input.cls += ' input-' + this.size;
7910 if (this.disabled) {
7911 input.disabled=true;
7914 var inputblock = input;
7916 if (this.before || this.after) {
7919 cls : 'input-group',
7923 inputblock.cn.push({
7925 cls : 'input-group-addon',
7929 inputblock.cn.push(input);
7931 inputblock.cn.push({
7933 cls : 'input-group-addon',
7946 cls: 'form-hidden-field'
7954 Roo.log('multiple');
7962 cls: 'form-hidden-field'
7966 cls: 'select2-choices',
7970 cls: 'select2-search-field',
7983 cls: 'select2-container input-group',
7988 // cls: 'typeahead typeahead-long dropdown-menu',
7989 // style: 'display:none'
7994 if(!this.multiple && this.showToggleBtn){
7997 cls : 'input-group-addon btn dropdown-toggle',
8005 cls: 'combobox-clear',
8019 combobox.cls += ' select2-container-multi';
8022 if (align ==='left' && this.fieldLabel.length) {
8024 Roo.log("left and has label");
8030 cls : 'control-label col-sm-' + this.labelWidth,
8031 html : this.fieldLabel
8035 cls : "col-sm-" + (12 - this.labelWidth),
8042 } else if ( this.fieldLabel.length) {
8048 //cls : 'input-group-addon',
8049 html : this.fieldLabel
8059 Roo.log(" no label && no align");
8066 ['xs','sm','md','lg'].map(function(size){
8067 if (settings[size]) {
8068 cfg.cls += ' col-' + size + '-' + settings[size];
8079 onResize : function(w, h){
8080 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8081 // if(typeof w == 'number'){
8082 // var x = w - this.trigger.getWidth();
8083 // this.inputEl().setWidth(this.adjustWidth('input', x));
8084 // this.trigger.setStyle('left', x+'px');
8089 adjustSize : Roo.BoxComponent.prototype.adjustSize,
8092 getResizeEl : function(){
8093 return this.inputEl();
8097 getPositionEl : function(){
8098 return this.inputEl();
8102 alignErrorIcon : function(){
8103 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8107 initEvents : function(){
8111 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8112 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8113 if(!this.multiple && this.showToggleBtn){
8114 this.trigger = this.el.select('span.dropdown-toggle',true).first();
8115 if(this.hideTrigger){
8116 this.trigger.setDisplayed(false);
8118 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8122 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8125 //this.trigger.addClassOnOver('x-form-trigger-over');
8126 //this.trigger.addClassOnClick('x-form-trigger-click');
8129 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8133 createList : function()
8135 this.list = Roo.get(document.body).createChild({
8137 cls: 'typeahead typeahead-long dropdown-menu',
8138 style: 'display:none'
8141 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8146 initTrigger : function(){
8151 onDestroy : function(){
8153 this.trigger.removeAllListeners();
8154 // this.trigger.remove();
8157 // this.wrap.remove();
8159 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8163 onFocus : function(){
8164 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8167 this.wrap.addClass('x-trigger-wrap-focus');
8168 this.mimicing = true;
8169 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8170 if(this.monitorTab){
8171 this.el.on("keydown", this.checkTab, this);
8178 checkTab : function(e){
8179 if(e.getKey() == e.TAB){
8185 onBlur : function(){
8190 mimicBlur : function(e, t){
8192 if(!this.wrap.contains(t) && this.validateBlur()){
8199 triggerBlur : function(){
8200 this.mimicing = false;
8201 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8202 if(this.monitorTab){
8203 this.el.un("keydown", this.checkTab, this);
8205 //this.wrap.removeClass('x-trigger-wrap-focus');
8206 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8210 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8211 validateBlur : function(e, t){
8216 onDisable : function(){
8217 this.inputEl().dom.disabled = true;
8218 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8220 // this.wrap.addClass('x-item-disabled');
8225 onEnable : function(){
8226 this.inputEl().dom.disabled = false;
8227 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8229 // this.el.removeClass('x-item-disabled');
8234 onShow : function(){
8235 var ae = this.getActionEl();
8238 ae.dom.style.display = '';
8239 ae.dom.style.visibility = 'visible';
8245 onHide : function(){
8246 var ae = this.getActionEl();
8247 ae.dom.style.display = 'none';
8251 * The function that should handle the trigger's click event. This method does nothing by default until overridden
8252 * by an implementing function.
8254 * @param {EventObject} e
8256 onTriggerClick : Roo.emptyFn
8260 * Ext JS Library 1.1.1
8261 * Copyright(c) 2006-2007, Ext JS, LLC.
8263 * Originally Released Under LGPL - original licence link has changed is not relivant.
8266 * <script type="text/javascript">
8271 * @class Roo.data.SortTypes
8273 * Defines the default sorting (casting?) comparison functions used when sorting data.
8275 Roo.data.SortTypes = {
8277 * Default sort that does nothing
8278 * @param {Mixed} s The value being converted
8279 * @return {Mixed} The comparison value
8286 * The regular expression used to strip tags
8290 stripTagsRE : /<\/?[^>]+>/gi,
8293 * Strips all HTML tags to sort on text only
8294 * @param {Mixed} s The value being converted
8295 * @return {String} The comparison value
8297 asText : function(s){
8298 return String(s).replace(this.stripTagsRE, "");
8302 * Strips all HTML tags to sort on text only - Case insensitive
8303 * @param {Mixed} s The value being converted
8304 * @return {String} The comparison value
8306 asUCText : function(s){
8307 return String(s).toUpperCase().replace(this.stripTagsRE, "");
8311 * Case insensitive string
8312 * @param {Mixed} s The value being converted
8313 * @return {String} The comparison value
8315 asUCString : function(s) {
8316 return String(s).toUpperCase();
8321 * @param {Mixed} s The value being converted
8322 * @return {Number} The comparison value
8324 asDate : function(s) {
8328 if(s instanceof Date){
8331 return Date.parse(String(s));
8336 * @param {Mixed} s The value being converted
8337 * @return {Float} The comparison value
8339 asFloat : function(s) {
8340 var val = parseFloat(String(s).replace(/,/g, ""));
8341 if(isNaN(val)) val = 0;
8347 * @param {Mixed} s The value being converted
8348 * @return {Number} The comparison value
8350 asInt : function(s) {
8351 var val = parseInt(String(s).replace(/,/g, ""));
8352 if(isNaN(val)) val = 0;
8357 * Ext JS Library 1.1.1
8358 * Copyright(c) 2006-2007, Ext JS, LLC.
8360 * Originally Released Under LGPL - original licence link has changed is not relivant.
8363 * <script type="text/javascript">
8367 * @class Roo.data.Record
8368 * Instances of this class encapsulate both record <em>definition</em> information, and record
8369 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8370 * to access Records cached in an {@link Roo.data.Store} object.<br>
8372 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8373 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8376 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8378 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8379 * {@link #create}. The parameters are the same.
8380 * @param {Array} data An associative Array of data values keyed by the field name.
8381 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8382 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8383 * not specified an integer id is generated.
8385 Roo.data.Record = function(data, id){
8386 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8391 * Generate a constructor for a specific record layout.
8392 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8393 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8394 * Each field definition object may contain the following properties: <ul>
8395 * <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,
8396 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8397 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8398 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8399 * is being used, then this is a string containing the javascript expression to reference the data relative to
8400 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8401 * to the data item relative to the record element. If the mapping expression is the same as the field name,
8402 * this may be omitted.</p></li>
8403 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8404 * <ul><li>auto (Default, implies no conversion)</li>
8409 * <li>date</li></ul></p></li>
8410 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8411 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8412 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8413 * by the Reader into an object that will be stored in the Record. It is passed the
8414 * following parameters:<ul>
8415 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8417 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8419 * <br>usage:<br><pre><code>
8420 var TopicRecord = Roo.data.Record.create(
8421 {name: 'title', mapping: 'topic_title'},
8422 {name: 'author', mapping: 'username'},
8423 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8424 {name: 'lastPost', mapping: 'post_time', type: 'date'},
8425 {name: 'lastPoster', mapping: 'user2'},
8426 {name: 'excerpt', mapping: 'post_text'}
8429 var myNewRecord = new TopicRecord({
8430 title: 'Do my job please',
8433 lastPost: new Date(),
8434 lastPoster: 'Animal',
8435 excerpt: 'No way dude!'
8437 myStore.add(myNewRecord);
8442 Roo.data.Record.create = function(o){
8444 f.superclass.constructor.apply(this, arguments);
8446 Roo.extend(f, Roo.data.Record);
8447 var p = f.prototype;
8448 p.fields = new Roo.util.MixedCollection(false, function(field){
8451 for(var i = 0, len = o.length; i < len; i++){
8452 p.fields.add(new Roo.data.Field(o[i]));
8454 f.getField = function(name){
8455 return p.fields.get(name);
8460 Roo.data.Record.AUTO_ID = 1000;
8461 Roo.data.Record.EDIT = 'edit';
8462 Roo.data.Record.REJECT = 'reject';
8463 Roo.data.Record.COMMIT = 'commit';
8465 Roo.data.Record.prototype = {
8467 * Readonly flag - true if this record has been modified.
8476 join : function(store){
8481 * Set the named field to the specified value.
8482 * @param {String} name The name of the field to set.
8483 * @param {Object} value The value to set the field to.
8485 set : function(name, value){
8486 if(this.data[name] == value){
8493 if(typeof this.modified[name] == 'undefined'){
8494 this.modified[name] = this.data[name];
8496 this.data[name] = value;
8497 if(!this.editing && this.store){
8498 this.store.afterEdit(this);
8503 * Get the value of the named field.
8504 * @param {String} name The name of the field to get the value of.
8505 * @return {Object} The value of the field.
8507 get : function(name){
8508 return this.data[name];
8512 beginEdit : function(){
8513 this.editing = true;
8518 cancelEdit : function(){
8519 this.editing = false;
8520 delete this.modified;
8524 endEdit : function(){
8525 this.editing = false;
8526 if(this.dirty && this.store){
8527 this.store.afterEdit(this);
8532 * Usually called by the {@link Roo.data.Store} which owns the Record.
8533 * Rejects all changes made to the Record since either creation, or the last commit operation.
8534 * Modified fields are reverted to their original values.
8536 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8537 * of reject operations.
8539 reject : function(){
8540 var m = this.modified;
8542 if(typeof m[n] != "function"){
8543 this.data[n] = m[n];
8547 delete this.modified;
8548 this.editing = false;
8550 this.store.afterReject(this);
8555 * Usually called by the {@link Roo.data.Store} which owns the Record.
8556 * Commits all changes made to the Record since either creation, or the last commit operation.
8558 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8559 * of commit operations.
8561 commit : function(){
8563 delete this.modified;
8564 this.editing = false;
8566 this.store.afterCommit(this);
8571 hasError : function(){
8572 return this.error != null;
8576 clearError : function(){
8581 * Creates a copy of this record.
8582 * @param {String} id (optional) A new record id if you don't want to use this record's id
8585 copy : function(newId) {
8586 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8590 * Ext JS Library 1.1.1
8591 * Copyright(c) 2006-2007, Ext JS, LLC.
8593 * Originally Released Under LGPL - original licence link has changed is not relivant.
8596 * <script type="text/javascript">
8602 * @class Roo.data.Store
8603 * @extends Roo.util.Observable
8604 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8605 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8607 * 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
8608 * has no knowledge of the format of the data returned by the Proxy.<br>
8610 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8611 * instances from the data object. These records are cached and made available through accessor functions.
8613 * Creates a new Store.
8614 * @param {Object} config A config object containing the objects needed for the Store to access data,
8615 * and read the data into Records.
8617 Roo.data.Store = function(config){
8618 this.data = new Roo.util.MixedCollection(false);
8619 this.data.getKey = function(o){
8622 this.baseParams = {};
8629 "multisort" : "_multisort"
8632 if(config && config.data){
8633 this.inlineData = config.data;
8637 Roo.apply(this, config);
8639 if(this.reader){ // reader passed
8640 this.reader = Roo.factory(this.reader, Roo.data);
8641 this.reader.xmodule = this.xmodule || false;
8642 if(!this.recordType){
8643 this.recordType = this.reader.recordType;
8645 if(this.reader.onMetaChange){
8646 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8650 if(this.recordType){
8651 this.fields = this.recordType.prototype.fields;
8657 * @event datachanged
8658 * Fires when the data cache has changed, and a widget which is using this Store
8659 * as a Record cache should refresh its view.
8660 * @param {Store} this
8665 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8666 * @param {Store} this
8667 * @param {Object} meta The JSON metadata
8672 * Fires when Records have been added to the Store
8673 * @param {Store} this
8674 * @param {Roo.data.Record[]} records The array of Records added
8675 * @param {Number} index The index at which the record(s) were added
8680 * Fires when a Record has been removed from the Store
8681 * @param {Store} this
8682 * @param {Roo.data.Record} record The Record that was removed
8683 * @param {Number} index The index at which the record was removed
8688 * Fires when a Record has been updated
8689 * @param {Store} this
8690 * @param {Roo.data.Record} record The Record that was updated
8691 * @param {String} operation The update operation being performed. Value may be one of:
8693 Roo.data.Record.EDIT
8694 Roo.data.Record.REJECT
8695 Roo.data.Record.COMMIT
8701 * Fires when the data cache has been cleared.
8702 * @param {Store} this
8707 * Fires before a request is made for a new data object. If the beforeload handler returns false
8708 * the load action will be canceled.
8709 * @param {Store} this
8710 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8714 * @event beforeloadadd
8715 * Fires after a new set of Records has been loaded.
8716 * @param {Store} this
8717 * @param {Roo.data.Record[]} records The Records that were loaded
8718 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8720 beforeloadadd : true,
8723 * Fires after a new set of Records has been loaded, before they are added to the store.
8724 * @param {Store} this
8725 * @param {Roo.data.Record[]} records The Records that were loaded
8726 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8727 * @params {Object} return from reader
8731 * @event loadexception
8732 * Fires if an exception occurs in the Proxy during loading.
8733 * Called with the signature of the Proxy's "loadexception" event.
8734 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8737 * @param {Object} return from JsonData.reader() - success, totalRecords, records
8738 * @param {Object} load options
8739 * @param {Object} jsonData from your request (normally this contains the Exception)
8741 loadexception : true
8745 this.proxy = Roo.factory(this.proxy, Roo.data);
8746 this.proxy.xmodule = this.xmodule || false;
8747 this.relayEvents(this.proxy, ["loadexception"]);
8749 this.sortToggle = {};
8750 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8752 Roo.data.Store.superclass.constructor.call(this);
8754 if(this.inlineData){
8755 this.loadData(this.inlineData);
8756 delete this.inlineData;
8760 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8762 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
8763 * without a remote query - used by combo/forms at present.
8767 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8770 * @cfg {Array} data Inline data to be loaded when the store is initialized.
8773 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8774 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8777 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8778 * on any HTTP request
8781 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8784 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8788 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8789 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8794 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8795 * loaded or when a record is removed. (defaults to false).
8797 pruneModifiedRecords : false,
8803 * Add Records to the Store and fires the add event.
8804 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8806 add : function(records){
8807 records = [].concat(records);
8808 for(var i = 0, len = records.length; i < len; i++){
8809 records[i].join(this);
8811 var index = this.data.length;
8812 this.data.addAll(records);
8813 this.fireEvent("add", this, records, index);
8817 * Remove a Record from the Store and fires the remove event.
8818 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8820 remove : function(record){
8821 var index = this.data.indexOf(record);
8822 this.data.removeAt(index);
8823 if(this.pruneModifiedRecords){
8824 this.modified.remove(record);
8826 this.fireEvent("remove", this, record, index);
8830 * Remove all Records from the Store and fires the clear event.
8832 removeAll : function(){
8834 if(this.pruneModifiedRecords){
8837 this.fireEvent("clear", this);
8841 * Inserts Records to the Store at the given index and fires the add event.
8842 * @param {Number} index The start index at which to insert the passed Records.
8843 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8845 insert : function(index, records){
8846 records = [].concat(records);
8847 for(var i = 0, len = records.length; i < len; i++){
8848 this.data.insert(index, records[i]);
8849 records[i].join(this);
8851 this.fireEvent("add", this, records, index);
8855 * Get the index within the cache of the passed Record.
8856 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8857 * @return {Number} The index of the passed Record. Returns -1 if not found.
8859 indexOf : function(record){
8860 return this.data.indexOf(record);
8864 * Get the index within the cache of the Record with the passed id.
8865 * @param {String} id The id of the Record to find.
8866 * @return {Number} The index of the Record. Returns -1 if not found.
8868 indexOfId : function(id){
8869 return this.data.indexOfKey(id);
8873 * Get the Record with the specified id.
8874 * @param {String} id The id of the Record to find.
8875 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8877 getById : function(id){
8878 return this.data.key(id);
8882 * Get the Record at the specified index.
8883 * @param {Number} index The index of the Record to find.
8884 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8886 getAt : function(index){
8887 return this.data.itemAt(index);
8891 * Returns a range of Records between specified indices.
8892 * @param {Number} startIndex (optional) The starting index (defaults to 0)
8893 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8894 * @return {Roo.data.Record[]} An array of Records
8896 getRange : function(start, end){
8897 return this.data.getRange(start, end);
8901 storeOptions : function(o){
8902 o = Roo.apply({}, o);
8905 this.lastOptions = o;
8909 * Loads the Record cache from the configured Proxy using the configured Reader.
8911 * If using remote paging, then the first load call must specify the <em>start</em>
8912 * and <em>limit</em> properties in the options.params property to establish the initial
8913 * position within the dataset, and the number of Records to cache on each read from the Proxy.
8915 * <strong>It is important to note that for remote data sources, loading is asynchronous,
8916 * and this call will return before the new data has been loaded. Perform any post-processing
8917 * in a callback function, or in a "load" event handler.</strong>
8919 * @param {Object} options An object containing properties which control loading options:<ul>
8920 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8921 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8922 * passed the following arguments:<ul>
8923 * <li>r : Roo.data.Record[]</li>
8924 * <li>options: Options object from the load call</li>
8925 * <li>success: Boolean success indicator</li></ul></li>
8926 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8927 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8930 load : function(options){
8931 options = options || {};
8932 if(this.fireEvent("beforeload", this, options) !== false){
8933 this.storeOptions(options);
8934 var p = Roo.apply(options.params || {}, this.baseParams);
8935 // if meta was not loaded from remote source.. try requesting it.
8936 if (!this.reader.metaFromRemote) {
8939 if(this.sortInfo && this.remoteSort){
8940 var pn = this.paramNames;
8941 p[pn["sort"]] = this.sortInfo.field;
8942 p[pn["dir"]] = this.sortInfo.direction;
8944 if (this.multiSort) {
8945 var pn = this.paramNames;
8946 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8949 this.proxy.load(p, this.reader, this.loadRecords, this, options);
8954 * Reloads the Record cache from the configured Proxy using the configured Reader and
8955 * the options from the last load operation performed.
8956 * @param {Object} options (optional) An object containing properties which may override the options
8957 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8958 * the most recently used options are reused).
8960 reload : function(options){
8961 this.load(Roo.applyIf(options||{}, this.lastOptions));
8965 // Called as a callback by the Reader during a load operation.
8966 loadRecords : function(o, options, success){
8967 if(!o || success === false){
8968 if(success !== false){
8969 this.fireEvent("load", this, [], options, o);
8971 if(options.callback){
8972 options.callback.call(options.scope || this, [], options, false);
8976 // if data returned failure - throw an exception.
8977 if (o.success === false) {
8978 // show a message if no listener is registered.
8979 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8980 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8982 // loadmask wil be hooked into this..
8983 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8986 var r = o.records, t = o.totalRecords || r.length;
8988 this.fireEvent("beforeloadadd", this, r, options, o);
8990 if(!options || options.add !== true){
8991 if(this.pruneModifiedRecords){
8994 for(var i = 0, len = r.length; i < len; i++){
8998 this.data = this.snapshot;
8999 delete this.snapshot;
9002 this.data.addAll(r);
9003 this.totalLength = t;
9005 this.fireEvent("datachanged", this);
9007 this.totalLength = Math.max(t, this.data.length+r.length);
9010 this.fireEvent("load", this, r, options, o);
9011 if(options.callback){
9012 options.callback.call(options.scope || this, r, options, true);
9018 * Loads data from a passed data block. A Reader which understands the format of the data
9019 * must have been configured in the constructor.
9020 * @param {Object} data The data block from which to read the Records. The format of the data expected
9021 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9022 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9024 loadData : function(o, append){
9025 var r = this.reader.readRecords(o);
9026 this.loadRecords(r, {add: append}, true);
9030 * Gets the number of cached records.
9032 * <em>If using paging, this may not be the total size of the dataset. If the data object
9033 * used by the Reader contains the dataset size, then the getTotalCount() function returns
9034 * the data set size</em>
9036 getCount : function(){
9037 return this.data.length || 0;
9041 * Gets the total number of records in the dataset as returned by the server.
9043 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9044 * the dataset size</em>
9046 getTotalCount : function(){
9047 return this.totalLength || 0;
9051 * Returns the sort state of the Store as an object with two properties:
9053 field {String} The name of the field by which the Records are sorted
9054 direction {String} The sort order, "ASC" or "DESC"
9057 getSortState : function(){
9058 return this.sortInfo;
9062 applySort : function(){
9063 if(this.sortInfo && !this.remoteSort){
9064 var s = this.sortInfo, f = s.field;
9065 var st = this.fields.get(f).sortType;
9066 var fn = function(r1, r2){
9067 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9068 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9070 this.data.sort(s.direction, fn);
9071 if(this.snapshot && this.snapshot != this.data){
9072 this.snapshot.sort(s.direction, fn);
9078 * Sets the default sort column and order to be used by the next load operation.
9079 * @param {String} fieldName The name of the field to sort by.
9080 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9082 setDefaultSort : function(field, dir){
9083 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9088 * If remote sorting is used, the sort is performed on the server, and the cache is
9089 * reloaded. If local sorting is used, the cache is sorted internally.
9090 * @param {String} fieldName The name of the field to sort by.
9091 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9093 sort : function(fieldName, dir){
9094 var f = this.fields.get(fieldName);
9096 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9098 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9099 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9104 this.sortToggle[f.name] = dir;
9105 this.sortInfo = {field: f.name, direction: dir};
9106 if(!this.remoteSort){
9108 this.fireEvent("datachanged", this);
9110 this.load(this.lastOptions);
9115 * Calls the specified function for each of the Records in the cache.
9116 * @param {Function} fn The function to call. The Record is passed as the first parameter.
9117 * Returning <em>false</em> aborts and exits the iteration.
9118 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9120 each : function(fn, scope){
9121 this.data.each(fn, scope);
9125 * Gets all records modified since the last commit. Modified records are persisted across load operations
9126 * (e.g., during paging).
9127 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9129 getModifiedRecords : function(){
9130 return this.modified;
9134 createFilterFn : function(property, value, anyMatch){
9135 if(!value.exec){ // not a regex
9136 value = String(value);
9137 if(value.length == 0){
9140 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9143 return value.test(r.data[property]);
9148 * Sums the value of <i>property</i> for each record between start and end and returns the result.
9149 * @param {String} property A field on your records
9150 * @param {Number} start The record index to start at (defaults to 0)
9151 * @param {Number} end The last record index to include (defaults to length - 1)
9152 * @return {Number} The sum
9154 sum : function(property, start, end){
9155 var rs = this.data.items, v = 0;
9157 end = (end || end === 0) ? end : rs.length-1;
9159 for(var i = start; i <= end; i++){
9160 v += (rs[i].data[property] || 0);
9166 * Filter the records by a specified property.
9167 * @param {String} field A field on your records
9168 * @param {String/RegExp} value Either a string that the field
9169 * should start with or a RegExp to test against the field
9170 * @param {Boolean} anyMatch True to match any part not just the beginning
9172 filter : function(property, value, anyMatch){
9173 var fn = this.createFilterFn(property, value, anyMatch);
9174 return fn ? this.filterBy(fn) : this.clearFilter();
9178 * Filter by a function. The specified function will be called with each
9179 * record in this data source. If the function returns true the record is included,
9180 * otherwise it is filtered.
9181 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9182 * @param {Object} scope (optional) The scope of the function (defaults to this)
9184 filterBy : function(fn, scope){
9185 this.snapshot = this.snapshot || this.data;
9186 this.data = this.queryBy(fn, scope||this);
9187 this.fireEvent("datachanged", this);
9191 * Query the records by a specified property.
9192 * @param {String} field A field on your records
9193 * @param {String/RegExp} value Either a string that the field
9194 * should start with or a RegExp to test against the field
9195 * @param {Boolean} anyMatch True to match any part not just the beginning
9196 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9198 query : function(property, value, anyMatch){
9199 var fn = this.createFilterFn(property, value, anyMatch);
9200 return fn ? this.queryBy(fn) : this.data.clone();
9204 * Query by a function. The specified function will be called with each
9205 * record in this data source. If the function returns true the record is included
9207 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9208 * @param {Object} scope (optional) The scope of the function (defaults to this)
9209 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9211 queryBy : function(fn, scope){
9212 var data = this.snapshot || this.data;
9213 return data.filterBy(fn, scope||this);
9217 * Collects unique values for a particular dataIndex from this store.
9218 * @param {String} dataIndex The property to collect
9219 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9220 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9221 * @return {Array} An array of the unique values
9223 collect : function(dataIndex, allowNull, bypassFilter){
9224 var d = (bypassFilter === true && this.snapshot) ?
9225 this.snapshot.items : this.data.items;
9226 var v, sv, r = [], l = {};
9227 for(var i = 0, len = d.length; i < len; i++){
9228 v = d[i].data[dataIndex];
9230 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9239 * Revert to a view of the Record cache with no filtering applied.
9240 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9242 clearFilter : function(suppressEvent){
9243 if(this.snapshot && this.snapshot != this.data){
9244 this.data = this.snapshot;
9245 delete this.snapshot;
9246 if(suppressEvent !== true){
9247 this.fireEvent("datachanged", this);
9253 afterEdit : function(record){
9254 if(this.modified.indexOf(record) == -1){
9255 this.modified.push(record);
9257 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9261 afterReject : function(record){
9262 this.modified.remove(record);
9263 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9267 afterCommit : function(record){
9268 this.modified.remove(record);
9269 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9273 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9274 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9276 commitChanges : function(){
9277 var m = this.modified.slice(0);
9279 for(var i = 0, len = m.length; i < len; i++){
9285 * Cancel outstanding changes on all changed records.
9287 rejectChanges : function(){
9288 var m = this.modified.slice(0);
9290 for(var i = 0, len = m.length; i < len; i++){
9295 onMetaChange : function(meta, rtype, o){
9296 this.recordType = rtype;
9297 this.fields = rtype.prototype.fields;
9298 delete this.snapshot;
9299 this.sortInfo = meta.sortInfo || this.sortInfo;
9301 this.fireEvent('metachange', this, this.reader.meta);
9304 moveIndex : function(data, type)
9306 var index = this.indexOf(data);
9308 var newIndex = index + type;
9312 this.insert(newIndex, data);
9317 * Ext JS Library 1.1.1
9318 * Copyright(c) 2006-2007, Ext JS, LLC.
9320 * Originally Released Under LGPL - original licence link has changed is not relivant.
9323 * <script type="text/javascript">
9327 * @class Roo.data.SimpleStore
9328 * @extends Roo.data.Store
9329 * Small helper class to make creating Stores from Array data easier.
9330 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9331 * @cfg {Array} fields An array of field definition objects, or field name strings.
9332 * @cfg {Array} data The multi-dimensional array of data
9334 * @param {Object} config
9336 Roo.data.SimpleStore = function(config){
9337 Roo.data.SimpleStore.superclass.constructor.call(this, {
9339 reader: new Roo.data.ArrayReader({
9342 Roo.data.Record.create(config.fields)
9344 proxy : new Roo.data.MemoryProxy(config.data)
9348 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9350 * Ext JS Library 1.1.1
9351 * Copyright(c) 2006-2007, Ext JS, LLC.
9353 * Originally Released Under LGPL - original licence link has changed is not relivant.
9356 * <script type="text/javascript">
9361 * @extends Roo.data.Store
9362 * @class Roo.data.JsonStore
9363 * Small helper class to make creating Stores for JSON data easier. <br/>
9365 var store = new Roo.data.JsonStore({
9366 url: 'get-images.php',
9368 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9371 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9372 * JsonReader and HttpProxy (unless inline data is provided).</b>
9373 * @cfg {Array} fields An array of field definition objects, or field name strings.
9375 * @param {Object} config
9377 Roo.data.JsonStore = function(c){
9378 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9379 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9380 reader: new Roo.data.JsonReader(c, c.fields)
9383 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9385 * Ext JS Library 1.1.1
9386 * Copyright(c) 2006-2007, Ext JS, LLC.
9388 * Originally Released Under LGPL - original licence link has changed is not relivant.
9391 * <script type="text/javascript">
9395 Roo.data.Field = function(config){
9396 if(typeof config == "string"){
9397 config = {name: config};
9399 Roo.apply(this, config);
9405 var st = Roo.data.SortTypes;
9406 // named sortTypes are supported, here we look them up
9407 if(typeof this.sortType == "string"){
9408 this.sortType = st[this.sortType];
9411 // set default sortType for strings and dates
9415 this.sortType = st.asUCString;
9418 this.sortType = st.asDate;
9421 this.sortType = st.none;
9426 var stripRe = /[\$,%]/g;
9428 // prebuilt conversion function for this field, instead of
9429 // switching every time we're reading a value
9431 var cv, dateFormat = this.dateFormat;
9436 cv = function(v){ return v; };
9439 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9443 return v !== undefined && v !== null && v !== '' ?
9444 parseInt(String(v).replace(stripRe, ""), 10) : '';
9449 return v !== undefined && v !== null && v !== '' ?
9450 parseFloat(String(v).replace(stripRe, ""), 10) : '';
9455 cv = function(v){ return v === true || v === "true" || v == 1; };
9462 if(v instanceof Date){
9466 if(dateFormat == "timestamp"){
9467 return new Date(v*1000);
9469 return Date.parseDate(v, dateFormat);
9471 var parsed = Date.parse(v);
9472 return parsed ? new Date(parsed) : null;
9481 Roo.data.Field.prototype = {
9489 * Ext JS Library 1.1.1
9490 * Copyright(c) 2006-2007, Ext JS, LLC.
9492 * Originally Released Under LGPL - original licence link has changed is not relivant.
9495 * <script type="text/javascript">
9498 // Base class for reading structured data from a data source. This class is intended to be
9499 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9502 * @class Roo.data.DataReader
9503 * Base class for reading structured data from a data source. This class is intended to be
9504 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9507 Roo.data.DataReader = function(meta, recordType){
9511 this.recordType = recordType instanceof Array ?
9512 Roo.data.Record.create(recordType) : recordType;
9515 Roo.data.DataReader.prototype = {
9517 * Create an empty record
9518 * @param {Object} data (optional) - overlay some values
9519 * @return {Roo.data.Record} record created.
9521 newRow : function(d) {
9523 this.recordType.prototype.fields.each(function(c) {
9525 case 'int' : da[c.name] = 0; break;
9526 case 'date' : da[c.name] = new Date(); break;
9527 case 'float' : da[c.name] = 0.0; break;
9528 case 'boolean' : da[c.name] = false; break;
9529 default : da[c.name] = ""; break;
9533 return new this.recordType(Roo.apply(da, d));
9538 * Ext JS Library 1.1.1
9539 * Copyright(c) 2006-2007, Ext JS, LLC.
9541 * Originally Released Under LGPL - original licence link has changed is not relivant.
9544 * <script type="text/javascript">
9548 * @class Roo.data.DataProxy
9549 * @extends Roo.data.Observable
9550 * This class is an abstract base class for implementations which provide retrieval of
9551 * unformatted data objects.<br>
9553 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9554 * (of the appropriate type which knows how to parse the data object) to provide a block of
9555 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9557 * Custom implementations must implement the load method as described in
9558 * {@link Roo.data.HttpProxy#load}.
9560 Roo.data.DataProxy = function(){
9564 * Fires before a network request is made to retrieve a data object.
9565 * @param {Object} This DataProxy object.
9566 * @param {Object} params The params parameter to the load function.
9571 * Fires before the load method's callback is called.
9572 * @param {Object} This DataProxy object.
9573 * @param {Object} o The data object.
9574 * @param {Object} arg The callback argument object passed to the load function.
9578 * @event loadexception
9579 * Fires if an Exception occurs during data retrieval.
9580 * @param {Object} This DataProxy object.
9581 * @param {Object} o The data object.
9582 * @param {Object} arg The callback argument object passed to the load function.
9583 * @param {Object} e The Exception.
9585 loadexception : true
9587 Roo.data.DataProxy.superclass.constructor.call(this);
9590 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9593 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9597 * Ext JS Library 1.1.1
9598 * Copyright(c) 2006-2007, Ext JS, LLC.
9600 * Originally Released Under LGPL - original licence link has changed is not relivant.
9603 * <script type="text/javascript">
9606 * @class Roo.data.MemoryProxy
9607 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9608 * to the Reader when its load method is called.
9610 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9612 Roo.data.MemoryProxy = function(data){
9616 Roo.data.MemoryProxy.superclass.constructor.call(this);
9620 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9622 * Load data from the requested source (in this case an in-memory
9623 * data object passed to the constructor), read the data object into
9624 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9625 * process that block using the passed callback.
9626 * @param {Object} params This parameter is not used by the MemoryProxy class.
9627 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9628 * object into a block of Roo.data.Records.
9629 * @param {Function} callback The function into which to pass the block of Roo.data.records.
9630 * The function must be passed <ul>
9631 * <li>The Record block object</li>
9632 * <li>The "arg" argument from the load function</li>
9633 * <li>A boolean success indicator</li>
9635 * @param {Object} scope The scope in which to call the callback
9636 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9638 load : function(params, reader, callback, scope, arg){
9639 params = params || {};
9642 result = reader.readRecords(this.data);
9644 this.fireEvent("loadexception", this, arg, null, e);
9645 callback.call(scope, null, arg, false);
9648 callback.call(scope, result, arg, true);
9652 update : function(params, records){
9657 * Ext JS Library 1.1.1
9658 * Copyright(c) 2006-2007, Ext JS, LLC.
9660 * Originally Released Under LGPL - original licence link has changed is not relivant.
9663 * <script type="text/javascript">
9666 * @class Roo.data.HttpProxy
9667 * @extends Roo.data.DataProxy
9668 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9669 * configured to reference a certain URL.<br><br>
9671 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9672 * from which the running page was served.<br><br>
9674 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9676 * Be aware that to enable the browser to parse an XML document, the server must set
9677 * the Content-Type header in the HTTP response to "text/xml".
9679 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9680 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
9681 * will be used to make the request.
9683 Roo.data.HttpProxy = function(conn){
9684 Roo.data.HttpProxy.superclass.constructor.call(this);
9685 // is conn a conn config or a real conn?
9687 this.useAjax = !conn || !conn.events;
9691 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9692 // thse are take from connection...
9695 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9698 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9699 * extra parameters to each request made by this object. (defaults to undefined)
9702 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9703 * to each request made by this object. (defaults to undefined)
9706 * @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)
9709 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9712 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9718 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9722 * Return the {@link Roo.data.Connection} object being used by this Proxy.
9723 * @return {Connection} The Connection object. This object may be used to subscribe to events on
9724 * a finer-grained basis than the DataProxy events.
9726 getConnection : function(){
9727 return this.useAjax ? Roo.Ajax : this.conn;
9731 * Load data from the configured {@link Roo.data.Connection}, read the data object into
9732 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9733 * process that block using the passed callback.
9734 * @param {Object} params An object containing properties which are to be used as HTTP parameters
9735 * for the request to the remote server.
9736 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9737 * object into a block of Roo.data.Records.
9738 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9739 * The function must be passed <ul>
9740 * <li>The Record block object</li>
9741 * <li>The "arg" argument from the load function</li>
9742 * <li>A boolean success indicator</li>
9744 * @param {Object} scope The scope in which to call the callback
9745 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9747 load : function(params, reader, callback, scope, arg){
9748 if(this.fireEvent("beforeload", this, params) !== false){
9750 params : params || {},
9752 callback : callback,
9757 callback : this.loadResponse,
9761 Roo.applyIf(o, this.conn);
9762 if(this.activeRequest){
9763 Roo.Ajax.abort(this.activeRequest);
9765 this.activeRequest = Roo.Ajax.request(o);
9767 this.conn.request(o);
9770 callback.call(scope||this, null, arg, false);
9775 loadResponse : function(o, success, response){
9776 delete this.activeRequest;
9778 this.fireEvent("loadexception", this, o, response);
9779 o.request.callback.call(o.request.scope, null, o.request.arg, false);
9784 result = o.reader.read(response);
9786 this.fireEvent("loadexception", this, o, response, e);
9787 o.request.callback.call(o.request.scope, null, o.request.arg, false);
9791 this.fireEvent("load", this, o, o.request.arg);
9792 o.request.callback.call(o.request.scope, result, o.request.arg, true);
9796 update : function(dataSet){
9801 updateResponse : function(dataSet){
9806 * Ext JS Library 1.1.1
9807 * Copyright(c) 2006-2007, Ext JS, LLC.
9809 * Originally Released Under LGPL - original licence link has changed is not relivant.
9812 * <script type="text/javascript">
9816 * @class Roo.data.ScriptTagProxy
9817 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9818 * other than the originating domain of the running page.<br><br>
9820 * <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
9821 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9823 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9824 * source code that is used as the source inside a <script> tag.<br><br>
9826 * In order for the browser to process the returned data, the server must wrap the data object
9827 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9828 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9829 * depending on whether the callback name was passed:
9832 boolean scriptTag = false;
9833 String cb = request.getParameter("callback");
9836 response.setContentType("text/javascript");
9838 response.setContentType("application/x-json");
9840 Writer out = response.getWriter();
9842 out.write(cb + "(");
9844 out.print(dataBlock.toJsonString());
9851 * @param {Object} config A configuration object.
9853 Roo.data.ScriptTagProxy = function(config){
9854 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9855 Roo.apply(this, config);
9856 this.head = document.getElementsByTagName("head")[0];
9859 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9861 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9863 * @cfg {String} url The URL from which to request the data object.
9866 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9870 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9871 * the server the name of the callback function set up by the load call to process the returned data object.
9872 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9873 * javascript output which calls this named function passing the data object as its only parameter.
9875 callbackParam : "callback",
9877 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9878 * name to the request.
9883 * Load data from the configured URL, read the data object into
9884 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9885 * process that block using the passed callback.
9886 * @param {Object} params An object containing properties which are to be used as HTTP parameters
9887 * for the request to the remote server.
9888 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9889 * object into a block of Roo.data.Records.
9890 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9891 * The function must be passed <ul>
9892 * <li>The Record block object</li>
9893 * <li>The "arg" argument from the load function</li>
9894 * <li>A boolean success indicator</li>
9896 * @param {Object} scope The scope in which to call the callback
9897 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9899 load : function(params, reader, callback, scope, arg){
9900 if(this.fireEvent("beforeload", this, params) !== false){
9902 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9905 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9907 url += "&_dc=" + (new Date().getTime());
9909 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9912 cb : "stcCallback"+transId,
9913 scriptId : "stcScript"+transId,
9917 callback : callback,
9923 window[trans.cb] = function(o){
9924 conn.handleResponse(o, trans);
9927 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9929 if(this.autoAbort !== false){
9933 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9935 var script = document.createElement("script");
9936 script.setAttribute("src", url);
9937 script.setAttribute("type", "text/javascript");
9938 script.setAttribute("id", trans.scriptId);
9939 this.head.appendChild(script);
9943 callback.call(scope||this, null, arg, false);
9948 isLoading : function(){
9949 return this.trans ? true : false;
9953 * Abort the current server request.
9956 if(this.isLoading()){
9957 this.destroyTrans(this.trans);
9962 destroyTrans : function(trans, isLoaded){
9963 this.head.removeChild(document.getElementById(trans.scriptId));
9964 clearTimeout(trans.timeoutId);
9966 window[trans.cb] = undefined;
9968 delete window[trans.cb];
9971 // if hasn't been loaded, wait for load to remove it to prevent script error
9972 window[trans.cb] = function(){
9973 window[trans.cb] = undefined;
9975 delete window[trans.cb];
9982 handleResponse : function(o, trans){
9984 this.destroyTrans(trans, true);
9987 result = trans.reader.readRecords(o);
9989 this.fireEvent("loadexception", this, o, trans.arg, e);
9990 trans.callback.call(trans.scope||window, null, trans.arg, false);
9993 this.fireEvent("load", this, o, trans.arg);
9994 trans.callback.call(trans.scope||window, result, trans.arg, true);
9998 handleFailure : function(trans){
10000 this.destroyTrans(trans, false);
10001 this.fireEvent("loadexception", this, null, trans.arg);
10002 trans.callback.call(trans.scope||window, null, trans.arg, false);
10006 * Ext JS Library 1.1.1
10007 * Copyright(c) 2006-2007, Ext JS, LLC.
10009 * Originally Released Under LGPL - original licence link has changed is not relivant.
10012 * <script type="text/javascript">
10016 * @class Roo.data.JsonReader
10017 * @extends Roo.data.DataReader
10018 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10019 * based on mappings in a provided Roo.data.Record constructor.
10021 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10022 * in the reply previously.
10027 var RecordDef = Roo.data.Record.create([
10028 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
10029 {name: 'occupation'} // This field will use "occupation" as the mapping.
10031 var myReader = new Roo.data.JsonReader({
10032 totalProperty: "results", // The property which contains the total dataset size (optional)
10033 root: "rows", // The property which contains an Array of row objects
10034 id: "id" // The property within each row object that provides an ID for the record (optional)
10038 * This would consume a JSON file like this:
10040 { 'results': 2, 'rows': [
10041 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10042 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10045 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10046 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10047 * paged from the remote server.
10048 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10049 * @cfg {String} root name of the property which contains the Array of row objects.
10050 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10052 * Create a new JsonReader
10053 * @param {Object} meta Metadata configuration options
10054 * @param {Object} recordType Either an Array of field definition objects,
10055 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10057 Roo.data.JsonReader = function(meta, recordType){
10060 // set some defaults:
10061 Roo.applyIf(meta, {
10062 totalProperty: 'total',
10063 successProperty : 'success',
10068 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10070 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10073 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
10074 * Used by Store query builder to append _requestMeta to params.
10077 metaFromRemote : false,
10079 * This method is only used by a DataProxy which has retrieved data from a remote server.
10080 * @param {Object} response The XHR object which contains the JSON data in its responseText.
10081 * @return {Object} data A data block which is used by an Roo.data.Store object as
10082 * a cache of Roo.data.Records.
10084 read : function(response){
10085 var json = response.responseText;
10087 var o = /* eval:var:o */ eval("("+json+")");
10089 throw {message: "JsonReader.read: Json object not found"};
10095 this.metaFromRemote = true;
10096 this.meta = o.metaData;
10097 this.recordType = Roo.data.Record.create(o.metaData.fields);
10098 this.onMetaChange(this.meta, this.recordType, o);
10100 return this.readRecords(o);
10103 // private function a store will implement
10104 onMetaChange : function(meta, recordType, o){
10111 simpleAccess: function(obj, subsc) {
10118 getJsonAccessor: function(){
10120 return function(expr) {
10122 return(re.test(expr))
10123 ? new Function("obj", "return obj." + expr)
10128 return Roo.emptyFn;
10133 * Create a data block containing Roo.data.Records from an XML document.
10134 * @param {Object} o An object which contains an Array of row objects in the property specified
10135 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10136 * which contains the total size of the dataset.
10137 * @return {Object} data A data block which is used by an Roo.data.Store object as
10138 * a cache of Roo.data.Records.
10140 readRecords : function(o){
10142 * After any data loads, the raw JSON data is available for further custom processing.
10146 var s = this.meta, Record = this.recordType,
10147 f = Record.prototype.fields, fi = f.items, fl = f.length;
10149 // Generate extraction functions for the totalProperty, the root, the id, and for each field
10151 if(s.totalProperty) {
10152 this.getTotal = this.getJsonAccessor(s.totalProperty);
10154 if(s.successProperty) {
10155 this.getSuccess = this.getJsonAccessor(s.successProperty);
10157 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10159 var g = this.getJsonAccessor(s.id);
10160 this.getId = function(rec) {
10162 return (r === undefined || r === "") ? null : r;
10165 this.getId = function(){return null;};
10168 for(var jj = 0; jj < fl; jj++){
10170 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10171 this.ef[jj] = this.getJsonAccessor(map);
10175 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10176 if(s.totalProperty){
10177 var vt = parseInt(this.getTotal(o), 10);
10182 if(s.successProperty){
10183 var vs = this.getSuccess(o);
10184 if(vs === false || vs === 'false'){
10189 for(var i = 0; i < c; i++){
10192 var id = this.getId(n);
10193 for(var j = 0; j < fl; j++){
10195 var v = this.ef[j](n);
10197 Roo.log('missing convert for ' + f.name);
10201 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10203 var record = new Record(values, id);
10205 records[i] = record;
10211 totalRecords : totalRecords
10216 * Ext JS Library 1.1.1
10217 * Copyright(c) 2006-2007, Ext JS, LLC.
10219 * Originally Released Under LGPL - original licence link has changed is not relivant.
10222 * <script type="text/javascript">
10226 * @class Roo.data.ArrayReader
10227 * @extends Roo.data.DataReader
10228 * Data reader class to create an Array of Roo.data.Record objects from an Array.
10229 * Each element of that Array represents a row of data fields. The
10230 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10231 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10235 var RecordDef = Roo.data.Record.create([
10236 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
10237 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
10239 var myReader = new Roo.data.ArrayReader({
10240 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
10244 * This would consume an Array like this:
10246 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10248 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10250 * Create a new JsonReader
10251 * @param {Object} meta Metadata configuration options.
10252 * @param {Object} recordType Either an Array of field definition objects
10253 * as specified to {@link Roo.data.Record#create},
10254 * or an {@link Roo.data.Record} object
10255 * created using {@link Roo.data.Record#create}.
10257 Roo.data.ArrayReader = function(meta, recordType){
10258 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10261 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10263 * Create a data block containing Roo.data.Records from an XML document.
10264 * @param {Object} o An Array of row objects which represents the dataset.
10265 * @return {Object} data A data block which is used by an Roo.data.Store object as
10266 * a cache of Roo.data.Records.
10268 readRecords : function(o){
10269 var sid = this.meta ? this.meta.id : null;
10270 var recordType = this.recordType, fields = recordType.prototype.fields;
10273 for(var i = 0; i < root.length; i++){
10276 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10277 for(var j = 0, jlen = fields.length; j < jlen; j++){
10278 var f = fields.items[j];
10279 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10280 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10282 values[f.name] = v;
10284 var record = new recordType(values, id);
10286 records[records.length] = record;
10290 totalRecords : records.length
10299 * @class Roo.bootstrap.ComboBox
10300 * @extends Roo.bootstrap.TriggerField
10301 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10302 * @cfg {Boolean} append (true|false) default false
10303 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10304 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10305 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10306 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10307 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10309 * Create a new ComboBox.
10310 * @param {Object} config Configuration options
10312 Roo.bootstrap.ComboBox = function(config){
10313 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10317 * Fires when the dropdown list is expanded
10318 * @param {Roo.bootstrap.ComboBox} combo This combo box
10323 * Fires when the dropdown list is collapsed
10324 * @param {Roo.bootstrap.ComboBox} combo This combo box
10328 * @event beforeselect
10329 * Fires before a list item is selected. Return false to cancel the selection.
10330 * @param {Roo.bootstrap.ComboBox} combo This combo box
10331 * @param {Roo.data.Record} record The data record returned from the underlying store
10332 * @param {Number} index The index of the selected item in the dropdown list
10334 'beforeselect' : true,
10337 * Fires when a list item is selected
10338 * @param {Roo.bootstrap.ComboBox} combo This combo box
10339 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10340 * @param {Number} index The index of the selected item in the dropdown list
10344 * @event beforequery
10345 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10346 * The event object passed has these properties:
10347 * @param {Roo.bootstrap.ComboBox} combo This combo box
10348 * @param {String} query The query
10349 * @param {Boolean} forceAll true to force "all" query
10350 * @param {Boolean} cancel true to cancel the query
10351 * @param {Object} e The query event object
10353 'beforequery': true,
10356 * Fires when the 'add' icon is pressed (add a listener to enable add button)
10357 * @param {Roo.bootstrap.ComboBox} combo This combo box
10362 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10363 * @param {Roo.bootstrap.ComboBox} combo This combo box
10364 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10369 * Fires when the remove value from the combobox array
10370 * @param {Roo.bootstrap.ComboBox} combo This combo box
10377 this.tickItems = [];
10379 this.selectedIndex = -1;
10380 if(this.mode == 'local'){
10381 if(config.queryDelay === undefined){
10382 this.queryDelay = 10;
10384 if(config.minChars === undefined){
10390 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10393 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10394 * rendering into an Roo.Editor, defaults to false)
10397 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10398 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10401 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10404 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10405 * the dropdown list (defaults to undefined, with no header element)
10409 * @cfg {String/Roo.Template} tpl The template to use to render the output
10413 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10415 listWidth: undefined,
10417 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10418 * mode = 'remote' or 'text' if mode = 'local')
10420 displayField: undefined,
10422 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10423 * mode = 'remote' or 'value' if mode = 'local').
10424 * Note: use of a valueField requires the user make a selection
10425 * in order for a value to be mapped.
10427 valueField: undefined,
10431 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10432 * field's data value (defaults to the underlying DOM element's name)
10434 hiddenName: undefined,
10436 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10440 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10442 selectedClass: 'active',
10445 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10449 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10450 * anchor positions (defaults to 'tl-bl')
10452 listAlign: 'tl-bl?',
10454 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10458 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
10459 * query specified by the allQuery config option (defaults to 'query')
10461 triggerAction: 'query',
10463 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10464 * (defaults to 4, does not apply if editable = false)
10468 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10469 * delay (typeAheadDelay) if it matches a known value (defaults to false)
10473 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10474 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10478 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10479 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
10483 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
10484 * when editable = true (defaults to false)
10486 selectOnFocus:false,
10488 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10490 queryParam: 'query',
10492 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
10493 * when mode = 'remote' (defaults to 'Loading...')
10495 loadingText: 'Loading...',
10497 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10501 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10505 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10506 * traditional select (defaults to true)
10510 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10514 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10518 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10519 * listWidth has a higher value)
10523 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10524 * allow the user to set arbitrary text into the field (defaults to false)
10526 forceSelection:false,
10528 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10529 * if typeAhead = true (defaults to 250)
10531 typeAheadDelay : 250,
10533 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10534 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10536 valueNotFoundText : undefined,
10538 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10540 blockFocus : false,
10543 * @cfg {Boolean} disableClear Disable showing of clear button.
10545 disableClear : false,
10547 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
10549 alwaysQuery : false,
10552 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
10566 btnPosition : 'right',
10567 triggerList : true,
10568 showToggleBtn : true,
10569 // element that contains real text value.. (when hidden is used..)
10571 getAutoCreate : function()
10578 if(!this.tickable){
10579 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10584 * ComboBox with tickable selections
10587 var align = this.labelAlign || this.parentLabelAlign();
10590 cls : 'form-group roo-combobox-tickable' //input-group
10596 cls : 'tickable-buttons',
10601 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10608 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10615 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10622 Roo.each(buttons.cn, function(c){
10624 c.cls += ' btn-' + _this.size;
10627 if (_this.disabled) {
10638 cls: 'form-hidden-field'
10642 cls: 'select2-choices',
10646 cls: 'select2-search-field',
10658 cls: 'select2-container input-group select2-container-multi',
10663 // cls: 'typeahead typeahead-long dropdown-menu',
10664 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
10669 if (align ==='left' && this.fieldLabel.length) {
10671 Roo.log("left and has label");
10677 cls : 'control-label col-sm-' + this.labelWidth,
10678 html : this.fieldLabel
10682 cls : "col-sm-" + (12 - this.labelWidth),
10689 } else if ( this.fieldLabel.length) {
10695 //cls : 'input-group-addon',
10696 html : this.fieldLabel
10706 Roo.log(" no label && no align");
10713 ['xs','sm','md','lg'].map(function(size){
10714 if (settings[size]) {
10715 cfg.cls += ' col-' + size + '-' + settings[size];
10724 initEvents: function()
10728 throw "can not find store for combo";
10730 this.store = Roo.factory(this.store, Roo.data);
10733 this.initTickableEvents();
10737 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10739 if(this.hiddenName){
10741 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10743 this.hiddenField.dom.value =
10744 this.hiddenValue !== undefined ? this.hiddenValue :
10745 this.value !== undefined ? this.value : '';
10747 // prevent input submission
10748 this.el.dom.removeAttribute('name');
10749 this.hiddenField.dom.setAttribute('name', this.hiddenName);
10754 // this.el.dom.setAttribute('autocomplete', 'off');
10757 var cls = 'x-combo-list';
10759 //this.list = new Roo.Layer({
10760 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10766 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10767 _this.list.setWidth(lw);
10770 this.list.on('mouseover', this.onViewOver, this);
10771 this.list.on('mousemove', this.onViewMove, this);
10773 this.list.on('scroll', this.onViewScroll, this);
10776 this.list.swallowEvent('mousewheel');
10777 this.assetHeight = 0;
10780 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10781 this.assetHeight += this.header.getHeight();
10784 this.innerList = this.list.createChild({cls:cls+'-inner'});
10785 this.innerList.on('mouseover', this.onViewOver, this);
10786 this.innerList.on('mousemove', this.onViewMove, this);
10787 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10789 if(this.allowBlank && !this.pageSize && !this.disableClear){
10790 this.footer = this.list.createChild({cls:cls+'-ft'});
10791 this.pageTb = new Roo.Toolbar(this.footer);
10795 this.footer = this.list.createChild({cls:cls+'-ft'});
10796 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10797 {pageSize: this.pageSize});
10801 if (this.pageTb && this.allowBlank && !this.disableClear) {
10803 this.pageTb.add(new Roo.Toolbar.Fill(), {
10804 cls: 'x-btn-icon x-btn-clear',
10806 handler: function()
10809 _this.clearValue();
10810 _this.onSelect(false, -1);
10815 this.assetHeight += this.footer.getHeight();
10820 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10823 this.view = new Roo.View(this.list, this.tpl, {
10824 singleSelect:true, store: this.store, selectedClass: this.selectedClass
10826 //this.view.wrapEl.setDisplayed(false);
10827 this.view.on('click', this.onViewClick, this);
10831 this.store.on('beforeload', this.onBeforeLoad, this);
10832 this.store.on('load', this.onLoad, this);
10833 this.store.on('loadexception', this.onLoadException, this);
10835 if(this.resizable){
10836 this.resizer = new Roo.Resizable(this.list, {
10837 pinned:true, handles:'se'
10839 this.resizer.on('resize', function(r, w, h){
10840 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10841 this.listWidth = w;
10842 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10843 this.restrictHeight();
10845 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10848 if(!this.editable){
10849 this.editable = true;
10850 this.setEditable(false);
10855 if (typeof(this.events.add.listeners) != 'undefined') {
10857 this.addicon = this.wrap.createChild(
10858 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
10860 this.addicon.on('click', function(e) {
10861 this.fireEvent('add', this);
10864 if (typeof(this.events.edit.listeners) != 'undefined') {
10866 this.editicon = this.wrap.createChild(
10867 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
10868 if (this.addicon) {
10869 this.editicon.setStyle('margin-left', '40px');
10871 this.editicon.on('click', function(e) {
10873 // we fire even if inothing is selected..
10874 this.fireEvent('edit', this, this.lastData );
10880 this.keyNav = new Roo.KeyNav(this.inputEl(), {
10881 "up" : function(e){
10882 this.inKeyMode = true;
10886 "down" : function(e){
10887 if(!this.isExpanded()){
10888 this.onTriggerClick();
10890 this.inKeyMode = true;
10895 "enter" : function(e){
10896 // this.onViewClick();
10900 if(this.fireEvent("specialkey", this, e)){
10901 this.onViewClick(false);
10907 "esc" : function(e){
10911 "tab" : function(e){
10914 if(this.fireEvent("specialkey", this, e)){
10915 this.onViewClick(false);
10923 doRelay : function(foo, bar, hname){
10924 if(hname == 'down' || this.scope.isExpanded()){
10925 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10934 this.queryDelay = Math.max(this.queryDelay || 10,
10935 this.mode == 'local' ? 10 : 250);
10938 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10940 if(this.typeAhead){
10941 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10943 if(this.editable !== false){
10944 this.inputEl().on("keyup", this.onKeyUp, this);
10946 if(this.forceSelection){
10947 this.inputEl().on('blur', this.doForce, this);
10951 this.choices = this.el.select('ul.select2-choices', true).first();
10952 this.searchField = this.el.select('ul li.select2-search-field', true).first();
10956 initTickableEvents: function()
10960 if(this.hiddenName){
10962 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10964 this.hiddenField.dom.value =
10965 this.hiddenValue !== undefined ? this.hiddenValue :
10966 this.value !== undefined ? this.value : '';
10968 // prevent input submission
10969 this.el.dom.removeAttribute('name');
10970 this.hiddenField.dom.setAttribute('name', this.hiddenName);
10975 // this.list = this.el.select('ul.dropdown-menu',true).first();
10977 this.choices = this.el.select('ul.select2-choices', true).first();
10978 this.searchField = this.el.select('ul li.select2-search-field', true).first();
10979 if(this.triggerList){
10980 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10983 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10984 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10986 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10987 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10989 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10990 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10992 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10993 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10994 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10997 this.cancelBtn.hide();
11002 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11003 _this.list.setWidth(lw);
11006 this.list.on('mouseover', this.onViewOver, this);
11007 this.list.on('mousemove', this.onViewMove, this);
11009 this.list.on('scroll', this.onViewScroll, this);
11012 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>';
11015 this.view = new Roo.View(this.list, this.tpl, {
11016 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11019 //this.view.wrapEl.setDisplayed(false);
11020 this.view.on('click', this.onViewClick, this);
11024 this.store.on('beforeload', this.onBeforeLoad, this);
11025 this.store.on('load', this.onLoad, this);
11026 this.store.on('loadexception', this.onLoadException, this);
11028 // this.keyNav = new Roo.KeyNav(this.inputEl(), {
11029 // "up" : function(e){
11030 // this.inKeyMode = true;
11031 // this.selectPrev();
11034 // "down" : function(e){
11035 // if(!this.isExpanded()){
11036 // this.onTriggerClick();
11038 // this.inKeyMode = true;
11039 // this.selectNext();
11043 // "enter" : function(e){
11044 //// this.onViewClick();
11046 // this.collapse();
11048 // if(this.fireEvent("specialkey", this, e)){
11049 // this.onViewClick(false);
11055 // "esc" : function(e){
11056 // this.collapse();
11059 // "tab" : function(e){
11060 // this.collapse();
11062 // if(this.fireEvent("specialkey", this, e)){
11063 // this.onViewClick(false);
11071 // doRelay : function(foo, bar, hname){
11072 // if(hname == 'down' || this.scope.isExpanded()){
11073 // return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11078 // forceKeyDown: true
11082 this.queryDelay = Math.max(this.queryDelay || 10,
11083 this.mode == 'local' ? 10 : 250);
11086 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11088 if(this.typeAhead){
11089 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11093 onDestroy : function(){
11095 this.view.setStore(null);
11096 this.view.el.removeAllListeners();
11097 this.view.el.remove();
11098 this.view.purgeListeners();
11101 this.list.dom.innerHTML = '';
11105 this.store.un('beforeload', this.onBeforeLoad, this);
11106 this.store.un('load', this.onLoad, this);
11107 this.store.un('loadexception', this.onLoadException, this);
11109 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11113 fireKey : function(e){
11114 if(e.isNavKeyPress() && !this.list.isVisible()){
11115 this.fireEvent("specialkey", this, e);
11120 onResize: function(w, h){
11121 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11123 // if(typeof w != 'number'){
11124 // // we do not handle it!?!?
11127 // var tw = this.trigger.getWidth();
11128 // // tw += this.addicon ? this.addicon.getWidth() : 0;
11129 // // tw += this.editicon ? this.editicon.getWidth() : 0;
11131 // this.inputEl().setWidth( this.adjustWidth('input', x));
11133 // //this.trigger.setStyle('left', x+'px');
11135 // if(this.list && this.listWidth === undefined){
11136 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11137 // this.list.setWidth(lw);
11138 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11146 * Allow or prevent the user from directly editing the field text. If false is passed,
11147 * the user will only be able to select from the items defined in the dropdown list. This method
11148 * is the runtime equivalent of setting the 'editable' config option at config time.
11149 * @param {Boolean} value True to allow the user to directly edit the field text
11151 setEditable : function(value){
11152 if(value == this.editable){
11155 this.editable = value;
11157 this.inputEl().dom.setAttribute('readOnly', true);
11158 this.inputEl().on('mousedown', this.onTriggerClick, this);
11159 this.inputEl().addClass('x-combo-noedit');
11161 this.inputEl().dom.setAttribute('readOnly', false);
11162 this.inputEl().un('mousedown', this.onTriggerClick, this);
11163 this.inputEl().removeClass('x-combo-noedit');
11169 onBeforeLoad : function(combo,opts){
11170 if(!this.hasFocus){
11174 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11176 this.restrictHeight();
11177 this.selectedIndex = -1;
11181 onLoad : function(){
11183 this.hasQuery = false;
11185 if(!this.hasFocus){
11189 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11190 this.loading.hide();
11193 if(this.store.getCount() > 0){
11195 // this.restrictHeight();
11196 if(this.lastQuery == this.allQuery){
11197 if(this.editable && !this.tickable){
11198 this.inputEl().dom.select();
11202 !this.selectByValue(this.value, true) &&
11203 this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' ||
11204 this.store.lastOptions.add != true)
11206 this.select(0, true);
11209 if(this.autoFocus){
11212 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11213 this.taTask.delay(this.typeAheadDelay);
11217 this.onEmptyResults();
11223 onLoadException : function()
11225 this.hasQuery = false;
11227 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11228 this.loading.hide();
11232 Roo.log(this.store.reader.jsonData);
11233 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11235 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11241 onTypeAhead : function(){
11242 if(this.store.getCount() > 0){
11243 var r = this.store.getAt(0);
11244 var newValue = r.data[this.displayField];
11245 var len = newValue.length;
11246 var selStart = this.getRawValue().length;
11248 if(selStart != len){
11249 this.setRawValue(newValue);
11250 this.selectText(selStart, newValue.length);
11256 onSelect : function(record, index){
11258 if(this.fireEvent('beforeselect', this, record, index) !== false){
11260 this.setFromData(index > -1 ? record.data : false);
11263 this.fireEvent('select', this, record, index);
11268 * Returns the currently selected field value or empty string if no value is set.
11269 * @return {String} value The selected value
11271 getValue : function(){
11274 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11277 if(this.valueField){
11278 return typeof this.value != 'undefined' ? this.value : '';
11280 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11285 * Clears any text/value currently set in the field
11287 clearValue : function(){
11288 if(this.hiddenField){
11289 this.hiddenField.dom.value = '';
11292 this.setRawValue('');
11293 this.lastSelectionText = '';
11298 * Sets the specified value into the field. If the value finds a match, the corresponding record text
11299 * will be displayed in the field. If the value does not match the data value of an existing item,
11300 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11301 * Otherwise the field will be blank (although the value will still be set).
11302 * @param {String} value The value to match
11304 setValue : function(v){
11311 if(this.valueField){
11312 var r = this.findRecord(this.valueField, v);
11314 text = r.data[this.displayField];
11315 }else if(this.valueNotFoundText !== undefined){
11316 text = this.valueNotFoundText;
11319 this.lastSelectionText = text;
11320 if(this.hiddenField){
11321 this.hiddenField.dom.value = v;
11323 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11327 * @property {Object} the last set data for the element
11332 * Sets the value of the field based on a object which is related to the record format for the store.
11333 * @param {Object} value the value to set as. or false on reset?
11335 setFromData : function(o){
11342 var dv = ''; // display value
11343 var vv = ''; // value value..
11345 if (this.displayField) {
11346 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11348 // this is an error condition!!!
11349 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
11352 if(this.valueField){
11353 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11356 if(this.hiddenField){
11357 this.hiddenField.dom.value = vv;
11359 this.lastSelectionText = dv;
11360 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11364 // no hidden field.. - we store the value in 'value', but still display
11365 // display field!!!!
11366 this.lastSelectionText = dv;
11367 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11373 reset : function(){
11374 // overridden so that last data is reset..
11375 this.setValue(this.originalValue);
11376 this.clearInvalid();
11377 this.lastData = false;
11379 this.view.clearSelections();
11383 findRecord : function(prop, value){
11385 if(this.store.getCount() > 0){
11386 this.store.each(function(r){
11387 if(r.data[prop] == value){
11397 getName: function()
11399 // returns hidden if it's set..
11400 if (!this.rendered) {return ''};
11401 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
11405 onViewMove : function(e, t){
11406 this.inKeyMode = false;
11410 onViewOver : function(e, t){
11411 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11414 var item = this.view.findItemFromChild(t);
11417 var index = this.view.indexOf(item);
11418 this.select(index, false);
11423 onViewClick : function(view, doFocus, el, e)
11425 var index = this.view.getSelectedIndexes()[0];
11427 var r = this.store.getAt(index);
11431 if(e.getTarget().nodeName.toLowerCase() != 'input'){
11438 Roo.each(this.tickItems, function(v,k){
11440 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11441 _this.tickItems.splice(k, 1);
11451 this.tickItems.push(r.data);
11456 this.onSelect(r, index);
11458 if(doFocus !== false && !this.blockFocus){
11459 this.inputEl().focus();
11464 restrictHeight : function(){
11465 //this.innerList.dom.style.height = '';
11466 //var inner = this.innerList.dom;
11467 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11468 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11469 //this.list.beginUpdate();
11470 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11471 this.list.alignTo(this.inputEl(), this.listAlign);
11472 this.list.alignTo(this.inputEl(), this.listAlign);
11473 //this.list.endUpdate();
11477 onEmptyResults : function(){
11482 * Returns true if the dropdown list is expanded, else false.
11484 isExpanded : function(){
11485 return this.list.isVisible();
11489 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11490 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11491 * @param {String} value The data value of the item to select
11492 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11493 * selected item if it is not currently in view (defaults to true)
11494 * @return {Boolean} True if the value matched an item in the list, else false
11496 selectByValue : function(v, scrollIntoView){
11497 if(v !== undefined && v !== null){
11498 var r = this.findRecord(this.valueField || this.displayField, v);
11500 this.select(this.store.indexOf(r), scrollIntoView);
11508 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11509 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11510 * @param {Number} index The zero-based index of the list item to select
11511 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11512 * selected item if it is not currently in view (defaults to true)
11514 select : function(index, scrollIntoView){
11515 this.selectedIndex = index;
11516 this.view.select(index);
11517 if(scrollIntoView !== false){
11518 var el = this.view.getNode(index);
11519 if(el && !this.multiple && !this.tickable){
11520 this.list.scrollChildIntoView(el, false);
11526 selectNext : function(){
11527 var ct = this.store.getCount();
11529 if(this.selectedIndex == -1){
11531 }else if(this.selectedIndex < ct-1){
11532 this.select(this.selectedIndex+1);
11538 selectPrev : function(){
11539 var ct = this.store.getCount();
11541 if(this.selectedIndex == -1){
11543 }else if(this.selectedIndex != 0){
11544 this.select(this.selectedIndex-1);
11550 onKeyUp : function(e){
11551 if(this.editable !== false && !e.isSpecialKey()){
11552 this.lastKey = e.getKey();
11553 this.dqTask.delay(this.queryDelay);
11558 validateBlur : function(){
11559 return !this.list || !this.list.isVisible();
11563 initQuery : function(){
11564 this.doQuery(this.getRawValue());
11568 doForce : function(){
11569 if(this.inputEl().dom.value.length > 0){
11570 this.inputEl().dom.value =
11571 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11577 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
11578 * query allowing the query action to be canceled if needed.
11579 * @param {String} query The SQL query to execute
11580 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11581 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
11582 * saved in the current store (defaults to false)
11584 doQuery : function(q, forceAll){
11586 if(q === undefined || q === null){
11591 forceAll: forceAll,
11595 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11600 forceAll = qe.forceAll;
11601 if(forceAll === true || (q.length >= this.minChars)){
11603 this.hasQuery = true;
11605 if(this.lastQuery != q || this.alwaysQuery){
11606 this.lastQuery = q;
11607 if(this.mode == 'local'){
11608 this.selectedIndex = -1;
11610 this.store.clearFilter();
11612 this.store.filter(this.displayField, q);
11616 this.store.baseParams[this.queryParam] = q;
11618 var options = {params : this.getParams(q)};
11621 options.add = true;
11622 options.params.start = this.page * this.pageSize;
11625 this.store.load(options);
11627 * this code will make the page width larger, at the beginning, the list not align correctly,
11628 * we should expand the list on onLoad
11629 * so command out it
11634 this.selectedIndex = -1;
11639 this.loadNext = false;
11643 getParams : function(q){
11645 //p[this.queryParam] = q;
11649 p.limit = this.pageSize;
11655 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11657 collapse : function(){
11658 if(!this.isExpanded()){
11666 this.cancelBtn.hide();
11667 this.trigger.show();
11670 Roo.get(document).un('mousedown', this.collapseIf, this);
11671 Roo.get(document).un('mousewheel', this.collapseIf, this);
11672 if (!this.editable) {
11673 Roo.get(document).un('keydown', this.listKeyPress, this);
11675 this.fireEvent('collapse', this);
11679 collapseIf : function(e){
11680 var in_combo = e.within(this.el);
11681 var in_list = e.within(this.list);
11682 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11684 if (in_combo || in_list || is_list) {
11685 //e.stopPropagation();
11690 this.onTickableFooterButtonClick(e, false, false);
11698 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11700 expand : function(){
11702 if(this.isExpanded() || !this.hasFocus){
11706 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11707 this.list.setWidth(lw);
11714 this.restrictHeight();
11718 this.tickItems = Roo.apply([], this.item);
11721 this.cancelBtn.show();
11722 this.trigger.hide();
11726 Roo.get(document).on('mousedown', this.collapseIf, this);
11727 Roo.get(document).on('mousewheel', this.collapseIf, this);
11728 if (!this.editable) {
11729 Roo.get(document).on('keydown', this.listKeyPress, this);
11732 this.fireEvent('expand', this);
11736 // Implements the default empty TriggerField.onTriggerClick function
11737 onTriggerClick : function(e)
11739 Roo.log('trigger click');
11741 if(this.disabled || !this.triggerList){
11746 this.loadNext = false;
11748 if(this.isExpanded()){
11750 if (!this.blockFocus) {
11751 this.inputEl().focus();
11755 this.hasFocus = true;
11756 if(this.triggerAction == 'all') {
11757 this.doQuery(this.allQuery, true);
11759 this.doQuery(this.getRawValue());
11761 if (!this.blockFocus) {
11762 this.inputEl().focus();
11767 onTickableTriggerClick : function(e)
11774 this.loadNext = false;
11775 this.hasFocus = true;
11777 if(this.triggerAction == 'all') {
11778 this.doQuery(this.allQuery, true);
11780 this.doQuery(this.getRawValue());
11784 onSearchFieldClick : function(e)
11786 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11791 this.loadNext = false;
11792 this.hasFocus = true;
11794 if(this.triggerAction == 'all') {
11795 this.doQuery(this.allQuery, true);
11797 this.doQuery(this.getRawValue());
11801 listKeyPress : function(e)
11803 //Roo.log('listkeypress');
11804 // scroll to first matching element based on key pres..
11805 if (e.isSpecialKey()) {
11808 var k = String.fromCharCode(e.getKey()).toUpperCase();
11811 var csel = this.view.getSelectedNodes();
11812 var cselitem = false;
11814 var ix = this.view.indexOf(csel[0]);
11815 cselitem = this.store.getAt(ix);
11816 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11822 this.store.each(function(v) {
11824 // start at existing selection.
11825 if (cselitem.id == v.id) {
11831 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11832 match = this.store.indexOf(v);
11838 if (match === false) {
11839 return true; // no more action?
11842 this.view.select(match);
11843 var sn = Roo.get(this.view.getSelectedNodes()[0])
11844 //sn.scrollIntoView(sn.dom.parentNode, false);
11847 onViewScroll : function(e, t){
11849 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){
11853 this.hasQuery = true;
11855 this.loading = this.list.select('.loading', true).first();
11857 if(this.loading === null){
11858 this.list.createChild({
11860 cls: 'loading select2-more-results select2-active',
11861 html: 'Loading more results...'
11864 this.loading = this.list.select('.loading', true).first();
11866 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11868 this.loading.hide();
11871 this.loading.show();
11876 this.loadNext = true;
11878 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11883 addItem : function(o)
11885 var dv = ''; // display value
11887 if (this.displayField) {
11888 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11890 // this is an error condition!!!
11891 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
11898 var choice = this.choices.createChild({
11900 cls: 'select2-search-choice',
11909 cls: 'select2-search-choice-close',
11914 }, this.searchField);
11916 var close = choice.select('a.select2-search-choice-close', true).first()
11918 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11926 this.inputEl().dom.value = '';
11930 onRemoveItem : function(e, _self, o)
11932 e.preventDefault();
11933 var index = this.item.indexOf(o.data) * 1;
11936 Roo.log('not this item?!');
11940 this.item.splice(index, 1);
11945 this.fireEvent('remove', this, e);
11949 syncValue : function()
11951 if(!this.item.length){
11958 Roo.each(this.item, function(i){
11959 if(_this.valueField){
11960 value.push(i[_this.valueField]);
11967 this.value = value.join(',');
11969 if(this.hiddenField){
11970 this.hiddenField.dom.value = this.value;
11974 clearItem : function()
11976 if(!this.multiple){
11982 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11989 inputEl: function ()
11992 return this.searchField;
11994 return this.el.select('input.form-control',true).first();
11998 onTickableFooterButtonClick : function(e, btn, el)
12000 e.preventDefault();
12002 if(btn && btn.name == 'cancel'){
12003 this.tickItems = Roo.apply([], this.item);
12012 Roo.each(this.tickItems, function(o){
12023 * @cfg {Boolean} grow
12027 * @cfg {Number} growMin
12031 * @cfg {Number} growMax
12041 * Ext JS Library 1.1.1
12042 * Copyright(c) 2006-2007, Ext JS, LLC.
12044 * Originally Released Under LGPL - original licence link has changed is not relivant.
12047 * <script type="text/javascript">
12052 * @extends Roo.util.Observable
12053 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
12054 * This class also supports single and multi selection modes. <br>
12055 * Create a data model bound view:
12057 var store = new Roo.data.Store(...);
12059 var view = new Roo.View({
12061 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
12063 singleSelect: true,
12064 selectedClass: "ydataview-selected",
12068 // listen for node click?
12069 view.on("click", function(vw, index, node, e){
12070 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12074 dataModel.load("foobar.xml");
12076 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12078 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12079 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12081 * Note: old style constructor is still suported (container, template, config)
12084 * Create a new View
12085 * @param {Object} config The config object
12088 Roo.View = function(config, depreciated_tpl, depreciated_config){
12090 this.parent = false;
12092 if (typeof(depreciated_tpl) == 'undefined') {
12093 // new way.. - universal constructor.
12094 Roo.apply(this, config);
12095 this.el = Roo.get(this.el);
12098 this.el = Roo.get(config);
12099 this.tpl = depreciated_tpl;
12100 Roo.apply(this, depreciated_config);
12102 this.wrapEl = this.el.wrap().wrap();
12103 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12106 if(typeof(this.tpl) == "string"){
12107 this.tpl = new Roo.Template(this.tpl);
12109 // support xtype ctors..
12110 this.tpl = new Roo.factory(this.tpl, Roo);
12114 this.tpl.compile();
12119 * @event beforeclick
12120 * Fires before a click is processed. Returns false to cancel the default action.
12121 * @param {Roo.View} this
12122 * @param {Number} index The index of the target node
12123 * @param {HTMLElement} node The target node
12124 * @param {Roo.EventObject} e The raw event object
12126 "beforeclick" : true,
12129 * Fires when a template node is clicked.
12130 * @param {Roo.View} this
12131 * @param {Number} index The index of the target node
12132 * @param {HTMLElement} node The target node
12133 * @param {Roo.EventObject} e The raw event object
12138 * Fires when a template node is double clicked.
12139 * @param {Roo.View} this
12140 * @param {Number} index The index of the target node
12141 * @param {HTMLElement} node The target node
12142 * @param {Roo.EventObject} e The raw event object
12146 * @event contextmenu
12147 * Fires when a template node is right clicked.
12148 * @param {Roo.View} this
12149 * @param {Number} index The index of the target node
12150 * @param {HTMLElement} node The target node
12151 * @param {Roo.EventObject} e The raw event object
12153 "contextmenu" : true,
12155 * @event selectionchange
12156 * Fires when the selected nodes change.
12157 * @param {Roo.View} this
12158 * @param {Array} selections Array of the selected nodes
12160 "selectionchange" : true,
12163 * @event beforeselect
12164 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12165 * @param {Roo.View} this
12166 * @param {HTMLElement} node The node to be selected
12167 * @param {Array} selections Array of currently selected nodes
12169 "beforeselect" : true,
12171 * @event preparedata
12172 * Fires on every row to render, to allow you to change the data.
12173 * @param {Roo.View} this
12174 * @param {Object} data to be rendered (change this)
12176 "preparedata" : true
12184 "click": this.onClick,
12185 "dblclick": this.onDblClick,
12186 "contextmenu": this.onContextMenu,
12190 this.selections = [];
12192 this.cmp = new Roo.CompositeElementLite([]);
12194 this.store = Roo.factory(this.store, Roo.data);
12195 this.setStore(this.store, true);
12198 if ( this.footer && this.footer.xtype) {
12200 var fctr = this.wrapEl.appendChild(document.createElement("div"));
12202 this.footer.dataSource = this.store
12203 this.footer.container = fctr;
12204 this.footer = Roo.factory(this.footer, Roo);
12205 fctr.insertFirst(this.el);
12207 // this is a bit insane - as the paging toolbar seems to detach the el..
12208 // dom.parentNode.parentNode.parentNode
12209 // they get detached?
12213 Roo.View.superclass.constructor.call(this);
12218 Roo.extend(Roo.View, Roo.util.Observable, {
12221 * @cfg {Roo.data.Store} store Data store to load data from.
12226 * @cfg {String|Roo.Element} el The container element.
12231 * @cfg {String|Roo.Template} tpl The template used by this View
12235 * @cfg {String} dataName the named area of the template to use as the data area
12236 * Works with domtemplates roo-name="name"
12240 * @cfg {String} selectedClass The css class to add to selected nodes
12242 selectedClass : "x-view-selected",
12244 * @cfg {String} emptyText The empty text to show when nothing is loaded.
12249 * @cfg {String} text to display on mask (default Loading)
12253 * @cfg {Boolean} multiSelect Allow multiple selection
12255 multiSelect : false,
12257 * @cfg {Boolean} singleSelect Allow single selection
12259 singleSelect: false,
12262 * @cfg {Boolean} toggleSelect - selecting
12264 toggleSelect : false,
12267 * @cfg {Boolean} tickable - selecting
12272 * Returns the element this view is bound to.
12273 * @return {Roo.Element}
12275 getEl : function(){
12276 return this.wrapEl;
12282 * Refreshes the view. - called by datachanged on the store. - do not call directly.
12284 refresh : function(){
12285 Roo.log('refresh');
12288 // if we are using something like 'domtemplate', then
12289 // the what gets used is:
12290 // t.applySubtemplate(NAME, data, wrapping data..)
12291 // the outer template then get' applied with
12292 // the store 'extra data'
12293 // and the body get's added to the
12294 // roo-name="data" node?
12295 // <span class='roo-tpl-{name}'></span> ?????
12299 this.clearSelections();
12300 this.el.update("");
12302 var records = this.store.getRange();
12303 if(records.length < 1) {
12305 // is this valid?? = should it render a template??
12307 this.el.update(this.emptyText);
12311 if (this.dataName) {
12312 this.el.update(t.apply(this.store.meta)); //????
12313 el = this.el.child('.roo-tpl-' + this.dataName);
12316 for(var i = 0, len = records.length; i < len; i++){
12317 var data = this.prepareData(records[i].data, i, records[i]);
12318 this.fireEvent("preparedata", this, data, i, records[i]);
12320 var d = Roo.apply({}, data);
12323 Roo.apply(d, {'roo-id' : Roo.id()});
12327 Roo.each(this.parent.item, function(item){
12328 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12331 Roo.apply(d, {'roo-data-checked' : 'checked'});
12335 html[html.length] = Roo.util.Format.trim(
12337 t.applySubtemplate(this.dataName, d, this.store.meta) :
12344 el.update(html.join(""));
12345 this.nodes = el.dom.childNodes;
12346 this.updateIndexes(0);
12351 * Function to override to reformat the data that is sent to
12352 * the template for each node.
12353 * DEPRICATED - use the preparedata event handler.
12354 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12355 * a JSON object for an UpdateManager bound view).
12357 prepareData : function(data, index, record)
12359 this.fireEvent("preparedata", this, data, index, record);
12363 onUpdate : function(ds, record){
12364 Roo.log('on update');
12365 this.clearSelections();
12366 var index = this.store.indexOf(record);
12367 var n = this.nodes[index];
12368 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12369 n.parentNode.removeChild(n);
12370 this.updateIndexes(index, index);
12376 onAdd : function(ds, records, index)
12378 Roo.log(['on Add', ds, records, index] );
12379 this.clearSelections();
12380 if(this.nodes.length == 0){
12384 var n = this.nodes[index];
12385 for(var i = 0, len = records.length; i < len; i++){
12386 var d = this.prepareData(records[i].data, i, records[i]);
12388 this.tpl.insertBefore(n, d);
12391 this.tpl.append(this.el, d);
12394 this.updateIndexes(index);
12397 onRemove : function(ds, record, index){
12398 Roo.log('onRemove');
12399 this.clearSelections();
12400 var el = this.dataName ?
12401 this.el.child('.roo-tpl-' + this.dataName) :
12404 el.dom.removeChild(this.nodes[index]);
12405 this.updateIndexes(index);
12409 * Refresh an individual node.
12410 * @param {Number} index
12412 refreshNode : function(index){
12413 this.onUpdate(this.store, this.store.getAt(index));
12416 updateIndexes : function(startIndex, endIndex){
12417 var ns = this.nodes;
12418 startIndex = startIndex || 0;
12419 endIndex = endIndex || ns.length - 1;
12420 for(var i = startIndex; i <= endIndex; i++){
12421 ns[i].nodeIndex = i;
12426 * Changes the data store this view uses and refresh the view.
12427 * @param {Store} store
12429 setStore : function(store, initial){
12430 if(!initial && this.store){
12431 this.store.un("datachanged", this.refresh);
12432 this.store.un("add", this.onAdd);
12433 this.store.un("remove", this.onRemove);
12434 this.store.un("update", this.onUpdate);
12435 this.store.un("clear", this.refresh);
12436 this.store.un("beforeload", this.onBeforeLoad);
12437 this.store.un("load", this.onLoad);
12438 this.store.un("loadexception", this.onLoad);
12442 store.on("datachanged", this.refresh, this);
12443 store.on("add", this.onAdd, this);
12444 store.on("remove", this.onRemove, this);
12445 store.on("update", this.onUpdate, this);
12446 store.on("clear", this.refresh, this);
12447 store.on("beforeload", this.onBeforeLoad, this);
12448 store.on("load", this.onLoad, this);
12449 store.on("loadexception", this.onLoad, this);
12457 * onbeforeLoad - masks the loading area.
12460 onBeforeLoad : function(store,opts)
12462 Roo.log('onBeforeLoad');
12464 this.el.update("");
12466 this.el.mask(this.mask ? this.mask : "Loading" );
12468 onLoad : function ()
12475 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12476 * @param {HTMLElement} node
12477 * @return {HTMLElement} The template node
12479 findItemFromChild : function(node){
12480 var el = this.dataName ?
12481 this.el.child('.roo-tpl-' + this.dataName,true) :
12484 if(!node || node.parentNode == el){
12487 var p = node.parentNode;
12488 while(p && p != el){
12489 if(p.parentNode == el){
12498 onClick : function(e){
12499 var item = this.findItemFromChild(e.getTarget());
12501 var index = this.indexOf(item);
12502 if(this.onItemClick(item, index, e) !== false){
12503 this.fireEvent("click", this, index, item, e);
12506 this.clearSelections();
12511 onContextMenu : function(e){
12512 var item = this.findItemFromChild(e.getTarget());
12514 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12519 onDblClick : function(e){
12520 var item = this.findItemFromChild(e.getTarget());
12522 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12526 onItemClick : function(item, index, e)
12528 if(this.fireEvent("beforeclick", this, index, item, e) === false){
12531 if (this.toggleSelect) {
12532 var m = this.isSelected(item) ? 'unselect' : 'select';
12535 _t[m](item, true, false);
12538 if(this.multiSelect || this.singleSelect){
12539 if(this.multiSelect && e.shiftKey && this.lastSelection){
12540 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12542 this.select(item, this.multiSelect && e.ctrlKey);
12543 this.lastSelection = item;
12546 if(!this.tickable){
12547 e.preventDefault();
12555 * Get the number of selected nodes.
12558 getSelectionCount : function(){
12559 return this.selections.length;
12563 * Get the currently selected nodes.
12564 * @return {Array} An array of HTMLElements
12566 getSelectedNodes : function(){
12567 return this.selections;
12571 * Get the indexes of the selected nodes.
12574 getSelectedIndexes : function(){
12575 var indexes = [], s = this.selections;
12576 for(var i = 0, len = s.length; i < len; i++){
12577 indexes.push(s[i].nodeIndex);
12583 * Clear all selections
12584 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12586 clearSelections : function(suppressEvent){
12587 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12588 this.cmp.elements = this.selections;
12589 this.cmp.removeClass(this.selectedClass);
12590 this.selections = [];
12591 if(!suppressEvent){
12592 this.fireEvent("selectionchange", this, this.selections);
12598 * Returns true if the passed node is selected
12599 * @param {HTMLElement/Number} node The node or node index
12600 * @return {Boolean}
12602 isSelected : function(node){
12603 var s = this.selections;
12607 node = this.getNode(node);
12608 return s.indexOf(node) !== -1;
12613 * @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
12614 * @param {Boolean} keepExisting (optional) true to keep existing selections
12615 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12617 select : function(nodeInfo, keepExisting, suppressEvent){
12618 if(nodeInfo instanceof Array){
12620 this.clearSelections(true);
12622 for(var i = 0, len = nodeInfo.length; i < len; i++){
12623 this.select(nodeInfo[i], true, true);
12627 var node = this.getNode(nodeInfo);
12628 if(!node || this.isSelected(node)){
12629 return; // already selected.
12632 this.clearSelections(true);
12634 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12635 Roo.fly(node).addClass(this.selectedClass);
12636 this.selections.push(node);
12637 if(!suppressEvent){
12638 this.fireEvent("selectionchange", this, this.selections);
12646 * @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
12647 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12648 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12650 unselect : function(nodeInfo, keepExisting, suppressEvent)
12652 if(nodeInfo instanceof Array){
12653 Roo.each(this.selections, function(s) {
12654 this.unselect(s, nodeInfo);
12658 var node = this.getNode(nodeInfo);
12659 if(!node || !this.isSelected(node)){
12660 Roo.log("not selected");
12661 return; // not selected.
12665 Roo.each(this.selections, function(s) {
12667 Roo.fly(node).removeClass(this.selectedClass);
12674 this.selections= ns;
12675 this.fireEvent("selectionchange", this, this.selections);
12679 * Gets a template node.
12680 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12681 * @return {HTMLElement} The node or null if it wasn't found
12683 getNode : function(nodeInfo){
12684 if(typeof nodeInfo == "string"){
12685 return document.getElementById(nodeInfo);
12686 }else if(typeof nodeInfo == "number"){
12687 return this.nodes[nodeInfo];
12693 * Gets a range template nodes.
12694 * @param {Number} startIndex
12695 * @param {Number} endIndex
12696 * @return {Array} An array of nodes
12698 getNodes : function(start, end){
12699 var ns = this.nodes;
12700 start = start || 0;
12701 end = typeof end == "undefined" ? ns.length - 1 : end;
12704 for(var i = start; i <= end; i++){
12708 for(var i = start; i >= end; i--){
12716 * Finds the index of the passed node
12717 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12718 * @return {Number} The index of the node or -1
12720 indexOf : function(node){
12721 node = this.getNode(node);
12722 if(typeof node.nodeIndex == "number"){
12723 return node.nodeIndex;
12725 var ns = this.nodes;
12726 for(var i = 0, len = ns.length; i < len; i++){
12737 * based on jquery fullcalendar
12741 Roo.bootstrap = Roo.bootstrap || {};
12743 * @class Roo.bootstrap.Calendar
12744 * @extends Roo.bootstrap.Component
12745 * Bootstrap Calendar class
12746 * @cfg {Boolean} loadMask (true|false) default false
12747 * @cfg {Object} header generate the user specific header of the calendar, default false
12750 * Create a new Container
12751 * @param {Object} config The config object
12756 Roo.bootstrap.Calendar = function(config){
12757 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12761 * Fires when a date is selected
12762 * @param {DatePicker} this
12763 * @param {Date} date The selected date
12767 * @event monthchange
12768 * Fires when the displayed month changes
12769 * @param {DatePicker} this
12770 * @param {Date} date The selected month
12772 'monthchange': true,
12774 * @event evententer
12775 * Fires when mouse over an event
12776 * @param {Calendar} this
12777 * @param {event} Event
12779 'evententer': true,
12781 * @event eventleave
12782 * Fires when the mouse leaves an
12783 * @param {Calendar} this
12786 'eventleave': true,
12788 * @event eventclick
12789 * Fires when the mouse click an
12790 * @param {Calendar} this
12799 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
12802 * @cfg {Number} startDay
12803 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12811 getAutoCreate : function(){
12814 var fc_button = function(name, corner, style, content ) {
12815 return Roo.apply({},{
12817 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
12819 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12822 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12833 style : 'width:100%',
12840 cls : 'fc-header-left',
12842 fc_button('prev', 'left', 'arrow', '‹' ),
12843 fc_button('next', 'right', 'arrow', '›' ),
12844 { tag: 'span', cls: 'fc-header-space' },
12845 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
12853 cls : 'fc-header-center',
12857 cls: 'fc-header-title',
12860 html : 'month / year'
12868 cls : 'fc-header-right',
12870 /* fc_button('month', 'left', '', 'month' ),
12871 fc_button('week', '', '', 'week' ),
12872 fc_button('day', 'right', '', 'day' )
12884 header = this.header;
12887 var cal_heads = function() {
12889 // fixme - handle this.
12891 for (var i =0; i < Date.dayNames.length; i++) {
12892 var d = Date.dayNames[i];
12895 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12896 html : d.substring(0,3)
12900 ret[0].cls += ' fc-first';
12901 ret[6].cls += ' fc-last';
12904 var cal_cell = function(n) {
12907 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12912 cls: 'fc-day-number',
12916 cls: 'fc-day-content',
12920 style: 'position: relative;' // height: 17px;
12932 var cal_rows = function() {
12935 for (var r = 0; r < 6; r++) {
12942 for (var i =0; i < Date.dayNames.length; i++) {
12943 var d = Date.dayNames[i];
12944 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12947 row.cn[0].cls+=' fc-first';
12948 row.cn[0].cn[0].style = 'min-height:90px';
12949 row.cn[6].cls+=' fc-last';
12953 ret[0].cls += ' fc-first';
12954 ret[4].cls += ' fc-prev-last';
12955 ret[5].cls += ' fc-last';
12962 cls: 'fc-border-separate',
12963 style : 'width:100%',
12971 cls : 'fc-first fc-last',
12989 cls : 'fc-content',
12990 style : "position: relative;",
12993 cls : 'fc-view fc-view-month fc-grid',
12994 style : 'position: relative',
12995 unselectable : 'on',
12998 cls : 'fc-event-container',
12999 style : 'position:absolute;z-index:8;top:0;left:0;'
13017 initEvents : function()
13020 throw "can not find store for calendar";
13026 style: "text-align:center",
13030 style: "background-color:white;width:50%;margin:250 auto",
13034 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
13045 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13047 var size = this.el.select('.fc-content', true).first().getSize();
13048 this.maskEl.setSize(size.width, size.height);
13049 this.maskEl.enableDisplayMode("block");
13050 if(!this.loadMask){
13051 this.maskEl.hide();
13054 this.store = Roo.factory(this.store, Roo.data);
13055 this.store.on('load', this.onLoad, this);
13056 this.store.on('beforeload', this.onBeforeLoad, this);
13060 this.cells = this.el.select('.fc-day',true);
13061 //Roo.log(this.cells);
13062 this.textNodes = this.el.query('.fc-day-number');
13063 this.cells.addClassOnOver('fc-state-hover');
13065 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13066 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13067 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13068 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13070 this.on('monthchange', this.onMonthChange, this);
13072 this.update(new Date().clearTime());
13075 resize : function() {
13076 var sz = this.el.getSize();
13078 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13079 this.el.select('.fc-day-content div',true).setHeight(34);
13084 showPrevMonth : function(e){
13085 this.update(this.activeDate.add("mo", -1));
13087 showToday : function(e){
13088 this.update(new Date().clearTime());
13091 showNextMonth : function(e){
13092 this.update(this.activeDate.add("mo", 1));
13096 showPrevYear : function(){
13097 this.update(this.activeDate.add("y", -1));
13101 showNextYear : function(){
13102 this.update(this.activeDate.add("y", 1));
13107 update : function(date)
13109 var vd = this.activeDate;
13110 this.activeDate = date;
13111 // if(vd && this.el){
13112 // var t = date.getTime();
13113 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13114 // Roo.log('using add remove');
13116 // this.fireEvent('monthchange', this, date);
13118 // this.cells.removeClass("fc-state-highlight");
13119 // this.cells.each(function(c){
13120 // if(c.dateValue == t){
13121 // c.addClass("fc-state-highlight");
13122 // setTimeout(function(){
13123 // try{c.dom.firstChild.focus();}catch(e){}
13133 var days = date.getDaysInMonth();
13135 var firstOfMonth = date.getFirstDateOfMonth();
13136 var startingPos = firstOfMonth.getDay()-this.startDay;
13138 if(startingPos < this.startDay){
13142 var pm = date.add(Date.MONTH, -1);
13143 var prevStart = pm.getDaysInMonth()-startingPos;
13145 this.cells = this.el.select('.fc-day',true);
13146 this.textNodes = this.el.query('.fc-day-number');
13147 this.cells.addClassOnOver('fc-state-hover');
13149 var cells = this.cells.elements;
13150 var textEls = this.textNodes;
13152 Roo.each(cells, function(cell){
13153 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13156 days += startingPos;
13158 // convert everything to numbers so it's fast
13159 var day = 86400000;
13160 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13163 //Roo.log(prevStart);
13165 var today = new Date().clearTime().getTime();
13166 var sel = date.clearTime().getTime();
13167 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13168 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13169 var ddMatch = this.disabledDatesRE;
13170 var ddText = this.disabledDatesText;
13171 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13172 var ddaysText = this.disabledDaysText;
13173 var format = this.format;
13175 var setCellClass = function(cal, cell){
13179 //Roo.log('set Cell Class');
13181 var t = d.getTime();
13185 cell.dateValue = t;
13187 cell.className += " fc-today";
13188 cell.className += " fc-state-highlight";
13189 cell.title = cal.todayText;
13192 // disable highlight in other month..
13193 //cell.className += " fc-state-highlight";
13198 cell.className = " fc-state-disabled";
13199 cell.title = cal.minText;
13203 cell.className = " fc-state-disabled";
13204 cell.title = cal.maxText;
13208 if(ddays.indexOf(d.getDay()) != -1){
13209 cell.title = ddaysText;
13210 cell.className = " fc-state-disabled";
13213 if(ddMatch && format){
13214 var fvalue = d.dateFormat(format);
13215 if(ddMatch.test(fvalue)){
13216 cell.title = ddText.replace("%0", fvalue);
13217 cell.className = " fc-state-disabled";
13221 if (!cell.initialClassName) {
13222 cell.initialClassName = cell.dom.className;
13225 cell.dom.className = cell.initialClassName + ' ' + cell.className;
13230 for(; i < startingPos; i++) {
13231 textEls[i].innerHTML = (++prevStart);
13232 d.setDate(d.getDate()+1);
13234 cells[i].className = "fc-past fc-other-month";
13235 setCellClass(this, cells[i]);
13240 for(; i < days; i++){
13241 intDay = i - startingPos + 1;
13242 textEls[i].innerHTML = (intDay);
13243 d.setDate(d.getDate()+1);
13245 cells[i].className = ''; // "x-date-active";
13246 setCellClass(this, cells[i]);
13250 for(; i < 42; i++) {
13251 textEls[i].innerHTML = (++extraDays);
13252 d.setDate(d.getDate()+1);
13254 cells[i].className = "fc-future fc-other-month";
13255 setCellClass(this, cells[i]);
13258 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13260 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13262 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13263 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13265 if(totalRows != 6){
13266 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13267 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13270 this.fireEvent('monthchange', this, date);
13274 if(!this.internalRender){
13275 var main = this.el.dom.firstChild;
13276 var w = main.offsetWidth;
13277 this.el.setWidth(w + this.el.getBorderWidth("lr"));
13278 Roo.fly(main).setWidth(w);
13279 this.internalRender = true;
13280 // opera does not respect the auto grow header center column
13281 // then, after it gets a width opera refuses to recalculate
13282 // without a second pass
13283 if(Roo.isOpera && !this.secondPass){
13284 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13285 this.secondPass = true;
13286 this.update.defer(10, this, [date]);
13293 findCell : function(dt) {
13294 dt = dt.clearTime().getTime();
13296 this.cells.each(function(c){
13297 //Roo.log("check " +c.dateValue + '?=' + dt);
13298 if(c.dateValue == dt){
13308 findCells : function(ev) {
13309 var s = ev.start.clone().clearTime().getTime();
13311 var e= ev.end.clone().clearTime().getTime();
13314 this.cells.each(function(c){
13315 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13317 if(c.dateValue > e){
13320 if(c.dateValue < s){
13329 // findBestRow: function(cells)
13333 // for (var i =0 ; i < cells.length;i++) {
13334 // ret = Math.max(cells[i].rows || 0,ret);
13341 addItem : function(ev)
13343 // look for vertical location slot in
13344 var cells = this.findCells(ev);
13346 // ev.row = this.findBestRow(cells);
13348 // work out the location.
13352 for(var i =0; i < cells.length; i++) {
13354 cells[i].row = cells[0].row;
13357 cells[i].row = cells[i].row + 1;
13367 if (crow.start.getY() == cells[i].getY()) {
13369 crow.end = cells[i];
13386 cells[0].events.push(ev);
13388 this.calevents.push(ev);
13391 clearEvents: function() {
13393 if(!this.calevents){
13397 Roo.each(this.cells.elements, function(c){
13403 Roo.each(this.calevents, function(e) {
13404 Roo.each(e.els, function(el) {
13405 el.un('mouseenter' ,this.onEventEnter, this);
13406 el.un('mouseleave' ,this.onEventLeave, this);
13411 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13417 renderEvents: function()
13421 this.cells.each(function(c) {
13430 if(c.row != c.events.length){
13431 r = 4 - (4 - (c.row - c.events.length));
13434 c.events = ev.slice(0, r);
13435 c.more = ev.slice(r);
13437 if(c.more.length && c.more.length == 1){
13438 c.events.push(c.more.pop());
13441 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13445 this.cells.each(function(c) {
13447 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13450 for (var e = 0; e < c.events.length; e++){
13451 var ev = c.events[e];
13452 var rows = ev.rows;
13454 for(var i = 0; i < rows.length; i++) {
13456 // how many rows should it span..
13459 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13460 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13462 unselectable : "on",
13465 cls: 'fc-event-inner',
13469 // cls: 'fc-event-time',
13470 // html : cells.length > 1 ? '' : ev.time
13474 cls: 'fc-event-title',
13475 html : String.format('{0}', ev.title)
13482 cls: 'ui-resizable-handle ui-resizable-e',
13483 html : '  '
13490 cfg.cls += ' fc-event-start';
13492 if ((i+1) == rows.length) {
13493 cfg.cls += ' fc-event-end';
13496 var ctr = _this.el.select('.fc-event-container',true).first();
13497 var cg = ctr.createChild(cfg);
13499 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13500 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13502 var r = (c.more.length) ? 1 : 0;
13503 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
13504 cg.setWidth(ebox.right - sbox.x -2);
13506 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13507 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13508 cg.on('click', _this.onEventClick, _this, ev);
13519 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13520 style : 'position: absolute',
13521 unselectable : "on",
13524 cls: 'fc-event-inner',
13528 cls: 'fc-event-title',
13536 cls: 'ui-resizable-handle ui-resizable-e',
13537 html : '  '
13543 var ctr = _this.el.select('.fc-event-container',true).first();
13544 var cg = ctr.createChild(cfg);
13546 var sbox = c.select('.fc-day-content',true).first().getBox();
13547 var ebox = c.select('.fc-day-content',true).first().getBox();
13549 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
13550 cg.setWidth(ebox.right - sbox.x -2);
13552 cg.on('click', _this.onMoreEventClick, _this, c.more);
13562 onEventEnter: function (e, el,event,d) {
13563 this.fireEvent('evententer', this, el, event);
13566 onEventLeave: function (e, el,event,d) {
13567 this.fireEvent('eventleave', this, el, event);
13570 onEventClick: function (e, el,event,d) {
13571 this.fireEvent('eventclick', this, el, event);
13574 onMonthChange: function () {
13578 onMoreEventClick: function(e, el, more)
13582 this.calpopover.placement = 'right';
13583 this.calpopover.setTitle('More');
13585 this.calpopover.setContent('');
13587 var ctr = this.calpopover.el.select('.popover-content', true).first();
13589 Roo.each(more, function(m){
13591 cls : 'fc-event-hori fc-event-draggable',
13594 var cg = ctr.createChild(cfg);
13596 cg.on('click', _this.onEventClick, _this, m);
13599 this.calpopover.show(el);
13604 onLoad: function ()
13606 this.calevents = [];
13609 if(this.store.getCount() > 0){
13610 this.store.data.each(function(d){
13613 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13614 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13615 time : d.data.start_time,
13616 title : d.data.title,
13617 description : d.data.description,
13618 venue : d.data.venue
13623 this.renderEvents();
13625 if(this.calevents.length && this.loadMask){
13626 this.maskEl.hide();
13630 onBeforeLoad: function()
13632 this.clearEvents();
13634 this.maskEl.show();
13648 * @class Roo.bootstrap.Popover
13649 * @extends Roo.bootstrap.Component
13650 * Bootstrap Popover class
13651 * @cfg {String} html contents of the popover (or false to use children..)
13652 * @cfg {String} title of popover (or false to hide)
13653 * @cfg {String} placement how it is placed
13654 * @cfg {String} trigger click || hover (or false to trigger manually)
13655 * @cfg {String} over what (parent or false to trigger manually.)
13656 * @cfg {Number} delay - delay before showing
13659 * Create a new Popover
13660 * @param {Object} config The config object
13663 Roo.bootstrap.Popover = function(config){
13664 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13667 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
13669 title: 'Fill in a title',
13672 placement : 'right',
13673 trigger : 'hover', // hover
13679 can_build_overlaid : false,
13681 getChildContainer : function()
13683 return this.el.select('.popover-content',true).first();
13686 getAutoCreate : function(){
13687 Roo.log('make popover?');
13689 cls : 'popover roo-dynamic',
13690 style: 'display:block',
13696 cls : 'popover-inner',
13700 cls: 'popover-title',
13704 cls : 'popover-content',
13715 setTitle: function(str)
13717 this.el.select('.popover-title',true).first().dom.innerHTML = str;
13719 setContent: function(str)
13721 this.el.select('.popover-content',true).first().dom.innerHTML = str;
13723 // as it get's added to the bottom of the page.
13724 onRender : function(ct, position)
13726 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13728 var cfg = Roo.apply({}, this.getAutoCreate());
13732 cfg.cls += ' ' + this.cls;
13735 cfg.style = this.style;
13737 Roo.log("adding to ")
13738 this.el = Roo.get(document.body).createChild(cfg, position);
13744 initEvents : function()
13746 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13747 this.el.enableDisplayMode('block');
13749 if (this.over === false) {
13752 if (this.triggers === false) {
13755 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13756 var triggers = this.trigger ? this.trigger.split(' ') : [];
13757 Roo.each(triggers, function(trigger) {
13759 if (trigger == 'click') {
13760 on_el.on('click', this.toggle, this);
13761 } else if (trigger != 'manual') {
13762 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
13763 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13765 on_el.on(eventIn ,this.enter, this);
13766 on_el.on(eventOut, this.leave, this);
13777 toggle : function () {
13778 this.hoverState == 'in' ? this.leave() : this.enter();
13781 enter : function () {
13784 clearTimeout(this.timeout);
13786 this.hoverState = 'in'
13788 if (!this.delay || !this.delay.show) {
13793 this.timeout = setTimeout(function () {
13794 if (_t.hoverState == 'in') {
13797 }, this.delay.show)
13799 leave : function() {
13800 clearTimeout(this.timeout);
13802 this.hoverState = 'out'
13804 if (!this.delay || !this.delay.hide) {
13809 this.timeout = setTimeout(function () {
13810 if (_t.hoverState == 'out') {
13813 }, this.delay.hide)
13816 show : function (on_el)
13819 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13822 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13823 if (this.html !== false) {
13824 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13826 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13827 if (!this.title.length) {
13828 this.el.select('.popover-title',true).hide();
13831 var placement = typeof this.placement == 'function' ?
13832 this.placement.call(this, this.el, on_el) :
13835 var autoToken = /\s?auto?\s?/i;
13836 var autoPlace = autoToken.test(placement);
13838 placement = placement.replace(autoToken, '') || 'top';
13842 //this.el.setXY([0,0]);
13844 this.el.dom.style.display='block';
13845 this.el.addClass(placement);
13847 //this.el.appendTo(on_el);
13849 var p = this.getPosition();
13850 var box = this.el.getBox();
13855 var align = Roo.bootstrap.Popover.alignment[placement]
13856 this.el.alignTo(on_el, align[0],align[1]);
13857 //var arrow = this.el.select('.arrow',true).first();
13858 //arrow.set(align[2],
13860 this.el.addClass('in');
13861 this.hoverState = null;
13863 if (this.el.hasClass('fade')) {
13870 this.el.setXY([0,0]);
13871 this.el.removeClass('in');
13878 Roo.bootstrap.Popover.alignment = {
13879 'left' : ['r-l', [-10,0], 'right'],
13880 'right' : ['l-r', [10,0], 'left'],
13881 'bottom' : ['t-b', [0,10], 'top'],
13882 'top' : [ 'b-t', [0,-10], 'bottom']
13893 * @class Roo.bootstrap.Progress
13894 * @extends Roo.bootstrap.Component
13895 * Bootstrap Progress class
13896 * @cfg {Boolean} striped striped of the progress bar
13897 * @cfg {Boolean} active animated of the progress bar
13901 * Create a new Progress
13902 * @param {Object} config The config object
13905 Roo.bootstrap.Progress = function(config){
13906 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13909 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
13914 getAutoCreate : function(){
13922 cfg.cls += ' progress-striped';
13926 cfg.cls += ' active';
13945 * @class Roo.bootstrap.ProgressBar
13946 * @extends Roo.bootstrap.Component
13947 * Bootstrap ProgressBar class
13948 * @cfg {Number} aria_valuenow aria-value now
13949 * @cfg {Number} aria_valuemin aria-value min
13950 * @cfg {Number} aria_valuemax aria-value max
13951 * @cfg {String} label label for the progress bar
13952 * @cfg {String} panel (success | info | warning | danger )
13953 * @cfg {String} role role of the progress bar
13954 * @cfg {String} sr_only text
13958 * Create a new ProgressBar
13959 * @param {Object} config The config object
13962 Roo.bootstrap.ProgressBar = function(config){
13963 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13966 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
13970 aria_valuemax : 100,
13976 getAutoCreate : function()
13981 cls: 'progress-bar',
13982 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13994 cfg.role = this.role;
13997 if(this.aria_valuenow){
13998 cfg['aria-valuenow'] = this.aria_valuenow;
14001 if(this.aria_valuemin){
14002 cfg['aria-valuemin'] = this.aria_valuemin;
14005 if(this.aria_valuemax){
14006 cfg['aria-valuemax'] = this.aria_valuemax;
14009 if(this.label && !this.sr_only){
14010 cfg.html = this.label;
14014 cfg.cls += ' progress-bar-' + this.panel;
14020 update : function(aria_valuenow)
14022 this.aria_valuenow = aria_valuenow;
14024 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14039 * @class Roo.bootstrap.TabGroup
14040 * @extends Roo.bootstrap.Column
14041 * Bootstrap Column class
14042 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14043 * @cfg {Boolean} carousel true to make the group behave like a carousel
14046 * Create a new TabGroup
14047 * @param {Object} config The config object
14050 Roo.bootstrap.TabGroup = function(config){
14051 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14053 this.navId = Roo.id();
14056 Roo.bootstrap.TabGroup.register(this);
14060 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
14063 transition : false,
14065 getAutoCreate : function()
14067 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14069 cfg.cls += ' tab-content';
14071 if (this.carousel) {
14072 cfg.cls += ' carousel slide';
14074 cls : 'carousel-inner'
14081 getChildContainer : function()
14083 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14087 * register a Navigation item
14088 * @param {Roo.bootstrap.NavItem} the navitem to add
14090 register : function(item)
14092 this.tabs.push( item);
14093 item.navId = this.navId; // not really needed..
14097 getActivePanel : function()
14100 Roo.each(this.tabs, function(t) {
14110 getPanelByName : function(n)
14113 Roo.each(this.tabs, function(t) {
14114 if (t.tabId == n) {
14122 indexOfPanel : function(p)
14125 Roo.each(this.tabs, function(t,i) {
14126 if (t.tabId == p.tabId) {
14135 * show a specific panel
14136 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14137 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14139 showPanel : function (pan)
14142 if (typeof(pan) == 'number') {
14143 pan = this.tabs[pan];
14145 if (typeof(pan) == 'string') {
14146 pan = this.getPanelByName(pan);
14148 if (pan.tabId == this.getActivePanel().tabId) {
14151 var cur = this.getActivePanel();
14153 if (false === cur.fireEvent('beforedeactivate')) {
14157 if (this.carousel) {
14158 this.transition = true;
14159 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
14160 var lr = dir == 'next' ? 'left' : 'right';
14161 pan.el.addClass(dir); // or prev
14162 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14163 cur.el.addClass(lr); // or right
14164 pan.el.addClass(lr);
14167 cur.el.on('transitionend', function() {
14168 Roo.log("trans end?");
14170 pan.el.removeClass([lr,dir]);
14171 pan.setActive(true);
14173 cur.el.removeClass([lr]);
14174 cur.setActive(false);
14176 _this.transition = false;
14178 }, this, { single: true } );
14182 cur.setActive(false);
14183 pan.setActive(true);
14187 showPanelNext : function()
14189 var i = this.indexOfPanel(this.getActivePanel());
14190 if (i > this.tabs.length) {
14193 this.showPanel(this.tabs[i+1]);
14195 showPanelPrev : function()
14197 var i = this.indexOfPanel(this.getActivePanel());
14201 this.showPanel(this.tabs[i-1]);
14212 Roo.apply(Roo.bootstrap.TabGroup, {
14216 * register a Navigation Group
14217 * @param {Roo.bootstrap.NavGroup} the navgroup to add
14219 register : function(navgrp)
14221 this.groups[navgrp.navId] = navgrp;
14225 * fetch a Navigation Group based on the navigation ID
14226 * if one does not exist , it will get created.
14227 * @param {string} the navgroup to add
14228 * @returns {Roo.bootstrap.NavGroup} the navgroup
14230 get: function(navId) {
14231 if (typeof(this.groups[navId]) == 'undefined') {
14232 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14234 return this.groups[navId] ;
14249 * @class Roo.bootstrap.TabPanel
14250 * @extends Roo.bootstrap.Component
14251 * Bootstrap TabPanel class
14252 * @cfg {Boolean} active panel active
14253 * @cfg {String} html panel content
14254 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14255 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14259 * Create a new TabPanel
14260 * @param {Object} config The config object
14263 Roo.bootstrap.TabPanel = function(config){
14264 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14268 * Fires when the active status changes
14269 * @param {Roo.bootstrap.TabPanel} this
14270 * @param {Boolean} state the new state
14275 * @event beforedeactivate
14276 * Fires before a tab is de-activated - can be used to do validation on a form.
14277 * @param {Roo.bootstrap.TabPanel} this
14278 * @return {Boolean} false if there is an error
14281 'beforedeactivate': true
14284 this.tabId = this.tabId || Roo.id();
14288 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
14295 getAutoCreate : function(){
14298 // item is needed for carousel - not sure if it has any effect otherwise
14299 cls: 'tab-pane item',
14300 html: this.html || ''
14304 cfg.cls += ' active';
14308 cfg.tabId = this.tabId;
14315 initEvents: function()
14317 Roo.log('-------- init events on tab panel ---------');
14319 var p = this.parent();
14320 this.navId = this.navId || p.navId;
14322 if (typeof(this.navId) != 'undefined') {
14323 // not really needed.. but just in case.. parent should be a NavGroup.
14324 var tg = Roo.bootstrap.TabGroup.get(this.navId);
14325 Roo.log(['register', tg, this]);
14331 onRender : function(ct, position)
14333 // Roo.log("Call onRender: " + this.xtype);
14335 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14343 setActive: function(state)
14345 Roo.log("panel - set active " + this.tabId + "=" + state);
14347 this.active = state;
14349 this.el.removeClass('active');
14351 } else if (!this.el.hasClass('active')) {
14352 this.el.addClass('active');
14354 this.fireEvent('changed', this, state);
14371 * @class Roo.bootstrap.DateField
14372 * @extends Roo.bootstrap.Input
14373 * Bootstrap DateField class
14374 * @cfg {Number} weekStart default 0
14375 * @cfg {Number} weekStart default 0
14376 * @cfg {Number} viewMode default empty, (months|years)
14377 * @cfg {Number} minViewMode default empty, (months|years)
14378 * @cfg {Number} startDate default -Infinity
14379 * @cfg {Number} endDate default Infinity
14380 * @cfg {Boolean} todayHighlight default false
14381 * @cfg {Boolean} todayBtn default false
14382 * @cfg {Boolean} calendarWeeks default false
14383 * @cfg {Object} daysOfWeekDisabled default empty
14385 * @cfg {Boolean} keyboardNavigation default true
14386 * @cfg {String} language default en
14389 * Create a new DateField
14390 * @param {Object} config The config object
14393 Roo.bootstrap.DateField = function(config){
14394 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14398 * Fires when this field show.
14399 * @param {Roo.bootstrap.DateField} this
14400 * @param {Mixed} date The date value
14405 * Fires when this field hide.
14406 * @param {Roo.bootstrap.DateField} this
14407 * @param {Mixed} date The date value
14412 * Fires when select a date.
14413 * @param {Roo.bootstrap.DateField} this
14414 * @param {Mixed} date The date value
14420 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
14423 * @cfg {String} format
14424 * The default date format string which can be overriden for localization support. The format must be
14425 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14429 * @cfg {String} altFormats
14430 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14431 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14433 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14441 todayHighlight : false,
14447 keyboardNavigation: true,
14449 calendarWeeks: false,
14451 startDate: -Infinity,
14455 daysOfWeekDisabled: [],
14459 UTCDate: function()
14461 return new Date(Date.UTC.apply(Date, arguments));
14464 UTCToday: function()
14466 var today = new Date();
14467 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14470 getDate: function() {
14471 var d = this.getUTCDate();
14472 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14475 getUTCDate: function() {
14479 setDate: function(d) {
14480 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14483 setUTCDate: function(d) {
14485 this.setValue(this.formatDate(this.date));
14488 onRender: function(ct, position)
14491 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14493 this.language = this.language || 'en';
14494 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14495 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14497 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14498 this.format = this.format || 'm/d/y';
14499 this.isInline = false;
14500 this.isInput = true;
14501 this.component = this.el.select('.add-on', true).first() || false;
14502 this.component = (this.component && this.component.length === 0) ? false : this.component;
14503 this.hasInput = this.component && this.inputEL().length;
14505 if (typeof(this.minViewMode === 'string')) {
14506 switch (this.minViewMode) {
14508 this.minViewMode = 1;
14511 this.minViewMode = 2;
14514 this.minViewMode = 0;
14519 if (typeof(this.viewMode === 'string')) {
14520 switch (this.viewMode) {
14533 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14535 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14537 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14539 this.picker().on('mousedown', this.onMousedown, this);
14540 this.picker().on('click', this.onClick, this);
14542 this.picker().addClass('datepicker-dropdown');
14544 this.startViewMode = this.viewMode;
14547 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14548 if(!this.calendarWeeks){
14553 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14554 v.attr('colspan', function(i, val){
14555 return parseInt(val) + 1;
14560 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14562 this.setStartDate(this.startDate);
14563 this.setEndDate(this.endDate);
14565 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14572 if(this.isInline) {
14577 picker : function()
14579 return this.pickerEl;
14580 // return this.el.select('.datepicker', true).first();
14583 fillDow: function()
14585 var dowCnt = this.weekStart;
14594 if(this.calendarWeeks){
14602 while (dowCnt < this.weekStart + 7) {
14606 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14610 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14613 fillMonths: function()
14616 var months = this.picker().select('>.datepicker-months td', true).first();
14618 months.dom.innerHTML = '';
14624 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14627 months.createChild(month);
14634 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;
14636 if (this.date < this.startDate) {
14637 this.viewDate = new Date(this.startDate);
14638 } else if (this.date > this.endDate) {
14639 this.viewDate = new Date(this.endDate);
14641 this.viewDate = new Date(this.date);
14649 var d = new Date(this.viewDate),
14650 year = d.getUTCFullYear(),
14651 month = d.getUTCMonth(),
14652 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14653 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14654 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14655 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14656 currentDate = this.date && this.date.valueOf(),
14657 today = this.UTCToday();
14659 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14661 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14663 // this.picker.select('>tfoot th.today').
14664 // .text(dates[this.language].today)
14665 // .toggle(this.todayBtn !== false);
14667 this.updateNavArrows();
14670 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14672 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14674 prevMonth.setUTCDate(day);
14676 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14678 var nextMonth = new Date(prevMonth);
14680 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14682 nextMonth = nextMonth.valueOf();
14684 var fillMonths = false;
14686 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14688 while(prevMonth.valueOf() < nextMonth) {
14691 if (prevMonth.getUTCDay() === this.weekStart) {
14693 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14701 if(this.calendarWeeks){
14702 // ISO 8601: First week contains first thursday.
14703 // ISO also states week starts on Monday, but we can be more abstract here.
14705 // Start of current week: based on weekstart/current date
14706 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14707 // Thursday of this week
14708 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14709 // First Thursday of year, year from thursday
14710 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14711 // Calendar week: ms between thursdays, div ms per day, div 7 days
14712 calWeek = (th - yth) / 864e5 / 7 + 1;
14714 fillMonths.cn.push({
14722 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14724 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14727 if (this.todayHighlight &&
14728 prevMonth.getUTCFullYear() == today.getFullYear() &&
14729 prevMonth.getUTCMonth() == today.getMonth() &&
14730 prevMonth.getUTCDate() == today.getDate()) {
14731 clsName += ' today';
14734 if (currentDate && prevMonth.valueOf() === currentDate) {
14735 clsName += ' active';
14738 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14739 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14740 clsName += ' disabled';
14743 fillMonths.cn.push({
14745 cls: 'day ' + clsName,
14746 html: prevMonth.getDate()
14749 prevMonth.setDate(prevMonth.getDate()+1);
14752 var currentYear = this.date && this.date.getUTCFullYear();
14753 var currentMonth = this.date && this.date.getUTCMonth();
14755 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14757 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14758 v.removeClass('active');
14760 if(currentYear === year && k === currentMonth){
14761 v.addClass('active');
14764 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14765 v.addClass('disabled');
14771 year = parseInt(year/10, 10) * 10;
14773 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14775 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14778 for (var i = -1; i < 11; i++) {
14779 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14781 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14789 showMode: function(dir)
14792 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14794 Roo.each(this.picker().select('>div',true).elements, function(v){
14795 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14798 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14803 if(this.isInline) return;
14805 this.picker().removeClass(['bottom', 'top']);
14807 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14809 * place to the top of element!
14813 this.picker().addClass('top');
14814 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14819 this.picker().addClass('bottom');
14821 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14824 parseDate : function(value)
14826 if(!value || value instanceof Date){
14829 var v = Date.parseDate(value, this.format);
14830 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
14831 v = Date.parseDate(value, 'Y-m-d');
14833 if(!v && this.altFormats){
14834 if(!this.altFormatsArray){
14835 this.altFormatsArray = this.altFormats.split("|");
14837 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14838 v = Date.parseDate(value, this.altFormatsArray[i]);
14844 formatDate : function(date, fmt)
14846 return (!date || !(date instanceof Date)) ?
14847 date : date.dateFormat(fmt || this.format);
14850 onFocus : function()
14852 Roo.bootstrap.DateField.superclass.onFocus.call(this);
14856 onBlur : function()
14858 Roo.bootstrap.DateField.superclass.onBlur.call(this);
14860 var d = this.inputEl().getValue();
14869 this.picker().show();
14873 this.fireEvent('show', this, this.date);
14878 if(this.isInline) return;
14879 this.picker().hide();
14880 this.viewMode = this.startViewMode;
14883 this.fireEvent('hide', this, this.date);
14887 onMousedown: function(e)
14889 e.stopPropagation();
14890 e.preventDefault();
14895 Roo.bootstrap.DateField.superclass.keyup.call(this);
14899 setValue: function(v)
14902 // v can be a string or a date..
14905 var d = new Date(this.parseDate(v) ).clearTime();
14909 if(isNaN(d.getTime())){
14910 this.date = this.viewDate = '';
14911 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14915 v = this.formatDate(d);
14917 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14919 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14923 this.fireEvent('select', this, this.date);
14927 getValue: function()
14929 return this.formatDate(this.date);
14932 fireKey: function(e)
14934 if (!this.picker().isVisible()){
14935 if (e.keyCode == 27) // allow escape to hide and re-show picker
14940 var dateChanged = false,
14942 newDate, newViewDate;
14947 e.preventDefault();
14951 if (!this.keyboardNavigation) break;
14952 dir = e.keyCode == 37 ? -1 : 1;
14955 newDate = this.moveYear(this.date, dir);
14956 newViewDate = this.moveYear(this.viewDate, dir);
14957 } else if (e.shiftKey){
14958 newDate = this.moveMonth(this.date, dir);
14959 newViewDate = this.moveMonth(this.viewDate, dir);
14961 newDate = new Date(this.date);
14962 newDate.setUTCDate(this.date.getUTCDate() + dir);
14963 newViewDate = new Date(this.viewDate);
14964 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14966 if (this.dateWithinRange(newDate)){
14967 this.date = newDate;
14968 this.viewDate = newViewDate;
14969 this.setValue(this.formatDate(this.date));
14971 e.preventDefault();
14972 dateChanged = true;
14977 if (!this.keyboardNavigation) break;
14978 dir = e.keyCode == 38 ? -1 : 1;
14980 newDate = this.moveYear(this.date, dir);
14981 newViewDate = this.moveYear(this.viewDate, dir);
14982 } else if (e.shiftKey){
14983 newDate = this.moveMonth(this.date, dir);
14984 newViewDate = this.moveMonth(this.viewDate, dir);
14986 newDate = new Date(this.date);
14987 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14988 newViewDate = new Date(this.viewDate);
14989 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14991 if (this.dateWithinRange(newDate)){
14992 this.date = newDate;
14993 this.viewDate = newViewDate;
14994 this.setValue(this.formatDate(this.date));
14996 e.preventDefault();
14997 dateChanged = true;
15001 this.setValue(this.formatDate(this.date));
15003 e.preventDefault();
15006 this.setValue(this.formatDate(this.date));
15020 onClick: function(e)
15022 e.stopPropagation();
15023 e.preventDefault();
15025 var target = e.getTarget();
15027 if(target.nodeName.toLowerCase() === 'i'){
15028 target = Roo.get(target).dom.parentNode;
15031 var nodeName = target.nodeName;
15032 var className = target.className;
15033 var html = target.innerHTML;
15034 //Roo.log(nodeName);
15036 switch(nodeName.toLowerCase()) {
15038 switch(className) {
15044 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15045 switch(this.viewMode){
15047 this.viewDate = this.moveMonth(this.viewDate, dir);
15051 this.viewDate = this.moveYear(this.viewDate, dir);
15057 var date = new Date();
15058 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15060 this.setValue(this.formatDate(this.date));
15067 if (className.indexOf('disabled') < 0) {
15068 this.viewDate.setUTCDate(1);
15069 if (className.indexOf('month') > -1) {
15070 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15072 var year = parseInt(html, 10) || 0;
15073 this.viewDate.setUTCFullYear(year);
15082 //Roo.log(className);
15083 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15084 var day = parseInt(html, 10) || 1;
15085 var year = this.viewDate.getUTCFullYear(),
15086 month = this.viewDate.getUTCMonth();
15088 if (className.indexOf('old') > -1) {
15095 } else if (className.indexOf('new') > -1) {
15103 //Roo.log([year,month,day]);
15104 this.date = this.UTCDate(year, month, day,0,0,0,0);
15105 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15107 //Roo.log(this.formatDate(this.date));
15108 this.setValue(this.formatDate(this.date));
15115 setStartDate: function(startDate)
15117 this.startDate = startDate || -Infinity;
15118 if (this.startDate !== -Infinity) {
15119 this.startDate = this.parseDate(this.startDate);
15122 this.updateNavArrows();
15125 setEndDate: function(endDate)
15127 this.endDate = endDate || Infinity;
15128 if (this.endDate !== Infinity) {
15129 this.endDate = this.parseDate(this.endDate);
15132 this.updateNavArrows();
15135 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15137 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15138 if (typeof(this.daysOfWeekDisabled) !== 'object') {
15139 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15141 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15142 return parseInt(d, 10);
15145 this.updateNavArrows();
15148 updateNavArrows: function()
15150 var d = new Date(this.viewDate),
15151 year = d.getUTCFullYear(),
15152 month = d.getUTCMonth();
15154 Roo.each(this.picker().select('.prev', true).elements, function(v){
15156 switch (this.viewMode) {
15159 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15165 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15172 Roo.each(this.picker().select('.next', true).elements, function(v){
15174 switch (this.viewMode) {
15177 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15183 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15191 moveMonth: function(date, dir)
15193 if (!dir) return date;
15194 var new_date = new Date(date.valueOf()),
15195 day = new_date.getUTCDate(),
15196 month = new_date.getUTCMonth(),
15197 mag = Math.abs(dir),
15199 dir = dir > 0 ? 1 : -1;
15202 // If going back one month, make sure month is not current month
15203 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15205 return new_date.getUTCMonth() == month;
15207 // If going forward one month, make sure month is as expected
15208 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15210 return new_date.getUTCMonth() != new_month;
15212 new_month = month + dir;
15213 new_date.setUTCMonth(new_month);
15214 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15215 if (new_month < 0 || new_month > 11)
15216 new_month = (new_month + 12) % 12;
15218 // For magnitudes >1, move one month at a time...
15219 for (var i=0; i<mag; i++)
15220 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15221 new_date = this.moveMonth(new_date, dir);
15222 // ...then reset the day, keeping it in the new month
15223 new_month = new_date.getUTCMonth();
15224 new_date.setUTCDate(day);
15226 return new_month != new_date.getUTCMonth();
15229 // Common date-resetting loop -- if date is beyond end of month, make it
15232 new_date.setUTCDate(--day);
15233 new_date.setUTCMonth(new_month);
15238 moveYear: function(date, dir)
15240 return this.moveMonth(date, dir*12);
15243 dateWithinRange: function(date)
15245 return date >= this.startDate && date <= this.endDate;
15251 this.picker().remove();
15256 Roo.apply(Roo.bootstrap.DateField, {
15267 html: '<i class="fa fa-arrow-left"/>'
15277 html: '<i class="fa fa-arrow-right"/>'
15319 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15320 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15321 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15322 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15323 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15336 navFnc: 'FullYear',
15341 navFnc: 'FullYear',
15346 Roo.apply(Roo.bootstrap.DateField, {
15350 cls: 'datepicker dropdown-menu',
15354 cls: 'datepicker-days',
15358 cls: 'table-condensed',
15360 Roo.bootstrap.DateField.head,
15364 Roo.bootstrap.DateField.footer
15371 cls: 'datepicker-months',
15375 cls: 'table-condensed',
15377 Roo.bootstrap.DateField.head,
15378 Roo.bootstrap.DateField.content,
15379 Roo.bootstrap.DateField.footer
15386 cls: 'datepicker-years',
15390 cls: 'table-condensed',
15392 Roo.bootstrap.DateField.head,
15393 Roo.bootstrap.DateField.content,
15394 Roo.bootstrap.DateField.footer
15413 * @class Roo.bootstrap.TimeField
15414 * @extends Roo.bootstrap.Input
15415 * Bootstrap DateField class
15419 * Create a new TimeField
15420 * @param {Object} config The config object
15423 Roo.bootstrap.TimeField = function(config){
15424 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15428 * Fires when this field show.
15429 * @param {Roo.bootstrap.DateField} this
15430 * @param {Mixed} date The date value
15435 * Fires when this field hide.
15436 * @param {Roo.bootstrap.DateField} this
15437 * @param {Mixed} date The date value
15442 * Fires when select a date.
15443 * @param {Roo.bootstrap.DateField} this
15444 * @param {Mixed} date The date value
15450 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
15453 * @cfg {String} format
15454 * The default time format string which can be overriden for localization support. The format must be
15455 * valid according to {@link Date#parseDate} (defaults to 'H:i').
15459 onRender: function(ct, position)
15462 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15464 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15466 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15468 this.pop = this.picker().select('>.datepicker-time',true).first();
15469 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
15471 this.picker().on('mousedown', this.onMousedown, this);
15472 this.picker().on('click', this.onClick, this);
15474 this.picker().addClass('datepicker-dropdown');
15479 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15480 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15481 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15482 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15483 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15484 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15488 fireKey: function(e){
15489 if (!this.picker().isVisible()){
15490 if (e.keyCode == 27) // allow escape to hide and re-show picker
15495 e.preventDefault();
15503 this.onTogglePeriod();
15506 this.onIncrementMinutes();
15509 this.onDecrementMinutes();
15518 onClick: function(e) {
15519 e.stopPropagation();
15520 e.preventDefault();
15523 picker : function()
15525 return this.el.select('.datepicker', true).first();
15528 fillTime: function()
15530 var time = this.pop.select('tbody', true).first();
15532 time.dom.innerHTML = '';
15547 cls: 'hours-up glyphicon glyphicon-chevron-up'
15567 cls: 'minutes-up glyphicon glyphicon-chevron-up'
15588 cls: 'timepicker-hour',
15603 cls: 'timepicker-minute',
15618 cls: 'btn btn-primary period',
15640 cls: 'hours-down glyphicon glyphicon-chevron-down'
15660 cls: 'minutes-down glyphicon glyphicon-chevron-down'
15678 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15685 var hours = this.time.getHours();
15686 var minutes = this.time.getMinutes();
15699 hours = hours - 12;
15703 hours = '0' + hours;
15707 minutes = '0' + minutes;
15710 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15711 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15712 this.pop.select('button', true).first().dom.innerHTML = period;
15718 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15720 var cls = ['bottom'];
15722 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15729 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15734 this.picker().addClass(cls.join('-'));
15738 Roo.each(cls, function(c){
15740 _this.picker().setTop(_this.inputEl().getHeight());
15744 _this.picker().setTop(0 - _this.picker().getHeight());
15749 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15753 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15760 onFocus : function()
15762 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15766 onBlur : function()
15768 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15774 this.picker().show();
15779 this.fireEvent('show', this, this.date);
15784 this.picker().hide();
15787 this.fireEvent('hide', this, this.date);
15790 setTime : function()
15793 this.setValue(this.time.format(this.format));
15795 this.fireEvent('select', this, this.date);
15800 onMousedown: function(e){
15801 e.stopPropagation();
15802 e.preventDefault();
15805 onIncrementHours: function()
15807 Roo.log('onIncrementHours');
15808 this.time = this.time.add(Date.HOUR, 1);
15813 onDecrementHours: function()
15815 Roo.log('onDecrementHours');
15816 this.time = this.time.add(Date.HOUR, -1);
15820 onIncrementMinutes: function()
15822 Roo.log('onIncrementMinutes');
15823 this.time = this.time.add(Date.MINUTE, 1);
15827 onDecrementMinutes: function()
15829 Roo.log('onDecrementMinutes');
15830 this.time = this.time.add(Date.MINUTE, -1);
15834 onTogglePeriod: function()
15836 Roo.log('onTogglePeriod');
15837 this.time = this.time.add(Date.HOUR, 12);
15844 Roo.apply(Roo.bootstrap.TimeField, {
15874 cls: 'btn btn-info ok',
15886 Roo.apply(Roo.bootstrap.TimeField, {
15890 cls: 'datepicker dropdown-menu',
15894 cls: 'datepicker-time',
15898 cls: 'table-condensed',
15900 Roo.bootstrap.TimeField.content,
15901 Roo.bootstrap.TimeField.footer
15920 * @class Roo.bootstrap.CheckBox
15921 * @extends Roo.bootstrap.Input
15922 * Bootstrap CheckBox class
15924 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15925 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15926 * @cfg {String} boxLabel The text that appears beside the checkbox
15927 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15928 * @cfg {Boolean} checked initnal the element
15932 * Create a new CheckBox
15933 * @param {Object} config The config object
15936 Roo.bootstrap.CheckBox = function(config){
15937 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15942 * Fires when the element is checked or unchecked.
15943 * @param {Roo.bootstrap.CheckBox} this This input
15944 * @param {Boolean} checked The new checked value
15950 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
15952 inputType: 'checkbox',
15959 getAutoCreate : function()
15961 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15967 cfg.cls = 'form-group checkbox' //input-group
15975 type : this.inputType,
15976 value : (!this.checked) ? this.valueOff : this.inputValue,
15977 cls : 'roo-checkbox', //'form-box',
15978 placeholder : this.placeholder || ''
15982 if (this.weight) { // Validity check?
15983 cfg.cls += " checkbox-" + this.weight;
15986 if (this.disabled) {
15987 input.disabled=true;
15991 input.checked = this.checked;
15995 input.name = this.name;
15999 input.cls += ' input-' + this.size;
16003 ['xs','sm','md','lg'].map(function(size){
16004 if (settings[size]) {
16005 cfg.cls += ' col-' + size + '-' + settings[size];
16011 var inputblock = input;
16016 if (this.before || this.after) {
16019 cls : 'input-group',
16023 inputblock.cn.push({
16025 cls : 'input-group-addon',
16029 inputblock.cn.push(input);
16031 inputblock.cn.push({
16033 cls : 'input-group-addon',
16040 if (align ==='left' && this.fieldLabel.length) {
16041 Roo.log("left and has label");
16047 cls : 'control-label col-md-' + this.labelWidth,
16048 html : this.fieldLabel
16052 cls : "col-md-" + (12 - this.labelWidth),
16059 } else if ( this.fieldLabel.length) {
16064 tag: this.boxLabel ? 'span' : 'label',
16066 cls: 'control-label box-input-label',
16067 //cls : 'input-group-addon',
16068 html : this.fieldLabel
16078 Roo.log(" no label && no align");
16079 cfg.cn = [ inputblock ] ;
16088 html: this.boxLabel
16100 * return the real input element.
16102 inputEl: function ()
16104 return this.el.select('input.roo-checkbox',true).first();
16109 return this.el.select('label.control-label',true).first();
16112 initEvents : function()
16114 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16116 this.inputEl().on('click', this.onClick, this);
16120 onClick : function()
16122 this.setChecked(!this.checked);
16125 setChecked : function(state,suppressEvent)
16127 this.checked = state;
16129 this.inputEl().dom.checked = state;
16131 if(suppressEvent !== true){
16132 this.fireEvent('check', this, state);
16135 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16139 setValue : function(v,suppressEvent)
16141 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16155 * @class Roo.bootstrap.Radio
16156 * @extends Roo.bootstrap.CheckBox
16157 * Bootstrap Radio class
16160 * Create a new Radio
16161 * @param {Object} config The config object
16164 Roo.bootstrap.Radio = function(config){
16165 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16169 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
16171 inputType: 'radio',
16175 getAutoCreate : function()
16177 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16183 cfg.cls = 'form-group radio' //input-group
16188 type : this.inputType,
16189 value : (!this.checked) ? this.valueOff : this.inputValue,
16191 placeholder : this.placeholder || ''
16194 if (this.weight) { // Validity check?
16195 cfg.cls += " radio-" + this.weight;
16197 if (this.disabled) {
16198 input.disabled=true;
16202 input.checked = this.checked;
16206 input.name = this.name;
16210 input.cls += ' input-' + this.size;
16214 ['xs','sm','md','lg'].map(function(size){
16215 if (settings[size]) {
16216 cfg.cls += ' col-' + size + '-' + settings[size];
16220 var inputblock = input;
16222 if (this.before || this.after) {
16225 cls : 'input-group',
16229 inputblock.cn.push({
16231 cls : 'input-group-addon',
16235 inputblock.cn.push(input);
16237 inputblock.cn.push({
16239 cls : 'input-group-addon',
16246 if (align ==='left' && this.fieldLabel.length) {
16247 Roo.log("left and has label");
16253 cls : 'control-label col-md-' + this.labelWidth,
16254 html : this.fieldLabel
16258 cls : "col-md-" + (12 - this.labelWidth),
16265 } else if ( this.fieldLabel.length) {
16272 cls: 'control-label box-input-label',
16273 //cls : 'input-group-addon',
16274 html : this.fieldLabel
16284 Roo.log(" no label && no align");
16299 html: this.boxLabel
16306 inputEl: function ()
16308 return this.el.select('input.roo-radio',true).first();
16310 onClick : function()
16312 this.setChecked(true);
16315 setChecked : function(state,suppressEvent)
16318 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16319 v.dom.checked = false;
16323 this.checked = state;
16324 this.inputEl().dom.checked = state;
16326 if(suppressEvent !== true){
16327 this.fireEvent('check', this, state);
16330 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16334 getGroupValue : function()
16337 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16338 if(v.dom.checked == true){
16339 value = v.dom.value;
16347 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
16348 * @return {Mixed} value The field value
16350 getValue : function(){
16351 return this.getGroupValue();
16357 //<script type="text/javascript">
16360 * Based Ext JS Library 1.1.1
16361 * Copyright(c) 2006-2007, Ext JS, LLC.
16367 * @class Roo.HtmlEditorCore
16368 * @extends Roo.Component
16369 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16371 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16374 Roo.HtmlEditorCore = function(config){
16377 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16382 * @event initialize
16383 * Fires when the editor is fully initialized (including the iframe)
16384 * @param {Roo.HtmlEditorCore} this
16389 * Fires when the editor is first receives the focus. Any insertion must wait
16390 * until after this event.
16391 * @param {Roo.HtmlEditorCore} this
16395 * @event beforesync
16396 * Fires before the textarea is updated with content from the editor iframe. Return false
16397 * to cancel the sync.
16398 * @param {Roo.HtmlEditorCore} this
16399 * @param {String} html
16403 * @event beforepush
16404 * Fires before the iframe editor is updated with content from the textarea. Return false
16405 * to cancel the push.
16406 * @param {Roo.HtmlEditorCore} this
16407 * @param {String} html
16412 * Fires when the textarea is updated with content from the editor iframe.
16413 * @param {Roo.HtmlEditorCore} this
16414 * @param {String} html
16419 * Fires when the iframe editor is updated with content from the textarea.
16420 * @param {Roo.HtmlEditorCore} this
16421 * @param {String} html
16426 * @event editorevent
16427 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16428 * @param {Roo.HtmlEditorCore} this
16433 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16435 // defaults : white / black...
16436 this.applyBlacklists();
16443 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
16447 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
16453 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
16458 * @cfg {Number} height (in pixels)
16462 * @cfg {Number} width (in pixels)
16467 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16470 stylesheets: false,
16475 // private properties
16476 validationEvent : false,
16478 initialized : false,
16480 sourceEditMode : false,
16481 onFocus : Roo.emptyFn,
16483 hideMode:'offsets',
16487 // blacklist + whitelisted elements..
16494 * Protected method that will not generally be called directly. It
16495 * is called when the editor initializes the iframe with HTML contents. Override this method if you
16496 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16498 getDocMarkup : function(){
16501 Roo.log(this.stylesheets);
16503 // inherit styels from page...??
16504 if (this.stylesheets === false) {
16506 Roo.get(document.head).select('style').each(function(node) {
16507 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16510 Roo.get(document.head).select('link').each(function(node) {
16511 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16514 } else if (!this.stylesheets.length) {
16516 st = '<style type="text/css">' +
16517 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16520 Roo.each(this.stylesheets, function(s) {
16521 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16526 st += '<style type="text/css">' +
16527 'IMG { cursor: pointer } ' +
16531 return '<html><head>' + st +
16532 //<style type="text/css">' +
16533 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16535 ' </head><body class="roo-htmleditor-body"></body></html>';
16539 onRender : function(ct, position)
16542 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16543 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16546 this.el.dom.style.border = '0 none';
16547 this.el.dom.setAttribute('tabIndex', -1);
16548 this.el.addClass('x-hidden hide');
16552 if(Roo.isIE){ // fix IE 1px bogus margin
16553 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16557 this.frameId = Roo.id();
16561 var iframe = this.owner.wrap.createChild({
16563 cls: 'form-control', // bootstrap..
16565 name: this.frameId,
16566 frameBorder : 'no',
16567 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
16572 this.iframe = iframe.dom;
16574 this.assignDocWin();
16576 this.doc.designMode = 'on';
16579 this.doc.write(this.getDocMarkup());
16583 var task = { // must defer to wait for browser to be ready
16585 //console.log("run task?" + this.doc.readyState);
16586 this.assignDocWin();
16587 if(this.doc.body || this.doc.readyState == 'complete'){
16589 this.doc.designMode="on";
16593 Roo.TaskMgr.stop(task);
16594 this.initEditor.defer(10, this);
16601 Roo.TaskMgr.start(task);
16608 onResize : function(w, h)
16610 Roo.log('resize: ' +w + ',' + h );
16611 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16615 if(typeof w == 'number'){
16617 this.iframe.style.width = w + 'px';
16619 if(typeof h == 'number'){
16621 this.iframe.style.height = h + 'px';
16623 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16630 * Toggles the editor between standard and source edit mode.
16631 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16633 toggleSourceEdit : function(sourceEditMode){
16635 this.sourceEditMode = sourceEditMode === true;
16637 if(this.sourceEditMode){
16639 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
16642 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16643 //this.iframe.className = '';
16646 //this.setSize(this.owner.wrap.getSize());
16647 //this.fireEvent('editmodechange', this, this.sourceEditMode);
16654 * Protected method that will not generally be called directly. If you need/want
16655 * custom HTML cleanup, this is the method you should override.
16656 * @param {String} html The HTML to be cleaned
16657 * return {String} The cleaned HTML
16659 cleanHtml : function(html){
16660 html = String(html);
16661 if(html.length > 5){
16662 if(Roo.isSafari){ // strip safari nonsense
16663 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16666 if(html == ' '){
16673 * HTML Editor -> Textarea
16674 * Protected method that will not generally be called directly. Syncs the contents
16675 * of the editor iframe with the textarea.
16677 syncValue : function(){
16678 if(this.initialized){
16679 var bd = (this.doc.body || this.doc.documentElement);
16680 //this.cleanUpPaste(); -- this is done else where and causes havoc..
16681 var html = bd.innerHTML;
16683 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16684 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16686 html = '<div style="'+m[0]+'">' + html + '</div>';
16689 html = this.cleanHtml(html);
16690 // fix up the special chars.. normaly like back quotes in word...
16691 // however we do not want to do this with chinese..
16692 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16693 var cc = b.charCodeAt();
16695 (cc >= 0x4E00 && cc < 0xA000 ) ||
16696 (cc >= 0x3400 && cc < 0x4E00 ) ||
16697 (cc >= 0xf900 && cc < 0xfb00 )
16703 if(this.owner.fireEvent('beforesync', this, html) !== false){
16704 this.el.dom.value = html;
16705 this.owner.fireEvent('sync', this, html);
16711 * Protected method that will not generally be called directly. Pushes the value of the textarea
16712 * into the iframe editor.
16714 pushValue : function(){
16715 if(this.initialized){
16716 var v = this.el.dom.value.trim();
16718 // if(v.length < 1){
16722 if(this.owner.fireEvent('beforepush', this, v) !== false){
16723 var d = (this.doc.body || this.doc.documentElement);
16725 this.cleanUpPaste();
16726 this.el.dom.value = d.innerHTML;
16727 this.owner.fireEvent('push', this, v);
16733 deferFocus : function(){
16734 this.focus.defer(10, this);
16738 focus : function(){
16739 if(this.win && !this.sourceEditMode){
16746 assignDocWin: function()
16748 var iframe = this.iframe;
16751 this.doc = iframe.contentWindow.document;
16752 this.win = iframe.contentWindow;
16754 // if (!Roo.get(this.frameId)) {
16757 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16758 // this.win = Roo.get(this.frameId).dom.contentWindow;
16760 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16764 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16765 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16770 initEditor : function(){
16771 //console.log("INIT EDITOR");
16772 this.assignDocWin();
16776 this.doc.designMode="on";
16778 this.doc.write(this.getDocMarkup());
16781 var dbody = (this.doc.body || this.doc.documentElement);
16782 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16783 // this copies styles from the containing element into thsi one..
16784 // not sure why we need all of this..
16785 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16787 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16788 //ss['background-attachment'] = 'fixed'; // w3c
16789 dbody.bgProperties = 'fixed'; // ie
16790 //Roo.DomHelper.applyStyles(dbody, ss);
16791 Roo.EventManager.on(this.doc, {
16792 //'mousedown': this.onEditorEvent,
16793 'mouseup': this.onEditorEvent,
16794 'dblclick': this.onEditorEvent,
16795 'click': this.onEditorEvent,
16796 'keyup': this.onEditorEvent,
16801 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16803 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16804 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16806 this.initialized = true;
16808 this.owner.fireEvent('initialize', this);
16813 onDestroy : function(){
16819 //for (var i =0; i < this.toolbars.length;i++) {
16820 // // fixme - ask toolbars for heights?
16821 // this.toolbars[i].onDestroy();
16824 //this.wrap.dom.innerHTML = '';
16825 //this.wrap.remove();
16830 onFirstFocus : function(){
16832 this.assignDocWin();
16835 this.activated = true;
16838 if(Roo.isGecko){ // prevent silly gecko errors
16840 var s = this.win.getSelection();
16841 if(!s.focusNode || s.focusNode.nodeType != 3){
16842 var r = s.getRangeAt(0);
16843 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16848 this.execCmd('useCSS', true);
16849 this.execCmd('styleWithCSS', false);
16852 this.owner.fireEvent('activate', this);
16856 adjustFont: function(btn){
16857 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16858 //if(Roo.isSafari){ // safari
16861 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16862 if(Roo.isSafari){ // safari
16863 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16864 v = (v < 10) ? 10 : v;
16865 v = (v > 48) ? 48 : v;
16866 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16871 v = Math.max(1, v+adjust);
16873 this.execCmd('FontSize', v );
16876 onEditorEvent : function(e){
16877 this.owner.fireEvent('editorevent', this, e);
16878 // this.updateToolbar();
16879 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16882 insertTag : function(tg)
16884 // could be a bit smarter... -> wrap the current selected tRoo..
16885 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16887 range = this.createRange(this.getSelection());
16888 var wrappingNode = this.doc.createElement(tg.toLowerCase());
16889 wrappingNode.appendChild(range.extractContents());
16890 range.insertNode(wrappingNode);
16897 this.execCmd("formatblock", tg);
16901 insertText : function(txt)
16905 var range = this.createRange();
16906 range.deleteContents();
16907 //alert(Sender.getAttribute('label'));
16909 range.insertNode(this.doc.createTextNode(txt));
16915 * Executes a Midas editor command on the editor document and performs necessary focus and
16916 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16917 * @param {String} cmd The Midas command
16918 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16920 relayCmd : function(cmd, value){
16922 this.execCmd(cmd, value);
16923 this.owner.fireEvent('editorevent', this);
16924 //this.updateToolbar();
16925 this.owner.deferFocus();
16929 * Executes a Midas editor command directly on the editor document.
16930 * For visual commands, you should use {@link #relayCmd} instead.
16931 * <b>This should only be called after the editor is initialized.</b>
16932 * @param {String} cmd The Midas command
16933 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16935 execCmd : function(cmd, value){
16936 this.doc.execCommand(cmd, false, value === undefined ? null : value);
16943 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16945 * @param {String} text | dom node..
16947 insertAtCursor : function(text)
16952 if(!this.activated){
16958 var r = this.doc.selection.createRange();
16969 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16973 // from jquery ui (MIT licenced)
16975 var win = this.win;
16977 if (win.getSelection && win.getSelection().getRangeAt) {
16978 range = win.getSelection().getRangeAt(0);
16979 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16980 range.insertNode(node);
16981 } else if (win.document.selection && win.document.selection.createRange) {
16982 // no firefox support
16983 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16984 win.document.selection.createRange().pasteHTML(txt);
16986 // no firefox support
16987 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16988 this.execCmd('InsertHTML', txt);
16997 mozKeyPress : function(e){
16999 var c = e.getCharCode(), cmd;
17002 c = String.fromCharCode(c).toLowerCase();
17016 this.cleanUpPaste.defer(100, this);
17024 e.preventDefault();
17032 fixKeys : function(){ // load time branching for fastest keydown performance
17034 return function(e){
17035 var k = e.getKey(), r;
17038 r = this.doc.selection.createRange();
17041 r.pasteHTML('    ');
17048 r = this.doc.selection.createRange();
17050 var target = r.parentElement();
17051 if(!target || target.tagName.toLowerCase() != 'li'){
17053 r.pasteHTML('<br />');
17059 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17060 this.cleanUpPaste.defer(100, this);
17066 }else if(Roo.isOpera){
17067 return function(e){
17068 var k = e.getKey();
17072 this.execCmd('InsertHTML','    ');
17075 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17076 this.cleanUpPaste.defer(100, this);
17081 }else if(Roo.isSafari){
17082 return function(e){
17083 var k = e.getKey();
17087 this.execCmd('InsertText','\t');
17091 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17092 this.cleanUpPaste.defer(100, this);
17100 getAllAncestors: function()
17102 var p = this.getSelectedNode();
17105 a.push(p); // push blank onto stack..
17106 p = this.getParentElement();
17110 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17114 a.push(this.doc.body);
17118 lastSelNode : false,
17121 getSelection : function()
17123 this.assignDocWin();
17124 return Roo.isIE ? this.doc.selection : this.win.getSelection();
17127 getSelectedNode: function()
17129 // this may only work on Gecko!!!
17131 // should we cache this!!!!
17136 var range = this.createRange(this.getSelection()).cloneRange();
17139 var parent = range.parentElement();
17141 var testRange = range.duplicate();
17142 testRange.moveToElementText(parent);
17143 if (testRange.inRange(range)) {
17146 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17149 parent = parent.parentElement;
17154 // is ancestor a text element.
17155 var ac = range.commonAncestorContainer;
17156 if (ac.nodeType == 3) {
17157 ac = ac.parentNode;
17160 var ar = ac.childNodes;
17163 var other_nodes = [];
17164 var has_other_nodes = false;
17165 for (var i=0;i<ar.length;i++) {
17166 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
17169 // fullly contained node.
17171 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17176 // probably selected..
17177 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17178 other_nodes.push(ar[i]);
17182 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
17187 has_other_nodes = true;
17189 if (!nodes.length && other_nodes.length) {
17190 nodes= other_nodes;
17192 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17198 createRange: function(sel)
17200 // this has strange effects when using with
17201 // top toolbar - not sure if it's a great idea.
17202 //this.editor.contentWindow.focus();
17203 if (typeof sel != "undefined") {
17205 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17207 return this.doc.createRange();
17210 return this.doc.createRange();
17213 getParentElement: function()
17216 this.assignDocWin();
17217 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17219 var range = this.createRange(sel);
17222 var p = range.commonAncestorContainer;
17223 while (p.nodeType == 3) { // text node
17234 * Range intersection.. the hard stuff...
17238 * [ -- selected range --- ]
17242 * if end is before start or hits it. fail.
17243 * if start is after end or hits it fail.
17245 * if either hits (but other is outside. - then it's not
17251 // @see http://www.thismuchiknow.co.uk/?p=64.
17252 rangeIntersectsNode : function(range, node)
17254 var nodeRange = node.ownerDocument.createRange();
17256 nodeRange.selectNode(node);
17258 nodeRange.selectNodeContents(node);
17261 var rangeStartRange = range.cloneRange();
17262 rangeStartRange.collapse(true);
17264 var rangeEndRange = range.cloneRange();
17265 rangeEndRange.collapse(false);
17267 var nodeStartRange = nodeRange.cloneRange();
17268 nodeStartRange.collapse(true);
17270 var nodeEndRange = nodeRange.cloneRange();
17271 nodeEndRange.collapse(false);
17273 return rangeStartRange.compareBoundaryPoints(
17274 Range.START_TO_START, nodeEndRange) == -1 &&
17275 rangeEndRange.compareBoundaryPoints(
17276 Range.START_TO_START, nodeStartRange) == 1;
17280 rangeCompareNode : function(range, node)
17282 var nodeRange = node.ownerDocument.createRange();
17284 nodeRange.selectNode(node);
17286 nodeRange.selectNodeContents(node);
17290 range.collapse(true);
17292 nodeRange.collapse(true);
17294 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17295 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
17297 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17299 var nodeIsBefore = ss == 1;
17300 var nodeIsAfter = ee == -1;
17302 if (nodeIsBefore && nodeIsAfter)
17304 if (!nodeIsBefore && nodeIsAfter)
17305 return 1; //right trailed.
17307 if (nodeIsBefore && !nodeIsAfter)
17308 return 2; // left trailed.
17313 // private? - in a new class?
17314 cleanUpPaste : function()
17316 // cleans up the whole document..
17317 Roo.log('cleanuppaste');
17319 this.cleanUpChildren(this.doc.body);
17320 var clean = this.cleanWordChars(this.doc.body.innerHTML);
17321 if (clean != this.doc.body.innerHTML) {
17322 this.doc.body.innerHTML = clean;
17327 cleanWordChars : function(input) {// change the chars to hex code
17328 var he = Roo.HtmlEditorCore;
17330 var output = input;
17331 Roo.each(he.swapCodes, function(sw) {
17332 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17334 output = output.replace(swapper, sw[1]);
17341 cleanUpChildren : function (n)
17343 if (!n.childNodes.length) {
17346 for (var i = n.childNodes.length-1; i > -1 ; i--) {
17347 this.cleanUpChild(n.childNodes[i]);
17354 cleanUpChild : function (node)
17357 //console.log(node);
17358 if (node.nodeName == "#text") {
17359 // clean up silly Windows -- stuff?
17362 if (node.nodeName == "#comment") {
17363 node.parentNode.removeChild(node);
17364 // clean up silly Windows -- stuff?
17367 var lcname = node.tagName.toLowerCase();
17368 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17369 // whitelist of tags..
17371 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17373 node.parentNode.removeChild(node);
17378 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17380 // remove <a name=....> as rendering on yahoo mailer is borked with this.
17381 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17383 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17384 // remove_keep_children = true;
17387 if (remove_keep_children) {
17388 this.cleanUpChildren(node);
17389 // inserts everything just before this node...
17390 while (node.childNodes.length) {
17391 var cn = node.childNodes[0];
17392 node.removeChild(cn);
17393 node.parentNode.insertBefore(cn, node);
17395 node.parentNode.removeChild(node);
17399 if (!node.attributes || !node.attributes.length) {
17400 this.cleanUpChildren(node);
17404 function cleanAttr(n,v)
17407 if (v.match(/^\./) || v.match(/^\//)) {
17410 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17413 if (v.match(/^#/)) {
17416 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17417 node.removeAttribute(n);
17421 var cwhite = this.cwhite;
17422 var cblack = this.cblack;
17424 function cleanStyle(n,v)
17426 if (v.match(/expression/)) { //XSS?? should we even bother..
17427 node.removeAttribute(n);
17431 var parts = v.split(/;/);
17434 Roo.each(parts, function(p) {
17435 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17439 var l = p.split(':').shift().replace(/\s+/g,'');
17440 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17442 if ( cwhite.length && cblack.indexOf(l) > -1) {
17443 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17444 //node.removeAttribute(n);
17448 // only allow 'c whitelisted system attributes'
17449 if ( cwhite.length && cwhite.indexOf(l) < 0) {
17450 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17451 //node.removeAttribute(n);
17461 if (clean.length) {
17462 node.setAttribute(n, clean.join(';'));
17464 node.removeAttribute(n);
17470 for (var i = node.attributes.length-1; i > -1 ; i--) {
17471 var a = node.attributes[i];
17474 if (a.name.toLowerCase().substr(0,2)=='on') {
17475 node.removeAttribute(a.name);
17478 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17479 node.removeAttribute(a.name);
17482 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17483 cleanAttr(a.name,a.value); // fixme..
17486 if (a.name == 'style') {
17487 cleanStyle(a.name,a.value);
17490 /// clean up MS crap..
17491 // tecnically this should be a list of valid class'es..
17494 if (a.name == 'class') {
17495 if (a.value.match(/^Mso/)) {
17496 node.className = '';
17499 if (a.value.match(/body/)) {
17500 node.className = '';
17511 this.cleanUpChildren(node);
17516 * Clean up MS wordisms...
17518 cleanWord : function(node)
17521 var cleanWordChildren = function()
17523 if (!node.childNodes.length) {
17526 for (var i = node.childNodes.length-1; i > -1 ; i--) {
17527 _t.cleanWord(node.childNodes[i]);
17533 this.cleanWord(this.doc.body);
17536 if (node.nodeName == "#text") {
17537 // clean up silly Windows -- stuff?
17540 if (node.nodeName == "#comment") {
17541 node.parentNode.removeChild(node);
17542 // clean up silly Windows -- stuff?
17546 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17547 node.parentNode.removeChild(node);
17551 // remove - but keep children..
17552 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17553 while (node.childNodes.length) {
17554 var cn = node.childNodes[0];
17555 node.removeChild(cn);
17556 node.parentNode.insertBefore(cn, node);
17558 node.parentNode.removeChild(node);
17559 cleanWordChildren();
17563 if (node.className.length) {
17565 var cn = node.className.split(/\W+/);
17567 Roo.each(cn, function(cls) {
17568 if (cls.match(/Mso[a-zA-Z]+/)) {
17573 node.className = cna.length ? cna.join(' ') : '';
17575 node.removeAttribute("class");
17579 if (node.hasAttribute("lang")) {
17580 node.removeAttribute("lang");
17583 if (node.hasAttribute("style")) {
17585 var styles = node.getAttribute("style").split(";");
17587 Roo.each(styles, function(s) {
17588 if (!s.match(/:/)) {
17591 var kv = s.split(":");
17592 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17595 // what ever is left... we allow.
17598 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17599 if (!nstyle.length) {
17600 node.removeAttribute('style');
17604 cleanWordChildren();
17608 domToHTML : function(currentElement, depth, nopadtext) {
17610 depth = depth || 0;
17611 nopadtext = nopadtext || false;
17613 if (!currentElement) {
17614 return this.domToHTML(this.doc.body);
17617 //Roo.log(currentElement);
17619 var allText = false;
17620 var nodeName = currentElement.nodeName;
17621 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17623 if (nodeName == '#text') {
17624 return currentElement.nodeValue;
17629 if (nodeName != 'BODY') {
17632 // Prints the node tagName, such as <A>, <IMG>, etc
17635 for(i = 0; i < currentElement.attributes.length;i++) {
17637 var aname = currentElement.attributes.item(i).name;
17638 if (!currentElement.attributes.item(i).value.length) {
17641 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17644 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17653 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17656 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17661 // Traverse the tree
17663 var currentElementChild = currentElement.childNodes.item(i);
17664 var allText = true;
17665 var innerHTML = '';
17667 while (currentElementChild) {
17668 // Formatting code (indent the tree so it looks nice on the screen)
17669 var nopad = nopadtext;
17670 if (lastnode == 'SPAN') {
17674 if (currentElementChild.nodeName == '#text') {
17675 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17676 if (!nopad && toadd.length > 80) {
17677 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
17679 innerHTML += toadd;
17682 currentElementChild = currentElement.childNodes.item(i);
17688 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
17690 // Recursively traverse the tree structure of the child node
17691 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
17692 lastnode = currentElementChild.nodeName;
17694 currentElementChild=currentElement.childNodes.item(i);
17700 // The remaining code is mostly for formatting the tree
17701 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
17706 ret+= "</"+tagName+">";
17712 applyBlacklists : function()
17714 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
17715 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
17719 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17720 if (b.indexOf(tag) > -1) {
17723 this.white.push(tag);
17727 Roo.each(w, function(tag) {
17728 if (b.indexOf(tag) > -1) {
17731 if (this.white.indexOf(tag) > -1) {
17734 this.white.push(tag);
17739 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17740 if (w.indexOf(tag) > -1) {
17743 this.black.push(tag);
17747 Roo.each(b, function(tag) {
17748 if (w.indexOf(tag) > -1) {
17751 if (this.black.indexOf(tag) > -1) {
17754 this.black.push(tag);
17759 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
17760 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
17764 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17765 if (b.indexOf(tag) > -1) {
17768 this.cwhite.push(tag);
17772 Roo.each(w, function(tag) {
17773 if (b.indexOf(tag) > -1) {
17776 if (this.cwhite.indexOf(tag) > -1) {
17779 this.cwhite.push(tag);
17784 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17785 if (w.indexOf(tag) > -1) {
17788 this.cblack.push(tag);
17792 Roo.each(b, function(tag) {
17793 if (w.indexOf(tag) > -1) {
17796 if (this.cblack.indexOf(tag) > -1) {
17799 this.cblack.push(tag);
17804 // hide stuff that is not compatible
17818 * @event specialkey
17822 * @cfg {String} fieldClass @hide
17825 * @cfg {String} focusClass @hide
17828 * @cfg {String} autoCreate @hide
17831 * @cfg {String} inputType @hide
17834 * @cfg {String} invalidClass @hide
17837 * @cfg {String} invalidText @hide
17840 * @cfg {String} msgFx @hide
17843 * @cfg {String} validateOnBlur @hide
17847 Roo.HtmlEditorCore.white = [
17848 'area', 'br', 'img', 'input', 'hr', 'wbr',
17850 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
17851 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
17852 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
17853 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
17854 'table', 'ul', 'xmp',
17856 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
17859 'dir', 'menu', 'ol', 'ul', 'dl',
17865 Roo.HtmlEditorCore.black = [
17866 // 'embed', 'object', // enable - backend responsiblity to clean thiese
17868 'base', 'basefont', 'bgsound', 'blink', 'body',
17869 'frame', 'frameset', 'head', 'html', 'ilayer',
17870 'iframe', 'layer', 'link', 'meta', 'object',
17871 'script', 'style' ,'title', 'xml' // clean later..
17873 Roo.HtmlEditorCore.clean = [
17874 'script', 'style', 'title', 'xml'
17876 Roo.HtmlEditorCore.remove = [
17881 Roo.HtmlEditorCore.ablack = [
17885 Roo.HtmlEditorCore.aclean = [
17886 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
17890 Roo.HtmlEditorCore.pwhite= [
17891 'http', 'https', 'mailto'
17894 // white listed style attributes.
17895 Roo.HtmlEditorCore.cwhite= [
17896 // 'text-align', /// default is to allow most things..
17902 // black listed style attributes.
17903 Roo.HtmlEditorCore.cblack= [
17904 // 'font-size' -- this can be set by the project
17908 Roo.HtmlEditorCore.swapCodes =[
17927 * @class Roo.bootstrap.HtmlEditor
17928 * @extends Roo.bootstrap.TextArea
17929 * Bootstrap HtmlEditor class
17932 * Create a new HtmlEditor
17933 * @param {Object} config The config object
17936 Roo.bootstrap.HtmlEditor = function(config){
17937 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17938 if (!this.toolbars) {
17939 this.toolbars = [];
17941 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17944 * @event initialize
17945 * Fires when the editor is fully initialized (including the iframe)
17946 * @param {HtmlEditor} this
17951 * Fires when the editor is first receives the focus. Any insertion must wait
17952 * until after this event.
17953 * @param {HtmlEditor} this
17957 * @event beforesync
17958 * Fires before the textarea is updated with content from the editor iframe. Return false
17959 * to cancel the sync.
17960 * @param {HtmlEditor} this
17961 * @param {String} html
17965 * @event beforepush
17966 * Fires before the iframe editor is updated with content from the textarea. Return false
17967 * to cancel the push.
17968 * @param {HtmlEditor} this
17969 * @param {String} html
17974 * Fires when the textarea is updated with content from the editor iframe.
17975 * @param {HtmlEditor} this
17976 * @param {String} html
17981 * Fires when the iframe editor is updated with content from the textarea.
17982 * @param {HtmlEditor} this
17983 * @param {String} html
17987 * @event editmodechange
17988 * Fires when the editor switches edit modes
17989 * @param {HtmlEditor} this
17990 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17992 editmodechange: true,
17994 * @event editorevent
17995 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17996 * @param {HtmlEditor} this
18000 * @event firstfocus
18001 * Fires when on first focus - needed by toolbars..
18002 * @param {HtmlEditor} this
18007 * Auto save the htmlEditor value as a file into Events
18008 * @param {HtmlEditor} this
18012 * @event savedpreview
18013 * preview the saved version of htmlEditor
18014 * @param {HtmlEditor} this
18021 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
18025 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
18030 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
18035 * @cfg {Number} height (in pixels)
18039 * @cfg {Number} width (in pixels)
18044 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18047 stylesheets: false,
18052 // private properties
18053 validationEvent : false,
18055 initialized : false,
18058 onFocus : Roo.emptyFn,
18060 hideMode:'offsets',
18063 tbContainer : false,
18065 toolbarContainer :function() {
18066 return this.wrap.select('.x-html-editor-tb',true).first();
18070 * Protected method that will not generally be called directly. It
18071 * is called when the editor creates its toolbar. Override this method if you need to
18072 * add custom toolbar buttons.
18073 * @param {HtmlEditor} editor
18075 createToolbar : function(){
18077 Roo.log("create toolbars");
18079 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18080 this.toolbars[0].render(this.toolbarContainer());
18084 // if (!editor.toolbars || !editor.toolbars.length) {
18085 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18088 // for (var i =0 ; i < editor.toolbars.length;i++) {
18089 // editor.toolbars[i] = Roo.factory(
18090 // typeof(editor.toolbars[i]) == 'string' ?
18091 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
18092 // Roo.bootstrap.HtmlEditor);
18093 // editor.toolbars[i].init(editor);
18099 onRender : function(ct, position)
18101 // Roo.log("Call onRender: " + this.xtype);
18103 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18105 this.wrap = this.inputEl().wrap({
18106 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18109 this.editorcore.onRender(ct, position);
18111 if (this.resizable) {
18112 this.resizeEl = new Roo.Resizable(this.wrap, {
18116 minHeight : this.height,
18117 height: this.height,
18118 handles : this.resizable,
18121 resize : function(r, w, h) {
18122 _t.onResize(w,h); // -something
18128 this.createToolbar(this);
18131 if(!this.width && this.resizable){
18132 this.setSize(this.wrap.getSize());
18134 if (this.resizeEl) {
18135 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18136 // should trigger onReize..
18142 onResize : function(w, h)
18144 Roo.log('resize: ' +w + ',' + h );
18145 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18149 if(this.inputEl() ){
18150 if(typeof w == 'number'){
18151 var aw = w - this.wrap.getFrameWidth('lr');
18152 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18155 if(typeof h == 'number'){
18156 var tbh = -11; // fixme it needs to tool bar size!
18157 for (var i =0; i < this.toolbars.length;i++) {
18158 // fixme - ask toolbars for heights?
18159 tbh += this.toolbars[i].el.getHeight();
18160 //if (this.toolbars[i].footer) {
18161 // tbh += this.toolbars[i].footer.el.getHeight();
18169 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18170 ah -= 5; // knock a few pixes off for look..
18171 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18175 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18176 this.editorcore.onResize(ew,eh);
18181 * Toggles the editor between standard and source edit mode.
18182 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18184 toggleSourceEdit : function(sourceEditMode)
18186 this.editorcore.toggleSourceEdit(sourceEditMode);
18188 if(this.editorcore.sourceEditMode){
18189 Roo.log('editor - showing textarea');
18192 // Roo.log(this.syncValue());
18194 this.inputEl().removeClass(['hide', 'x-hidden']);
18195 this.inputEl().dom.removeAttribute('tabIndex');
18196 this.inputEl().focus();
18198 Roo.log('editor - hiding textarea');
18200 // Roo.log(this.pushValue());
18203 this.inputEl().addClass(['hide', 'x-hidden']);
18204 this.inputEl().dom.setAttribute('tabIndex', -1);
18205 //this.deferFocus();
18208 if(this.resizable){
18209 this.setSize(this.wrap.getSize());
18212 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18215 // private (for BoxComponent)
18216 adjustSize : Roo.BoxComponent.prototype.adjustSize,
18218 // private (for BoxComponent)
18219 getResizeEl : function(){
18223 // private (for BoxComponent)
18224 getPositionEl : function(){
18229 initEvents : function(){
18230 this.originalValue = this.getValue();
18234 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18237 // markInvalid : Roo.emptyFn,
18239 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18242 // clearInvalid : Roo.emptyFn,
18244 setValue : function(v){
18245 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18246 this.editorcore.pushValue();
18251 deferFocus : function(){
18252 this.focus.defer(10, this);
18256 focus : function(){
18257 this.editorcore.focus();
18263 onDestroy : function(){
18269 for (var i =0; i < this.toolbars.length;i++) {
18270 // fixme - ask toolbars for heights?
18271 this.toolbars[i].onDestroy();
18274 this.wrap.dom.innerHTML = '';
18275 this.wrap.remove();
18280 onFirstFocus : function(){
18281 //Roo.log("onFirstFocus");
18282 this.editorcore.onFirstFocus();
18283 for (var i =0; i < this.toolbars.length;i++) {
18284 this.toolbars[i].onFirstFocus();
18290 syncValue : function()
18292 this.editorcore.syncValue();
18295 pushValue : function()
18297 this.editorcore.pushValue();
18301 // hide stuff that is not compatible
18315 * @event specialkey
18319 * @cfg {String} fieldClass @hide
18322 * @cfg {String} focusClass @hide
18325 * @cfg {String} autoCreate @hide
18328 * @cfg {String} inputType @hide
18331 * @cfg {String} invalidClass @hide
18334 * @cfg {String} invalidText @hide
18337 * @cfg {String} msgFx @hide
18340 * @cfg {String} validateOnBlur @hide
18349 Roo.namespace('Roo.bootstrap.htmleditor');
18351 * @class Roo.bootstrap.HtmlEditorToolbar1
18356 new Roo.bootstrap.HtmlEditor({
18359 new Roo.bootstrap.HtmlEditorToolbar1({
18360 disable : { fonts: 1 , format: 1, ..., ... , ...],
18366 * @cfg {Object} disable List of elements to disable..
18367 * @cfg {Array} btns List of additional buttons.
18371 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18374 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18377 Roo.apply(this, config);
18379 // default disabled, based on 'good practice'..
18380 this.disable = this.disable || {};
18381 Roo.applyIf(this.disable, {
18384 specialElements : true
18386 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18388 this.editor = config.editor;
18389 this.editorcore = config.editor.editorcore;
18391 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18393 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18394 // dont call parent... till later.
18396 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
18401 editorcore : false,
18406 "h1","h2","h3","h4","h5","h6",
18408 "abbr", "acronym", "address", "cite", "samp", "var",
18412 onRender : function(ct, position)
18414 // Roo.log("Call onRender: " + this.xtype);
18416 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18418 this.el.dom.style.marginBottom = '0';
18420 var editorcore = this.editorcore;
18421 var editor= this.editor;
18424 var btn = function(id,cmd , toggle, handler){
18426 var event = toggle ? 'toggle' : 'click';
18431 xns: Roo.bootstrap,
18434 enableToggle:toggle !== false,
18436 pressed : toggle ? false : null,
18439 a.listeners[toggle ? 'toggle' : 'click'] = function() {
18440 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
18449 xns: Roo.bootstrap,
18450 glyphicon : 'font',
18454 xns: Roo.bootstrap,
18458 Roo.each(this.formats, function(f) {
18459 style.menu.items.push({
18461 xns: Roo.bootstrap,
18462 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18467 editorcore.insertTag(this.tagname);
18474 children.push(style);
18477 btn('bold',false,true);
18478 btn('italic',false,true);
18479 btn('align-left', 'justifyleft',true);
18480 btn('align-center', 'justifycenter',true);
18481 btn('align-right' , 'justifyright',true);
18482 btn('link', false, false, function(btn) {
18483 //Roo.log("create link?");
18484 var url = prompt(this.createLinkText, this.defaultLinkValue);
18485 if(url && url != 'http:/'+'/'){
18486 this.editorcore.relayCmd('createlink', url);
18489 btn('list','insertunorderedlist',true);
18490 btn('pencil', false,true, function(btn){
18493 this.toggleSourceEdit(btn.pressed);
18499 xns: Roo.bootstrap,
18504 xns: Roo.bootstrap,
18509 cog.menu.items.push({
18511 xns: Roo.bootstrap,
18512 html : Clean styles,
18517 editorcore.insertTag(this.tagname);
18526 this.xtype = 'NavSimplebar';
18528 for(var i=0;i< children.length;i++) {
18530 this.buttons.add(this.addxtypeChild(children[i]));
18534 editor.on('editorevent', this.updateToolbar, this);
18536 onBtnClick : function(id)
18538 this.editorcore.relayCmd(id);
18539 this.editorcore.focus();
18543 * Protected method that will not generally be called directly. It triggers
18544 * a toolbar update by reading the markup state of the current selection in the editor.
18546 updateToolbar: function(){
18548 if(!this.editorcore.activated){
18549 this.editor.onFirstFocus(); // is this neeed?
18553 var btns = this.buttons;
18554 var doc = this.editorcore.doc;
18555 btns.get('bold').setActive(doc.queryCommandState('bold'));
18556 btns.get('italic').setActive(doc.queryCommandState('italic'));
18557 //btns.get('underline').setActive(doc.queryCommandState('underline'));
18559 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18560 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18561 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18563 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18564 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18567 var ans = this.editorcore.getAllAncestors();
18568 if (this.formatCombo) {
18571 var store = this.formatCombo.store;
18572 this.formatCombo.setValue("");
18573 for (var i =0; i < ans.length;i++) {
18574 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18576 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18584 // hides menus... - so this cant be on a menu...
18585 Roo.bootstrap.MenuMgr.hideAll();
18587 Roo.bootstrap.MenuMgr.hideAll();
18588 //this.editorsyncValue();
18590 onFirstFocus: function() {
18591 this.buttons.each(function(item){
18595 toggleSourceEdit : function(sourceEditMode){
18598 if(sourceEditMode){
18599 Roo.log("disabling buttons");
18600 this.buttons.each( function(item){
18601 if(item.cmd != 'pencil'){
18607 Roo.log("enabling buttons");
18608 if(this.editorcore.initialized){
18609 this.buttons.each( function(item){
18615 Roo.log("calling toggole on editor");
18616 // tell the editor that it's been pressed..
18617 this.editor.toggleSourceEdit(sourceEditMode);
18627 * @class Roo.bootstrap.Table.AbstractSelectionModel
18628 * @extends Roo.util.Observable
18629 * Abstract base class for grid SelectionModels. It provides the interface that should be
18630 * implemented by descendant classes. This class should not be directly instantiated.
18633 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18634 this.locked = false;
18635 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18639 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
18640 /** @ignore Called by the grid automatically. Do not call directly. */
18641 init : function(grid){
18647 * Locks the selections.
18650 this.locked = true;
18654 * Unlocks the selections.
18656 unlock : function(){
18657 this.locked = false;
18661 * Returns true if the selections are locked.
18662 * @return {Boolean}
18664 isLocked : function(){
18665 return this.locked;
18669 * @extends Roo.bootstrap.Table.AbstractSelectionModel
18670 * @class Roo.bootstrap.Table.RowSelectionModel
18671 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18672 * It supports multiple selections and keyboard selection/navigation.
18674 * @param {Object} config
18677 Roo.bootstrap.Table.RowSelectionModel = function(config){
18678 Roo.apply(this, config);
18679 this.selections = new Roo.util.MixedCollection(false, function(o){
18684 this.lastActive = false;
18688 * @event selectionchange
18689 * Fires when the selection changes
18690 * @param {SelectionModel} this
18692 "selectionchange" : true,
18694 * @event afterselectionchange
18695 * Fires after the selection changes (eg. by key press or clicking)
18696 * @param {SelectionModel} this
18698 "afterselectionchange" : true,
18700 * @event beforerowselect
18701 * Fires when a row is selected being selected, return false to cancel.
18702 * @param {SelectionModel} this
18703 * @param {Number} rowIndex The selected index
18704 * @param {Boolean} keepExisting False if other selections will be cleared
18706 "beforerowselect" : true,
18709 * Fires when a row is selected.
18710 * @param {SelectionModel} this
18711 * @param {Number} rowIndex The selected index
18712 * @param {Roo.data.Record} r The record
18714 "rowselect" : true,
18716 * @event rowdeselect
18717 * Fires when a row is deselected.
18718 * @param {SelectionModel} this
18719 * @param {Number} rowIndex The selected index
18721 "rowdeselect" : true
18723 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18724 this.locked = false;
18727 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
18729 * @cfg {Boolean} singleSelect
18730 * True to allow selection of only one row at a time (defaults to false)
18732 singleSelect : false,
18735 initEvents : function(){
18737 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18738 this.grid.on("mousedown", this.handleMouseDown, this);
18739 }else{ // allow click to work like normal
18740 this.grid.on("rowclick", this.handleDragableRowClick, this);
18743 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18744 "up" : function(e){
18746 this.selectPrevious(e.shiftKey);
18747 }else if(this.last !== false && this.lastActive !== false){
18748 var last = this.last;
18749 this.selectRange(this.last, this.lastActive-1);
18750 this.grid.getView().focusRow(this.lastActive);
18751 if(last !== false){
18755 this.selectFirstRow();
18757 this.fireEvent("afterselectionchange", this);
18759 "down" : function(e){
18761 this.selectNext(e.shiftKey);
18762 }else if(this.last !== false && this.lastActive !== false){
18763 var last = this.last;
18764 this.selectRange(this.last, this.lastActive+1);
18765 this.grid.getView().focusRow(this.lastActive);
18766 if(last !== false){
18770 this.selectFirstRow();
18772 this.fireEvent("afterselectionchange", this);
18777 var view = this.grid.view;
18778 view.on("refresh", this.onRefresh, this);
18779 view.on("rowupdated", this.onRowUpdated, this);
18780 view.on("rowremoved", this.onRemove, this);
18784 onRefresh : function(){
18785 var ds = this.grid.dataSource, i, v = this.grid.view;
18786 var s = this.selections;
18787 s.each(function(r){
18788 if((i = ds.indexOfId(r.id)) != -1){
18797 onRemove : function(v, index, r){
18798 this.selections.remove(r);
18802 onRowUpdated : function(v, index, r){
18803 if(this.isSelected(r)){
18804 v.onRowSelect(index);
18810 * @param {Array} records The records to select
18811 * @param {Boolean} keepExisting (optional) True to keep existing selections
18813 selectRecords : function(records, keepExisting){
18815 this.clearSelections();
18817 var ds = this.grid.dataSource;
18818 for(var i = 0, len = records.length; i < len; i++){
18819 this.selectRow(ds.indexOf(records[i]), true);
18824 * Gets the number of selected rows.
18827 getCount : function(){
18828 return this.selections.length;
18832 * Selects the first row in the grid.
18834 selectFirstRow : function(){
18839 * Select the last row.
18840 * @param {Boolean} keepExisting (optional) True to keep existing selections
18842 selectLastRow : function(keepExisting){
18843 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18847 * Selects the row immediately following the last selected row.
18848 * @param {Boolean} keepExisting (optional) True to keep existing selections
18850 selectNext : function(keepExisting){
18851 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18852 this.selectRow(this.last+1, keepExisting);
18853 this.grid.getView().focusRow(this.last);
18858 * Selects the row that precedes the last selected row.
18859 * @param {Boolean} keepExisting (optional) True to keep existing selections
18861 selectPrevious : function(keepExisting){
18863 this.selectRow(this.last-1, keepExisting);
18864 this.grid.getView().focusRow(this.last);
18869 * Returns the selected records
18870 * @return {Array} Array of selected records
18872 getSelections : function(){
18873 return [].concat(this.selections.items);
18877 * Returns the first selected record.
18880 getSelected : function(){
18881 return this.selections.itemAt(0);
18886 * Clears all selections.
18888 clearSelections : function(fast){
18889 if(this.locked) return;
18891 var ds = this.grid.dataSource;
18892 var s = this.selections;
18893 s.each(function(r){
18894 this.deselectRow(ds.indexOfId(r.id));
18898 this.selections.clear();
18905 * Selects all rows.
18907 selectAll : function(){
18908 if(this.locked) return;
18909 this.selections.clear();
18910 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18911 this.selectRow(i, true);
18916 * Returns True if there is a selection.
18917 * @return {Boolean}
18919 hasSelection : function(){
18920 return this.selections.length > 0;
18924 * Returns True if the specified row is selected.
18925 * @param {Number/Record} record The record or index of the record to check
18926 * @return {Boolean}
18928 isSelected : function(index){
18929 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18930 return (r && this.selections.key(r.id) ? true : false);
18934 * Returns True if the specified record id is selected.
18935 * @param {String} id The id of record to check
18936 * @return {Boolean}
18938 isIdSelected : function(id){
18939 return (this.selections.key(id) ? true : false);
18943 handleMouseDown : function(e, t){
18944 var view = this.grid.getView(), rowIndex;
18945 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18948 if(e.shiftKey && this.last !== false){
18949 var last = this.last;
18950 this.selectRange(last, rowIndex, e.ctrlKey);
18951 this.last = last; // reset the last
18952 view.focusRow(rowIndex);
18954 var isSelected = this.isSelected(rowIndex);
18955 if(e.button !== 0 && isSelected){
18956 view.focusRow(rowIndex);
18957 }else if(e.ctrlKey && isSelected){
18958 this.deselectRow(rowIndex);
18959 }else if(!isSelected){
18960 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18961 view.focusRow(rowIndex);
18964 this.fireEvent("afterselectionchange", this);
18967 handleDragableRowClick : function(grid, rowIndex, e)
18969 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18970 this.selectRow(rowIndex, false);
18971 grid.view.focusRow(rowIndex);
18972 this.fireEvent("afterselectionchange", this);
18977 * Selects multiple rows.
18978 * @param {Array} rows Array of the indexes of the row to select
18979 * @param {Boolean} keepExisting (optional) True to keep existing selections
18981 selectRows : function(rows, keepExisting){
18983 this.clearSelections();
18985 for(var i = 0, len = rows.length; i < len; i++){
18986 this.selectRow(rows[i], true);
18991 * Selects a range of rows. All rows in between startRow and endRow are also selected.
18992 * @param {Number} startRow The index of the first row in the range
18993 * @param {Number} endRow The index of the last row in the range
18994 * @param {Boolean} keepExisting (optional) True to retain existing selections
18996 selectRange : function(startRow, endRow, keepExisting){
18997 if(this.locked) return;
18999 this.clearSelections();
19001 if(startRow <= endRow){
19002 for(var i = startRow; i <= endRow; i++){
19003 this.selectRow(i, true);
19006 for(var i = startRow; i >= endRow; i--){
19007 this.selectRow(i, true);
19013 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
19014 * @param {Number} startRow The index of the first row in the range
19015 * @param {Number} endRow The index of the last row in the range
19017 deselectRange : function(startRow, endRow, preventViewNotify){
19018 if(this.locked) return;
19019 for(var i = startRow; i <= endRow; i++){
19020 this.deselectRow(i, preventViewNotify);
19026 * @param {Number} row The index of the row to select
19027 * @param {Boolean} keepExisting (optional) True to keep existing selections
19029 selectRow : function(index, keepExisting, preventViewNotify){
19030 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
19031 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
19032 if(!keepExisting || this.singleSelect){
19033 this.clearSelections();
19035 var r = this.grid.dataSource.getAt(index);
19036 this.selections.add(r);
19037 this.last = this.lastActive = index;
19038 if(!preventViewNotify){
19039 this.grid.getView().onRowSelect(index);
19041 this.fireEvent("rowselect", this, index, r);
19042 this.fireEvent("selectionchange", this);
19048 * @param {Number} row The index of the row to deselect
19050 deselectRow : function(index, preventViewNotify){
19051 if(this.locked) return;
19052 if(this.last == index){
19055 if(this.lastActive == index){
19056 this.lastActive = false;
19058 var r = this.grid.dataSource.getAt(index);
19059 this.selections.remove(r);
19060 if(!preventViewNotify){
19061 this.grid.getView().onRowDeselect(index);
19063 this.fireEvent("rowdeselect", this, index);
19064 this.fireEvent("selectionchange", this);
19068 restoreLast : function(){
19070 this.last = this._last;
19075 acceptsNav : function(row, col, cm){
19076 return !cm.isHidden(col) && cm.isCellEditable(col, row);
19080 onEditorKey : function(field, e){
19081 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19086 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19088 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19090 }else if(k == e.ENTER && !e.ctrlKey){
19094 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19096 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19098 }else if(k == e.ESC){
19102 g.startEditing(newCell[0], newCell[1]);
19107 * Ext JS Library 1.1.1
19108 * Copyright(c) 2006-2007, Ext JS, LLC.
19110 * Originally Released Under LGPL - original licence link has changed is not relivant.
19113 * <script type="text/javascript">
19117 * @class Roo.bootstrap.PagingToolbar
19119 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19121 * Create a new PagingToolbar
19122 * @param {Object} config The config object
19124 Roo.bootstrap.PagingToolbar = function(config)
19126 // old args format still supported... - xtype is prefered..
19127 // created from xtype...
19128 var ds = config.dataSource;
19129 this.toolbarItems = [];
19130 if (config.items) {
19131 this.toolbarItems = config.items;
19132 // config.items = [];
19135 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19142 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19146 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19148 * @cfg {Roo.data.Store} dataSource
19149 * The underlying data store providing the paged data
19152 * @cfg {String/HTMLElement/Element} container
19153 * container The id or element that will contain the toolbar
19156 * @cfg {Boolean} displayInfo
19157 * True to display the displayMsg (defaults to false)
19160 * @cfg {Number} pageSize
19161 * The number of records to display per page (defaults to 20)
19165 * @cfg {String} displayMsg
19166 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19168 displayMsg : 'Displaying {0} - {1} of {2}',
19170 * @cfg {String} emptyMsg
19171 * The message to display when no records are found (defaults to "No data to display")
19173 emptyMsg : 'No data to display',
19175 * Customizable piece of the default paging text (defaults to "Page")
19178 beforePageText : "Page",
19180 * Customizable piece of the default paging text (defaults to "of %0")
19183 afterPageText : "of {0}",
19185 * Customizable piece of the default paging text (defaults to "First Page")
19188 firstText : "First Page",
19190 * Customizable piece of the default paging text (defaults to "Previous Page")
19193 prevText : "Previous Page",
19195 * Customizable piece of the default paging text (defaults to "Next Page")
19198 nextText : "Next Page",
19200 * Customizable piece of the default paging text (defaults to "Last Page")
19203 lastText : "Last Page",
19205 * Customizable piece of the default paging text (defaults to "Refresh")
19208 refreshText : "Refresh",
19212 onRender : function(ct, position)
19214 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19215 this.navgroup.parentId = this.id;
19216 this.navgroup.onRender(this.el, null);
19217 // add the buttons to the navgroup
19219 if(this.displayInfo){
19220 Roo.log(this.el.select('ul.navbar-nav',true).first());
19221 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19222 this.displayEl = this.el.select('.x-paging-info', true).first();
19223 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19224 // this.displayEl = navel.el.select('span',true).first();
19230 Roo.each(_this.buttons, function(e){
19231 Roo.factory(e).onRender(_this.el, null);
19235 Roo.each(_this.toolbarItems, function(e) {
19236 _this.navgroup.addItem(e);
19239 this.first = this.navgroup.addItem({
19240 tooltip: this.firstText,
19242 icon : 'fa fa-backward',
19244 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19247 this.prev = this.navgroup.addItem({
19248 tooltip: this.prevText,
19250 icon : 'fa fa-step-backward',
19252 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
19254 //this.addSeparator();
19257 var field = this.navgroup.addItem( {
19259 cls : 'x-paging-position',
19261 html : this.beforePageText +
19262 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19263 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
19266 this.field = field.el.select('input', true).first();
19267 this.field.on("keydown", this.onPagingKeydown, this);
19268 this.field.on("focus", function(){this.dom.select();});
19271 this.afterTextEl = field.el.select('.x-paging-after',true).first();
19272 //this.field.setHeight(18);
19273 //this.addSeparator();
19274 this.next = this.navgroup.addItem({
19275 tooltip: this.nextText,
19277 html : ' <i class="fa fa-step-forward">',
19279 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
19281 this.last = this.navgroup.addItem({
19282 tooltip: this.lastText,
19283 icon : 'fa fa-forward',
19286 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
19288 //this.addSeparator();
19289 this.loading = this.navgroup.addItem({
19290 tooltip: this.refreshText,
19291 icon: 'fa fa-refresh',
19293 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19299 updateInfo : function(){
19300 if(this.displayEl){
19301 var count = this.ds.getCount();
19302 var msg = count == 0 ?
19306 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
19308 this.displayEl.update(msg);
19313 onLoad : function(ds, r, o){
19314 this.cursor = o.params ? o.params.start : 0;
19315 var d = this.getPageData(),
19319 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19320 this.field.dom.value = ap;
19321 this.first.setDisabled(ap == 1);
19322 this.prev.setDisabled(ap == 1);
19323 this.next.setDisabled(ap == ps);
19324 this.last.setDisabled(ap == ps);
19325 this.loading.enable();
19330 getPageData : function(){
19331 var total = this.ds.getTotalCount();
19334 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19335 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19340 onLoadError : function(){
19341 this.loading.enable();
19345 onPagingKeydown : function(e){
19346 var k = e.getKey();
19347 var d = this.getPageData();
19349 var v = this.field.dom.value, pageNum;
19350 if(!v || isNaN(pageNum = parseInt(v, 10))){
19351 this.field.dom.value = d.activePage;
19354 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19355 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19358 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))
19360 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19361 this.field.dom.value = pageNum;
19362 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19365 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19367 var v = this.field.dom.value, pageNum;
19368 var increment = (e.shiftKey) ? 10 : 1;
19369 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19371 if(!v || isNaN(pageNum = parseInt(v, 10))) {
19372 this.field.dom.value = d.activePage;
19375 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19377 this.field.dom.value = parseInt(v, 10) + increment;
19378 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19379 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19386 beforeLoad : function(){
19388 this.loading.disable();
19393 onClick : function(which){
19400 ds.load({params:{start: 0, limit: this.pageSize}});
19403 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19406 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19409 var total = ds.getTotalCount();
19410 var extra = total % this.pageSize;
19411 var lastStart = extra ? (total - extra) : total-this.pageSize;
19412 ds.load({params:{start: lastStart, limit: this.pageSize}});
19415 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19421 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19422 * @param {Roo.data.Store} store The data store to unbind
19424 unbind : function(ds){
19425 ds.un("beforeload", this.beforeLoad, this);
19426 ds.un("load", this.onLoad, this);
19427 ds.un("loadexception", this.onLoadError, this);
19428 ds.un("remove", this.updateInfo, this);
19429 ds.un("add", this.updateInfo, this);
19430 this.ds = undefined;
19434 * Binds the paging toolbar to the specified {@link Roo.data.Store}
19435 * @param {Roo.data.Store} store The data store to bind
19437 bind : function(ds){
19438 ds.on("beforeload", this.beforeLoad, this);
19439 ds.on("load", this.onLoad, this);
19440 ds.on("loadexception", this.onLoadError, this);
19441 ds.on("remove", this.updateInfo, this);
19442 ds.on("add", this.updateInfo, this);
19453 * @class Roo.bootstrap.MessageBar
19454 * @extends Roo.bootstrap.Component
19455 * Bootstrap MessageBar class
19456 * @cfg {String} html contents of the MessageBar
19457 * @cfg {String} weight (info | success | warning | danger) default info
19458 * @cfg {String} beforeClass insert the bar before the given class
19459 * @cfg {Boolean} closable (true | false) default false
19460 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19463 * Create a new Element
19464 * @param {Object} config The config object
19467 Roo.bootstrap.MessageBar = function(config){
19468 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19471 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
19477 beforeClass: 'bootstrap-sticky-wrap',
19479 getAutoCreate : function(){
19483 cls: 'alert alert-dismissable alert-' + this.weight,
19488 html: this.html || ''
19494 cfg.cls += ' alert-messages-fixed';
19508 onRender : function(ct, position)
19510 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19513 var cfg = Roo.apply({}, this.getAutoCreate());
19517 cfg.cls += ' ' + this.cls;
19520 cfg.style = this.style;
19522 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19524 this.el.setVisibilityMode(Roo.Element.DISPLAY);
19527 this.el.select('>button.close').on('click', this.hide, this);
19533 if (!this.rendered) {
19539 this.fireEvent('show', this);
19545 if (!this.rendered) {
19551 this.fireEvent('hide', this);
19554 update : function()
19556 // var e = this.el.dom.firstChild;
19558 // if(this.closable){
19559 // e = e.nextSibling;
19562 // e.data = this.html || '';
19564 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19580 * @class Roo.bootstrap.Graph
19581 * @extends Roo.bootstrap.Component
19582 * Bootstrap Graph class
19586 @cfg {String} graphtype bar | vbar | pie
19587 @cfg {number} g_x coodinator | centre x (pie)
19588 @cfg {number} g_y coodinator | centre y (pie)
19589 @cfg {number} g_r radius (pie)
19590 @cfg {number} g_height height of the chart (respected by all elements in the set)
19591 @cfg {number} g_width width of the chart (respected by all elements in the set)
19592 @cfg {Object} title The title of the chart
19595 -opts (object) options for the chart
19597 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19598 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19600 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.
19601 o stacked (boolean) whether or not to tread values as in a stacked bar chart
19603 o stretch (boolean)
19605 -opts (object) options for the pie
19608 o startAngle (number)
19609 o endAngle (number)
19613 * Create a new Input
19614 * @param {Object} config The config object
19617 Roo.bootstrap.Graph = function(config){
19618 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19624 * The img click event for the img.
19625 * @param {Roo.EventObject} e
19631 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
19642 //g_colors: this.colors,
19649 getAutoCreate : function(){
19660 onRender : function(ct,position){
19661 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19662 this.raphael = Raphael(this.el.dom);
19664 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19665 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19666 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19667 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19669 r.text(160, 10, "Single Series Chart").attr(txtattr);
19670 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19671 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19672 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19674 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19675 r.barchart(330, 10, 300, 220, data1);
19676 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19677 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19680 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19681 // r.barchart(30, 30, 560, 250, xdata, {
19682 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19683 // axis : "0 0 1 1",
19684 // axisxlabels : xdata
19685 // //yvalues : cols,
19688 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19690 // this.load(null,xdata,{
19691 // axis : "0 0 1 1",
19692 // axisxlabels : xdata
19697 load : function(graphtype,xdata,opts){
19698 this.raphael.clear();
19700 graphtype = this.graphtype;
19705 var r = this.raphael,
19706 fin = function () {
19707 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19709 fout = function () {
19710 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19712 pfin = function() {
19713 this.sector.stop();
19714 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19717 this.label[0].stop();
19718 this.label[0].attr({ r: 7.5 });
19719 this.label[1].attr({ "font-weight": 800 });
19722 pfout = function() {
19723 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19726 this.label[0].animate({ r: 5 }, 500, "bounce");
19727 this.label[1].attr({ "font-weight": 400 });
19733 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19736 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19739 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
19740 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19742 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19749 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19754 setTitle: function(o)
19759 initEvents: function() {
19762 this.el.on('click', this.onClick, this);
19766 onClick : function(e)
19768 Roo.log('img onclick');
19769 this.fireEvent('click', this, e);
19781 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19784 * @class Roo.bootstrap.dash.NumberBox
19785 * @extends Roo.bootstrap.Component
19786 * Bootstrap NumberBox class
19787 * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19788 * @cfg {String} headline Box headline
19789 * @cfg {String} content Box content
19790 * @cfg {String} icon Box icon
19791 * @cfg {String} footer Footer text
19792 * @cfg {String} fhref Footer href
19795 * Create a new NumberBox
19796 * @param {Object} config The config object
19800 Roo.bootstrap.dash.NumberBox = function(config){
19801 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19805 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
19815 getAutoCreate : function(){
19819 cls : 'small-box bg-' + this.bgcolor,
19827 cls : 'roo-headline',
19828 html : this.headline
19832 cls : 'roo-content',
19833 html : this.content
19847 cls : 'ion ' + this.icon
19856 cls : 'small-box-footer',
19857 href : this.fhref || '#',
19861 cfg.cn.push(footer);
19868 onRender : function(ct,position){
19869 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19876 setHeadline: function (value)
19878 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19881 setFooter: function (value, href)
19883 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19886 this.el.select('a.small-box-footer',true).first().attr('href', href);
19891 setContent: function (value)
19893 this.el.select('.roo-content',true).first().dom.innerHTML = value;
19896 initEvents: function()
19910 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19913 * @class Roo.bootstrap.dash.TabBox
19914 * @extends Roo.bootstrap.Component
19915 * Bootstrap TabBox class
19916 * @cfg {String} title Title of the TabBox
19917 * @cfg {String} icon Icon of the TabBox
19918 * @cfg {Boolean} showtabs (true|false) show the tabs default true
19919 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
19922 * Create a new TabBox
19923 * @param {Object} config The config object
19927 Roo.bootstrap.dash.TabBox = function(config){
19928 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19933 * When a pane is added
19934 * @param {Roo.bootstrap.dash.TabPane} pane
19938 * @event activatepane
19939 * When a pane is activated
19940 * @param {Roo.bootstrap.dash.TabPane} pane
19942 "activatepane" : true
19950 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
19955 tabScrollable : false,
19957 getChildContainer : function()
19959 return this.el.select('.tab-content', true).first();
19962 getAutoCreate : function(){
19966 cls: 'pull-left header',
19974 cls: 'fa ' + this.icon
19980 cls: 'nav nav-tabs pull-right',
19986 if(this.tabScrollable){
19993 cls: 'nav nav-tabs pull-right',
20004 cls: 'nav-tabs-custom',
20009 cls: 'tab-content no-padding',
20017 initEvents : function()
20019 //Roo.log('add add pane handler');
20020 this.on('addpane', this.onAddPane, this);
20023 * Updates the box title
20024 * @param {String} html to set the title to.
20026 setTitle : function(value)
20028 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
20030 onAddPane : function(pane)
20032 this.panes.push(pane);
20033 //Roo.log('addpane');
20035 // tabs are rendere left to right..
20036 if(!this.showtabs){
20040 var ctr = this.el.select('.nav-tabs', true).first();
20043 var existing = ctr.select('.nav-tab',true);
20044 var qty = existing.getCount();;
20047 var tab = ctr.createChild({
20049 cls : 'nav-tab' + (qty ? '' : ' active'),
20057 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
20060 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
20062 pane.el.addClass('active');
20067 onTabClick : function(ev,un,ob,pane)
20069 //Roo.log('tab - prev default');
20070 ev.preventDefault();
20073 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20074 pane.tab.addClass('active');
20075 //Roo.log(pane.title);
20076 this.getChildContainer().select('.tab-pane',true).removeClass('active');
20077 // technically we should have a deactivate event.. but maybe add later.
20078 // and it should not de-activate the selected tab...
20079 this.fireEvent('activatepane', pane);
20080 pane.el.addClass('active');
20081 pane.fireEvent('activate');
20086 getActivePane : function()
20089 Roo.each(this.panes, function(p) {
20090 if(p.el.hasClass('active')){
20111 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20113 * @class Roo.bootstrap.TabPane
20114 * @extends Roo.bootstrap.Component
20115 * Bootstrap TabPane class
20116 * @cfg {Boolean} active (false | true) Default false
20117 * @cfg {String} title title of panel
20121 * Create a new TabPane
20122 * @param {Object} config The config object
20125 Roo.bootstrap.dash.TabPane = function(config){
20126 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20132 * When a pane is activated
20133 * @param {Roo.bootstrap.dash.TabPane} pane
20140 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
20145 // the tabBox that this is attached to.
20148 getAutoCreate : function()
20156 cfg.cls += ' active';
20161 initEvents : function()
20163 //Roo.log('trigger add pane handler');
20164 this.parent().fireEvent('addpane', this)
20168 * Updates the tab title
20169 * @param {String} html to set the title to.
20171 setTitle: function(str)
20177 this.tab.select('a', true).first().dom.innerHTML = str;
20194 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20197 * @class Roo.bootstrap.menu.Menu
20198 * @extends Roo.bootstrap.Component
20199 * Bootstrap Menu class - container for Menu
20200 * @cfg {String} html Text of the menu
20201 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20202 * @cfg {String} icon Font awesome icon
20203 * @cfg {String} pos Menu align to (top | bottom) default bottom
20207 * Create a new Menu
20208 * @param {Object} config The config object
20212 Roo.bootstrap.menu.Menu = function(config){
20213 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20217 * @event beforeshow
20218 * Fires before this menu is displayed
20219 * @param {Roo.bootstrap.menu.Menu} this
20223 * @event beforehide
20224 * Fires before this menu is hidden
20225 * @param {Roo.bootstrap.menu.Menu} this
20230 * Fires after this menu is displayed
20231 * @param {Roo.bootstrap.menu.Menu} this
20236 * Fires after this menu is hidden
20237 * @param {Roo.bootstrap.menu.Menu} this
20242 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20243 * @param {Roo.bootstrap.menu.Menu} this
20244 * @param {Roo.EventObject} e
20251 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
20255 weight : 'default',
20260 getChildContainer : function() {
20261 if(this.isSubMenu){
20265 return this.el.select('ul.dropdown-menu', true).first();
20268 getAutoCreate : function()
20273 cls : 'roo-menu-text',
20281 cls : 'fa ' + this.icon
20292 cls : 'dropdown-button btn btn-' + this.weight,
20297 cls : 'dropdown-toggle btn btn-' + this.weight,
20307 cls : 'dropdown-menu'
20313 if(this.pos == 'top'){
20314 cfg.cls += ' dropup';
20317 if(this.isSubMenu){
20320 cls : 'dropdown-menu'
20327 onRender : function(ct, position)
20329 this.isSubMenu = ct.hasClass('dropdown-submenu');
20331 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20334 initEvents : function()
20336 if(this.isSubMenu){
20340 this.hidden = true;
20342 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20343 this.triggerEl.on('click', this.onTriggerPress, this);
20345 this.buttonEl = this.el.select('button.dropdown-button', true).first();
20346 this.buttonEl.on('click', this.onClick, this);
20352 if(this.isSubMenu){
20356 return this.el.select('ul.dropdown-menu', true).first();
20359 onClick : function(e)
20361 this.fireEvent("click", this, e);
20364 onTriggerPress : function(e)
20366 if (this.isVisible()) {
20373 isVisible : function(){
20374 return !this.hidden;
20379 this.fireEvent("beforeshow", this);
20381 this.hidden = false;
20382 this.el.addClass('open');
20384 Roo.get(document).on("mouseup", this.onMouseUp, this);
20386 this.fireEvent("show", this);
20393 this.fireEvent("beforehide", this);
20395 this.hidden = true;
20396 this.el.removeClass('open');
20398 Roo.get(document).un("mouseup", this.onMouseUp);
20400 this.fireEvent("hide", this);
20403 onMouseUp : function()
20417 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20420 * @class Roo.bootstrap.menu.Item
20421 * @extends Roo.bootstrap.Component
20422 * Bootstrap MenuItem class
20423 * @cfg {Boolean} submenu (true | false) default false
20424 * @cfg {String} html text of the item
20425 * @cfg {String} href the link
20426 * @cfg {Boolean} disable (true | false) default false
20427 * @cfg {Boolean} preventDefault (true | false) default true
20428 * @cfg {String} icon Font awesome icon
20429 * @cfg {String} pos Submenu align to (left | right) default right
20433 * Create a new Item
20434 * @param {Object} config The config object
20438 Roo.bootstrap.menu.Item = function(config){
20439 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20443 * Fires when the mouse is hovering over this menu
20444 * @param {Roo.bootstrap.menu.Item} this
20445 * @param {Roo.EventObject} e
20450 * Fires when the mouse exits this menu
20451 * @param {Roo.bootstrap.menu.Item} this
20452 * @param {Roo.EventObject} e
20458 * The raw click event for the entire grid.
20459 * @param {Roo.EventObject} e
20465 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
20470 preventDefault: true,
20475 getAutoCreate : function()
20480 cls : 'roo-menu-item-text',
20488 cls : 'fa ' + this.icon
20497 href : this.href || '#',
20504 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20508 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20510 if(this.pos == 'left'){
20511 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20518 initEvents : function()
20520 this.el.on('mouseover', this.onMouseOver, this);
20521 this.el.on('mouseout', this.onMouseOut, this);
20523 this.el.select('a', true).first().on('click', this.onClick, this);
20527 onClick : function(e)
20529 if(this.preventDefault){
20530 e.preventDefault();
20533 this.fireEvent("click", this, e);
20536 onMouseOver : function(e)
20538 if(this.submenu && this.pos == 'left'){
20539 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20542 this.fireEvent("mouseover", this, e);
20545 onMouseOut : function(e)
20547 this.fireEvent("mouseout", this, e);
20559 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20562 * @class Roo.bootstrap.menu.Separator
20563 * @extends Roo.bootstrap.Component
20564 * Bootstrap Separator class
20567 * Create a new Separator
20568 * @param {Object} config The config object
20572 Roo.bootstrap.menu.Separator = function(config){
20573 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20576 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
20578 getAutoCreate : function(){