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;
113 this.el = ct.createChild(cfg, position);
116 this.tooltipEl().attr('tooltip', this.tooltip);
119 if(this.tabIndex !== undefined){
120 this.el.dom.setAttribute('tabIndex', this.tabIndex);
127 * Fetch the element to add children to
128 * @return {Roo.Element} defaults to this.el
130 getChildContainer : function()
135 * Fetch the element to display the tooltip on.
136 * @return {Roo.Element} defaults to this.el
138 tooltipEl : function()
143 addxtype : function(tree,cntr)
147 cn = Roo.factory(tree);
149 cn.parentType = this.xtype; //??
150 cn.parentId = this.id;
152 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
154 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
156 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
158 var build_from_html = Roo.XComponent.build_from_html;
160 var is_body = (tree.xtype == 'Body') ;
162 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
164 var self_cntr_el = Roo.get(this[cntr](false));
166 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
167 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
168 return this.addxtypeChild(tree,cntr);
171 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
174 return this.addxtypeChild(Roo.apply({}, tree),cntr);
177 Roo.log('skipping render');
185 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
191 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
195 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
200 addxtypeChild : function (tree, cntr)
202 Roo.log('addxtypeChild:' + cntr);
204 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
207 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
208 (typeof(tree['flexy:foreach']) != 'undefined');
212 skip_children = false;
213 // render the element if it's not BODY.
214 if (tree.xtype != 'Body') {
216 cn = Roo.factory(tree);
218 cn.parentType = this.xtype; //??
219 cn.parentId = this.id;
221 var build_from_html = Roo.XComponent.build_from_html;
224 // does the container contain child eleemnts with 'xtype' attributes.
225 // that match this xtype..
226 // note - when we render we create these as well..
227 // so we should check to see if body has xtype set.
228 if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
230 var self_cntr_el = Roo.get(this[cntr](false));
231 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
234 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
235 // and are not displayed -this causes this to use up the wrong element when matching.
236 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
239 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
240 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
246 //echild.dom.removeAttribute('xtype');
248 Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
249 Roo.log(self_cntr_el);
257 // if object has flexy:if - then it may or may not be rendered.
258 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
259 // skip a flexy if element.
260 Roo.log('skipping render');
263 Roo.log('skipping all children');
264 skip_children = true;
269 // actually if flexy:foreach is found, we really want to create
270 // multiple copies here...
272 //Roo.log(this[cntr]());
273 cn.render(this[cntr](true));
275 // then add the element..
283 if (typeof (tree.menu) != 'undefined') {
284 tree.menu.parentType = cn.xtype;
285 tree.menu.triggerEl = cn.el;
286 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
290 if (!tree.items || !tree.items.length) {
294 var items = tree.items;
297 //Roo.log(items.length);
299 if (!skip_children) {
300 for(var i =0;i < items.length;i++) {
301 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
323 * @class Roo.bootstrap.Body
324 * @extends Roo.bootstrap.Component
325 * Bootstrap Body class
329 * @param {Object} config The config object
332 Roo.bootstrap.Body = function(config){
333 Roo.bootstrap.Body.superclass.constructor.call(this, config);
334 this.el = Roo.get(document.body);
335 if (this.cls && this.cls.length) {
336 Roo.get(document.body).addClass(this.cls);
340 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
345 onRender : function(ct, position)
347 /* Roo.log("Roo.bootstrap.Body - onRender");
348 if (this.cls && this.cls.length) {
349 Roo.get(document.body).addClass(this.cls);
369 * @class Roo.bootstrap.ButtonGroup
370 * @extends Roo.bootstrap.Component
371 * Bootstrap ButtonGroup class
372 * @cfg {String} size lg | sm | xs (default empty normal)
373 * @cfg {String} align vertical | justified (default none)
374 * @cfg {String} direction up | down (default down)
375 * @cfg {Boolean} toolbar false | true
376 * @cfg {Boolean} btn true | false
381 * @param {Object} config The config object
384 Roo.bootstrap.ButtonGroup = function(config){
385 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
388 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
396 getAutoCreate : function(){
402 cfg.html = this.html || cfg.html;
413 if (['vertical','justified'].indexOf(this.align)!==-1) {
414 cfg.cls = 'btn-group-' + this.align;
416 if (this.align == 'justified') {
417 console.log(this.items);
421 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
422 cfg.cls += ' btn-group-' + this.size;
425 if (this.direction == 'up') {
426 cfg.cls += ' dropup' ;
442 * @class Roo.bootstrap.Button
443 * @extends Roo.bootstrap.Component
444 * Bootstrap Button class
445 * @cfg {String} html The button content
446 * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
447 * @cfg {String} size empty | lg | sm | xs
448 * @cfg {String} tag empty | a | input | submit
449 * @cfg {String} href empty or href
450 * @cfg {Boolean} disabled false | true
451 * @cfg {Boolean} isClose false | true
452 * @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
453 * @cfg {String} badge text for badge
454 * @cfg {String} theme default (or empty) | glow
455 * @cfg {Boolean} inverse false | true
456 * @cfg {Boolean} toggle false | true
457 * @cfg {String} ontext text for on toggle state
458 * @cfg {String} offtext text for off toggle state
459 * @cfg {Boolean} defaulton true | false
460 * @cfg {Boolean} preventDefault (true | false) default true
461 * @cfg {Boolean} removeClass true | false remove the standard class..
462 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
465 * Create a new button
466 * @param {Object} config The config object
470 Roo.bootstrap.Button = function(config){
471 Roo.bootstrap.Button.superclass.constructor.call(this, config);
476 * When a butotn is pressed
477 * @param {Roo.EventObject} e
482 * After the button has been toggles
483 * @param {Roo.EventObject} e
484 * @param {boolean} pressed (also available as button.pressed)
490 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
508 preventDefault: true,
517 getAutoCreate : function(){
525 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
526 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
531 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
533 if (this.toggle == true) {
536 cls: 'slider-frame roo-button',
541 'data-off-text':'OFF',
542 cls: 'slider-button',
548 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
549 cfg.cls += ' '+this.weight;
558 cfg["aria-hidden"] = true;
560 cfg.html = "×";
566 if (this.theme==='default') {
567 cfg.cls = 'btn roo-button';
569 //if (this.parentType != 'Navbar') {
570 this.weight = this.weight.length ? this.weight : 'default';
572 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
574 cfg.cls += ' btn-' + this.weight;
576 } else if (this.theme==='glow') {
579 cfg.cls = 'btn-glow roo-button';
581 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
583 cfg.cls += ' ' + this.weight;
589 this.cls += ' inverse';
594 cfg.cls += ' active';
598 cfg.disabled = 'disabled';
602 Roo.log('changing to ul' );
604 this.glyphicon = 'caret';
607 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
609 //gsRoo.log(this.parentType);
610 if (this.parentType === 'Navbar' && !this.parent().bar) {
611 Roo.log('changing to li?');
620 href : this.href || '#'
623 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
624 cfg.cls += ' dropdown';
631 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
633 if (this.glyphicon) {
634 cfg.html = ' ' + cfg.html;
639 cls: 'glyphicon glyphicon-' + this.glyphicon
649 // cfg.cls='btn roo-button';
653 var value = cfg.html;
658 cls: 'glyphicon glyphicon-' + this.glyphicon,
677 cfg.cls += ' dropdown';
678 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
681 if (cfg.tag !== 'a' && this.href !== '') {
682 throw "Tag must be a to set href.";
683 } else if (this.href.length > 0) {
684 cfg.href = this.href;
687 if(this.removeClass){
692 cfg.target = this.target;
697 initEvents: function() {
698 // Roo.log('init events?');
699 // Roo.log(this.el.dom);
702 if (typeof (this.menu) != 'undefined') {
703 this.menu.parentType = this.xtype;
704 this.menu.triggerEl = this.el;
705 this.addxtype(Roo.apply({}, this.menu));
709 if (this.el.hasClass('roo-button')) {
710 this.el.on('click', this.onClick, this);
712 this.el.select('.roo-button').on('click', this.onClick, this);
715 if(this.removeClass){
716 this.el.on('click', this.onClick, this);
719 this.el.enableDisplayMode();
722 onClick : function(e)
728 Roo.log('button on click ');
729 if(this.preventDefault){
732 if (this.pressed === true || this.pressed === false) {
733 this.pressed = !this.pressed;
734 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
735 this.fireEvent('toggle', this, e, this.pressed);
739 this.fireEvent('click', this, e);
743 * Enables this button
747 this.disabled = false;
748 this.el.removeClass('disabled');
752 * Disable this button
756 this.disabled = true;
757 this.el.addClass('disabled');
760 * sets the active state on/off,
761 * @param {Boolean} state (optional) Force a particular state
763 setActive : function(v) {
765 this.el[v ? 'addClass' : 'removeClass']('active');
768 * toggles the current active state
770 toggleActive : function()
772 var active = this.el.hasClass('active');
773 this.setActive(!active);
777 setText : function(str)
779 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
783 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
806 * @class Roo.bootstrap.Column
807 * @extends Roo.bootstrap.Component
808 * Bootstrap Column class
809 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
810 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
811 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
812 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
813 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
814 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
815 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
816 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
819 * @cfg {Boolean} hidden (true|false) hide the element
820 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
821 * @cfg {String} fa (ban|check|...) font awesome icon
822 * @cfg {Number} fasize (1|2|....) font awsome size
824 * @cfg {String} icon (info-sign|check|...) glyphicon name
826 * @cfg {String} html content of column.
829 * Create a new Column
830 * @param {Object} config The config object
833 Roo.bootstrap.Column = function(config){
834 Roo.bootstrap.Column.superclass.constructor.call(this, config);
837 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
855 getAutoCreate : function(){
856 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
864 ['xs','sm','md','lg'].map(function(size){
865 //Roo.log( size + ':' + settings[size]);
867 if (settings[size+'off'] !== false) {
868 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
871 if (settings[size] === false) {
874 Roo.log(settings[size]);
875 if (!settings[size]) { // 0 = hidden
876 cfg.cls += ' hidden-' + size;
879 cfg.cls += ' col-' + size + '-' + settings[size];
884 cfg.cls += ' hidden';
887 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
888 cfg.cls +=' alert alert-' + this.alert;
892 if (this.html.length) {
893 cfg.html = this.html;
897 if (this.fasize > 1) {
898 fasize = ' fa-' + this.fasize + 'x';
900 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
905 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
924 * @class Roo.bootstrap.Container
925 * @extends Roo.bootstrap.Component
926 * Bootstrap Container class
927 * @cfg {Boolean} jumbotron is it a jumbotron element
928 * @cfg {String} html content of element
929 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
930 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
931 * @cfg {String} header content of header (for panel)
932 * @cfg {String} footer content of footer (for panel)
933 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
934 * @cfg {String} tag (header|aside|section) type of HTML tag.
935 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
936 * @cfg {String} fa (ban|check|...) font awesome icon
937 * @cfg {String} icon (info-sign|check|...) glyphicon name
938 * @cfg {Boolean} hidden (true|false) hide the element
942 * Create a new Container
943 * @param {Object} config The config object
946 Roo.bootstrap.Container = function(config){
947 Roo.bootstrap.Container.superclass.constructor.call(this, config);
950 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
964 getChildContainer : function() {
970 if (this.panel.length) {
971 return this.el.select('.panel-body',true).first();
978 getAutoCreate : function(){
981 tag : this.tag || 'div',
985 if (this.jumbotron) {
986 cfg.cls = 'jumbotron';
991 // - this is applied by the parent..
993 // cfg.cls = this.cls + '';
996 if (this.sticky.length) {
998 var bd = Roo.get(document.body);
999 if (!bd.hasClass('bootstrap-sticky')) {
1000 bd.addClass('bootstrap-sticky');
1001 Roo.select('html',true).setStyle('height', '100%');
1004 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1008 if (this.well.length) {
1009 switch (this.well) {
1012 cfg.cls +=' well well-' +this.well;
1021 cfg.cls += ' hidden';
1025 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1026 cfg.cls +=' alert alert-' + this.alert;
1031 if (this.panel.length) {
1032 cfg.cls += ' panel panel-' + this.panel;
1034 if (this.header.length) {
1037 cls : 'panel-heading',
1040 cls : 'panel-title',
1053 if (this.footer.length) {
1055 cls : 'panel-footer',
1064 body.html = this.html || cfg.html;
1065 // prefix with the icons..
1067 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1070 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1075 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1076 cfg.cls = 'container';
1082 titleEl : function()
1084 if(!this.el || !this.panel.length || !this.header.length){
1088 return this.el.select('.panel-title',true).first();
1091 setTitle : function(v)
1093 var titleEl = this.titleEl();
1099 titleEl.dom.innerHTML = v;
1102 getTitle : function()
1105 var titleEl = this.titleEl();
1111 return titleEl.dom.innerHTML;
1125 * @class Roo.bootstrap.Img
1126 * @extends Roo.bootstrap.Component
1127 * Bootstrap Img class
1128 * @cfg {Boolean} imgResponsive false | true
1129 * @cfg {String} border rounded | circle | thumbnail
1130 * @cfg {String} src image source
1131 * @cfg {String} alt image alternative text
1132 * @cfg {String} href a tag href
1133 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1136 * Create a new Input
1137 * @param {Object} config The config object
1140 Roo.bootstrap.Img = function(config){
1141 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1147 * The img click event for the img.
1148 * @param {Roo.EventObject} e
1154 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1156 imgResponsive: true,
1162 getAutoCreate : function(){
1166 cls: (this.imgResponsive) ? 'img-responsive' : '',
1170 cfg.html = this.html || cfg.html;
1172 cfg.src = this.src || cfg.src;
1174 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1175 cfg.cls += ' img-' + this.border;
1192 a.target = this.target;
1198 return (this.href) ? a : cfg;
1201 initEvents: function() {
1204 this.el.on('click', this.onClick, this);
1208 onClick : function(e)
1210 Roo.log('img onclick');
1211 this.fireEvent('click', this, e);
1225 * @class Roo.bootstrap.Link
1226 * @extends Roo.bootstrap.Component
1227 * Bootstrap Link Class
1228 * @cfg {String} alt image alternative text
1229 * @cfg {String} href a tag href
1230 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1231 * @cfg {String} html the content of the link.
1232 * @cfg {String} anchor name for the anchor link
1234 * @cfg {Boolean} preventDefault (true | false) default false
1238 * Create a new Input
1239 * @param {Object} config The config object
1242 Roo.bootstrap.Link = function(config){
1243 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1249 * The img click event for the img.
1250 * @param {Roo.EventObject} e
1256 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1260 preventDefault: false,
1264 getAutoCreate : function()
1270 // anchor's do not require html/href...
1271 if (this.anchor === false) {
1272 cfg.html = this.html || 'html-missing';
1273 cfg.href = this.href || '#';
1275 cfg.name = this.anchor;
1276 if (this.html !== false) {
1277 cfg.html = this.html;
1279 if (this.href !== false) {
1280 cfg.href = this.href;
1284 if(this.alt !== false){
1289 if(this.target !== false) {
1290 cfg.target = this.target;
1296 initEvents: function() {
1298 if(!this.href || this.preventDefault){
1299 this.el.on('click', this.onClick, this);
1303 onClick : function(e)
1305 if(this.preventDefault){
1308 //Roo.log('img onclick');
1309 this.fireEvent('click', this, e);
1322 * @class Roo.bootstrap.Header
1323 * @extends Roo.bootstrap.Component
1324 * Bootstrap Header class
1325 * @cfg {String} html content of header
1326 * @cfg {Number} level (1|2|3|4|5|6) default 1
1329 * Create a new Header
1330 * @param {Object} config The config object
1334 Roo.bootstrap.Header = function(config){
1335 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1338 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1346 getAutoCreate : function(){
1349 tag: 'h' + (1 *this.level),
1350 html: this.html || 'fill in html'
1362 * Ext JS Library 1.1.1
1363 * Copyright(c) 2006-2007, Ext JS, LLC.
1365 * Originally Released Under LGPL - original licence link has changed is not relivant.
1368 * <script type="text/javascript">
1372 * @class Roo.bootstrap.MenuMgr
1373 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1376 Roo.bootstrap.MenuMgr = function(){
1377 var menus, active, groups = {}, attached = false, lastShow = new Date();
1379 // private - called when first menu is created
1382 active = new Roo.util.MixedCollection();
1383 Roo.get(document).addKeyListener(27, function(){
1384 if(active.length > 0){
1392 if(active && active.length > 0){
1393 var c = active.clone();
1403 if(active.length < 1){
1404 Roo.get(document).un("mouseup", onMouseDown);
1412 var last = active.last();
1413 lastShow = new Date();
1416 Roo.get(document).on("mouseup", onMouseDown);
1421 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1422 m.parentMenu.activeChild = m;
1423 }else if(last && last.isVisible()){
1424 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1429 function onBeforeHide(m){
1431 m.activeChild.hide();
1433 if(m.autoHideTimer){
1434 clearTimeout(m.autoHideTimer);
1435 delete m.autoHideTimer;
1440 function onBeforeShow(m){
1441 var pm = m.parentMenu;
1442 if(!pm && !m.allowOtherMenus){
1444 }else if(pm && pm.activeChild && active != m){
1445 pm.activeChild.hide();
1450 function onMouseDown(e){
1451 Roo.log("on MouseDown");
1452 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1460 function onBeforeCheck(mi, state){
1462 var g = groups[mi.group];
1463 for(var i = 0, l = g.length; i < l; i++){
1465 g[i].setChecked(false);
1474 * Hides all menus that are currently visible
1476 hideAll : function(){
1481 register : function(menu){
1485 menus[menu.id] = menu;
1486 menu.on("beforehide", onBeforeHide);
1487 menu.on("hide", onHide);
1488 menu.on("beforeshow", onBeforeShow);
1489 menu.on("show", onShow);
1491 if(g && menu.events["checkchange"]){
1495 groups[g].push(menu);
1496 menu.on("checkchange", onCheck);
1501 * Returns a {@link Roo.menu.Menu} object
1502 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1503 * be used to generate and return a new Menu instance.
1505 get : function(menu){
1506 if(typeof menu == "string"){ // menu id
1508 }else if(menu.events){ // menu instance
1511 /*else if(typeof menu.length == 'number'){ // array of menu items?
1512 return new Roo.bootstrap.Menu({items:menu});
1513 }else{ // otherwise, must be a config
1514 return new Roo.bootstrap.Menu(menu);
1521 unregister : function(menu){
1522 delete menus[menu.id];
1523 menu.un("beforehide", onBeforeHide);
1524 menu.un("hide", onHide);
1525 menu.un("beforeshow", onBeforeShow);
1526 menu.un("show", onShow);
1528 if(g && menu.events["checkchange"]){
1529 groups[g].remove(menu);
1530 menu.un("checkchange", onCheck);
1535 registerCheckable : function(menuItem){
1536 var g = menuItem.group;
1541 groups[g].push(menuItem);
1542 menuItem.on("beforecheckchange", onBeforeCheck);
1547 unregisterCheckable : function(menuItem){
1548 var g = menuItem.group;
1550 groups[g].remove(menuItem);
1551 menuItem.un("beforecheckchange", onBeforeCheck);
1563 * @class Roo.bootstrap.Menu
1564 * @extends Roo.bootstrap.Component
1565 * Bootstrap Menu class - container for MenuItems
1566 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1570 * @param {Object} config The config object
1574 Roo.bootstrap.Menu = function(config){
1575 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1576 if (this.registerMenu) {
1577 Roo.bootstrap.MenuMgr.register(this);
1582 * Fires before this menu is displayed
1583 * @param {Roo.menu.Menu} this
1588 * Fires before this menu is hidden
1589 * @param {Roo.menu.Menu} this
1594 * Fires after this menu is displayed
1595 * @param {Roo.menu.Menu} this
1600 * Fires after this menu is hidden
1601 * @param {Roo.menu.Menu} this
1606 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1607 * @param {Roo.menu.Menu} this
1608 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1609 * @param {Roo.EventObject} e
1614 * Fires when the mouse is hovering over this menu
1615 * @param {Roo.menu.Menu} this
1616 * @param {Roo.EventObject} e
1617 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1622 * Fires when the mouse exits this menu
1623 * @param {Roo.menu.Menu} this
1624 * @param {Roo.EventObject} e
1625 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1630 * Fires when a menu item contained in this menu is clicked
1631 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1632 * @param {Roo.EventObject} e
1636 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1639 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1643 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1646 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1648 registerMenu : true,
1650 menuItems :false, // stores the menu items..
1656 getChildContainer : function() {
1660 getAutoCreate : function(){
1662 //if (['right'].indexOf(this.align)!==-1) {
1663 // cfg.cn[1].cls += ' pull-right'
1669 cls : 'dropdown-menu' ,
1670 style : 'z-index:1000'
1674 if (this.type === 'submenu') {
1675 cfg.cls = 'submenu active';
1677 if (this.type === 'treeview') {
1678 cfg.cls = 'treeview-menu';
1683 initEvents : function() {
1685 // Roo.log("ADD event");
1686 // Roo.log(this.triggerEl.dom);
1687 this.triggerEl.on('click', this.onTriggerPress, this);
1688 this.triggerEl.addClass('dropdown-toggle');
1689 this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1691 this.el.on("mouseover", this.onMouseOver, this);
1692 this.el.on("mouseout", this.onMouseOut, this);
1696 findTargetItem : function(e){
1697 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1701 //Roo.log(t); Roo.log(t.id);
1703 //Roo.log(this.menuitems);
1704 return this.menuitems.get(t.id);
1706 //return this.items.get(t.menuItemId);
1711 onClick : function(e){
1712 Roo.log("menu.onClick");
1713 var t = this.findTargetItem(e);
1714 if(!t || t.isContainer){
1719 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
1720 if(t == this.activeItem && t.shouldDeactivate(e)){
1721 this.activeItem.deactivate();
1722 delete this.activeItem;
1726 this.setActiveItem(t, true);
1734 Roo.log('pass click event');
1738 this.fireEvent("click", this, t, e);
1742 onMouseOver : function(e){
1743 var t = this.findTargetItem(e);
1746 // if(t.canActivate && !t.disabled){
1747 // this.setActiveItem(t, true);
1751 this.fireEvent("mouseover", this, e, t);
1753 isVisible : function(){
1754 return !this.hidden;
1756 onMouseOut : function(e){
1757 var t = this.findTargetItem(e);
1760 // if(t == this.activeItem && t.shouldDeactivate(e)){
1761 // this.activeItem.deactivate();
1762 // delete this.activeItem;
1765 this.fireEvent("mouseout", this, e, t);
1770 * Displays this menu relative to another element
1771 * @param {String/HTMLElement/Roo.Element} element The element to align to
1772 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1773 * the element (defaults to this.defaultAlign)
1774 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1776 show : function(el, pos, parentMenu){
1777 this.parentMenu = parentMenu;
1781 this.fireEvent("beforeshow", this);
1782 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1785 * Displays this menu at a specific xy position
1786 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1787 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1789 showAt : function(xy, parentMenu, /* private: */_e){
1790 this.parentMenu = parentMenu;
1795 this.fireEvent("beforeshow", this);
1797 //xy = this.el.adjustForConstraints(xy);
1799 //this.el.setXY(xy);
1801 this.hideMenuItems();
1802 this.hidden = false;
1803 this.triggerEl.addClass('open');
1805 this.fireEvent("show", this);
1811 this.doFocus.defer(50, this);
1815 doFocus : function(){
1817 this.focusEl.focus();
1822 * Hides this menu and optionally all parent menus
1823 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1825 hide : function(deep){
1827 this.hideMenuItems();
1828 if(this.el && this.isVisible()){
1829 this.fireEvent("beforehide", this);
1830 if(this.activeItem){
1831 this.activeItem.deactivate();
1832 this.activeItem = null;
1834 this.triggerEl.removeClass('open');;
1836 this.fireEvent("hide", this);
1838 if(deep === true && this.parentMenu){
1839 this.parentMenu.hide(true);
1843 onTriggerPress : function(e)
1846 Roo.log('trigger press');
1847 //Roo.log(e.getTarget());
1848 // Roo.log(this.triggerEl.dom);
1849 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1852 if (this.isVisible()) {
1856 this.show(this.triggerEl, false, false);
1865 hideMenuItems : function()
1867 //$(backdrop).remove()
1868 Roo.select('.open',true).each(function(aa) {
1870 aa.removeClass('open');
1871 //var parent = getParent($(this))
1872 //var relatedTarget = { relatedTarget: this }
1874 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1875 //if (e.isDefaultPrevented()) return
1876 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1879 addxtypeChild : function (tree, cntr) {
1880 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1882 this.menuitems.add(comp);
1903 * @class Roo.bootstrap.MenuItem
1904 * @extends Roo.bootstrap.Component
1905 * Bootstrap MenuItem class
1906 * @cfg {String} html the menu label
1907 * @cfg {String} href the link
1908 * @cfg {Boolean} preventDefault (true | false) default true
1909 * @cfg {Boolean} isContainer (true | false) default false
1913 * Create a new MenuItem
1914 * @param {Object} config The config object
1918 Roo.bootstrap.MenuItem = function(config){
1919 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1924 * The raw click event for the entire grid.
1925 * @param {Roo.EventObject} e
1931 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
1935 preventDefault: true,
1936 isContainer : false,
1938 getAutoCreate : function(){
1940 if(this.isContainer){
1943 cls: 'dropdown-menu-item'
1949 cls: 'dropdown-menu-item',
1958 if (this.parent().type == 'treeview') {
1959 cfg.cls = 'treeview-menu';
1962 cfg.cn[0].href = this.href || cfg.cn[0].href ;
1963 cfg.cn[0].html = this.html || cfg.cn[0].html ;
1967 initEvents: function() {
1969 //this.el.select('a').on('click', this.onClick, this);
1972 onClick : function(e)
1974 Roo.log('item on click ');
1975 //if(this.preventDefault){
1976 // e.preventDefault();
1978 //this.parent().hideMenuItems();
1980 this.fireEvent('click', this, e);
1999 * @class Roo.bootstrap.MenuSeparator
2000 * @extends Roo.bootstrap.Component
2001 * Bootstrap MenuSeparator class
2004 * Create a new MenuItem
2005 * @param {Object} config The config object
2009 Roo.bootstrap.MenuSeparator = function(config){
2010 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2013 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2015 getAutoCreate : function(){
2030 <div class="modal fade">
2031 <div class="modal-dialog">
2032 <div class="modal-content">
2033 <div class="modal-header">
2034 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
2035 <h4 class="modal-title">Modal title</h4>
2037 <div class="modal-body">
2038 <p>One fine body…</p>
2040 <div class="modal-footer">
2041 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2042 <button type="button" class="btn btn-primary">Save changes</button>
2044 </div><!-- /.modal-content -->
2045 </div><!-- /.modal-dialog -->
2046 </div><!-- /.modal -->
2056 * @class Roo.bootstrap.Modal
2057 * @extends Roo.bootstrap.Component
2058 * Bootstrap Modal class
2059 * @cfg {String} title Title of dialog
2060 * @cfg {Boolean} specificTitle (true|false) default false
2061 * @cfg {Array} buttons Array of buttons or standard button set..
2062 * @cfg {String} buttonPosition (left|right|center) default right
2063 * @cfg {Boolean} animate (true | false) default true
2066 * Create a new Modal Dialog
2067 * @param {Object} config The config object
2070 Roo.bootstrap.Modal = function(config){
2071 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2076 * The raw btnclick event for the button
2077 * @param {Roo.EventObject} e
2081 this.buttons = this.buttons || [];
2084 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2086 title : 'test dialog',
2093 specificTitle: false,
2095 buttonPosition: 'right',
2099 onRender : function(ct, position)
2101 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2104 var cfg = Roo.apply({}, this.getAutoCreate());
2107 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2109 //if (!cfg.name.length) {
2113 cfg.cls += ' ' + this.cls;
2116 cfg.style = this.style;
2118 this.el = Roo.get(document.body).createChild(cfg, position);
2120 //var type = this.el.dom.type;
2122 if(this.tabIndex !== undefined){
2123 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2128 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2129 this.maskEl.enableDisplayMode("block");
2131 //this.el.addClass("x-dlg-modal");
2133 if (this.buttons.length) {
2134 Roo.each(this.buttons, function(bb) {
2135 b = Roo.apply({}, bb);
2136 b.xns = b.xns || Roo.bootstrap;
2137 b.xtype = b.xtype || 'Button';
2138 if (typeof(b.listeners) == 'undefined') {
2139 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2142 var btn = Roo.factory(b);
2144 btn.onRender(this.el.select('.modal-footer div').first());
2148 // render the children.
2151 if(typeof(this.items) != 'undefined'){
2152 var items = this.items;
2155 for(var i =0;i < items.length;i++) {
2156 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2160 this.items = nitems;
2162 this.body = this.el.select('.modal-body',true).first();
2163 this.close = this.el.select('.modal-header .close', true).first();
2164 this.footer = this.el.select('.modal-footer',true).first();
2166 //this.el.addClass([this.fieldClass, this.cls]);
2169 getAutoCreate : function(){
2174 html : this.html || ''
2179 cls : 'modal-title',
2183 if(this.specificTitle){
2189 style : 'display: none',
2192 cls: "modal-dialog",
2195 cls : "modal-content",
2198 cls : 'modal-header',
2210 cls : 'modal-footer',
2214 cls: 'btn-' + this.buttonPosition
2231 modal.cls += ' fade';
2237 getChildContainer : function() {
2239 return this.el.select('.modal-body',true).first();
2242 getButtonContainer : function() {
2243 return this.el.select('.modal-footer div',true).first();
2246 initEvents : function()
2248 this.el.select('.modal-header .close').on('click', this.hide, this);
2250 // this.addxtype(this);
2254 if (!this.rendered) {
2258 this.el.setStyle('display', 'block');
2262 (function(){ _this.el.addClass('in'); }).defer(50);
2264 this.el.addClass('in');
2267 Roo.get(document.body).addClass("x-body-masked");
2268 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2270 this.el.setStyle('zIndex', '10001');
2271 this.fireEvent('show', this);
2278 Roo.get(document.body).removeClass("x-body-masked");
2279 this.el.removeClass('in');
2283 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2285 this.el.setStyle('display', 'none');
2288 this.fireEvent('hide', this);
2291 addButton : function(str, cb)
2295 var b = Roo.apply({}, { html : str } );
2296 b.xns = b.xns || Roo.bootstrap;
2297 b.xtype = b.xtype || 'Button';
2298 if (typeof(b.listeners) == 'undefined') {
2299 b.listeners = { click : cb.createDelegate(this) };
2302 var btn = Roo.factory(b);
2304 btn.onRender(this.el.select('.modal-footer div').first());
2310 setDefaultButton : function(btn)
2312 //this.el.select('.modal-footer').()
2314 resizeTo: function(w,h)
2318 setContentSize : function(w, h)
2322 onButtonClick: function(btn,e)
2325 this.fireEvent('btnclick', btn.name, e);
2327 setTitle: function(str) {
2328 this.el.select('.modal-title',true).first().dom.innerHTML = str;
2334 Roo.apply(Roo.bootstrap.Modal, {
2336 * Button config that displays a single OK button
2345 * Button config that displays Yes and No buttons
2361 * Button config that displays OK and Cancel buttons
2376 * Button config that displays Yes, No and Cancel buttons
2398 * messagebox - can be used as a replace
2402 * @class Roo.MessageBox
2403 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2407 Roo.Msg.alert('Status', 'Changes saved successfully.');
2409 // Prompt for user data:
2410 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2412 // process text value...
2416 // Show a dialog using config options:
2418 title:'Save Changes?',
2419 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2420 buttons: Roo.Msg.YESNOCANCEL,
2427 Roo.bootstrap.MessageBox = function(){
2428 var dlg, opt, mask, waitTimer;
2429 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2430 var buttons, activeTextEl, bwidth;
2434 var handleButton = function(button){
2436 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2440 var handleHide = function(){
2442 dlg.el.removeClass(opt.cls);
2445 // Roo.TaskMgr.stop(waitTimer);
2446 // waitTimer = null;
2451 var updateButtons = function(b){
2454 buttons["ok"].hide();
2455 buttons["cancel"].hide();
2456 buttons["yes"].hide();
2457 buttons["no"].hide();
2458 //dlg.footer.dom.style.display = 'none';
2461 dlg.footer.dom.style.display = '';
2462 for(var k in buttons){
2463 if(typeof buttons[k] != "function"){
2466 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2467 width += buttons[k].el.getWidth()+15;
2477 var handleEsc = function(d, k, e){
2478 if(opt && opt.closable !== false){
2488 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2489 * @return {Roo.BasicDialog} The BasicDialog element
2491 getDialog : function(){
2493 dlg = new Roo.bootstrap.Modal( {
2496 //constraintoviewport:false,
2498 //collapsible : false,
2503 //buttonAlign:"center",
2504 closeClick : function(){
2505 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2508 handleButton("cancel");
2513 dlg.on("hide", handleHide);
2515 //dlg.addKeyListener(27, handleEsc);
2517 this.buttons = buttons;
2518 var bt = this.buttonText;
2519 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2520 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2521 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2522 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2524 bodyEl = dlg.body.createChild({
2526 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2527 '<textarea class="roo-mb-textarea"></textarea>' +
2528 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2530 msgEl = bodyEl.dom.firstChild;
2531 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2532 textboxEl.enableDisplayMode();
2533 textboxEl.addKeyListener([10,13], function(){
2534 if(dlg.isVisible() && opt && opt.buttons){
2537 }else if(opt.buttons.yes){
2538 handleButton("yes");
2542 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2543 textareaEl.enableDisplayMode();
2544 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2545 progressEl.enableDisplayMode();
2546 var pf = progressEl.dom.firstChild;
2548 pp = Roo.get(pf.firstChild);
2549 pp.setHeight(pf.offsetHeight);
2557 * Updates the message box body text
2558 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2559 * the XHTML-compliant non-breaking space character '&#160;')
2560 * @return {Roo.MessageBox} This message box
2562 updateText : function(text){
2563 if(!dlg.isVisible() && !opt.width){
2564 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2566 msgEl.innerHTML = text || ' ';
2568 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2569 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2571 Math.min(opt.width || cw , this.maxWidth),
2572 Math.max(opt.minWidth || this.minWidth, bwidth)
2575 activeTextEl.setWidth(w);
2577 if(dlg.isVisible()){
2578 dlg.fixedcenter = false;
2580 // to big, make it scroll. = But as usual stupid IE does not support
2583 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2584 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2585 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2587 bodyEl.dom.style.height = '';
2588 bodyEl.dom.style.overflowY = '';
2591 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2593 bodyEl.dom.style.overflowX = '';
2596 dlg.setContentSize(w, bodyEl.getHeight());
2597 if(dlg.isVisible()){
2598 dlg.fixedcenter = true;
2604 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2605 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2606 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2607 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2608 * @return {Roo.MessageBox} This message box
2610 updateProgress : function(value, text){
2612 this.updateText(text);
2614 if (pp) { // weird bug on my firefox - for some reason this is not defined
2615 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2621 * Returns true if the message box is currently displayed
2622 * @return {Boolean} True if the message box is visible, else false
2624 isVisible : function(){
2625 return dlg && dlg.isVisible();
2629 * Hides the message box if it is displayed
2632 if(this.isVisible()){
2638 * Displays a new message box, or reinitializes an existing message box, based on the config options
2639 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2640 * The following config object properties are supported:
2642 Property Type Description
2643 ---------- --------------- ------------------------------------------------------------------------------------
2644 animEl String/Element An id or Element from which the message box should animate as it opens and
2645 closes (defaults to undefined)
2646 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2647 cancel:'Bar'}), or false to not show any buttons (defaults to false)
2648 closable Boolean False to hide the top-right close button (defaults to true). Note that
2649 progress and wait dialogs will ignore this property and always hide the
2650 close button as they can only be closed programmatically.
2651 cls String A custom CSS class to apply to the message box element
2652 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
2653 displayed (defaults to 75)
2654 fn Function A callback function to execute after closing the dialog. The arguments to the
2655 function will be btn (the name of the button that was clicked, if applicable,
2656 e.g. "ok"), and text (the value of the active text field, if applicable).
2657 Progress and wait dialogs will ignore this option since they do not respond to
2658 user actions and can only be closed programmatically, so any required function
2659 should be called by the same code after it closes the dialog.
2660 icon String A CSS class that provides a background image to be used as an icon for
2661 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2662 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
2663 minWidth Number The minimum width in pixels of the message box (defaults to 100)
2664 modal Boolean False to allow user interaction with the page while the message box is
2665 displayed (defaults to true)
2666 msg String A string that will replace the existing message box body text (defaults
2667 to the XHTML-compliant non-breaking space character ' ')
2668 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
2669 progress Boolean True to display a progress bar (defaults to false)
2670 progressText String The text to display inside the progress bar if progress = true (defaults to '')
2671 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
2672 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
2673 title String The title text
2674 value String The string value to set into the active textbox element if displayed
2675 wait Boolean True to display a progress bar (defaults to false)
2676 width Number The width of the dialog in pixels
2683 msg: 'Please enter your address:',
2685 buttons: Roo.MessageBox.OKCANCEL,
2688 animEl: 'addAddressBtn'
2691 * @param {Object} config Configuration options
2692 * @return {Roo.MessageBox} This message box
2694 show : function(options)
2697 // this causes nightmares if you show one dialog after another
2698 // especially on callbacks..
2700 if(this.isVisible()){
2703 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2704 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
2705 Roo.log("New Dialog Message:" + options.msg )
2706 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2707 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2710 var d = this.getDialog();
2712 d.setTitle(opt.title || " ");
2713 d.close.setDisplayed(opt.closable !== false);
2714 activeTextEl = textboxEl;
2715 opt.prompt = opt.prompt || (opt.multiline ? true : false);
2720 textareaEl.setHeight(typeof opt.multiline == "number" ?
2721 opt.multiline : this.defaultTextHeight);
2722 activeTextEl = textareaEl;
2731 progressEl.setDisplayed(opt.progress === true);
2732 this.updateProgress(0);
2733 activeTextEl.dom.value = opt.value || "";
2735 dlg.setDefaultButton(activeTextEl);
2737 var bs = opt.buttons;
2741 }else if(bs && bs.yes){
2742 db = buttons["yes"];
2744 dlg.setDefaultButton(db);
2746 bwidth = updateButtons(opt.buttons);
2747 this.updateText(opt.msg);
2749 d.el.addClass(opt.cls);
2751 d.proxyDrag = opt.proxyDrag === true;
2752 d.modal = opt.modal !== false;
2753 d.mask = opt.modal !== false ? mask : false;
2755 // force it to the end of the z-index stack so it gets a cursor in FF
2756 document.body.appendChild(dlg.el.dom);
2757 d.animateTarget = null;
2758 d.show(options.animEl);
2764 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
2765 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2766 * and closing the message box when the process is complete.
2767 * @param {String} title The title bar text
2768 * @param {String} msg The message box body text
2769 * @return {Roo.MessageBox} This message box
2771 progress : function(title, msg){
2778 minWidth: this.minProgressWidth,
2785 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2786 * If a callback function is passed it will be called after the user clicks the button, and the
2787 * id of the button that was clicked will be passed as the only parameter to the callback
2788 * (could also be the top-right close button).
2789 * @param {String} title The title bar text
2790 * @param {String} msg The message box body text
2791 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2792 * @param {Object} scope (optional) The scope of the callback function
2793 * @return {Roo.MessageBox} This message box
2795 alert : function(title, msg, fn, scope){
2808 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
2809 * interaction while waiting for a long-running process to complete that does not have defined intervals.
2810 * You are responsible for closing the message box when the process is complete.
2811 * @param {String} msg The message box body text
2812 * @param {String} title (optional) The title bar text
2813 * @return {Roo.MessageBox} This message box
2815 wait : function(msg, title){
2826 waitTimer = Roo.TaskMgr.start({
2828 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2836 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2837 * If a callback function is passed it will be called after the user clicks either button, and the id of the
2838 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2839 * @param {String} title The title bar text
2840 * @param {String} msg The message box body text
2841 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2842 * @param {Object} scope (optional) The scope of the callback function
2843 * @return {Roo.MessageBox} This message box
2845 confirm : function(title, msg, fn, scope){
2849 buttons: this.YESNO,
2858 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2859 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
2860 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2861 * (could also be the top-right close button) and the text that was entered will be passed as the two
2862 * parameters to the callback.
2863 * @param {String} title The title bar text
2864 * @param {String} msg The message box body text
2865 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2866 * @param {Object} scope (optional) The scope of the callback function
2867 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2868 * property, or the height in pixels to create the textbox (defaults to false / single-line)
2869 * @return {Roo.MessageBox} This message box
2871 prompt : function(title, msg, fn, scope, multiline){
2875 buttons: this.OKCANCEL,
2880 multiline: multiline,
2887 * Button config that displays a single OK button
2892 * Button config that displays Yes and No buttons
2895 YESNO : {yes:true, no:true},
2897 * Button config that displays OK and Cancel buttons
2900 OKCANCEL : {ok:true, cancel:true},
2902 * Button config that displays Yes, No and Cancel buttons
2905 YESNOCANCEL : {yes:true, no:true, cancel:true},
2908 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2911 defaultTextHeight : 75,
2913 * The maximum width in pixels of the message box (defaults to 600)
2918 * The minimum width in pixels of the message box (defaults to 100)
2923 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
2924 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2927 minProgressWidth : 250,
2929 * An object containing the default button text strings that can be overriden for localized language support.
2930 * Supported properties are: ok, cancel, yes and no.
2931 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2944 * Shorthand for {@link Roo.MessageBox}
2946 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox
2947 Roo.Msg = Roo.Msg || Roo.MessageBox;
2956 * @class Roo.bootstrap.Navbar
2957 * @extends Roo.bootstrap.Component
2958 * Bootstrap Navbar class
2961 * Create a new Navbar
2962 * @param {Object} config The config object
2966 Roo.bootstrap.Navbar = function(config){
2967 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2971 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
2980 getAutoCreate : function(){
2983 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2987 initEvents :function ()
2989 //Roo.log(this.el.select('.navbar-toggle',true));
2990 this.el.select('.navbar-toggle',true).on('click', function() {
2991 // Roo.log('click');
2992 this.el.select('.navbar-collapse',true).toggleClass('in');
3000 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3002 var size = this.el.getSize();
3003 this.maskEl.setSize(size.width, size.height);
3004 this.maskEl.enableDisplayMode("block");
3013 getChildContainer : function()
3015 if (this.el.select('.collapse').getCount()) {
3016 return this.el.select('.collapse',true).first();
3049 * @class Roo.bootstrap.NavSimplebar
3050 * @extends Roo.bootstrap.Navbar
3051 * Bootstrap Sidebar class
3053 * @cfg {Boolean} inverse is inverted color
3055 * @cfg {String} type (nav | pills | tabs)
3056 * @cfg {Boolean} arrangement stacked | justified
3057 * @cfg {String} align (left | right) alignment
3059 * @cfg {Boolean} main (true|false) main nav bar? default false
3060 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3062 * @cfg {String} tag (header|footer|nav|div) default is nav
3068 * Create a new Sidebar
3069 * @param {Object} config The config object
3073 Roo.bootstrap.NavSimplebar = function(config){
3074 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3077 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3093 getAutoCreate : function(){
3097 tag : this.tag || 'div',
3110 this.type = this.type || 'nav';
3111 if (['tabs','pills'].indexOf(this.type)!==-1) {
3112 cfg.cn[0].cls += ' nav-' + this.type
3116 if (this.type!=='nav') {
3117 Roo.log('nav type must be nav/tabs/pills')
3119 cfg.cn[0].cls += ' navbar-nav'
3125 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3126 cfg.cn[0].cls += ' nav-' + this.arrangement;
3130 if (this.align === 'right') {
3131 cfg.cn[0].cls += ' navbar-right';
3135 cfg.cls += ' navbar-inverse';
3162 * @class Roo.bootstrap.NavHeaderbar
3163 * @extends Roo.bootstrap.NavSimplebar
3164 * Bootstrap Sidebar class
3166 * @cfg {String} brand what is brand
3167 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3168 * @cfg {String} brand_href href of the brand
3169 * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3170 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3173 * Create a new Sidebar
3174 * @param {Object} config The config object
3178 Roo.bootstrap.NavHeaderbar = function(config){
3179 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3182 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3190 getAutoCreate : function(){
3193 tag: this.nav || 'nav',
3202 cls: 'navbar-header',
3207 cls: 'navbar-toggle',
3208 'data-toggle': 'collapse',
3213 html: 'Toggle navigation'
3235 cls: 'collapse navbar-collapse',
3239 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3241 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3242 cfg.cls += ' navbar-' + this.position;
3244 // tag can override this..
3246 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3249 if (this.brand !== '') {
3252 href: this.brand_href ? this.brand_href : '#',
3253 cls: 'navbar-brand',
3261 cfg.cls += ' main-nav';
3269 initEvents : function()
3271 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3273 if (this.autohide) {
3278 Roo.get(document).on('scroll',function(e) {
3279 var ns = Roo.get(document).getScroll().top;
3280 var os = prevScroll;
3284 ft.removeClass('slideDown');
3285 ft.addClass('slideUp');
3288 ft.removeClass('slideUp');
3289 ft.addClass('slideDown');
3313 * @class Roo.bootstrap.NavSidebar
3314 * @extends Roo.bootstrap.Navbar
3315 * Bootstrap Sidebar class
3318 * Create a new Sidebar
3319 * @param {Object} config The config object
3323 Roo.bootstrap.NavSidebar = function(config){
3324 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3327 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3329 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3331 getAutoCreate : function(){
3336 cls: 'sidebar sidebar-nav'
3358 * @class Roo.bootstrap.NavGroup
3359 * @extends Roo.bootstrap.Component
3360 * Bootstrap NavGroup class
3361 * @cfg {String} align left | right
3362 * @cfg {Boolean} inverse false | true
3363 * @cfg {String} type (nav|pills|tab) default nav
3364 * @cfg {String} navId - reference Id for navbar.
3368 * Create a new nav group
3369 * @param {Object} config The config object
3372 Roo.bootstrap.NavGroup = function(config){
3373 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3376 Roo.bootstrap.NavGroup.register(this);
3380 * Fires when the active item changes
3381 * @param {Roo.bootstrap.NavGroup} this
3382 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3383 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3390 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3401 getAutoCreate : function()
3403 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3410 if (['tabs','pills'].indexOf(this.type)!==-1) {
3411 cfg.cls += ' nav-' + this.type
3413 if (this.type!=='nav') {
3414 Roo.log('nav type must be nav/tabs/pills')
3416 cfg.cls += ' navbar-nav'
3419 if (this.parent().sidebar) {
3422 cls: 'dashboard-menu sidebar-menu'
3428 if (this.form === true) {
3434 if (this.align === 'right') {
3435 cfg.cls += ' navbar-right';
3437 cfg.cls += ' navbar-left';
3441 if (this.align === 'right') {
3442 cfg.cls += ' navbar-right';
3446 cfg.cls += ' navbar-inverse';
3454 * sets the active Navigation item
3455 * @param {Roo.bootstrap.NavItem} the new current navitem
3457 setActiveItem : function(item)
3460 Roo.each(this.navItems, function(v){
3465 v.setActive(false, true);
3472 item.setActive(true, true);
3473 this.fireEvent('changed', this, item, prev);
3478 * gets the active Navigation item
3479 * @return {Roo.bootstrap.NavItem} the current navitem
3481 getActive : function()
3485 Roo.each(this.navItems, function(v){
3496 indexOfNav : function()
3500 Roo.each(this.navItems, function(v,i){
3511 * adds a Navigation item
3512 * @param {Roo.bootstrap.NavItem} the navitem to add
3514 addItem : function(cfg)
3516 var cn = new Roo.bootstrap.NavItem(cfg);
3518 cn.parentId = this.id;
3519 cn.onRender(this.el, null);
3523 * register a Navigation item
3524 * @param {Roo.bootstrap.NavItem} the navitem to add
3526 register : function(item)
3528 this.navItems.push( item);
3529 item.navId = this.navId;
3534 * clear all the Navigation item
3537 clearAll : function()
3540 this.el.dom.innerHTML = '';
3543 getNavItem: function(tabId)
3546 Roo.each(this.navItems, function(e) {
3547 if (e.tabId == tabId) {
3557 setActiveNext : function()
3559 var i = this.indexOfNav(this.getActive());
3560 if (i > this.navItems.length) {
3563 this.setActiveItem(this.navItems[i+1]);
3565 setActivePrev : function()
3567 var i = this.indexOfNav(this.getActive());
3571 this.setActiveItem(this.navItems[i-1]);
3573 clearWasActive : function(except) {
3574 Roo.each(this.navItems, function(e) {
3575 if (e.tabId != except.tabId && e.was_active) {
3576 e.was_active = false;
3583 getWasActive : function ()
3586 Roo.each(this.navItems, function(e) {
3601 Roo.apply(Roo.bootstrap.NavGroup, {
3605 * register a Navigation Group
3606 * @param {Roo.bootstrap.NavGroup} the navgroup to add
3608 register : function(navgrp)
3610 this.groups[navgrp.navId] = navgrp;
3614 * fetch a Navigation Group based on the navigation ID
3615 * @param {string} the navgroup to add
3616 * @returns {Roo.bootstrap.NavGroup} the navgroup
3618 get: function(navId) {
3619 if (typeof(this.groups[navId]) == 'undefined') {
3621 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3623 return this.groups[navId] ;
3638 * @class Roo.bootstrap.NavItem
3639 * @extends Roo.bootstrap.Component
3640 * Bootstrap Navbar.NavItem class
3641 * @cfg {String} href link to
3642 * @cfg {String} html content of button
3643 * @cfg {String} badge text inside badge
3644 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3645 * @cfg {String} glyphicon name of glyphicon
3646 * @cfg {String} icon name of font awesome icon
3647 * @cfg {Boolean} active Is item active
3648 * @cfg {Boolean} disabled Is item disabled
3650 * @cfg {Boolean} preventDefault (true | false) default false
3651 * @cfg {String} tabId the tab that this item activates.
3652 * @cfg {String} tagtype (a|span) render as a href or span?
3655 * Create a new Navbar Item
3656 * @param {Object} config The config object
3658 Roo.bootstrap.NavItem = function(config){
3659 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3664 * The raw click event for the entire grid.
3665 * @param {Roo.EventObject} e
3670 * Fires when the active item active state changes
3671 * @param {Roo.bootstrap.NavItem} this
3672 * @param {boolean} state the new state
3680 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
3688 preventDefault : false,
3695 getAutoCreate : function(){
3703 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3705 if (this.disabled) {
3706 cfg.cls += ' disabled';
3709 if (this.href || this.html || this.glyphicon || this.icon) {
3713 href : this.href || "#",
3714 html: this.html || ''
3719 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3722 if(this.glyphicon) {
3723 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
3728 cfg.cn[0].html += " <span class='caret'></span>";
3732 if (this.badge !== '') {
3734 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3742 initEvents: function()
3744 if (typeof (this.menu) != 'undefined') {
3745 this.menu.parentType = this.xtype;
3746 this.menu.triggerEl = this.el;
3747 this.addxtype(Roo.apply({}, this.menu));
3750 this.el.select('a',true).on('click', this.onClick, this);
3752 if(this.tagtype == 'span'){
3753 this.el.select('span',true).on('click', this.onClick, this);
3756 // at this point parent should be available..
3757 this.parent().register(this);
3760 onClick : function(e)
3763 if(this.preventDefault){
3766 if (this.disabled) {
3770 var tg = Roo.bootstrap.TabGroup.get(this.navId);
3771 if (tg && tg.transition) {
3772 Roo.log("waiting for the transitionend");
3776 Roo.log("fire event clicked");
3777 if(this.fireEvent('click', this, e) === false){
3781 if(this.tagtype == 'span'){
3785 var p = this.parent();
3786 if (['tabs','pills'].indexOf(p.type)!==-1) {
3787 if (typeof(p.setActiveItem) !== 'undefined') {
3788 p.setActiveItem(this);
3791 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3792 if (p.parentType == 'NavHeaderbar' && !this.menu) {
3793 // remove the collapsed menu expand...
3794 p.parent().el.select('.navbar-collapse',true).removeClass('in');
3799 isActive: function () {
3802 setActive : function(state, fire, is_was_active)
3804 if (this.active && !state & this.navId) {
3805 this.was_active = true;
3806 var nv = Roo.bootstrap.NavGroup.get(this.navId);
3808 nv.clearWasActive(this);
3812 this.active = state;
3815 this.el.removeClass('active');
3816 } else if (!this.el.hasClass('active')) {
3817 this.el.addClass('active');
3820 this.fireEvent('changed', this, state);
3823 // show a panel if it's registered and related..
3825 if (!this.navId || !this.tabId || !state || is_was_active) {
3829 var tg = Roo.bootstrap.TabGroup.get(this.navId);
3833 var pan = tg.getPanelByName(this.tabId);
3837 // if we can not flip to new panel - go back to old nav highlight..
3838 if (false == tg.showPanel(pan)) {
3839 var nv = Roo.bootstrap.NavGroup.get(this.navId);
3841 var onav = nv.getWasActive();
3843 onav.setActive(true, false, true);
3852 // this should not be here...
3853 setDisabled : function(state)
3855 this.disabled = state;
3857 this.el.removeClass('disabled');
3858 } else if (!this.el.hasClass('disabled')) {
3859 this.el.addClass('disabled');
3872 * <span> icon </span>
3873 * <span> text </span>
3874 * <span>badge </span>
3878 * @class Roo.bootstrap.NavSidebarItem
3879 * @extends Roo.bootstrap.NavItem
3880 * Bootstrap Navbar.NavSidebarItem class
3882 * Create a new Navbar Button
3883 * @param {Object} config The config object
3885 Roo.bootstrap.NavSidebarItem = function(config){
3886 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3891 * The raw click event for the entire grid.
3892 * @param {Roo.EventObject} e
3897 * Fires when the active item active state changes
3898 * @param {Roo.bootstrap.NavSidebarItem} this
3899 * @param {boolean} state the new state
3907 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
3910 getAutoCreate : function(){
3915 href : this.href || '#',
3927 html : this.html || ''
3932 cfg.cls += ' active';
3936 if (this.glyphicon || this.icon) {
3937 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
3938 a.cn.push({ tag : 'i', cls : c }) ;
3943 if (this.badge !== '') {
3944 a.cn.push({ tag: 'span', cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge });
3948 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3949 a.cls += 'dropdown-toggle treeview' ;
3973 * @class Roo.bootstrap.Row
3974 * @extends Roo.bootstrap.Component
3975 * Bootstrap Row class (contains columns...)
3979 * @param {Object} config The config object
3982 Roo.bootstrap.Row = function(config){
3983 Roo.bootstrap.Row.superclass.constructor.call(this, config);
3986 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
3988 getAutoCreate : function(){
4007 * @class Roo.bootstrap.Element
4008 * @extends Roo.bootstrap.Component
4009 * Bootstrap Element class
4010 * @cfg {String} html contents of the element
4011 * @cfg {String} tag tag of the element
4012 * @cfg {String} cls class of the element
4015 * Create a new Element
4016 * @param {Object} config The config object
4019 Roo.bootstrap.Element = function(config){
4020 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4023 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4030 getAutoCreate : function(){
4055 * @class Roo.bootstrap.Pagination
4056 * @extends Roo.bootstrap.Component
4057 * Bootstrap Pagination class
4058 * @cfg {String} size xs | sm | md | lg
4059 * @cfg {Boolean} inverse false | true
4062 * Create a new Pagination
4063 * @param {Object} config The config object
4066 Roo.bootstrap.Pagination = function(config){
4067 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4070 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4076 getAutoCreate : function(){
4082 cfg.cls += ' inverse';
4088 cfg.cls += " " + this.cls;
4106 * @class Roo.bootstrap.PaginationItem
4107 * @extends Roo.bootstrap.Component
4108 * Bootstrap PaginationItem class
4109 * @cfg {String} html text
4110 * @cfg {String} href the link
4111 * @cfg {Boolean} preventDefault (true | false) default true
4112 * @cfg {Boolean} active (true | false) default false
4116 * Create a new PaginationItem
4117 * @param {Object} config The config object
4121 Roo.bootstrap.PaginationItem = function(config){
4122 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4127 * The raw click event for the entire grid.
4128 * @param {Roo.EventObject} e
4134 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4138 preventDefault: true,
4142 getAutoCreate : function(){
4148 href : this.href ? this.href : '#',
4149 html : this.html ? this.html : ''
4159 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4165 initEvents: function() {
4167 this.el.on('click', this.onClick, this);
4170 onClick : function(e)
4172 Roo.log('PaginationItem on click ');
4173 if(this.preventDefault){
4177 this.fireEvent('click', this, e);
4193 * @class Roo.bootstrap.Slider
4194 * @extends Roo.bootstrap.Component
4195 * Bootstrap Slider class
4198 * Create a new Slider
4199 * @param {Object} config The config object
4202 Roo.bootstrap.Slider = function(config){
4203 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4206 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4208 getAutoCreate : function(){
4212 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4216 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4228 * Ext JS Library 1.1.1
4229 * Copyright(c) 2006-2007, Ext JS, LLC.
4231 * Originally Released Under LGPL - original licence link has changed is not relivant.
4234 * <script type="text/javascript">
4239 * @class Roo.grid.ColumnModel
4240 * @extends Roo.util.Observable
4241 * This is the default implementation of a ColumnModel used by the Grid. It defines
4242 * the columns in the grid.
4245 var colModel = new Roo.grid.ColumnModel([
4246 {header: "Ticker", width: 60, sortable: true, locked: true},
4247 {header: "Company Name", width: 150, sortable: true},
4248 {header: "Market Cap.", width: 100, sortable: true},
4249 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4250 {header: "Employees", width: 100, sortable: true, resizable: false}
4255 * The config options listed for this class are options which may appear in each
4256 * individual column definition.
4257 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4259 * @param {Object} config An Array of column config objects. See this class's
4260 * config objects for details.
4262 Roo.grid.ColumnModel = function(config){
4264 * The config passed into the constructor
4266 this.config = config;
4269 // if no id, create one
4270 // if the column does not have a dataIndex mapping,
4271 // map it to the order it is in the config
4272 for(var i = 0, len = config.length; i < len; i++){
4274 if(typeof c.dataIndex == "undefined"){
4277 if(typeof c.renderer == "string"){
4278 c.renderer = Roo.util.Format[c.renderer];
4280 if(typeof c.id == "undefined"){
4283 if(c.editor && c.editor.xtype){
4284 c.editor = Roo.factory(c.editor, Roo.grid);
4286 if(c.editor && c.editor.isFormField){
4287 c.editor = new Roo.grid.GridEditor(c.editor);
4289 this.lookup[c.id] = c;
4293 * The width of columns which have no width specified (defaults to 100)
4296 this.defaultWidth = 100;
4299 * Default sortable of columns which have no sortable specified (defaults to false)
4302 this.defaultSortable = false;
4306 * @event widthchange
4307 * Fires when the width of a column changes.
4308 * @param {ColumnModel} this
4309 * @param {Number} columnIndex The column index
4310 * @param {Number} newWidth The new width
4312 "widthchange": true,
4314 * @event headerchange
4315 * Fires when the text of a header changes.
4316 * @param {ColumnModel} this
4317 * @param {Number} columnIndex The column index
4318 * @param {Number} newText The new header text
4320 "headerchange": true,
4322 * @event hiddenchange
4323 * Fires when a column is hidden or "unhidden".
4324 * @param {ColumnModel} this
4325 * @param {Number} columnIndex The column index
4326 * @param {Boolean} hidden true if hidden, false otherwise
4328 "hiddenchange": true,
4330 * @event columnmoved
4331 * Fires when a column is moved.
4332 * @param {ColumnModel} this
4333 * @param {Number} oldIndex
4334 * @param {Number} newIndex
4336 "columnmoved" : true,
4338 * @event columlockchange
4339 * Fires when a column's locked state is changed
4340 * @param {ColumnModel} this
4341 * @param {Number} colIndex
4342 * @param {Boolean} locked true if locked
4344 "columnlockchange" : true
4346 Roo.grid.ColumnModel.superclass.constructor.call(this);
4348 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4350 * @cfg {String} header The header text to display in the Grid view.
4353 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4354 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4355 * specified, the column's index is used as an index into the Record's data Array.
4358 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4359 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4362 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4363 * Defaults to the value of the {@link #defaultSortable} property.
4364 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4367 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4370 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4373 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4376 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4379 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4380 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4381 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4382 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4385 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4388 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4392 * Returns the id of the column at the specified index.
4393 * @param {Number} index The column index
4394 * @return {String} the id
4396 getColumnId : function(index){
4397 return this.config[index].id;
4401 * Returns the column for a specified id.
4402 * @param {String} id The column id
4403 * @return {Object} the column
4405 getColumnById : function(id){
4406 return this.lookup[id];
4411 * Returns the column for a specified dataIndex.
4412 * @param {String} dataIndex The column dataIndex
4413 * @return {Object|Boolean} the column or false if not found
4415 getColumnByDataIndex: function(dataIndex){
4416 var index = this.findColumnIndex(dataIndex);
4417 return index > -1 ? this.config[index] : false;
4421 * Returns the index for a specified column id.
4422 * @param {String} id The column id
4423 * @return {Number} the index, or -1 if not found
4425 getIndexById : function(id){
4426 for(var i = 0, len = this.config.length; i < len; i++){
4427 if(this.config[i].id == id){
4435 * Returns the index for a specified column dataIndex.
4436 * @param {String} dataIndex The column dataIndex
4437 * @return {Number} the index, or -1 if not found
4440 findColumnIndex : function(dataIndex){
4441 for(var i = 0, len = this.config.length; i < len; i++){
4442 if(this.config[i].dataIndex == dataIndex){
4450 moveColumn : function(oldIndex, newIndex){
4451 var c = this.config[oldIndex];
4452 this.config.splice(oldIndex, 1);
4453 this.config.splice(newIndex, 0, c);
4454 this.dataMap = null;
4455 this.fireEvent("columnmoved", this, oldIndex, newIndex);
4458 isLocked : function(colIndex){
4459 return this.config[colIndex].locked === true;
4462 setLocked : function(colIndex, value, suppressEvent){
4463 if(this.isLocked(colIndex) == value){
4466 this.config[colIndex].locked = value;
4468 this.fireEvent("columnlockchange", this, colIndex, value);
4472 getTotalLockedWidth : function(){
4474 for(var i = 0; i < this.config.length; i++){
4475 if(this.isLocked(i) && !this.isHidden(i)){
4476 this.totalWidth += this.getColumnWidth(i);
4482 getLockedCount : function(){
4483 for(var i = 0, len = this.config.length; i < len; i++){
4484 if(!this.isLocked(i)){
4491 * Returns the number of columns.
4494 getColumnCount : function(visibleOnly){
4495 if(visibleOnly === true){
4497 for(var i = 0, len = this.config.length; i < len; i++){
4498 if(!this.isHidden(i)){
4504 return this.config.length;
4508 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4509 * @param {Function} fn
4510 * @param {Object} scope (optional)
4511 * @return {Array} result
4513 getColumnsBy : function(fn, scope){
4515 for(var i = 0, len = this.config.length; i < len; i++){
4516 var c = this.config[i];
4517 if(fn.call(scope||this, c, i) === true){
4525 * Returns true if the specified column is sortable.
4526 * @param {Number} col The column index
4529 isSortable : function(col){
4530 if(typeof this.config[col].sortable == "undefined"){
4531 return this.defaultSortable;
4533 return this.config[col].sortable;
4537 * Returns the rendering (formatting) function defined for the column.
4538 * @param {Number} col The column index.
4539 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4541 getRenderer : function(col){
4542 if(!this.config[col].renderer){
4543 return Roo.grid.ColumnModel.defaultRenderer;
4545 return this.config[col].renderer;
4549 * Sets the rendering (formatting) function for a column.
4550 * @param {Number} col The column index
4551 * @param {Function} fn The function to use to process the cell's raw data
4552 * to return HTML markup for the grid view. The render function is called with
4553 * the following parameters:<ul>
4554 * <li>Data value.</li>
4555 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4556 * <li>css A CSS style string to apply to the table cell.</li>
4557 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4558 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4559 * <li>Row index</li>
4560 * <li>Column index</li>
4561 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4563 setRenderer : function(col, fn){
4564 this.config[col].renderer = fn;
4568 * Returns the width for the specified column.
4569 * @param {Number} col The column index
4572 getColumnWidth : function(col){
4573 return this.config[col].width * 1 || this.defaultWidth;
4577 * Sets the width for a column.
4578 * @param {Number} col The column index
4579 * @param {Number} width The new width
4581 setColumnWidth : function(col, width, suppressEvent){
4582 this.config[col].width = width;
4583 this.totalWidth = null;
4585 this.fireEvent("widthchange", this, col, width);
4590 * Returns the total width of all columns.
4591 * @param {Boolean} includeHidden True to include hidden column widths
4594 getTotalWidth : function(includeHidden){
4595 if(!this.totalWidth){
4596 this.totalWidth = 0;
4597 for(var i = 0, len = this.config.length; i < len; i++){
4598 if(includeHidden || !this.isHidden(i)){
4599 this.totalWidth += this.getColumnWidth(i);
4603 return this.totalWidth;
4607 * Returns the header for the specified column.
4608 * @param {Number} col The column index
4611 getColumnHeader : function(col){
4612 return this.config[col].header;
4616 * Sets the header for a column.
4617 * @param {Number} col The column index
4618 * @param {String} header The new header
4620 setColumnHeader : function(col, header){
4621 this.config[col].header = header;
4622 this.fireEvent("headerchange", this, col, header);
4626 * Returns the tooltip for the specified column.
4627 * @param {Number} col The column index
4630 getColumnTooltip : function(col){
4631 return this.config[col].tooltip;
4634 * Sets the tooltip for a column.
4635 * @param {Number} col The column index
4636 * @param {String} tooltip The new tooltip
4638 setColumnTooltip : function(col, tooltip){
4639 this.config[col].tooltip = tooltip;
4643 * Returns the dataIndex for the specified column.
4644 * @param {Number} col The column index
4647 getDataIndex : function(col){
4648 return this.config[col].dataIndex;
4652 * Sets the dataIndex for a column.
4653 * @param {Number} col The column index
4654 * @param {Number} dataIndex The new dataIndex
4656 setDataIndex : function(col, dataIndex){
4657 this.config[col].dataIndex = dataIndex;
4663 * Returns true if the cell is editable.
4664 * @param {Number} colIndex The column index
4665 * @param {Number} rowIndex The row index
4668 isCellEditable : function(colIndex, rowIndex){
4669 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4673 * Returns the editor defined for the cell/column.
4674 * return false or null to disable editing.
4675 * @param {Number} colIndex The column index
4676 * @param {Number} rowIndex The row index
4679 getCellEditor : function(colIndex, rowIndex){
4680 return this.config[colIndex].editor;
4684 * Sets if a column is editable.
4685 * @param {Number} col The column index
4686 * @param {Boolean} editable True if the column is editable
4688 setEditable : function(col, editable){
4689 this.config[col].editable = editable;
4694 * Returns true if the column is hidden.
4695 * @param {Number} colIndex The column index
4698 isHidden : function(colIndex){
4699 return this.config[colIndex].hidden;
4704 * Returns true if the column width cannot be changed
4706 isFixed : function(colIndex){
4707 return this.config[colIndex].fixed;
4711 * Returns true if the column can be resized
4714 isResizable : function(colIndex){
4715 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4718 * Sets if a column is hidden.
4719 * @param {Number} colIndex The column index
4720 * @param {Boolean} hidden True if the column is hidden
4722 setHidden : function(colIndex, hidden){
4723 this.config[colIndex].hidden = hidden;
4724 this.totalWidth = null;
4725 this.fireEvent("hiddenchange", this, colIndex, hidden);
4729 * Sets the editor for a column.
4730 * @param {Number} col The column index
4731 * @param {Object} editor The editor object
4733 setEditor : function(col, editor){
4734 this.config[col].editor = editor;
4738 Roo.grid.ColumnModel.defaultRenderer = function(value){
4739 if(typeof value == "string" && value.length < 1){
4745 // Alias for backwards compatibility
4746 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4749 * Ext JS Library 1.1.1
4750 * Copyright(c) 2006-2007, Ext JS, LLC.
4752 * Originally Released Under LGPL - original licence link has changed is not relivant.
4755 * <script type="text/javascript">
4759 * @class Roo.LoadMask
4760 * A simple utility class for generically masking elements while loading data. If the element being masked has
4761 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4762 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
4763 * element's UpdateManager load indicator and will be destroyed after the initial load.
4765 * Create a new LoadMask
4766 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4767 * @param {Object} config The config object
4769 Roo.LoadMask = function(el, config){
4770 this.el = Roo.get(el);
4771 Roo.apply(this, config);
4773 this.store.on('beforeload', this.onBeforeLoad, this);
4774 this.store.on('load', this.onLoad, this);
4775 this.store.on('loadexception', this.onLoadException, this);
4776 this.removeMask = false;
4778 var um = this.el.getUpdateManager();
4779 um.showLoadIndicator = false; // disable the default indicator
4780 um.on('beforeupdate', this.onBeforeLoad, this);
4781 um.on('update', this.onLoad, this);
4782 um.on('failure', this.onLoad, this);
4783 this.removeMask = true;
4787 Roo.LoadMask.prototype = {
4789 * @cfg {Boolean} removeMask
4790 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4791 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
4795 * The text to display in a centered loading message box (defaults to 'Loading...')
4799 * @cfg {String} msgCls
4800 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4802 msgCls : 'x-mask-loading',
4805 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4811 * Disables the mask to prevent it from being displayed
4813 disable : function(){
4814 this.disabled = true;
4818 * Enables the mask so that it can be displayed
4820 enable : function(){
4821 this.disabled = false;
4824 onLoadException : function()
4828 if (typeof(arguments[3]) != 'undefined') {
4829 Roo.MessageBox.alert("Error loading",arguments[3]);
4833 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4834 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4843 this.el.unmask(this.removeMask);
4848 this.el.unmask(this.removeMask);
4852 onBeforeLoad : function(){
4854 this.el.mask(this.msg, this.msgCls);
4859 destroy : function(){
4861 this.store.un('beforeload', this.onBeforeLoad, this);
4862 this.store.un('load', this.onLoad, this);
4863 this.store.un('loadexception', this.onLoadException, this);
4865 var um = this.el.getUpdateManager();
4866 um.un('beforeupdate', this.onBeforeLoad, this);
4867 um.un('update', this.onLoad, this);
4868 um.un('failure', this.onLoad, this);
4879 * @class Roo.bootstrap.Table
4880 * @extends Roo.bootstrap.Component
4881 * Bootstrap Table class
4882 * @cfg {String} cls table class
4883 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4884 * @cfg {String} bgcolor Specifies the background color for a table
4885 * @cfg {Number} border Specifies whether the table cells should have borders or not
4886 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4887 * @cfg {Number} cellspacing Specifies the space between cells
4888 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4889 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4890 * @cfg {String} sortable Specifies that the table should be sortable
4891 * @cfg {String} summary Specifies a summary of the content of a table
4892 * @cfg {Number} width Specifies the width of a table
4893 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4895 * @cfg {boolean} striped Should the rows be alternative striped
4896 * @cfg {boolean} bordered Add borders to the table
4897 * @cfg {boolean} hover Add hover highlighting
4898 * @cfg {boolean} condensed Format condensed
4899 * @cfg {boolean} responsive Format condensed
4900 * @cfg {Boolean} loadMask (true|false) default false
4901 * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4902 * @cfg {Boolean} thead (true|false) generate thead, default true
4903 * @cfg {Boolean} RowSelection (true|false) default false
4904 * @cfg {Boolean} CellSelection (true|false) default false
4906 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
4910 * Create a new Table
4911 * @param {Object} config The config object
4914 Roo.bootstrap.Table = function(config){
4915 Roo.bootstrap.Table.superclass.constructor.call(this, config);
4918 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4919 this.sm = this.selModel;
4920 this.sm.xmodule = this.xmodule || false;
4922 if (this.cm && typeof(this.cm.config) == 'undefined') {
4923 this.colModel = new Roo.grid.ColumnModel(this.cm);
4924 this.cm = this.colModel;
4925 this.cm.xmodule = this.xmodule || false;
4928 this.store= Roo.factory(this.store, Roo.data);
4929 this.ds = this.store;
4930 this.ds.xmodule = this.xmodule || false;
4933 if (this.footer && this.store) {
4934 this.footer.dataSource = this.ds;
4935 this.footer = Roo.factory(this.footer);
4942 * Fires when a cell is clicked
4943 * @param {Roo.bootstrap.Table} this
4944 * @param {Roo.Element} el
4945 * @param {Number} rowIndex
4946 * @param {Number} columnIndex
4947 * @param {Roo.EventObject} e
4951 * @event celldblclick
4952 * Fires when a cell is double clicked
4953 * @param {Roo.bootstrap.Table} this
4954 * @param {Roo.Element} el
4955 * @param {Number} rowIndex
4956 * @param {Number} columnIndex
4957 * @param {Roo.EventObject} e
4959 "celldblclick" : true,
4962 * Fires when a row is clicked
4963 * @param {Roo.bootstrap.Table} this
4964 * @param {Roo.Element} el
4965 * @param {Number} rowIndex
4966 * @param {Roo.EventObject} e
4970 * @event rowdblclick
4971 * Fires when a row is double clicked
4972 * @param {Roo.bootstrap.Table} this
4973 * @param {Roo.Element} el
4974 * @param {Number} rowIndex
4975 * @param {Roo.EventObject} e
4977 "rowdblclick" : true,
4980 * Fires when a mouseover occur
4981 * @param {Roo.bootstrap.Table} this
4982 * @param {Roo.Element} el
4983 * @param {Number} rowIndex
4984 * @param {Number} columnIndex
4985 * @param {Roo.EventObject} e
4990 * Fires when a mouseout occur
4991 * @param {Roo.bootstrap.Table} this
4992 * @param {Roo.Element} el
4993 * @param {Number} rowIndex
4994 * @param {Number} columnIndex
4995 * @param {Roo.EventObject} e
5000 * Fires when a row is rendered, so you can change add a style to it.
5001 * @param {Roo.bootstrap.Table} this
5002 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5009 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5033 RowSelection : false,
5034 CellSelection : false,
5037 // Roo.Element - the tbody
5040 getAutoCreate : function(){
5041 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5050 cfg.cls += ' table-striped';
5054 cfg.cls += ' table-hover';
5056 if (this.bordered) {
5057 cfg.cls += ' table-bordered';
5059 if (this.condensed) {
5060 cfg.cls += ' table-condensed';
5062 if (this.responsive) {
5063 cfg.cls += ' table-responsive';
5067 cfg.cls+= ' ' +this.cls;
5070 // this lot should be simplifed...
5073 cfg.align=this.align;
5076 cfg.bgcolor=this.bgcolor;
5079 cfg.border=this.border;
5081 if (this.cellpadding) {
5082 cfg.cellpadding=this.cellpadding;
5084 if (this.cellspacing) {
5085 cfg.cellspacing=this.cellspacing;
5088 cfg.frame=this.frame;
5091 cfg.rules=this.rules;
5093 if (this.sortable) {
5094 cfg.sortable=this.sortable;
5097 cfg.summary=this.summary;
5100 cfg.width=this.width;
5103 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5106 if(this.store || this.cm){
5108 cfg.cn.push(this.renderHeader());
5111 cfg.cn.push(this.renderBody());
5114 cfg.cn.push(this.renderFooter());
5117 cfg.cls+= ' TableGrid';
5120 return { cn : [ cfg ] };
5123 initEvents : function()
5125 if(!this.store || !this.cm){
5129 //Roo.log('initEvents with ds!!!!');
5131 this.mainBody = this.el.select('tbody', true).first();
5136 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5137 e.on('click', _this.sort, _this);
5140 this.el.on("click", this.onClick, this);
5141 this.el.on("dblclick", this.onDblClick, this);
5143 this.parent().el.setStyle('position', 'relative');
5145 this.footer.parentId = this.id;
5146 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5149 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5151 this.store.on('load', this.onLoad, this);
5152 this.store.on('beforeload', this.onBeforeLoad, this);
5153 this.store.on('update', this.onUpdate, this);
5157 onMouseover : function(e, el)
5159 var cell = Roo.get(el);
5165 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5166 cell = cell.findParent('td', false, true);
5169 var row = cell.findParent('tr', false, true);
5170 var cellIndex = cell.dom.cellIndex;
5171 var rowIndex = row.dom.rowIndex - 1; // start from 0
5173 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5177 onMouseout : function(e, el)
5179 var cell = Roo.get(el);
5185 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5186 cell = cell.findParent('td', false, true);
5189 var row = cell.findParent('tr', false, true);
5190 var cellIndex = cell.dom.cellIndex;
5191 var rowIndex = row.dom.rowIndex - 1; // start from 0
5193 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5197 onClick : function(e, el)
5199 var cell = Roo.get(el);
5201 if(!cell || (!this.CellSelection && !this.RowSelection)){
5206 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5207 cell = cell.findParent('td', false, true);
5210 var row = cell.findParent('tr', false, true);
5211 var cellIndex = cell.dom.cellIndex;
5212 var rowIndex = row.dom.rowIndex - 1;
5214 if(this.CellSelection){
5215 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5218 if(this.RowSelection){
5219 this.fireEvent('rowclick', this, row, rowIndex, e);
5225 onDblClick : function(e,el)
5227 var cell = Roo.get(el);
5229 if(!cell || (!this.CellSelection && !this.RowSelection)){
5233 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5234 cell = cell.findParent('td', false, true);
5237 var row = cell.findParent('tr', false, true);
5238 var cellIndex = cell.dom.cellIndex;
5239 var rowIndex = row.dom.rowIndex - 1;
5241 if(this.CellSelection){
5242 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5245 if(this.RowSelection){
5246 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5250 sort : function(e,el)
5252 var col = Roo.get(el)
5254 if(!col.hasClass('sortable')){
5258 var sort = col.attr('sort');
5261 if(col.hasClass('glyphicon-arrow-up')){
5265 this.store.sortInfo = {field : sort, direction : dir};
5268 Roo.log("calling footer first");
5269 this.footer.onClick('first');
5272 this.store.load({ params : { start : 0 } });
5276 renderHeader : function()
5285 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5287 var config = cm.config[i];
5292 html: cm.getColumnHeader(i)
5295 if(typeof(config.hidden) != 'undefined' && config.hidden){
5296 c.style += ' display:none;';
5299 if(typeof(config.dataIndex) != 'undefined'){
5300 c.sort = config.dataIndex;
5303 if(typeof(config.sortable) != 'undefined' && config.sortable){
5307 if(typeof(config.align) != 'undefined' && config.align.length){
5308 c.style += ' text-align:' + config.align + ';';
5311 if(typeof(config.width) != 'undefined'){
5312 c.style += ' width:' + config.width + 'px;';
5321 renderBody : function()
5331 colspan : this.cm.getColumnCount()
5341 renderFooter : function()
5351 colspan : this.cm.getColumnCount()
5365 Roo.log('ds onload');
5370 var ds = this.store;
5372 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5373 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5375 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5376 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5379 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5380 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5384 var tbody = this.mainBody;
5386 if(ds.getCount() > 0){
5387 ds.data.each(function(d,rowIndex){
5388 var row = this.renderRow(cm, ds, rowIndex);
5390 tbody.createChild(row);
5394 if(row.cellObjects.length){
5395 Roo.each(row.cellObjects, function(r){
5396 _this.renderCellObject(r);
5403 Roo.each(this.el.select('tbody td', true).elements, function(e){
5404 e.on('mouseover', _this.onMouseover, _this);
5407 Roo.each(this.el.select('tbody td', true).elements, function(e){
5408 e.on('mouseout', _this.onMouseout, _this);
5411 //if(this.loadMask){
5412 // this.maskEl.hide();
5417 onUpdate : function(ds,record)
5419 this.refreshRow(record);
5421 onRemove : function(ds, record, index, isUpdate){
5422 if(isUpdate !== true){
5423 this.fireEvent("beforerowremoved", this, index, record);
5425 var bt = this.mainBody.dom;
5427 bt.removeChild(bt.rows[index]);
5430 if(isUpdate !== true){
5431 //this.stripeRows(index);
5432 //this.syncRowHeights(index, index);
5434 this.fireEvent("rowremoved", this, index, record);
5439 refreshRow : function(record){
5440 var ds = this.store, index;
5441 if(typeof record == 'number'){
5443 record = ds.getAt(index);
5445 index = ds.indexOf(record);
5447 this.insertRow(ds, index, true);
5448 this.onRemove(ds, record, index+1, true);
5449 //this.syncRowHeights(index, index);
5451 this.fireEvent("rowupdated", this, index, record);
5454 insertRow : function(dm, rowIndex, isUpdate){
5457 this.fireEvent("beforerowsinserted", this, rowIndex);
5459 //var s = this.getScrollState();
5460 var row = this.renderRow(this.cm, this.store, rowIndex);
5461 // insert before rowIndex..
5462 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5466 if(row.cellObjects.length){
5467 Roo.each(row.cellObjects, function(r){
5468 _this.renderCellObject(r);
5473 this.fireEvent("rowsinserted", this, rowIndex);
5474 //this.syncRowHeights(firstRow, lastRow);
5475 //this.stripeRows(firstRow);
5482 getRowDom : function(rowIndex)
5484 // not sure if I need to check this.. but let's do it anyway..
5485 return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5486 this.mainBody.dom.rows[rowIndex] : false
5488 // returns the object tree for a tr..
5491 renderRow : function(cm, ds, rowIndex) {
5493 var d = ds.getAt(rowIndex);
5500 var cellObjects = [];
5502 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5503 var config = cm.config[i];
5505 var renderer = cm.getRenderer(i);
5509 if(typeof(renderer) !== 'undefined'){
5510 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5512 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5513 // and are rendered into the cells after the row is rendered - using the id for the element.
5515 if(typeof(value) === 'object'){
5525 rowIndex : rowIndex,
5530 this.fireEvent('rowclass', this, rowcfg);
5534 cls : rowcfg.rowClass,
5536 html: (typeof(value) === 'object') ? '' : value
5543 if(typeof(config.hidden) != 'undefined' && config.hidden){
5544 td.style += ' display:none;';
5547 if(typeof(config.align) != 'undefined' && config.align.length){
5548 td.style += ' text-align:' + config.align + ';';
5551 if(typeof(config.width) != 'undefined'){
5552 td.style += ' width:' + config.width + 'px;';
5559 row.cellObjects = cellObjects;
5567 onBeforeLoad : function()
5569 //Roo.log('ds onBeforeLoad');
5573 //if(this.loadMask){
5574 // this.maskEl.show();
5580 this.el.select('tbody', true).first().dom.innerHTML = '';
5583 getSelectionModel : function(){
5585 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5587 return this.selModel;
5590 * Render the Roo.bootstrap object from renderder
5592 renderCellObject : function(r)
5596 var t = r.cfg.render(r.container);
5599 Roo.each(r.cfg.cn, function(c){
5601 container: t.getChildContainer(),
5604 _this.renderCellObject(child);
5621 * @class Roo.bootstrap.TableCell
5622 * @extends Roo.bootstrap.Component
5623 * Bootstrap TableCell class
5624 * @cfg {String} html cell contain text
5625 * @cfg {String} cls cell class
5626 * @cfg {String} tag cell tag (td|th) default td
5627 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5628 * @cfg {String} align Aligns the content in a cell
5629 * @cfg {String} axis Categorizes cells
5630 * @cfg {String} bgcolor Specifies the background color of a cell
5631 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5632 * @cfg {Number} colspan Specifies the number of columns a cell should span
5633 * @cfg {String} headers Specifies one or more header cells a cell is related to
5634 * @cfg {Number} height Sets the height of a cell
5635 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5636 * @cfg {Number} rowspan Sets the number of rows a cell should span
5637 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5638 * @cfg {String} valign Vertical aligns the content in a cell
5639 * @cfg {Number} width Specifies the width of a cell
5642 * Create a new TableCell
5643 * @param {Object} config The config object
5646 Roo.bootstrap.TableCell = function(config){
5647 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5650 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
5670 getAutoCreate : function(){
5671 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5691 cfg.align=this.align
5697 cfg.bgcolor=this.bgcolor
5700 cfg.charoff=this.charoff
5703 cfg.colspan=this.colspan
5706 cfg.headers=this.headers
5709 cfg.height=this.height
5712 cfg.nowrap=this.nowrap
5715 cfg.rowspan=this.rowspan
5718 cfg.scope=this.scope
5721 cfg.valign=this.valign
5724 cfg.width=this.width
5743 * @class Roo.bootstrap.TableRow
5744 * @extends Roo.bootstrap.Component
5745 * Bootstrap TableRow class
5746 * @cfg {String} cls row class
5747 * @cfg {String} align Aligns the content in a table row
5748 * @cfg {String} bgcolor Specifies a background color for a table row
5749 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5750 * @cfg {String} valign Vertical aligns the content in a table row
5753 * Create a new TableRow
5754 * @param {Object} config The config object
5757 Roo.bootstrap.TableRow = function(config){
5758 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5761 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
5769 getAutoCreate : function(){
5770 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5780 cfg.align = this.align;
5783 cfg.bgcolor = this.bgcolor;
5786 cfg.charoff = this.charoff;
5789 cfg.valign = this.valign;
5807 * @class Roo.bootstrap.TableBody
5808 * @extends Roo.bootstrap.Component
5809 * Bootstrap TableBody class
5810 * @cfg {String} cls element class
5811 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5812 * @cfg {String} align Aligns the content inside the element
5813 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5814 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5817 * Create a new TableBody
5818 * @param {Object} config The config object
5821 Roo.bootstrap.TableBody = function(config){
5822 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5825 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
5833 getAutoCreate : function(){
5834 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5848 cfg.align = this.align;
5851 cfg.charoff = this.charoff;
5854 cfg.valign = this.valign;
5861 // initEvents : function()
5868 // this.store = Roo.factory(this.store, Roo.data);
5869 // this.store.on('load', this.onLoad, this);
5871 // this.store.load();
5875 // onLoad: function ()
5877 // this.fireEvent('load', this);
5887 * Ext JS Library 1.1.1
5888 * Copyright(c) 2006-2007, Ext JS, LLC.
5890 * Originally Released Under LGPL - original licence link has changed is not relivant.
5893 * <script type="text/javascript">
5896 // as we use this in bootstrap.
5897 Roo.namespace('Roo.form');
5899 * @class Roo.form.Action
5900 * Internal Class used to handle form actions
5902 * @param {Roo.form.BasicForm} el The form element or its id
5903 * @param {Object} config Configuration options
5908 // define the action interface
5909 Roo.form.Action = function(form, options){
5911 this.options = options || {};
5914 * Client Validation Failed
5917 Roo.form.Action.CLIENT_INVALID = 'client';
5919 * Server Validation Failed
5922 Roo.form.Action.SERVER_INVALID = 'server';
5924 * Connect to Server Failed
5927 Roo.form.Action.CONNECT_FAILURE = 'connect';
5929 * Reading Data from Server Failed
5932 Roo.form.Action.LOAD_FAILURE = 'load';
5934 Roo.form.Action.prototype = {
5936 failureType : undefined,
5937 response : undefined,
5941 run : function(options){
5946 success : function(response){
5951 handleResponse : function(response){
5955 // default connection failure
5956 failure : function(response){
5958 this.response = response;
5959 this.failureType = Roo.form.Action.CONNECT_FAILURE;
5960 this.form.afterAction(this, false);
5963 processResponse : function(response){
5964 this.response = response;
5965 if(!response.responseText){
5968 this.result = this.handleResponse(response);
5972 // utility functions used internally
5973 getUrl : function(appendParams){
5974 var url = this.options.url || this.form.url || this.form.el.dom.action;
5976 var p = this.getParams();
5978 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5984 getMethod : function(){
5985 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5988 getParams : function(){
5989 var bp = this.form.baseParams;
5990 var p = this.options.params;
5992 if(typeof p == "object"){
5993 p = Roo.urlEncode(Roo.applyIf(p, bp));
5994 }else if(typeof p == 'string' && bp){
5995 p += '&' + Roo.urlEncode(bp);
5998 p = Roo.urlEncode(bp);
6003 createCallback : function(){
6005 success: this.success,
6006 failure: this.failure,
6008 timeout: (this.form.timeout*1000),
6009 upload: this.form.fileUpload ? this.success : undefined
6014 Roo.form.Action.Submit = function(form, options){
6015 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6018 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6021 haveProgress : false,
6022 uploadComplete : false,
6024 // uploadProgress indicator.
6025 uploadProgress : function()
6027 if (!this.form.progressUrl) {
6031 if (!this.haveProgress) {
6032 Roo.MessageBox.progress("Uploading", "Uploading");
6034 if (this.uploadComplete) {
6035 Roo.MessageBox.hide();
6039 this.haveProgress = true;
6041 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6043 var c = new Roo.data.Connection();
6045 url : this.form.progressUrl,
6050 success : function(req){
6051 //console.log(data);
6055 rdata = Roo.decode(req.responseText)
6057 Roo.log("Invalid data from server..");
6061 if (!rdata || !rdata.success) {
6063 Roo.MessageBox.alert(Roo.encode(rdata));
6066 var data = rdata.data;
6068 if (this.uploadComplete) {
6069 Roo.MessageBox.hide();
6074 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6075 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6078 this.uploadProgress.defer(2000,this);
6081 failure: function(data) {
6082 Roo.log('progress url failed ');
6093 // run get Values on the form, so it syncs any secondary forms.
6094 this.form.getValues();
6096 var o = this.options;
6097 var method = this.getMethod();
6098 var isPost = method == 'POST';
6099 if(o.clientValidation === false || this.form.isValid()){
6101 if (this.form.progressUrl) {
6102 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6103 (new Date() * 1) + '' + Math.random());
6108 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6109 form:this.form.el.dom,
6110 url:this.getUrl(!isPost),
6112 params:isPost ? this.getParams() : null,
6113 isUpload: this.form.fileUpload
6116 this.uploadProgress();
6118 }else if (o.clientValidation !== false){ // client validation failed
6119 this.failureType = Roo.form.Action.CLIENT_INVALID;
6120 this.form.afterAction(this, false);
6124 success : function(response)
6126 this.uploadComplete= true;
6127 if (this.haveProgress) {
6128 Roo.MessageBox.hide();
6132 var result = this.processResponse(response);
6133 if(result === true || result.success){
6134 this.form.afterAction(this, true);
6138 this.form.markInvalid(result.errors);
6139 this.failureType = Roo.form.Action.SERVER_INVALID;
6141 this.form.afterAction(this, false);
6143 failure : function(response)
6145 this.uploadComplete= true;
6146 if (this.haveProgress) {
6147 Roo.MessageBox.hide();
6150 this.response = response;
6151 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6152 this.form.afterAction(this, false);
6155 handleResponse : function(response){
6156 if(this.form.errorReader){
6157 var rs = this.form.errorReader.read(response);
6160 for(var i = 0, len = rs.records.length; i < len; i++) {
6161 var r = rs.records[i];
6165 if(errors.length < 1){
6169 success : rs.success,
6175 ret = Roo.decode(response.responseText);
6179 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6189 Roo.form.Action.Load = function(form, options){
6190 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6191 this.reader = this.form.reader;
6194 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6199 Roo.Ajax.request(Roo.apply(
6200 this.createCallback(), {
6201 method:this.getMethod(),
6202 url:this.getUrl(false),
6203 params:this.getParams()
6207 success : function(response){
6209 var result = this.processResponse(response);
6210 if(result === true || !result.success || !result.data){
6211 this.failureType = Roo.form.Action.LOAD_FAILURE;
6212 this.form.afterAction(this, false);
6215 this.form.clearInvalid();
6216 this.form.setValues(result.data);
6217 this.form.afterAction(this, true);
6220 handleResponse : function(response){
6221 if(this.form.reader){
6222 var rs = this.form.reader.read(response);
6223 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6225 success : rs.success,
6229 return Roo.decode(response.responseText);
6233 Roo.form.Action.ACTION_TYPES = {
6234 'load' : Roo.form.Action.Load,
6235 'submit' : Roo.form.Action.Submit
6244 * @class Roo.bootstrap.Form
6245 * @extends Roo.bootstrap.Component
6246 * Bootstrap Form class
6247 * @cfg {String} method GET | POST (default POST)
6248 * @cfg {String} labelAlign top | left (default top)
6249 * @cfg {String} align left | right - for navbars
6250 * @cfg {Boolean} loadMask load mask when submit (default true)
6255 * @param {Object} config The config object
6259 Roo.bootstrap.Form = function(config){
6260 Roo.bootstrap.Form.superclass.constructor.call(this, config);
6263 * @event clientvalidation
6264 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6265 * @param {Form} this
6266 * @param {Boolean} valid true if the form has passed client-side validation
6268 clientvalidation: true,
6270 * @event beforeaction
6271 * Fires before any action is performed. Return false to cancel the action.
6272 * @param {Form} this
6273 * @param {Action} action The action to be performed
6277 * @event actionfailed
6278 * Fires when an action fails.
6279 * @param {Form} this
6280 * @param {Action} action The action that failed
6282 actionfailed : true,
6284 * @event actioncomplete
6285 * Fires when an action is completed.
6286 * @param {Form} this
6287 * @param {Action} action The action that completed
6289 actioncomplete : true
6294 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
6297 * @cfg {String} method
6298 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6303 * The URL to use for form actions if one isn't supplied in the action options.
6306 * @cfg {Boolean} fileUpload
6307 * Set to true if this form is a file upload.
6311 * @cfg {Object} baseParams
6312 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6316 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6320 * @cfg {Sting} align (left|right) for navbar forms
6325 activeAction : null,
6328 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6329 * element by passing it or its id or mask the form itself by passing in true.
6332 waitMsgTarget : false,
6336 getAutoCreate : function(){
6340 method : this.method || 'POST',
6341 id : this.id || Roo.id(),
6344 if (this.parent().xtype.match(/^Nav/)) {
6345 cfg.cls = 'navbar-form navbar-' + this.align;
6349 if (this.labelAlign == 'left' ) {
6350 cfg.cls += ' form-horizontal';
6356 initEvents : function()
6358 this.el.on('submit', this.onSubmit, this);
6359 // this was added as random key presses on the form where triggering form submit.
6360 this.el.on('keypress', function(e) {
6361 if (e.getCharCode() != 13) {
6364 // we might need to allow it for textareas.. and some other items.
6365 // check e.getTarget().
6367 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6371 Roo.log("keypress blocked");
6379 onSubmit : function(e){
6384 * Returns true if client-side validation on the form is successful.
6387 isValid : function(){
6388 var items = this.getItems();
6390 items.each(function(f){
6399 * Returns true if any fields in this form have changed since their original load.
6402 isDirty : function(){
6404 var items = this.getItems();
6405 items.each(function(f){
6415 * Performs a predefined action (submit or load) or custom actions you define on this form.
6416 * @param {String} actionName The name of the action type
6417 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
6418 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6419 * accept other config options):
6421 Property Type Description
6422 ---------------- --------------- ----------------------------------------------------------------------------------
6423 url String The url for the action (defaults to the form's url)
6424 method String The form method to use (defaults to the form's method, or POST if not defined)
6425 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
6426 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
6427 validate the form on the client (defaults to false)
6429 * @return {BasicForm} this
6431 doAction : function(action, options){
6432 if(typeof action == 'string'){
6433 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6435 if(this.fireEvent('beforeaction', this, action) !== false){
6436 this.beforeAction(action);
6437 action.run.defer(100, action);
6443 beforeAction : function(action){
6444 var o = action.options;
6447 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6449 // not really supported yet.. ??
6451 //if(this.waitMsgTarget === true){
6452 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6453 //}else if(this.waitMsgTarget){
6454 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6455 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6457 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6463 afterAction : function(action, success){
6464 this.activeAction = null;
6465 var o = action.options;
6467 //if(this.waitMsgTarget === true){
6469 //}else if(this.waitMsgTarget){
6470 // this.waitMsgTarget.unmask();
6472 // Roo.MessageBox.updateProgress(1);
6473 // Roo.MessageBox.hide();
6480 Roo.callback(o.success, o.scope, [this, action]);
6481 this.fireEvent('actioncomplete', this, action);
6485 // failure condition..
6486 // we have a scenario where updates need confirming.
6487 // eg. if a locking scenario exists..
6488 // we look for { errors : { needs_confirm : true }} in the response.
6490 (typeof(action.result) != 'undefined') &&
6491 (typeof(action.result.errors) != 'undefined') &&
6492 (typeof(action.result.errors.needs_confirm) != 'undefined')
6495 Roo.log("not supported yet");
6498 Roo.MessageBox.confirm(
6499 "Change requires confirmation",
6500 action.result.errorMsg,
6505 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
6515 Roo.callback(o.failure, o.scope, [this, action]);
6516 // show an error message if no failed handler is set..
6517 if (!this.hasListener('actionfailed')) {
6518 Roo.log("need to add dialog support");
6520 Roo.MessageBox.alert("Error",
6521 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6522 action.result.errorMsg :
6523 "Saving Failed, please check your entries or try again"
6528 this.fireEvent('actionfailed', this, action);
6533 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6534 * @param {String} id The value to search for
6537 findField : function(id){
6538 var items = this.getItems();
6539 var field = items.get(id);
6541 items.each(function(f){
6542 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6549 return field || null;
6552 * Mark fields in this form invalid in bulk.
6553 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6554 * @return {BasicForm} this
6556 markInvalid : function(errors){
6557 if(errors instanceof Array){
6558 for(var i = 0, len = errors.length; i < len; i++){
6559 var fieldError = errors[i];
6560 var f = this.findField(fieldError.id);
6562 f.markInvalid(fieldError.msg);
6568 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6569 field.markInvalid(errors[id]);
6573 //Roo.each(this.childForms || [], function (f) {
6574 // f.markInvalid(errors);
6581 * Set values for fields in this form in bulk.
6582 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6583 * @return {BasicForm} this
6585 setValues : function(values){
6586 if(values instanceof Array){ // array of objects
6587 for(var i = 0, len = values.length; i < len; i++){
6589 var f = this.findField(v.id);
6591 f.setValue(v.value);
6592 if(this.trackResetOnLoad){
6593 f.originalValue = f.getValue();
6597 }else{ // object hash
6600 if(typeof values[id] != 'function' && (field = this.findField(id))){
6602 if (field.setFromData &&
6604 field.displayField &&
6605 // combos' with local stores can
6606 // be queried via setValue()
6607 // to set their value..
6608 (field.store && !field.store.isLocal)
6612 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6613 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6614 field.setFromData(sd);
6617 field.setValue(values[id]);
6621 if(this.trackResetOnLoad){
6622 field.originalValue = field.getValue();
6628 //Roo.each(this.childForms || [], function (f) {
6629 // f.setValues(values);
6636 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6637 * they are returned as an array.
6638 * @param {Boolean} asString
6641 getValues : function(asString){
6642 //if (this.childForms) {
6643 // copy values from the child forms
6644 // Roo.each(this.childForms, function (f) {
6645 // this.setValues(f.getValues());
6651 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6652 if(asString === true){
6655 return Roo.urlDecode(fs);
6659 * Returns the fields in this form as an object with key/value pairs.
6660 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6663 getFieldValues : function(with_hidden)
6665 var items = this.getItems();
6667 items.each(function(f){
6671 var v = f.getValue();
6672 if (f.inputType =='radio') {
6673 if (typeof(ret[f.getName()]) == 'undefined') {
6674 ret[f.getName()] = ''; // empty..
6677 if (!f.el.dom.checked) {
6685 // not sure if this supported any more..
6686 if ((typeof(v) == 'object') && f.getRawValue) {
6687 v = f.getRawValue() ; // dates..
6689 // combo boxes where name != hiddenName...
6690 if (f.name != f.getName()) {
6691 ret[f.name] = f.getRawValue();
6693 ret[f.getName()] = v;
6700 * Clears all invalid messages in this form.
6701 * @return {BasicForm} this
6703 clearInvalid : function(){
6704 var items = this.getItems();
6706 items.each(function(f){
6717 * @return {BasicForm} this
6720 var items = this.getItems();
6721 items.each(function(f){
6725 Roo.each(this.childForms || [], function (f) {
6732 getItems : function()
6734 var r=new Roo.util.MixedCollection(false, function(o){
6735 return o.id || (o.id = Roo.id());
6737 var iter = function(el) {
6744 Roo.each(el.items,function(e) {
6763 * Ext JS Library 1.1.1
6764 * Copyright(c) 2006-2007, Ext JS, LLC.
6766 * Originally Released Under LGPL - original licence link has changed is not relivant.
6769 * <script type="text/javascript">
6772 * @class Roo.form.VTypes
6773 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6776 Roo.form.VTypes = function(){
6777 // closure these in so they are only created once.
6778 var alpha = /^[a-zA-Z_]+$/;
6779 var alphanum = /^[a-zA-Z0-9_]+$/;
6780 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6781 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6783 // All these messages and functions are configurable
6786 * The function used to validate email addresses
6787 * @param {String} value The email address
6789 'email' : function(v){
6790 return email.test(v);
6793 * The error text to display when the email validation function returns false
6796 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6798 * The keystroke filter mask to be applied on email input
6801 'emailMask' : /[a-z0-9_\.\-@]/i,
6804 * The function used to validate URLs
6805 * @param {String} value The URL
6807 'url' : function(v){
6811 * The error text to display when the url validation function returns false
6814 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6817 * The function used to validate alpha values
6818 * @param {String} value The value
6820 'alpha' : function(v){
6821 return alpha.test(v);
6824 * The error text to display when the alpha validation function returns false
6827 'alphaText' : 'This field should only contain letters and _',
6829 * The keystroke filter mask to be applied on alpha input
6832 'alphaMask' : /[a-z_]/i,
6835 * The function used to validate alphanumeric values
6836 * @param {String} value The value
6838 'alphanum' : function(v){
6839 return alphanum.test(v);
6842 * The error text to display when the alphanumeric validation function returns false
6845 'alphanumText' : 'This field should only contain letters, numbers and _',
6847 * The keystroke filter mask to be applied on alphanumeric input
6850 'alphanumMask' : /[a-z0-9_]/i
6860 * @class Roo.bootstrap.Input
6861 * @extends Roo.bootstrap.Component
6862 * Bootstrap Input class
6863 * @cfg {Boolean} disabled is it disabled
6864 * @cfg {String} fieldLabel - the label associated
6865 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6866 * @cfg {String} name name of the input
6867 * @cfg {string} fieldLabel - the label associated
6868 * @cfg {string} inputType - input / file submit ...
6869 * @cfg {string} placeholder - placeholder to put in text.
6870 * @cfg {string} before - input group add on before
6871 * @cfg {string} after - input group add on after
6872 * @cfg {string} size - (lg|sm) or leave empty..
6873 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6874 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6875 * @cfg {Number} md colspan out of 12 for computer-sized screens
6876 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6877 * @cfg {string} value default value of the input
6878 * @cfg {Number} labelWidth set the width of label (0-12)
6879 * @cfg {String} labelAlign (top|left)
6880 * @cfg {Boolean} readOnly Specifies that the field should be read-only
6881 * @cfg {String} align (left|center|right) Default left
6885 * Create a new Input
6886 * @param {Object} config The config object
6889 Roo.bootstrap.Input = function(config){
6890 Roo.bootstrap.Input.superclass.constructor.call(this, config);
6895 * Fires when this field receives input focus.
6896 * @param {Roo.form.Field} this
6901 * Fires when this field loses input focus.
6902 * @param {Roo.form.Field} this
6907 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
6908 * {@link Roo.EventObject#getKey} to determine which key was pressed.
6909 * @param {Roo.form.Field} this
6910 * @param {Roo.EventObject} e The event object
6915 * Fires just before the field blurs if the field value has changed.
6916 * @param {Roo.form.Field} this
6917 * @param {Mixed} newValue The new value
6918 * @param {Mixed} oldValue The original value
6923 * Fires after the field has been marked as invalid.
6924 * @param {Roo.form.Field} this
6925 * @param {String} msg The validation message
6930 * Fires after the field has been validated with no errors.
6931 * @param {Roo.form.Field} this
6936 * Fires after the key up
6937 * @param {Roo.form.Field} this
6938 * @param {Roo.EventObject} e The event Object
6944 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
6946 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6947 automatic validation (defaults to "keyup").
6949 validationEvent : "keyup",
6951 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6953 validateOnBlur : true,
6955 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6957 validationDelay : 250,
6959 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6961 focusClass : "x-form-focus", // not needed???
6965 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6967 invalidClass : "has-error",
6970 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6972 selectOnFocus : false,
6975 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6979 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6984 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6986 disableKeyFilter : false,
6989 * @cfg {Boolean} disabled True to disable the field (defaults to false).
6993 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6997 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6999 blankText : "This field is required",
7002 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7006 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7008 maxLength : Number.MAX_VALUE,
7010 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7012 minLengthText : "The minimum length for this field is {0}",
7014 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7016 maxLengthText : "The maximum length for this field is {0}",
7020 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7021 * If available, this function will be called only after the basic validators all return true, and will be passed the
7022 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7026 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7027 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7028 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7032 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7055 formatedValue : false,
7057 parentLabelAlign : function()
7060 while (parent.parent()) {
7061 parent = parent.parent();
7062 if (typeof(parent.labelAlign) !='undefined') {
7063 return parent.labelAlign;
7070 getAutoCreate : function(){
7072 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7078 if(this.inputType != 'hidden'){
7079 cfg.cls = 'form-group' //input-group
7085 type : this.inputType,
7087 cls : 'form-control',
7088 placeholder : this.placeholder || ''
7093 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7096 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7097 input.maxLength = this.maxLength;
7100 if (this.disabled) {
7101 input.disabled=true;
7104 if (this.readOnly) {
7105 input.readonly=true;
7109 input.name = this.name;
7112 input.cls += ' input-' + this.size;
7115 ['xs','sm','md','lg'].map(function(size){
7116 if (settings[size]) {
7117 cfg.cls += ' col-' + size + '-' + settings[size];
7121 var inputblock = input;
7123 if (this.before || this.after) {
7126 cls : 'input-group',
7129 if (this.before && typeof(this.before) == 'string') {
7131 inputblock.cn.push({
7133 cls : 'roo-input-before input-group-addon',
7137 if (this.before && typeof(this.before) == 'object') {
7138 this.before = Roo.factory(this.before);
7139 Roo.log(this.before);
7140 inputblock.cn.push({
7142 cls : 'roo-input-before input-group-' +
7143 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7147 inputblock.cn.push(input);
7149 if (this.after && typeof(this.after) == 'string') {
7150 inputblock.cn.push({
7152 cls : 'roo-input-after input-group-addon',
7156 if (this.after && typeof(this.after) == 'object') {
7157 this.after = Roo.factory(this.after);
7158 Roo.log(this.after);
7159 inputblock.cn.push({
7161 cls : 'roo-input-after input-group-' +
7162 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7167 if (align ==='left' && this.fieldLabel.length) {
7168 Roo.log("left and has label");
7174 cls : 'control-label col-sm-' + this.labelWidth,
7175 html : this.fieldLabel
7179 cls : "col-sm-" + (12 - this.labelWidth),
7186 } else if ( this.fieldLabel.length) {
7192 //cls : 'input-group-addon',
7193 html : this.fieldLabel
7203 Roo.log(" no label && no align");
7212 Roo.log('input-parentType: ' + this.parentType);
7214 if (this.parentType === 'Navbar' && this.parent().bar) {
7215 cfg.cls += ' navbar-form';
7223 * return the real input element.
7225 inputEl: function ()
7227 return this.el.select('input.form-control',true).first();
7230 tooltipEl : function()
7232 return this.inputEl();
7235 setDisabled : function(v)
7237 var i = this.inputEl().dom;
7239 i.removeAttribute('disabled');
7243 i.setAttribute('disabled','true');
7245 initEvents : function()
7248 this.inputEl().on("keydown" , this.fireKey, this);
7249 this.inputEl().on("focus", this.onFocus, this);
7250 this.inputEl().on("blur", this.onBlur, this);
7252 this.inputEl().relayEvent('keyup', this);
7254 // reference to original value for reset
7255 this.originalValue = this.getValue();
7256 //Roo.form.TextField.superclass.initEvents.call(this);
7257 if(this.validationEvent == 'keyup'){
7258 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7259 this.inputEl().on('keyup', this.filterValidation, this);
7261 else if(this.validationEvent !== false){
7262 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7265 if(this.selectOnFocus){
7266 this.on("focus", this.preFocus, this);
7269 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7270 this.inputEl().on("keypress", this.filterKeys, this);
7273 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
7274 this.el.on("click", this.autoSize, this);
7277 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7278 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7281 if (typeof(this.before) == 'object') {
7282 this.before.render(this.el.select('.roo-input-before',true).first());
7284 if (typeof(this.after) == 'object') {
7285 this.after.render(this.el.select('.roo-input-after',true).first());
7290 filterValidation : function(e){
7291 if(!e.isNavKeyPress()){
7292 this.validationTask.delay(this.validationDelay);
7296 * Validates the field value
7297 * @return {Boolean} True if the value is valid, else false
7299 validate : function(){
7300 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7301 if(this.disabled || this.validateValue(this.getRawValue())){
7302 this.clearInvalid();
7310 * Validates a value according to the field's validation rules and marks the field as invalid
7311 * if the validation fails
7312 * @param {Mixed} value The value to validate
7313 * @return {Boolean} True if the value is valid, else false
7315 validateValue : function(value){
7316 if(value.length < 1) { // if it's blank
7317 if(this.allowBlank){
7318 this.clearInvalid();
7321 this.markInvalid(this.blankText);
7325 if(value.length < this.minLength){
7326 this.markInvalid(String.format(this.minLengthText, this.minLength));
7329 if(value.length > this.maxLength){
7330 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7334 var vt = Roo.form.VTypes;
7335 if(!vt[this.vtype](value, this)){
7336 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7340 if(typeof this.validator == "function"){
7341 var msg = this.validator(value);
7343 this.markInvalid(msg);
7347 if(this.regex && !this.regex.test(value)){
7348 this.markInvalid(this.regexText);
7357 fireKey : function(e){
7358 //Roo.log('field ' + e.getKey());
7359 if(e.isNavKeyPress()){
7360 this.fireEvent("specialkey", this, e);
7363 focus : function (selectText){
7365 this.inputEl().focus();
7366 if(selectText === true){
7367 this.inputEl().dom.select();
7373 onFocus : function(){
7374 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7375 // this.el.addClass(this.focusClass);
7378 this.hasFocus = true;
7379 this.startValue = this.getValue();
7380 this.fireEvent("focus", this);
7384 beforeBlur : Roo.emptyFn,
7388 onBlur : function(){
7390 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7391 //this.el.removeClass(this.focusClass);
7393 this.hasFocus = false;
7394 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7397 var v = this.getValue();
7398 if(String(v) !== String(this.startValue)){
7399 this.fireEvent('change', this, v, this.startValue);
7401 this.fireEvent("blur", this);
7405 * Resets the current field value to the originally loaded value and clears any validation messages
7408 this.setValue(this.originalValue);
7409 this.clearInvalid();
7412 * Returns the name of the field
7413 * @return {Mixed} name The name field
7415 getName: function(){
7419 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
7420 * @return {Mixed} value The field value
7422 getValue : function(){
7424 var v = this.inputEl().getValue();
7429 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
7430 * @return {Mixed} value The field value
7432 getRawValue : function(){
7433 var v = this.inputEl().getValue();
7439 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
7440 * @param {Mixed} value The value to set
7442 setRawValue : function(v){
7443 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7446 selectText : function(start, end){
7447 var v = this.getRawValue();
7449 start = start === undefined ? 0 : start;
7450 end = end === undefined ? v.length : end;
7451 var d = this.inputEl().dom;
7452 if(d.setSelectionRange){
7453 d.setSelectionRange(start, end);
7454 }else if(d.createTextRange){
7455 var range = d.createTextRange();
7456 range.moveStart("character", start);
7457 range.moveEnd("character", v.length-end);
7464 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
7465 * @param {Mixed} value The value to set
7467 setValue : function(v){
7470 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7476 processValue : function(value){
7477 if(this.stripCharsRe){
7478 var newValue = value.replace(this.stripCharsRe, '');
7479 if(newValue !== value){
7480 this.setRawValue(newValue);
7487 preFocus : function(){
7489 if(this.selectOnFocus){
7490 this.inputEl().dom.select();
7493 filterKeys : function(e){
7495 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7498 var c = e.getCharCode(), cc = String.fromCharCode(c);
7499 if(Roo.isIE && (e.isSpecialKey() || !cc)){
7502 if(!this.maskRe.test(cc)){
7507 * Clear any invalid styles/messages for this field
7509 clearInvalid : function(){
7511 if(!this.el || this.preventMark){ // not rendered
7514 this.el.removeClass(this.invalidClass);
7516 switch(this.msgTarget){
7518 this.el.dom.qtip = '';
7521 this.el.dom.title = '';
7525 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7530 this.errorIcon.dom.qtip = '';
7531 this.errorIcon.hide();
7532 this.un('resize', this.alignErrorIcon, this);
7536 var t = Roo.getDom(this.msgTarget);
7538 t.style.display = 'none';
7542 this.fireEvent('valid', this);
7545 * Mark this field as invalid
7546 * @param {String} msg The validation message
7548 markInvalid : function(msg){
7549 if(!this.el || this.preventMark){ // not rendered
7552 this.el.addClass(this.invalidClass);
7554 msg = msg || this.invalidText;
7555 switch(this.msgTarget){
7557 this.el.dom.qtip = msg;
7558 this.el.dom.qclass = 'x-form-invalid-tip';
7559 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7560 Roo.QuickTips.enable();
7564 this.el.dom.title = msg;
7568 var elp = this.el.findParent('.x-form-element', 5, true);
7569 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7570 this.errorEl.setWidth(elp.getWidth(true)-20);
7572 this.errorEl.update(msg);
7573 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7576 if(!this.errorIcon){
7577 var elp = this.el.findParent('.x-form-element', 5, true);
7578 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7580 this.alignErrorIcon();
7581 this.errorIcon.dom.qtip = msg;
7582 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7583 this.errorIcon.show();
7584 this.on('resize', this.alignErrorIcon, this);
7587 var t = Roo.getDom(this.msgTarget);
7589 t.style.display = this.msgDisplay;
7593 this.fireEvent('invalid', this, msg);
7596 SafariOnKeyDown : function(event)
7598 // this is a workaround for a password hang bug on chrome/ webkit.
7600 var isSelectAll = false;
7602 if(this.inputEl().dom.selectionEnd > 0){
7603 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7605 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7606 event.preventDefault();
7611 if(isSelectAll){ // backspace and delete key
7613 event.preventDefault();
7614 // this is very hacky as keydown always get's upper case.
7616 var cc = String.fromCharCode(event.getCharCode());
7617 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
7621 adjustWidth : function(tag, w){
7622 tag = tag.toLowerCase();
7623 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7624 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7628 if(tag == 'textarea'){
7631 }else if(Roo.isOpera){
7635 if(tag == 'textarea'){
7654 * @class Roo.bootstrap.TextArea
7655 * @extends Roo.bootstrap.Input
7656 * Bootstrap TextArea class
7657 * @cfg {Number} cols Specifies the visible width of a text area
7658 * @cfg {Number} rows Specifies the visible number of lines in a text area
7659 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7660 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7661 * @cfg {string} html text
7664 * Create a new TextArea
7665 * @param {Object} config The config object
7668 Roo.bootstrap.TextArea = function(config){
7669 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7673 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
7683 getAutoCreate : function(){
7685 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7696 value : this.value || '',
7697 html: this.html || '',
7698 cls : 'form-control',
7699 placeholder : this.placeholder || ''
7703 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7704 input.maxLength = this.maxLength;
7708 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7712 input.cols = this.cols;
7715 if (this.readOnly) {
7716 input.readonly = true;
7720 input.name = this.name;
7724 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7728 ['xs','sm','md','lg'].map(function(size){
7729 if (settings[size]) {
7730 cfg.cls += ' col-' + size + '-' + settings[size];
7734 var inputblock = input;
7736 if (this.before || this.after) {
7739 cls : 'input-group',
7743 inputblock.cn.push({
7745 cls : 'input-group-addon',
7749 inputblock.cn.push(input);
7751 inputblock.cn.push({
7753 cls : 'input-group-addon',
7760 if (align ==='left' && this.fieldLabel.length) {
7761 Roo.log("left and has label");
7767 cls : 'control-label col-sm-' + this.labelWidth,
7768 html : this.fieldLabel
7772 cls : "col-sm-" + (12 - this.labelWidth),
7779 } else if ( this.fieldLabel.length) {
7785 //cls : 'input-group-addon',
7786 html : this.fieldLabel
7796 Roo.log(" no label && no align");
7806 if (this.disabled) {
7807 input.disabled=true;
7814 * return the real textarea element.
7816 inputEl: function ()
7818 return this.el.select('textarea.form-control',true).first();
7826 * trigger field - base class for combo..
7831 * @class Roo.bootstrap.TriggerField
7832 * @extends Roo.bootstrap.Input
7833 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7834 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7835 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7836 * for which you can provide a custom implementation. For example:
7838 var trigger = new Roo.bootstrap.TriggerField();
7839 trigger.onTriggerClick = myTriggerFn;
7840 trigger.applyTo('my-field');
7843 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7844 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7845 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
7846 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7848 * Create a new TriggerField.
7849 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7850 * to the base TextField)
7852 Roo.bootstrap.TriggerField = function(config){
7853 this.mimicing = false;
7854 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7857 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
7859 * @cfg {String} triggerClass A CSS class to apply to the trigger
7862 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7866 /** @cfg {Boolean} grow @hide */
7867 /** @cfg {Number} growMin @hide */
7868 /** @cfg {Number} growMax @hide */
7874 autoSize: Roo.emptyFn,
7881 actionMode : 'wrap',
7885 getAutoCreate : function(){
7887 var align = this.labelAlign || this.parentLabelAlign();
7892 cls: 'form-group' //input-group
7899 type : this.inputType,
7900 cls : 'form-control',
7901 autocomplete: 'off',
7902 placeholder : this.placeholder || ''
7906 input.name = this.name;
7909 input.cls += ' input-' + this.size;
7912 if (this.disabled) {
7913 input.disabled=true;
7916 var inputblock = input;
7918 if (this.before || this.after) {
7921 cls : 'input-group',
7925 inputblock.cn.push({
7927 cls : 'input-group-addon',
7931 inputblock.cn.push(input);
7933 inputblock.cn.push({
7935 cls : 'input-group-addon',
7948 cls: 'form-hidden-field'
7956 Roo.log('multiple');
7964 cls: 'form-hidden-field'
7968 cls: 'select2-choices',
7972 cls: 'select2-search-field',
7985 cls: 'select2-container input-group',
7990 // cls: 'typeahead typeahead-long dropdown-menu',
7991 // style: 'display:none'
7996 if(!this.multiple && this.showToggleBtn){
7999 cls : 'input-group-addon btn dropdown-toggle',
8007 cls: 'combobox-clear',
8021 combobox.cls += ' select2-container-multi';
8024 if (align ==='left' && this.fieldLabel.length) {
8026 Roo.log("left and has label");
8032 cls : 'control-label col-sm-' + this.labelWidth,
8033 html : this.fieldLabel
8037 cls : "col-sm-" + (12 - this.labelWidth),
8044 } else if ( this.fieldLabel.length) {
8050 //cls : 'input-group-addon',
8051 html : this.fieldLabel
8061 Roo.log(" no label && no align");
8068 ['xs','sm','md','lg'].map(function(size){
8069 if (settings[size]) {
8070 cfg.cls += ' col-' + size + '-' + settings[size];
8081 onResize : function(w, h){
8082 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8083 // if(typeof w == 'number'){
8084 // var x = w - this.trigger.getWidth();
8085 // this.inputEl().setWidth(this.adjustWidth('input', x));
8086 // this.trigger.setStyle('left', x+'px');
8091 adjustSize : Roo.BoxComponent.prototype.adjustSize,
8094 getResizeEl : function(){
8095 return this.inputEl();
8099 getPositionEl : function(){
8100 return this.inputEl();
8104 alignErrorIcon : function(){
8105 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8109 initEvents : function(){
8113 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8114 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8115 if(!this.multiple && this.showToggleBtn){
8116 this.trigger = this.el.select('span.dropdown-toggle',true).first();
8117 if(this.hideTrigger){
8118 this.trigger.setDisplayed(false);
8120 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8124 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8127 //this.trigger.addClassOnOver('x-form-trigger-over');
8128 //this.trigger.addClassOnClick('x-form-trigger-click');
8131 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8135 createList : function()
8137 this.list = Roo.get(document.body).createChild({
8139 cls: 'typeahead typeahead-long dropdown-menu',
8140 style: 'display:none'
8143 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8148 initTrigger : function(){
8153 onDestroy : function(){
8155 this.trigger.removeAllListeners();
8156 // this.trigger.remove();
8159 // this.wrap.remove();
8161 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8165 onFocus : function(){
8166 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8169 this.wrap.addClass('x-trigger-wrap-focus');
8170 this.mimicing = true;
8171 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8172 if(this.monitorTab){
8173 this.el.on("keydown", this.checkTab, this);
8180 checkTab : function(e){
8181 if(e.getKey() == e.TAB){
8187 onBlur : function(){
8192 mimicBlur : function(e, t){
8194 if(!this.wrap.contains(t) && this.validateBlur()){
8201 triggerBlur : function(){
8202 this.mimicing = false;
8203 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8204 if(this.monitorTab){
8205 this.el.un("keydown", this.checkTab, this);
8207 //this.wrap.removeClass('x-trigger-wrap-focus');
8208 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8212 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8213 validateBlur : function(e, t){
8218 onDisable : function(){
8219 this.inputEl().dom.disabled = true;
8220 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8222 // this.wrap.addClass('x-item-disabled');
8227 onEnable : function(){
8228 this.inputEl().dom.disabled = false;
8229 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8231 // this.el.removeClass('x-item-disabled');
8236 onShow : function(){
8237 var ae = this.getActionEl();
8240 ae.dom.style.display = '';
8241 ae.dom.style.visibility = 'visible';
8247 onHide : function(){
8248 var ae = this.getActionEl();
8249 ae.dom.style.display = 'none';
8253 * The function that should handle the trigger's click event. This method does nothing by default until overridden
8254 * by an implementing function.
8256 * @param {EventObject} e
8258 onTriggerClick : Roo.emptyFn
8262 * Ext JS Library 1.1.1
8263 * Copyright(c) 2006-2007, Ext JS, LLC.
8265 * Originally Released Under LGPL - original licence link has changed is not relivant.
8268 * <script type="text/javascript">
8273 * @class Roo.data.SortTypes
8275 * Defines the default sorting (casting?) comparison functions used when sorting data.
8277 Roo.data.SortTypes = {
8279 * Default sort that does nothing
8280 * @param {Mixed} s The value being converted
8281 * @return {Mixed} The comparison value
8288 * The regular expression used to strip tags
8292 stripTagsRE : /<\/?[^>]+>/gi,
8295 * Strips all HTML tags to sort on text only
8296 * @param {Mixed} s The value being converted
8297 * @return {String} The comparison value
8299 asText : function(s){
8300 return String(s).replace(this.stripTagsRE, "");
8304 * Strips all HTML tags to sort on text only - Case insensitive
8305 * @param {Mixed} s The value being converted
8306 * @return {String} The comparison value
8308 asUCText : function(s){
8309 return String(s).toUpperCase().replace(this.stripTagsRE, "");
8313 * Case insensitive string
8314 * @param {Mixed} s The value being converted
8315 * @return {String} The comparison value
8317 asUCString : function(s) {
8318 return String(s).toUpperCase();
8323 * @param {Mixed} s The value being converted
8324 * @return {Number} The comparison value
8326 asDate : function(s) {
8330 if(s instanceof Date){
8333 return Date.parse(String(s));
8338 * @param {Mixed} s The value being converted
8339 * @return {Float} The comparison value
8341 asFloat : function(s) {
8342 var val = parseFloat(String(s).replace(/,/g, ""));
8343 if(isNaN(val)) val = 0;
8349 * @param {Mixed} s The value being converted
8350 * @return {Number} The comparison value
8352 asInt : function(s) {
8353 var val = parseInt(String(s).replace(/,/g, ""));
8354 if(isNaN(val)) val = 0;
8359 * Ext JS Library 1.1.1
8360 * Copyright(c) 2006-2007, Ext JS, LLC.
8362 * Originally Released Under LGPL - original licence link has changed is not relivant.
8365 * <script type="text/javascript">
8369 * @class Roo.data.Record
8370 * Instances of this class encapsulate both record <em>definition</em> information, and record
8371 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8372 * to access Records cached in an {@link Roo.data.Store} object.<br>
8374 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8375 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8378 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8380 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8381 * {@link #create}. The parameters are the same.
8382 * @param {Array} data An associative Array of data values keyed by the field name.
8383 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8384 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8385 * not specified an integer id is generated.
8387 Roo.data.Record = function(data, id){
8388 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8393 * Generate a constructor for a specific record layout.
8394 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8395 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8396 * Each field definition object may contain the following properties: <ul>
8397 * <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,
8398 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8399 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8400 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8401 * is being used, then this is a string containing the javascript expression to reference the data relative to
8402 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8403 * to the data item relative to the record element. If the mapping expression is the same as the field name,
8404 * this may be omitted.</p></li>
8405 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8406 * <ul><li>auto (Default, implies no conversion)</li>
8411 * <li>date</li></ul></p></li>
8412 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8413 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8414 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8415 * by the Reader into an object that will be stored in the Record. It is passed the
8416 * following parameters:<ul>
8417 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8419 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8421 * <br>usage:<br><pre><code>
8422 var TopicRecord = Roo.data.Record.create(
8423 {name: 'title', mapping: 'topic_title'},
8424 {name: 'author', mapping: 'username'},
8425 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8426 {name: 'lastPost', mapping: 'post_time', type: 'date'},
8427 {name: 'lastPoster', mapping: 'user2'},
8428 {name: 'excerpt', mapping: 'post_text'}
8431 var myNewRecord = new TopicRecord({
8432 title: 'Do my job please',
8435 lastPost: new Date(),
8436 lastPoster: 'Animal',
8437 excerpt: 'No way dude!'
8439 myStore.add(myNewRecord);
8444 Roo.data.Record.create = function(o){
8446 f.superclass.constructor.apply(this, arguments);
8448 Roo.extend(f, Roo.data.Record);
8449 var p = f.prototype;
8450 p.fields = new Roo.util.MixedCollection(false, function(field){
8453 for(var i = 0, len = o.length; i < len; i++){
8454 p.fields.add(new Roo.data.Field(o[i]));
8456 f.getField = function(name){
8457 return p.fields.get(name);
8462 Roo.data.Record.AUTO_ID = 1000;
8463 Roo.data.Record.EDIT = 'edit';
8464 Roo.data.Record.REJECT = 'reject';
8465 Roo.data.Record.COMMIT = 'commit';
8467 Roo.data.Record.prototype = {
8469 * Readonly flag - true if this record has been modified.
8478 join : function(store){
8483 * Set the named field to the specified value.
8484 * @param {String} name The name of the field to set.
8485 * @param {Object} value The value to set the field to.
8487 set : function(name, value){
8488 if(this.data[name] == value){
8495 if(typeof this.modified[name] == 'undefined'){
8496 this.modified[name] = this.data[name];
8498 this.data[name] = value;
8499 if(!this.editing && this.store){
8500 this.store.afterEdit(this);
8505 * Get the value of the named field.
8506 * @param {String} name The name of the field to get the value of.
8507 * @return {Object} The value of the field.
8509 get : function(name){
8510 return this.data[name];
8514 beginEdit : function(){
8515 this.editing = true;
8520 cancelEdit : function(){
8521 this.editing = false;
8522 delete this.modified;
8526 endEdit : function(){
8527 this.editing = false;
8528 if(this.dirty && this.store){
8529 this.store.afterEdit(this);
8534 * Usually called by the {@link Roo.data.Store} which owns the Record.
8535 * Rejects all changes made to the Record since either creation, or the last commit operation.
8536 * Modified fields are reverted to their original values.
8538 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8539 * of reject operations.
8541 reject : function(){
8542 var m = this.modified;
8544 if(typeof m[n] != "function"){
8545 this.data[n] = m[n];
8549 delete this.modified;
8550 this.editing = false;
8552 this.store.afterReject(this);
8557 * Usually called by the {@link Roo.data.Store} which owns the Record.
8558 * Commits all changes made to the Record since either creation, or the last commit operation.
8560 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8561 * of commit operations.
8563 commit : function(){
8565 delete this.modified;
8566 this.editing = false;
8568 this.store.afterCommit(this);
8573 hasError : function(){
8574 return this.error != null;
8578 clearError : function(){
8583 * Creates a copy of this record.
8584 * @param {String} id (optional) A new record id if you don't want to use this record's id
8587 copy : function(newId) {
8588 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8592 * Ext JS Library 1.1.1
8593 * Copyright(c) 2006-2007, Ext JS, LLC.
8595 * Originally Released Under LGPL - original licence link has changed is not relivant.
8598 * <script type="text/javascript">
8604 * @class Roo.data.Store
8605 * @extends Roo.util.Observable
8606 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8607 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8609 * 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
8610 * has no knowledge of the format of the data returned by the Proxy.<br>
8612 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8613 * instances from the data object. These records are cached and made available through accessor functions.
8615 * Creates a new Store.
8616 * @param {Object} config A config object containing the objects needed for the Store to access data,
8617 * and read the data into Records.
8619 Roo.data.Store = function(config){
8620 this.data = new Roo.util.MixedCollection(false);
8621 this.data.getKey = function(o){
8624 this.baseParams = {};
8631 "multisort" : "_multisort"
8634 if(config && config.data){
8635 this.inlineData = config.data;
8639 Roo.apply(this, config);
8641 if(this.reader){ // reader passed
8642 this.reader = Roo.factory(this.reader, Roo.data);
8643 this.reader.xmodule = this.xmodule || false;
8644 if(!this.recordType){
8645 this.recordType = this.reader.recordType;
8647 if(this.reader.onMetaChange){
8648 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8652 if(this.recordType){
8653 this.fields = this.recordType.prototype.fields;
8659 * @event datachanged
8660 * Fires when the data cache has changed, and a widget which is using this Store
8661 * as a Record cache should refresh its view.
8662 * @param {Store} this
8667 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8668 * @param {Store} this
8669 * @param {Object} meta The JSON metadata
8674 * Fires when Records have been added to the Store
8675 * @param {Store} this
8676 * @param {Roo.data.Record[]} records The array of Records added
8677 * @param {Number} index The index at which the record(s) were added
8682 * Fires when a Record has been removed from the Store
8683 * @param {Store} this
8684 * @param {Roo.data.Record} record The Record that was removed
8685 * @param {Number} index The index at which the record was removed
8690 * Fires when a Record has been updated
8691 * @param {Store} this
8692 * @param {Roo.data.Record} record The Record that was updated
8693 * @param {String} operation The update operation being performed. Value may be one of:
8695 Roo.data.Record.EDIT
8696 Roo.data.Record.REJECT
8697 Roo.data.Record.COMMIT
8703 * Fires when the data cache has been cleared.
8704 * @param {Store} this
8709 * Fires before a request is made for a new data object. If the beforeload handler returns false
8710 * the load action will be canceled.
8711 * @param {Store} this
8712 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8716 * @event beforeloadadd
8717 * Fires after a new set of Records has been loaded.
8718 * @param {Store} this
8719 * @param {Roo.data.Record[]} records The Records that were loaded
8720 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8722 beforeloadadd : true,
8725 * Fires after a new set of Records has been loaded, before they are added to the store.
8726 * @param {Store} this
8727 * @param {Roo.data.Record[]} records The Records that were loaded
8728 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8729 * @params {Object} return from reader
8733 * @event loadexception
8734 * Fires if an exception occurs in the Proxy during loading.
8735 * Called with the signature of the Proxy's "loadexception" event.
8736 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8739 * @param {Object} return from JsonData.reader() - success, totalRecords, records
8740 * @param {Object} load options
8741 * @param {Object} jsonData from your request (normally this contains the Exception)
8743 loadexception : true
8747 this.proxy = Roo.factory(this.proxy, Roo.data);
8748 this.proxy.xmodule = this.xmodule || false;
8749 this.relayEvents(this.proxy, ["loadexception"]);
8751 this.sortToggle = {};
8752 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8754 Roo.data.Store.superclass.constructor.call(this);
8756 if(this.inlineData){
8757 this.loadData(this.inlineData);
8758 delete this.inlineData;
8762 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8764 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
8765 * without a remote query - used by combo/forms at present.
8769 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8772 * @cfg {Array} data Inline data to be loaded when the store is initialized.
8775 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8776 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8779 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8780 * on any HTTP request
8783 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8786 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8790 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8791 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8796 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8797 * loaded or when a record is removed. (defaults to false).
8799 pruneModifiedRecords : false,
8805 * Add Records to the Store and fires the add event.
8806 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8808 add : function(records){
8809 records = [].concat(records);
8810 for(var i = 0, len = records.length; i < len; i++){
8811 records[i].join(this);
8813 var index = this.data.length;
8814 this.data.addAll(records);
8815 this.fireEvent("add", this, records, index);
8819 * Remove a Record from the Store and fires the remove event.
8820 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8822 remove : function(record){
8823 var index = this.data.indexOf(record);
8824 this.data.removeAt(index);
8825 if(this.pruneModifiedRecords){
8826 this.modified.remove(record);
8828 this.fireEvent("remove", this, record, index);
8832 * Remove all Records from the Store and fires the clear event.
8834 removeAll : function(){
8836 if(this.pruneModifiedRecords){
8839 this.fireEvent("clear", this);
8843 * Inserts Records to the Store at the given index and fires the add event.
8844 * @param {Number} index The start index at which to insert the passed Records.
8845 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8847 insert : function(index, records){
8848 records = [].concat(records);
8849 for(var i = 0, len = records.length; i < len; i++){
8850 this.data.insert(index, records[i]);
8851 records[i].join(this);
8853 this.fireEvent("add", this, records, index);
8857 * Get the index within the cache of the passed Record.
8858 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8859 * @return {Number} The index of the passed Record. Returns -1 if not found.
8861 indexOf : function(record){
8862 return this.data.indexOf(record);
8866 * Get the index within the cache of the Record with the passed id.
8867 * @param {String} id The id of the Record to find.
8868 * @return {Number} The index of the Record. Returns -1 if not found.
8870 indexOfId : function(id){
8871 return this.data.indexOfKey(id);
8875 * Get the Record with the specified id.
8876 * @param {String} id The id of the Record to find.
8877 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8879 getById : function(id){
8880 return this.data.key(id);
8884 * Get the Record at the specified index.
8885 * @param {Number} index The index of the Record to find.
8886 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8888 getAt : function(index){
8889 return this.data.itemAt(index);
8893 * Returns a range of Records between specified indices.
8894 * @param {Number} startIndex (optional) The starting index (defaults to 0)
8895 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8896 * @return {Roo.data.Record[]} An array of Records
8898 getRange : function(start, end){
8899 return this.data.getRange(start, end);
8903 storeOptions : function(o){
8904 o = Roo.apply({}, o);
8907 this.lastOptions = o;
8911 * Loads the Record cache from the configured Proxy using the configured Reader.
8913 * If using remote paging, then the first load call must specify the <em>start</em>
8914 * and <em>limit</em> properties in the options.params property to establish the initial
8915 * position within the dataset, and the number of Records to cache on each read from the Proxy.
8917 * <strong>It is important to note that for remote data sources, loading is asynchronous,
8918 * and this call will return before the new data has been loaded. Perform any post-processing
8919 * in a callback function, or in a "load" event handler.</strong>
8921 * @param {Object} options An object containing properties which control loading options:<ul>
8922 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8923 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8924 * passed the following arguments:<ul>
8925 * <li>r : Roo.data.Record[]</li>
8926 * <li>options: Options object from the load call</li>
8927 * <li>success: Boolean success indicator</li></ul></li>
8928 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8929 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8932 load : function(options){
8933 options = options || {};
8934 if(this.fireEvent("beforeload", this, options) !== false){
8935 this.storeOptions(options);
8936 var p = Roo.apply(options.params || {}, this.baseParams);
8937 // if meta was not loaded from remote source.. try requesting it.
8938 if (!this.reader.metaFromRemote) {
8941 if(this.sortInfo && this.remoteSort){
8942 var pn = this.paramNames;
8943 p[pn["sort"]] = this.sortInfo.field;
8944 p[pn["dir"]] = this.sortInfo.direction;
8946 if (this.multiSort) {
8947 var pn = this.paramNames;
8948 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8951 this.proxy.load(p, this.reader, this.loadRecords, this, options);
8956 * Reloads the Record cache from the configured Proxy using the configured Reader and
8957 * the options from the last load operation performed.
8958 * @param {Object} options (optional) An object containing properties which may override the options
8959 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8960 * the most recently used options are reused).
8962 reload : function(options){
8963 this.load(Roo.applyIf(options||{}, this.lastOptions));
8967 // Called as a callback by the Reader during a load operation.
8968 loadRecords : function(o, options, success){
8969 if(!o || success === false){
8970 if(success !== false){
8971 this.fireEvent("load", this, [], options, o);
8973 if(options.callback){
8974 options.callback.call(options.scope || this, [], options, false);
8978 // if data returned failure - throw an exception.
8979 if (o.success === false) {
8980 // show a message if no listener is registered.
8981 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8982 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8984 // loadmask wil be hooked into this..
8985 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8988 var r = o.records, t = o.totalRecords || r.length;
8990 this.fireEvent("beforeloadadd", this, r, options, o);
8992 if(!options || options.add !== true){
8993 if(this.pruneModifiedRecords){
8996 for(var i = 0, len = r.length; i < len; i++){
9000 this.data = this.snapshot;
9001 delete this.snapshot;
9004 this.data.addAll(r);
9005 this.totalLength = t;
9007 this.fireEvent("datachanged", this);
9009 this.totalLength = Math.max(t, this.data.length+r.length);
9012 this.fireEvent("load", this, r, options, o);
9013 if(options.callback){
9014 options.callback.call(options.scope || this, r, options, true);
9020 * Loads data from a passed data block. A Reader which understands the format of the data
9021 * must have been configured in the constructor.
9022 * @param {Object} data The data block from which to read the Records. The format of the data expected
9023 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9024 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9026 loadData : function(o, append){
9027 var r = this.reader.readRecords(o);
9028 this.loadRecords(r, {add: append}, true);
9032 * Gets the number of cached records.
9034 * <em>If using paging, this may not be the total size of the dataset. If the data object
9035 * used by the Reader contains the dataset size, then the getTotalCount() function returns
9036 * the data set size</em>
9038 getCount : function(){
9039 return this.data.length || 0;
9043 * Gets the total number of records in the dataset as returned by the server.
9045 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9046 * the dataset size</em>
9048 getTotalCount : function(){
9049 return this.totalLength || 0;
9053 * Returns the sort state of the Store as an object with two properties:
9055 field {String} The name of the field by which the Records are sorted
9056 direction {String} The sort order, "ASC" or "DESC"
9059 getSortState : function(){
9060 return this.sortInfo;
9064 applySort : function(){
9065 if(this.sortInfo && !this.remoteSort){
9066 var s = this.sortInfo, f = s.field;
9067 var st = this.fields.get(f).sortType;
9068 var fn = function(r1, r2){
9069 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9070 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9072 this.data.sort(s.direction, fn);
9073 if(this.snapshot && this.snapshot != this.data){
9074 this.snapshot.sort(s.direction, fn);
9080 * Sets the default sort column and order to be used by the next load operation.
9081 * @param {String} fieldName The name of the field to sort by.
9082 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9084 setDefaultSort : function(field, dir){
9085 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9090 * If remote sorting is used, the sort is performed on the server, and the cache is
9091 * reloaded. If local sorting is used, the cache is sorted internally.
9092 * @param {String} fieldName The name of the field to sort by.
9093 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9095 sort : function(fieldName, dir){
9096 var f = this.fields.get(fieldName);
9098 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9100 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9101 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9106 this.sortToggle[f.name] = dir;
9107 this.sortInfo = {field: f.name, direction: dir};
9108 if(!this.remoteSort){
9110 this.fireEvent("datachanged", this);
9112 this.load(this.lastOptions);
9117 * Calls the specified function for each of the Records in the cache.
9118 * @param {Function} fn The function to call. The Record is passed as the first parameter.
9119 * Returning <em>false</em> aborts and exits the iteration.
9120 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9122 each : function(fn, scope){
9123 this.data.each(fn, scope);
9127 * Gets all records modified since the last commit. Modified records are persisted across load operations
9128 * (e.g., during paging).
9129 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9131 getModifiedRecords : function(){
9132 return this.modified;
9136 createFilterFn : function(property, value, anyMatch){
9137 if(!value.exec){ // not a regex
9138 value = String(value);
9139 if(value.length == 0){
9142 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9145 return value.test(r.data[property]);
9150 * Sums the value of <i>property</i> for each record between start and end and returns the result.
9151 * @param {String} property A field on your records
9152 * @param {Number} start The record index to start at (defaults to 0)
9153 * @param {Number} end The last record index to include (defaults to length - 1)
9154 * @return {Number} The sum
9156 sum : function(property, start, end){
9157 var rs = this.data.items, v = 0;
9159 end = (end || end === 0) ? end : rs.length-1;
9161 for(var i = start; i <= end; i++){
9162 v += (rs[i].data[property] || 0);
9168 * Filter the records by a specified property.
9169 * @param {String} field A field on your records
9170 * @param {String/RegExp} value Either a string that the field
9171 * should start with or a RegExp to test against the field
9172 * @param {Boolean} anyMatch True to match any part not just the beginning
9174 filter : function(property, value, anyMatch){
9175 var fn = this.createFilterFn(property, value, anyMatch);
9176 return fn ? this.filterBy(fn) : this.clearFilter();
9180 * Filter by a function. The specified function will be called with each
9181 * record in this data source. If the function returns true the record is included,
9182 * otherwise it is filtered.
9183 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9184 * @param {Object} scope (optional) The scope of the function (defaults to this)
9186 filterBy : function(fn, scope){
9187 this.snapshot = this.snapshot || this.data;
9188 this.data = this.queryBy(fn, scope||this);
9189 this.fireEvent("datachanged", this);
9193 * Query the records by a specified property.
9194 * @param {String} field A field on your records
9195 * @param {String/RegExp} value Either a string that the field
9196 * should start with or a RegExp to test against the field
9197 * @param {Boolean} anyMatch True to match any part not just the beginning
9198 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9200 query : function(property, value, anyMatch){
9201 var fn = this.createFilterFn(property, value, anyMatch);
9202 return fn ? this.queryBy(fn) : this.data.clone();
9206 * Query by a function. The specified function will be called with each
9207 * record in this data source. If the function returns true the record is included
9209 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9210 * @param {Object} scope (optional) The scope of the function (defaults to this)
9211 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9213 queryBy : function(fn, scope){
9214 var data = this.snapshot || this.data;
9215 return data.filterBy(fn, scope||this);
9219 * Collects unique values for a particular dataIndex from this store.
9220 * @param {String} dataIndex The property to collect
9221 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9222 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9223 * @return {Array} An array of the unique values
9225 collect : function(dataIndex, allowNull, bypassFilter){
9226 var d = (bypassFilter === true && this.snapshot) ?
9227 this.snapshot.items : this.data.items;
9228 var v, sv, r = [], l = {};
9229 for(var i = 0, len = d.length; i < len; i++){
9230 v = d[i].data[dataIndex];
9232 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9241 * Revert to a view of the Record cache with no filtering applied.
9242 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9244 clearFilter : function(suppressEvent){
9245 if(this.snapshot && this.snapshot != this.data){
9246 this.data = this.snapshot;
9247 delete this.snapshot;
9248 if(suppressEvent !== true){
9249 this.fireEvent("datachanged", this);
9255 afterEdit : function(record){
9256 if(this.modified.indexOf(record) == -1){
9257 this.modified.push(record);
9259 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9263 afterReject : function(record){
9264 this.modified.remove(record);
9265 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9269 afterCommit : function(record){
9270 this.modified.remove(record);
9271 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9275 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9276 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9278 commitChanges : function(){
9279 var m = this.modified.slice(0);
9281 for(var i = 0, len = m.length; i < len; i++){
9287 * Cancel outstanding changes on all changed records.
9289 rejectChanges : function(){
9290 var m = this.modified.slice(0);
9292 for(var i = 0, len = m.length; i < len; i++){
9297 onMetaChange : function(meta, rtype, o){
9298 this.recordType = rtype;
9299 this.fields = rtype.prototype.fields;
9300 delete this.snapshot;
9301 this.sortInfo = meta.sortInfo || this.sortInfo;
9303 this.fireEvent('metachange', this, this.reader.meta);
9306 moveIndex : function(data, type)
9308 var index = this.indexOf(data);
9310 var newIndex = index + type;
9314 this.insert(newIndex, data);
9319 * Ext JS Library 1.1.1
9320 * Copyright(c) 2006-2007, Ext JS, LLC.
9322 * Originally Released Under LGPL - original licence link has changed is not relivant.
9325 * <script type="text/javascript">
9329 * @class Roo.data.SimpleStore
9330 * @extends Roo.data.Store
9331 * Small helper class to make creating Stores from Array data easier.
9332 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9333 * @cfg {Array} fields An array of field definition objects, or field name strings.
9334 * @cfg {Array} data The multi-dimensional array of data
9336 * @param {Object} config
9338 Roo.data.SimpleStore = function(config){
9339 Roo.data.SimpleStore.superclass.constructor.call(this, {
9341 reader: new Roo.data.ArrayReader({
9344 Roo.data.Record.create(config.fields)
9346 proxy : new Roo.data.MemoryProxy(config.data)
9350 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9352 * Ext JS Library 1.1.1
9353 * Copyright(c) 2006-2007, Ext JS, LLC.
9355 * Originally Released Under LGPL - original licence link has changed is not relivant.
9358 * <script type="text/javascript">
9363 * @extends Roo.data.Store
9364 * @class Roo.data.JsonStore
9365 * Small helper class to make creating Stores for JSON data easier. <br/>
9367 var store = new Roo.data.JsonStore({
9368 url: 'get-images.php',
9370 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9373 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9374 * JsonReader and HttpProxy (unless inline data is provided).</b>
9375 * @cfg {Array} fields An array of field definition objects, or field name strings.
9377 * @param {Object} config
9379 Roo.data.JsonStore = function(c){
9380 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9381 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9382 reader: new Roo.data.JsonReader(c, c.fields)
9385 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9387 * Ext JS Library 1.1.1
9388 * Copyright(c) 2006-2007, Ext JS, LLC.
9390 * Originally Released Under LGPL - original licence link has changed is not relivant.
9393 * <script type="text/javascript">
9397 Roo.data.Field = function(config){
9398 if(typeof config == "string"){
9399 config = {name: config};
9401 Roo.apply(this, config);
9407 var st = Roo.data.SortTypes;
9408 // named sortTypes are supported, here we look them up
9409 if(typeof this.sortType == "string"){
9410 this.sortType = st[this.sortType];
9413 // set default sortType for strings and dates
9417 this.sortType = st.asUCString;
9420 this.sortType = st.asDate;
9423 this.sortType = st.none;
9428 var stripRe = /[\$,%]/g;
9430 // prebuilt conversion function for this field, instead of
9431 // switching every time we're reading a value
9433 var cv, dateFormat = this.dateFormat;
9438 cv = function(v){ return v; };
9441 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9445 return v !== undefined && v !== null && v !== '' ?
9446 parseInt(String(v).replace(stripRe, ""), 10) : '';
9451 return v !== undefined && v !== null && v !== '' ?
9452 parseFloat(String(v).replace(stripRe, ""), 10) : '';
9457 cv = function(v){ return v === true || v === "true" || v == 1; };
9464 if(v instanceof Date){
9468 if(dateFormat == "timestamp"){
9469 return new Date(v*1000);
9471 return Date.parseDate(v, dateFormat);
9473 var parsed = Date.parse(v);
9474 return parsed ? new Date(parsed) : null;
9483 Roo.data.Field.prototype = {
9491 * Ext JS Library 1.1.1
9492 * Copyright(c) 2006-2007, Ext JS, LLC.
9494 * Originally Released Under LGPL - original licence link has changed is not relivant.
9497 * <script type="text/javascript">
9500 // Base class for reading structured data from a data source. This class is intended to be
9501 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9504 * @class Roo.data.DataReader
9505 * Base class for reading structured data from a data source. This class is intended to be
9506 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9509 Roo.data.DataReader = function(meta, recordType){
9513 this.recordType = recordType instanceof Array ?
9514 Roo.data.Record.create(recordType) : recordType;
9517 Roo.data.DataReader.prototype = {
9519 * Create an empty record
9520 * @param {Object} data (optional) - overlay some values
9521 * @return {Roo.data.Record} record created.
9523 newRow : function(d) {
9525 this.recordType.prototype.fields.each(function(c) {
9527 case 'int' : da[c.name] = 0; break;
9528 case 'date' : da[c.name] = new Date(); break;
9529 case 'float' : da[c.name] = 0.0; break;
9530 case 'boolean' : da[c.name] = false; break;
9531 default : da[c.name] = ""; break;
9535 return new this.recordType(Roo.apply(da, d));
9540 * Ext JS Library 1.1.1
9541 * Copyright(c) 2006-2007, Ext JS, LLC.
9543 * Originally Released Under LGPL - original licence link has changed is not relivant.
9546 * <script type="text/javascript">
9550 * @class Roo.data.DataProxy
9551 * @extends Roo.data.Observable
9552 * This class is an abstract base class for implementations which provide retrieval of
9553 * unformatted data objects.<br>
9555 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9556 * (of the appropriate type which knows how to parse the data object) to provide a block of
9557 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9559 * Custom implementations must implement the load method as described in
9560 * {@link Roo.data.HttpProxy#load}.
9562 Roo.data.DataProxy = function(){
9566 * Fires before a network request is made to retrieve a data object.
9567 * @param {Object} This DataProxy object.
9568 * @param {Object} params The params parameter to the load function.
9573 * Fires before the load method's callback is called.
9574 * @param {Object} This DataProxy object.
9575 * @param {Object} o The data object.
9576 * @param {Object} arg The callback argument object passed to the load function.
9580 * @event loadexception
9581 * Fires if an Exception occurs during data retrieval.
9582 * @param {Object} This DataProxy object.
9583 * @param {Object} o The data object.
9584 * @param {Object} arg The callback argument object passed to the load function.
9585 * @param {Object} e The Exception.
9587 loadexception : true
9589 Roo.data.DataProxy.superclass.constructor.call(this);
9592 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9595 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9599 * Ext JS Library 1.1.1
9600 * Copyright(c) 2006-2007, Ext JS, LLC.
9602 * Originally Released Under LGPL - original licence link has changed is not relivant.
9605 * <script type="text/javascript">
9608 * @class Roo.data.MemoryProxy
9609 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9610 * to the Reader when its load method is called.
9612 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9614 Roo.data.MemoryProxy = function(data){
9618 Roo.data.MemoryProxy.superclass.constructor.call(this);
9622 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9624 * Load data from the requested source (in this case an in-memory
9625 * data object passed to the constructor), read the data object into
9626 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9627 * process that block using the passed callback.
9628 * @param {Object} params This parameter is not used by the MemoryProxy class.
9629 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9630 * object into a block of Roo.data.Records.
9631 * @param {Function} callback The function into which to pass the block of Roo.data.records.
9632 * The function must be passed <ul>
9633 * <li>The Record block object</li>
9634 * <li>The "arg" argument from the load function</li>
9635 * <li>A boolean success indicator</li>
9637 * @param {Object} scope The scope in which to call the callback
9638 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9640 load : function(params, reader, callback, scope, arg){
9641 params = params || {};
9644 result = reader.readRecords(this.data);
9646 this.fireEvent("loadexception", this, arg, null, e);
9647 callback.call(scope, null, arg, false);
9650 callback.call(scope, result, arg, true);
9654 update : function(params, records){
9659 * Ext JS Library 1.1.1
9660 * Copyright(c) 2006-2007, Ext JS, LLC.
9662 * Originally Released Under LGPL - original licence link has changed is not relivant.
9665 * <script type="text/javascript">
9668 * @class Roo.data.HttpProxy
9669 * @extends Roo.data.DataProxy
9670 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9671 * configured to reference a certain URL.<br><br>
9673 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9674 * from which the running page was served.<br><br>
9676 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9678 * Be aware that to enable the browser to parse an XML document, the server must set
9679 * the Content-Type header in the HTTP response to "text/xml".
9681 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9682 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
9683 * will be used to make the request.
9685 Roo.data.HttpProxy = function(conn){
9686 Roo.data.HttpProxy.superclass.constructor.call(this);
9687 // is conn a conn config or a real conn?
9689 this.useAjax = !conn || !conn.events;
9693 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9694 // thse are take from connection...
9697 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9700 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9701 * extra parameters to each request made by this object. (defaults to undefined)
9704 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9705 * to each request made by this object. (defaults to undefined)
9708 * @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)
9711 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9714 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9720 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9724 * Return the {@link Roo.data.Connection} object being used by this Proxy.
9725 * @return {Connection} The Connection object. This object may be used to subscribe to events on
9726 * a finer-grained basis than the DataProxy events.
9728 getConnection : function(){
9729 return this.useAjax ? Roo.Ajax : this.conn;
9733 * Load data from the configured {@link Roo.data.Connection}, read the data object into
9734 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9735 * process that block using the passed callback.
9736 * @param {Object} params An object containing properties which are to be used as HTTP parameters
9737 * for the request to the remote server.
9738 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9739 * object into a block of Roo.data.Records.
9740 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9741 * The function must be passed <ul>
9742 * <li>The Record block object</li>
9743 * <li>The "arg" argument from the load function</li>
9744 * <li>A boolean success indicator</li>
9746 * @param {Object} scope The scope in which to call the callback
9747 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9749 load : function(params, reader, callback, scope, arg){
9750 if(this.fireEvent("beforeload", this, params) !== false){
9752 params : params || {},
9754 callback : callback,
9759 callback : this.loadResponse,
9763 Roo.applyIf(o, this.conn);
9764 if(this.activeRequest){
9765 Roo.Ajax.abort(this.activeRequest);
9767 this.activeRequest = Roo.Ajax.request(o);
9769 this.conn.request(o);
9772 callback.call(scope||this, null, arg, false);
9777 loadResponse : function(o, success, response){
9778 delete this.activeRequest;
9780 this.fireEvent("loadexception", this, o, response);
9781 o.request.callback.call(o.request.scope, null, o.request.arg, false);
9786 result = o.reader.read(response);
9788 this.fireEvent("loadexception", this, o, response, e);
9789 o.request.callback.call(o.request.scope, null, o.request.arg, false);
9793 this.fireEvent("load", this, o, o.request.arg);
9794 o.request.callback.call(o.request.scope, result, o.request.arg, true);
9798 update : function(dataSet){
9803 updateResponse : function(dataSet){
9808 * Ext JS Library 1.1.1
9809 * Copyright(c) 2006-2007, Ext JS, LLC.
9811 * Originally Released Under LGPL - original licence link has changed is not relivant.
9814 * <script type="text/javascript">
9818 * @class Roo.data.ScriptTagProxy
9819 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9820 * other than the originating domain of the running page.<br><br>
9822 * <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
9823 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9825 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9826 * source code that is used as the source inside a <script> tag.<br><br>
9828 * In order for the browser to process the returned data, the server must wrap the data object
9829 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9830 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9831 * depending on whether the callback name was passed:
9834 boolean scriptTag = false;
9835 String cb = request.getParameter("callback");
9838 response.setContentType("text/javascript");
9840 response.setContentType("application/x-json");
9842 Writer out = response.getWriter();
9844 out.write(cb + "(");
9846 out.print(dataBlock.toJsonString());
9853 * @param {Object} config A configuration object.
9855 Roo.data.ScriptTagProxy = function(config){
9856 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9857 Roo.apply(this, config);
9858 this.head = document.getElementsByTagName("head")[0];
9861 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9863 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9865 * @cfg {String} url The URL from which to request the data object.
9868 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9872 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9873 * the server the name of the callback function set up by the load call to process the returned data object.
9874 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9875 * javascript output which calls this named function passing the data object as its only parameter.
9877 callbackParam : "callback",
9879 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9880 * name to the request.
9885 * Load data from the configured URL, read the data object into
9886 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9887 * process that block using the passed callback.
9888 * @param {Object} params An object containing properties which are to be used as HTTP parameters
9889 * for the request to the remote server.
9890 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9891 * object into a block of Roo.data.Records.
9892 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9893 * The function must be passed <ul>
9894 * <li>The Record block object</li>
9895 * <li>The "arg" argument from the load function</li>
9896 * <li>A boolean success indicator</li>
9898 * @param {Object} scope The scope in which to call the callback
9899 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9901 load : function(params, reader, callback, scope, arg){
9902 if(this.fireEvent("beforeload", this, params) !== false){
9904 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9907 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9909 url += "&_dc=" + (new Date().getTime());
9911 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9914 cb : "stcCallback"+transId,
9915 scriptId : "stcScript"+transId,
9919 callback : callback,
9925 window[trans.cb] = function(o){
9926 conn.handleResponse(o, trans);
9929 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9931 if(this.autoAbort !== false){
9935 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9937 var script = document.createElement("script");
9938 script.setAttribute("src", url);
9939 script.setAttribute("type", "text/javascript");
9940 script.setAttribute("id", trans.scriptId);
9941 this.head.appendChild(script);
9945 callback.call(scope||this, null, arg, false);
9950 isLoading : function(){
9951 return this.trans ? true : false;
9955 * Abort the current server request.
9958 if(this.isLoading()){
9959 this.destroyTrans(this.trans);
9964 destroyTrans : function(trans, isLoaded){
9965 this.head.removeChild(document.getElementById(trans.scriptId));
9966 clearTimeout(trans.timeoutId);
9968 window[trans.cb] = undefined;
9970 delete window[trans.cb];
9973 // if hasn't been loaded, wait for load to remove it to prevent script error
9974 window[trans.cb] = function(){
9975 window[trans.cb] = undefined;
9977 delete window[trans.cb];
9984 handleResponse : function(o, trans){
9986 this.destroyTrans(trans, true);
9989 result = trans.reader.readRecords(o);
9991 this.fireEvent("loadexception", this, o, trans.arg, e);
9992 trans.callback.call(trans.scope||window, null, trans.arg, false);
9995 this.fireEvent("load", this, o, trans.arg);
9996 trans.callback.call(trans.scope||window, result, trans.arg, true);
10000 handleFailure : function(trans){
10001 this.trans = false;
10002 this.destroyTrans(trans, false);
10003 this.fireEvent("loadexception", this, null, trans.arg);
10004 trans.callback.call(trans.scope||window, null, trans.arg, false);
10008 * Ext JS Library 1.1.1
10009 * Copyright(c) 2006-2007, Ext JS, LLC.
10011 * Originally Released Under LGPL - original licence link has changed is not relivant.
10014 * <script type="text/javascript">
10018 * @class Roo.data.JsonReader
10019 * @extends Roo.data.DataReader
10020 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10021 * based on mappings in a provided Roo.data.Record constructor.
10023 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10024 * in the reply previously.
10029 var RecordDef = Roo.data.Record.create([
10030 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
10031 {name: 'occupation'} // This field will use "occupation" as the mapping.
10033 var myReader = new Roo.data.JsonReader({
10034 totalProperty: "results", // The property which contains the total dataset size (optional)
10035 root: "rows", // The property which contains an Array of row objects
10036 id: "id" // The property within each row object that provides an ID for the record (optional)
10040 * This would consume a JSON file like this:
10042 { 'results': 2, 'rows': [
10043 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10044 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10047 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10048 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10049 * paged from the remote server.
10050 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10051 * @cfg {String} root name of the property which contains the Array of row objects.
10052 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10054 * Create a new JsonReader
10055 * @param {Object} meta Metadata configuration options
10056 * @param {Object} recordType Either an Array of field definition objects,
10057 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10059 Roo.data.JsonReader = function(meta, recordType){
10062 // set some defaults:
10063 Roo.applyIf(meta, {
10064 totalProperty: 'total',
10065 successProperty : 'success',
10070 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10072 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10075 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
10076 * Used by Store query builder to append _requestMeta to params.
10079 metaFromRemote : false,
10081 * This method is only used by a DataProxy which has retrieved data from a remote server.
10082 * @param {Object} response The XHR object which contains the JSON data in its responseText.
10083 * @return {Object} data A data block which is used by an Roo.data.Store object as
10084 * a cache of Roo.data.Records.
10086 read : function(response){
10087 var json = response.responseText;
10089 var o = /* eval:var:o */ eval("("+json+")");
10091 throw {message: "JsonReader.read: Json object not found"};
10097 this.metaFromRemote = true;
10098 this.meta = o.metaData;
10099 this.recordType = Roo.data.Record.create(o.metaData.fields);
10100 this.onMetaChange(this.meta, this.recordType, o);
10102 return this.readRecords(o);
10105 // private function a store will implement
10106 onMetaChange : function(meta, recordType, o){
10113 simpleAccess: function(obj, subsc) {
10120 getJsonAccessor: function(){
10122 return function(expr) {
10124 return(re.test(expr))
10125 ? new Function("obj", "return obj." + expr)
10130 return Roo.emptyFn;
10135 * Create a data block containing Roo.data.Records from an XML document.
10136 * @param {Object} o An object which contains an Array of row objects in the property specified
10137 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10138 * which contains the total size of the dataset.
10139 * @return {Object} data A data block which is used by an Roo.data.Store object as
10140 * a cache of Roo.data.Records.
10142 readRecords : function(o){
10144 * After any data loads, the raw JSON data is available for further custom processing.
10148 var s = this.meta, Record = this.recordType,
10149 f = Record.prototype.fields, fi = f.items, fl = f.length;
10151 // Generate extraction functions for the totalProperty, the root, the id, and for each field
10153 if(s.totalProperty) {
10154 this.getTotal = this.getJsonAccessor(s.totalProperty);
10156 if(s.successProperty) {
10157 this.getSuccess = this.getJsonAccessor(s.successProperty);
10159 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10161 var g = this.getJsonAccessor(s.id);
10162 this.getId = function(rec) {
10164 return (r === undefined || r === "") ? null : r;
10167 this.getId = function(){return null;};
10170 for(var jj = 0; jj < fl; jj++){
10172 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10173 this.ef[jj] = this.getJsonAccessor(map);
10177 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10178 if(s.totalProperty){
10179 var vt = parseInt(this.getTotal(o), 10);
10184 if(s.successProperty){
10185 var vs = this.getSuccess(o);
10186 if(vs === false || vs === 'false'){
10191 for(var i = 0; i < c; i++){
10194 var id = this.getId(n);
10195 for(var j = 0; j < fl; j++){
10197 var v = this.ef[j](n);
10199 Roo.log('missing convert for ' + f.name);
10203 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10205 var record = new Record(values, id);
10207 records[i] = record;
10213 totalRecords : totalRecords
10218 * Ext JS Library 1.1.1
10219 * Copyright(c) 2006-2007, Ext JS, LLC.
10221 * Originally Released Under LGPL - original licence link has changed is not relivant.
10224 * <script type="text/javascript">
10228 * @class Roo.data.ArrayReader
10229 * @extends Roo.data.DataReader
10230 * Data reader class to create an Array of Roo.data.Record objects from an Array.
10231 * Each element of that Array represents a row of data fields. The
10232 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10233 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10237 var RecordDef = Roo.data.Record.create([
10238 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
10239 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
10241 var myReader = new Roo.data.ArrayReader({
10242 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
10246 * This would consume an Array like this:
10248 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10250 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10252 * Create a new JsonReader
10253 * @param {Object} meta Metadata configuration options.
10254 * @param {Object} recordType Either an Array of field definition objects
10255 * as specified to {@link Roo.data.Record#create},
10256 * or an {@link Roo.data.Record} object
10257 * created using {@link Roo.data.Record#create}.
10259 Roo.data.ArrayReader = function(meta, recordType){
10260 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10263 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10265 * Create a data block containing Roo.data.Records from an XML document.
10266 * @param {Object} o An Array of row objects which represents the dataset.
10267 * @return {Object} data A data block which is used by an Roo.data.Store object as
10268 * a cache of Roo.data.Records.
10270 readRecords : function(o){
10271 var sid = this.meta ? this.meta.id : null;
10272 var recordType = this.recordType, fields = recordType.prototype.fields;
10275 for(var i = 0; i < root.length; i++){
10278 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10279 for(var j = 0, jlen = fields.length; j < jlen; j++){
10280 var f = fields.items[j];
10281 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10282 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10284 values[f.name] = v;
10286 var record = new recordType(values, id);
10288 records[records.length] = record;
10292 totalRecords : records.length
10301 * @class Roo.bootstrap.ComboBox
10302 * @extends Roo.bootstrap.TriggerField
10303 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10304 * @cfg {Boolean} append (true|false) default false
10305 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10306 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10307 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10308 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10309 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10311 * Create a new ComboBox.
10312 * @param {Object} config Configuration options
10314 Roo.bootstrap.ComboBox = function(config){
10315 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10319 * Fires when the dropdown list is expanded
10320 * @param {Roo.bootstrap.ComboBox} combo This combo box
10325 * Fires when the dropdown list is collapsed
10326 * @param {Roo.bootstrap.ComboBox} combo This combo box
10330 * @event beforeselect
10331 * Fires before a list item is selected. Return false to cancel the selection.
10332 * @param {Roo.bootstrap.ComboBox} combo This combo box
10333 * @param {Roo.data.Record} record The data record returned from the underlying store
10334 * @param {Number} index The index of the selected item in the dropdown list
10336 'beforeselect' : true,
10339 * Fires when a list item is selected
10340 * @param {Roo.bootstrap.ComboBox} combo This combo box
10341 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10342 * @param {Number} index The index of the selected item in the dropdown list
10346 * @event beforequery
10347 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10348 * The event object passed has these properties:
10349 * @param {Roo.bootstrap.ComboBox} combo This combo box
10350 * @param {String} query The query
10351 * @param {Boolean} forceAll true to force "all" query
10352 * @param {Boolean} cancel true to cancel the query
10353 * @param {Object} e The query event object
10355 'beforequery': true,
10358 * Fires when the 'add' icon is pressed (add a listener to enable add button)
10359 * @param {Roo.bootstrap.ComboBox} combo This combo box
10364 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10365 * @param {Roo.bootstrap.ComboBox} combo This combo box
10366 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10371 * Fires when the remove value from the combobox array
10372 * @param {Roo.bootstrap.ComboBox} combo This combo box
10379 this.tickItems = [];
10381 this.selectedIndex = -1;
10382 if(this.mode == 'local'){
10383 if(config.queryDelay === undefined){
10384 this.queryDelay = 10;
10386 if(config.minChars === undefined){
10392 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10395 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10396 * rendering into an Roo.Editor, defaults to false)
10399 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10400 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10403 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10406 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10407 * the dropdown list (defaults to undefined, with no header element)
10411 * @cfg {String/Roo.Template} tpl The template to use to render the output
10415 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10417 listWidth: undefined,
10419 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10420 * mode = 'remote' or 'text' if mode = 'local')
10422 displayField: undefined,
10424 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10425 * mode = 'remote' or 'value' if mode = 'local').
10426 * Note: use of a valueField requires the user make a selection
10427 * in order for a value to be mapped.
10429 valueField: undefined,
10433 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10434 * field's data value (defaults to the underlying DOM element's name)
10436 hiddenName: undefined,
10438 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10442 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10444 selectedClass: 'active',
10447 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10451 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10452 * anchor positions (defaults to 'tl-bl')
10454 listAlign: 'tl-bl?',
10456 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10460 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
10461 * query specified by the allQuery config option (defaults to 'query')
10463 triggerAction: 'query',
10465 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10466 * (defaults to 4, does not apply if editable = false)
10470 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10471 * delay (typeAheadDelay) if it matches a known value (defaults to false)
10475 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10476 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10480 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10481 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
10485 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
10486 * when editable = true (defaults to false)
10488 selectOnFocus:false,
10490 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10492 queryParam: 'query',
10494 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
10495 * when mode = 'remote' (defaults to 'Loading...')
10497 loadingText: 'Loading...',
10499 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10503 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10507 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10508 * traditional select (defaults to true)
10512 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10516 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10520 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10521 * listWidth has a higher value)
10525 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10526 * allow the user to set arbitrary text into the field (defaults to false)
10528 forceSelection:false,
10530 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10531 * if typeAhead = true (defaults to 250)
10533 typeAheadDelay : 250,
10535 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10536 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10538 valueNotFoundText : undefined,
10540 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10542 blockFocus : false,
10545 * @cfg {Boolean} disableClear Disable showing of clear button.
10547 disableClear : false,
10549 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
10551 alwaysQuery : false,
10554 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
10568 btnPosition : 'right',
10569 triggerList : true,
10570 showToggleBtn : true,
10571 // element that contains real text value.. (when hidden is used..)
10573 getAutoCreate : function()
10580 if(!this.tickable){
10581 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10586 * ComboBox with tickable selections
10589 var align = this.labelAlign || this.parentLabelAlign();
10592 cls : 'form-group roo-combobox-tickable' //input-group
10598 cls : 'tickable-buttons',
10603 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10610 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10617 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10624 Roo.each(buttons.cn, function(c){
10626 c.cls += ' btn-' + _this.size;
10629 if (_this.disabled) {
10640 cls: 'form-hidden-field'
10644 cls: 'select2-choices',
10648 cls: 'select2-search-field',
10660 cls: 'select2-container input-group select2-container-multi',
10665 // cls: 'typeahead typeahead-long dropdown-menu',
10666 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
10671 if (align ==='left' && this.fieldLabel.length) {
10673 Roo.log("left and has label");
10679 cls : 'control-label col-sm-' + this.labelWidth,
10680 html : this.fieldLabel
10684 cls : "col-sm-" + (12 - this.labelWidth),
10691 } else if ( this.fieldLabel.length) {
10697 //cls : 'input-group-addon',
10698 html : this.fieldLabel
10708 Roo.log(" no label && no align");
10715 ['xs','sm','md','lg'].map(function(size){
10716 if (settings[size]) {
10717 cfg.cls += ' col-' + size + '-' + settings[size];
10726 initEvents: function()
10730 throw "can not find store for combo";
10732 this.store = Roo.factory(this.store, Roo.data);
10735 this.initTickableEvents();
10739 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10741 if(this.hiddenName){
10743 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10745 this.hiddenField.dom.value =
10746 this.hiddenValue !== undefined ? this.hiddenValue :
10747 this.value !== undefined ? this.value : '';
10749 // prevent input submission
10750 this.el.dom.removeAttribute('name');
10751 this.hiddenField.dom.setAttribute('name', this.hiddenName);
10756 // this.el.dom.setAttribute('autocomplete', 'off');
10759 var cls = 'x-combo-list';
10761 //this.list = new Roo.Layer({
10762 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10768 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10769 _this.list.setWidth(lw);
10772 this.list.on('mouseover', this.onViewOver, this);
10773 this.list.on('mousemove', this.onViewMove, this);
10775 this.list.on('scroll', this.onViewScroll, this);
10778 this.list.swallowEvent('mousewheel');
10779 this.assetHeight = 0;
10782 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10783 this.assetHeight += this.header.getHeight();
10786 this.innerList = this.list.createChild({cls:cls+'-inner'});
10787 this.innerList.on('mouseover', this.onViewOver, this);
10788 this.innerList.on('mousemove', this.onViewMove, this);
10789 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10791 if(this.allowBlank && !this.pageSize && !this.disableClear){
10792 this.footer = this.list.createChild({cls:cls+'-ft'});
10793 this.pageTb = new Roo.Toolbar(this.footer);
10797 this.footer = this.list.createChild({cls:cls+'-ft'});
10798 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10799 {pageSize: this.pageSize});
10803 if (this.pageTb && this.allowBlank && !this.disableClear) {
10805 this.pageTb.add(new Roo.Toolbar.Fill(), {
10806 cls: 'x-btn-icon x-btn-clear',
10808 handler: function()
10811 _this.clearValue();
10812 _this.onSelect(false, -1);
10817 this.assetHeight += this.footer.getHeight();
10822 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10825 this.view = new Roo.View(this.list, this.tpl, {
10826 singleSelect:true, store: this.store, selectedClass: this.selectedClass
10828 //this.view.wrapEl.setDisplayed(false);
10829 this.view.on('click', this.onViewClick, this);
10833 this.store.on('beforeload', this.onBeforeLoad, this);
10834 this.store.on('load', this.onLoad, this);
10835 this.store.on('loadexception', this.onLoadException, this);
10837 if(this.resizable){
10838 this.resizer = new Roo.Resizable(this.list, {
10839 pinned:true, handles:'se'
10841 this.resizer.on('resize', function(r, w, h){
10842 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10843 this.listWidth = w;
10844 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10845 this.restrictHeight();
10847 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10850 if(!this.editable){
10851 this.editable = true;
10852 this.setEditable(false);
10857 if (typeof(this.events.add.listeners) != 'undefined') {
10859 this.addicon = this.wrap.createChild(
10860 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
10862 this.addicon.on('click', function(e) {
10863 this.fireEvent('add', this);
10866 if (typeof(this.events.edit.listeners) != 'undefined') {
10868 this.editicon = this.wrap.createChild(
10869 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
10870 if (this.addicon) {
10871 this.editicon.setStyle('margin-left', '40px');
10873 this.editicon.on('click', function(e) {
10875 // we fire even if inothing is selected..
10876 this.fireEvent('edit', this, this.lastData );
10882 this.keyNav = new Roo.KeyNav(this.inputEl(), {
10883 "up" : function(e){
10884 this.inKeyMode = true;
10888 "down" : function(e){
10889 if(!this.isExpanded()){
10890 this.onTriggerClick();
10892 this.inKeyMode = true;
10897 "enter" : function(e){
10898 // this.onViewClick();
10902 if(this.fireEvent("specialkey", this, e)){
10903 this.onViewClick(false);
10909 "esc" : function(e){
10913 "tab" : function(e){
10916 if(this.fireEvent("specialkey", this, e)){
10917 this.onViewClick(false);
10925 doRelay : function(foo, bar, hname){
10926 if(hname == 'down' || this.scope.isExpanded()){
10927 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10936 this.queryDelay = Math.max(this.queryDelay || 10,
10937 this.mode == 'local' ? 10 : 250);
10940 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10942 if(this.typeAhead){
10943 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10945 if(this.editable !== false){
10946 this.inputEl().on("keyup", this.onKeyUp, this);
10948 if(this.forceSelection){
10949 this.inputEl().on('blur', this.doForce, this);
10953 this.choices = this.el.select('ul.select2-choices', true).first();
10954 this.searchField = this.el.select('ul li.select2-search-field', true).first();
10958 initTickableEvents: function()
10962 if(this.hiddenName){
10964 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10966 this.hiddenField.dom.value =
10967 this.hiddenValue !== undefined ? this.hiddenValue :
10968 this.value !== undefined ? this.value : '';
10970 // prevent input submission
10971 this.el.dom.removeAttribute('name');
10972 this.hiddenField.dom.setAttribute('name', this.hiddenName);
10977 // this.list = this.el.select('ul.dropdown-menu',true).first();
10979 this.choices = this.el.select('ul.select2-choices', true).first();
10980 this.searchField = this.el.select('ul li.select2-search-field', true).first();
10981 if(this.triggerList){
10982 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10985 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10986 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10988 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10989 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10991 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10992 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10994 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10995 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10996 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10999 this.cancelBtn.hide();
11004 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11005 _this.list.setWidth(lw);
11008 this.list.on('mouseover', this.onViewOver, this);
11009 this.list.on('mousemove', this.onViewMove, this);
11011 this.list.on('scroll', this.onViewScroll, this);
11014 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>';
11017 this.view = new Roo.View(this.list, this.tpl, {
11018 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11021 //this.view.wrapEl.setDisplayed(false);
11022 this.view.on('click', this.onViewClick, this);
11026 this.store.on('beforeload', this.onBeforeLoad, this);
11027 this.store.on('load', this.onLoad, this);
11028 this.store.on('loadexception', this.onLoadException, this);
11030 // this.keyNav = new Roo.KeyNav(this.inputEl(), {
11031 // "up" : function(e){
11032 // this.inKeyMode = true;
11033 // this.selectPrev();
11036 // "down" : function(e){
11037 // if(!this.isExpanded()){
11038 // this.onTriggerClick();
11040 // this.inKeyMode = true;
11041 // this.selectNext();
11045 // "enter" : function(e){
11046 //// this.onViewClick();
11048 // this.collapse();
11050 // if(this.fireEvent("specialkey", this, e)){
11051 // this.onViewClick(false);
11057 // "esc" : function(e){
11058 // this.collapse();
11061 // "tab" : function(e){
11062 // this.collapse();
11064 // if(this.fireEvent("specialkey", this, e)){
11065 // this.onViewClick(false);
11073 // doRelay : function(foo, bar, hname){
11074 // if(hname == 'down' || this.scope.isExpanded()){
11075 // return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11080 // forceKeyDown: true
11084 this.queryDelay = Math.max(this.queryDelay || 10,
11085 this.mode == 'local' ? 10 : 250);
11088 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11090 if(this.typeAhead){
11091 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11095 onDestroy : function(){
11097 this.view.setStore(null);
11098 this.view.el.removeAllListeners();
11099 this.view.el.remove();
11100 this.view.purgeListeners();
11103 this.list.dom.innerHTML = '';
11107 this.store.un('beforeload', this.onBeforeLoad, this);
11108 this.store.un('load', this.onLoad, this);
11109 this.store.un('loadexception', this.onLoadException, this);
11111 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11115 fireKey : function(e){
11116 if(e.isNavKeyPress() && !this.list.isVisible()){
11117 this.fireEvent("specialkey", this, e);
11122 onResize: function(w, h){
11123 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11125 // if(typeof w != 'number'){
11126 // // we do not handle it!?!?
11129 // var tw = this.trigger.getWidth();
11130 // // tw += this.addicon ? this.addicon.getWidth() : 0;
11131 // // tw += this.editicon ? this.editicon.getWidth() : 0;
11133 // this.inputEl().setWidth( this.adjustWidth('input', x));
11135 // //this.trigger.setStyle('left', x+'px');
11137 // if(this.list && this.listWidth === undefined){
11138 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11139 // this.list.setWidth(lw);
11140 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11148 * Allow or prevent the user from directly editing the field text. If false is passed,
11149 * the user will only be able to select from the items defined in the dropdown list. This method
11150 * is the runtime equivalent of setting the 'editable' config option at config time.
11151 * @param {Boolean} value True to allow the user to directly edit the field text
11153 setEditable : function(value){
11154 if(value == this.editable){
11157 this.editable = value;
11159 this.inputEl().dom.setAttribute('readOnly', true);
11160 this.inputEl().on('mousedown', this.onTriggerClick, this);
11161 this.inputEl().addClass('x-combo-noedit');
11163 this.inputEl().dom.setAttribute('readOnly', false);
11164 this.inputEl().un('mousedown', this.onTriggerClick, this);
11165 this.inputEl().removeClass('x-combo-noedit');
11171 onBeforeLoad : function(combo,opts){
11172 if(!this.hasFocus){
11176 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11178 this.restrictHeight();
11179 this.selectedIndex = -1;
11183 onLoad : function(){
11185 this.hasQuery = false;
11187 if(!this.hasFocus){
11191 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11192 this.loading.hide();
11195 if(this.store.getCount() > 0){
11197 // this.restrictHeight();
11198 if(this.lastQuery == this.allQuery){
11199 if(this.editable && !this.tickable){
11200 this.inputEl().dom.select();
11204 !this.selectByValue(this.value, true) &&
11205 this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' ||
11206 this.store.lastOptions.add != true)
11208 this.select(0, true);
11211 if(this.autoFocus){
11214 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11215 this.taTask.delay(this.typeAheadDelay);
11219 this.onEmptyResults();
11225 onLoadException : function()
11227 this.hasQuery = false;
11229 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11230 this.loading.hide();
11234 Roo.log(this.store.reader.jsonData);
11235 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11237 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11243 onTypeAhead : function(){
11244 if(this.store.getCount() > 0){
11245 var r = this.store.getAt(0);
11246 var newValue = r.data[this.displayField];
11247 var len = newValue.length;
11248 var selStart = this.getRawValue().length;
11250 if(selStart != len){
11251 this.setRawValue(newValue);
11252 this.selectText(selStart, newValue.length);
11258 onSelect : function(record, index){
11260 if(this.fireEvent('beforeselect', this, record, index) !== false){
11262 this.setFromData(index > -1 ? record.data : false);
11265 this.fireEvent('select', this, record, index);
11270 * Returns the currently selected field value or empty string if no value is set.
11271 * @return {String} value The selected value
11273 getValue : function(){
11276 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11279 if(this.valueField){
11280 return typeof this.value != 'undefined' ? this.value : '';
11282 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11287 * Clears any text/value currently set in the field
11289 clearValue : function(){
11290 if(this.hiddenField){
11291 this.hiddenField.dom.value = '';
11294 this.setRawValue('');
11295 this.lastSelectionText = '';
11300 * Sets the specified value into the field. If the value finds a match, the corresponding record text
11301 * will be displayed in the field. If the value does not match the data value of an existing item,
11302 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11303 * Otherwise the field will be blank (although the value will still be set).
11304 * @param {String} value The value to match
11306 setValue : function(v){
11313 if(this.valueField){
11314 var r = this.findRecord(this.valueField, v);
11316 text = r.data[this.displayField];
11317 }else if(this.valueNotFoundText !== undefined){
11318 text = this.valueNotFoundText;
11321 this.lastSelectionText = text;
11322 if(this.hiddenField){
11323 this.hiddenField.dom.value = v;
11325 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11329 * @property {Object} the last set data for the element
11334 * Sets the value of the field based on a object which is related to the record format for the store.
11335 * @param {Object} value the value to set as. or false on reset?
11337 setFromData : function(o){
11344 var dv = ''; // display value
11345 var vv = ''; // value value..
11347 if (this.displayField) {
11348 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11350 // this is an error condition!!!
11351 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
11354 if(this.valueField){
11355 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11358 if(this.hiddenField){
11359 this.hiddenField.dom.value = vv;
11361 this.lastSelectionText = dv;
11362 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11366 // no hidden field.. - we store the value in 'value', but still display
11367 // display field!!!!
11368 this.lastSelectionText = dv;
11369 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11375 reset : function(){
11376 // overridden so that last data is reset..
11377 this.setValue(this.originalValue);
11378 this.clearInvalid();
11379 this.lastData = false;
11381 this.view.clearSelections();
11385 findRecord : function(prop, value){
11387 if(this.store.getCount() > 0){
11388 this.store.each(function(r){
11389 if(r.data[prop] == value){
11399 getName: function()
11401 // returns hidden if it's set..
11402 if (!this.rendered) {return ''};
11403 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
11407 onViewMove : function(e, t){
11408 this.inKeyMode = false;
11412 onViewOver : function(e, t){
11413 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11416 var item = this.view.findItemFromChild(t);
11419 var index = this.view.indexOf(item);
11420 this.select(index, false);
11425 onViewClick : function(view, doFocus, el, e)
11427 var index = this.view.getSelectedIndexes()[0];
11429 var r = this.store.getAt(index);
11433 if(e.getTarget().nodeName.toLowerCase() != 'input'){
11440 Roo.each(this.tickItems, function(v,k){
11442 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11443 _this.tickItems.splice(k, 1);
11453 this.tickItems.push(r.data);
11458 this.onSelect(r, index);
11460 if(doFocus !== false && !this.blockFocus){
11461 this.inputEl().focus();
11466 restrictHeight : function(){
11467 //this.innerList.dom.style.height = '';
11468 //var inner = this.innerList.dom;
11469 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11470 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11471 //this.list.beginUpdate();
11472 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11473 this.list.alignTo(this.inputEl(), this.listAlign);
11474 this.list.alignTo(this.inputEl(), this.listAlign);
11475 //this.list.endUpdate();
11479 onEmptyResults : function(){
11484 * Returns true if the dropdown list is expanded, else false.
11486 isExpanded : function(){
11487 return this.list.isVisible();
11491 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11492 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11493 * @param {String} value The data value of the item to select
11494 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11495 * selected item if it is not currently in view (defaults to true)
11496 * @return {Boolean} True if the value matched an item in the list, else false
11498 selectByValue : function(v, scrollIntoView){
11499 if(v !== undefined && v !== null){
11500 var r = this.findRecord(this.valueField || this.displayField, v);
11502 this.select(this.store.indexOf(r), scrollIntoView);
11510 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11511 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11512 * @param {Number} index The zero-based index of the list item to select
11513 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11514 * selected item if it is not currently in view (defaults to true)
11516 select : function(index, scrollIntoView){
11517 this.selectedIndex = index;
11518 this.view.select(index);
11519 if(scrollIntoView !== false){
11520 var el = this.view.getNode(index);
11521 if(el && !this.multiple && !this.tickable){
11522 this.list.scrollChildIntoView(el, false);
11528 selectNext : function(){
11529 var ct = this.store.getCount();
11531 if(this.selectedIndex == -1){
11533 }else if(this.selectedIndex < ct-1){
11534 this.select(this.selectedIndex+1);
11540 selectPrev : function(){
11541 var ct = this.store.getCount();
11543 if(this.selectedIndex == -1){
11545 }else if(this.selectedIndex != 0){
11546 this.select(this.selectedIndex-1);
11552 onKeyUp : function(e){
11553 if(this.editable !== false && !e.isSpecialKey()){
11554 this.lastKey = e.getKey();
11555 this.dqTask.delay(this.queryDelay);
11560 validateBlur : function(){
11561 return !this.list || !this.list.isVisible();
11565 initQuery : function(){
11566 this.doQuery(this.getRawValue());
11570 doForce : function(){
11571 if(this.inputEl().dom.value.length > 0){
11572 this.inputEl().dom.value =
11573 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11579 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
11580 * query allowing the query action to be canceled if needed.
11581 * @param {String} query The SQL query to execute
11582 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11583 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
11584 * saved in the current store (defaults to false)
11586 doQuery : function(q, forceAll){
11588 if(q === undefined || q === null){
11593 forceAll: forceAll,
11597 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11602 forceAll = qe.forceAll;
11603 if(forceAll === true || (q.length >= this.minChars)){
11605 this.hasQuery = true;
11607 if(this.lastQuery != q || this.alwaysQuery){
11608 this.lastQuery = q;
11609 if(this.mode == 'local'){
11610 this.selectedIndex = -1;
11612 this.store.clearFilter();
11614 this.store.filter(this.displayField, q);
11618 this.store.baseParams[this.queryParam] = q;
11620 var options = {params : this.getParams(q)};
11623 options.add = true;
11624 options.params.start = this.page * this.pageSize;
11627 this.store.load(options);
11629 * this code will make the page width larger, at the beginning, the list not align correctly,
11630 * we should expand the list on onLoad
11631 * so command out it
11636 this.selectedIndex = -1;
11641 this.loadNext = false;
11645 getParams : function(q){
11647 //p[this.queryParam] = q;
11651 p.limit = this.pageSize;
11657 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11659 collapse : function(){
11660 if(!this.isExpanded()){
11668 this.cancelBtn.hide();
11669 this.trigger.show();
11672 Roo.get(document).un('mousedown', this.collapseIf, this);
11673 Roo.get(document).un('mousewheel', this.collapseIf, this);
11674 if (!this.editable) {
11675 Roo.get(document).un('keydown', this.listKeyPress, this);
11677 this.fireEvent('collapse', this);
11681 collapseIf : function(e){
11682 var in_combo = e.within(this.el);
11683 var in_list = e.within(this.list);
11684 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11686 if (in_combo || in_list || is_list) {
11687 //e.stopPropagation();
11692 this.onTickableFooterButtonClick(e, false, false);
11700 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11702 expand : function(){
11704 if(this.isExpanded() || !this.hasFocus){
11708 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11709 this.list.setWidth(lw);
11716 this.restrictHeight();
11720 this.tickItems = Roo.apply([], this.item);
11723 this.cancelBtn.show();
11724 this.trigger.hide();
11728 Roo.get(document).on('mousedown', this.collapseIf, this);
11729 Roo.get(document).on('mousewheel', this.collapseIf, this);
11730 if (!this.editable) {
11731 Roo.get(document).on('keydown', this.listKeyPress, this);
11734 this.fireEvent('expand', this);
11738 // Implements the default empty TriggerField.onTriggerClick function
11739 onTriggerClick : function(e)
11741 Roo.log('trigger click');
11743 if(this.disabled || !this.triggerList){
11748 this.loadNext = false;
11750 if(this.isExpanded()){
11752 if (!this.blockFocus) {
11753 this.inputEl().focus();
11757 this.hasFocus = true;
11758 if(this.triggerAction == 'all') {
11759 this.doQuery(this.allQuery, true);
11761 this.doQuery(this.getRawValue());
11763 if (!this.blockFocus) {
11764 this.inputEl().focus();
11769 onTickableTriggerClick : function(e)
11776 this.loadNext = false;
11777 this.hasFocus = true;
11779 if(this.triggerAction == 'all') {
11780 this.doQuery(this.allQuery, true);
11782 this.doQuery(this.getRawValue());
11786 onSearchFieldClick : function(e)
11788 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11793 this.loadNext = false;
11794 this.hasFocus = true;
11796 if(this.triggerAction == 'all') {
11797 this.doQuery(this.allQuery, true);
11799 this.doQuery(this.getRawValue());
11803 listKeyPress : function(e)
11805 //Roo.log('listkeypress');
11806 // scroll to first matching element based on key pres..
11807 if (e.isSpecialKey()) {
11810 var k = String.fromCharCode(e.getKey()).toUpperCase();
11813 var csel = this.view.getSelectedNodes();
11814 var cselitem = false;
11816 var ix = this.view.indexOf(csel[0]);
11817 cselitem = this.store.getAt(ix);
11818 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11824 this.store.each(function(v) {
11826 // start at existing selection.
11827 if (cselitem.id == v.id) {
11833 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11834 match = this.store.indexOf(v);
11840 if (match === false) {
11841 return true; // no more action?
11844 this.view.select(match);
11845 var sn = Roo.get(this.view.getSelectedNodes()[0])
11846 //sn.scrollIntoView(sn.dom.parentNode, false);
11849 onViewScroll : function(e, t){
11851 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){
11855 this.hasQuery = true;
11857 this.loading = this.list.select('.loading', true).first();
11859 if(this.loading === null){
11860 this.list.createChild({
11862 cls: 'loading select2-more-results select2-active',
11863 html: 'Loading more results...'
11866 this.loading = this.list.select('.loading', true).first();
11868 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11870 this.loading.hide();
11873 this.loading.show();
11878 this.loadNext = true;
11880 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11885 addItem : function(o)
11887 var dv = ''; // display value
11889 if (this.displayField) {
11890 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11892 // this is an error condition!!!
11893 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
11900 var choice = this.choices.createChild({
11902 cls: 'select2-search-choice',
11911 cls: 'select2-search-choice-close',
11916 }, this.searchField);
11918 var close = choice.select('a.select2-search-choice-close', true).first()
11920 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11928 this.inputEl().dom.value = '';
11932 onRemoveItem : function(e, _self, o)
11934 e.preventDefault();
11935 var index = this.item.indexOf(o.data) * 1;
11938 Roo.log('not this item?!');
11942 this.item.splice(index, 1);
11947 this.fireEvent('remove', this, e);
11951 syncValue : function()
11953 if(!this.item.length){
11960 Roo.each(this.item, function(i){
11961 if(_this.valueField){
11962 value.push(i[_this.valueField]);
11969 this.value = value.join(',');
11971 if(this.hiddenField){
11972 this.hiddenField.dom.value = this.value;
11976 clearItem : function()
11978 if(!this.multiple){
11984 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11991 inputEl: function ()
11994 return this.searchField;
11996 return this.el.select('input.form-control',true).first();
12000 onTickableFooterButtonClick : function(e, btn, el)
12002 e.preventDefault();
12004 if(btn && btn.name == 'cancel'){
12005 this.tickItems = Roo.apply([], this.item);
12014 Roo.each(this.tickItems, function(o){
12025 * @cfg {Boolean} grow
12029 * @cfg {Number} growMin
12033 * @cfg {Number} growMax
12043 * Ext JS Library 1.1.1
12044 * Copyright(c) 2006-2007, Ext JS, LLC.
12046 * Originally Released Under LGPL - original licence link has changed is not relivant.
12049 * <script type="text/javascript">
12054 * @extends Roo.util.Observable
12055 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
12056 * This class also supports single and multi selection modes. <br>
12057 * Create a data model bound view:
12059 var store = new Roo.data.Store(...);
12061 var view = new Roo.View({
12063 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
12065 singleSelect: true,
12066 selectedClass: "ydataview-selected",
12070 // listen for node click?
12071 view.on("click", function(vw, index, node, e){
12072 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12076 dataModel.load("foobar.xml");
12078 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12080 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12081 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12083 * Note: old style constructor is still suported (container, template, config)
12086 * Create a new View
12087 * @param {Object} config The config object
12090 Roo.View = function(config, depreciated_tpl, depreciated_config){
12092 this.parent = false;
12094 if (typeof(depreciated_tpl) == 'undefined') {
12095 // new way.. - universal constructor.
12096 Roo.apply(this, config);
12097 this.el = Roo.get(this.el);
12100 this.el = Roo.get(config);
12101 this.tpl = depreciated_tpl;
12102 Roo.apply(this, depreciated_config);
12104 this.wrapEl = this.el.wrap().wrap();
12105 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12108 if(typeof(this.tpl) == "string"){
12109 this.tpl = new Roo.Template(this.tpl);
12111 // support xtype ctors..
12112 this.tpl = new Roo.factory(this.tpl, Roo);
12116 this.tpl.compile();
12121 * @event beforeclick
12122 * Fires before a click is processed. Returns false to cancel the default action.
12123 * @param {Roo.View} this
12124 * @param {Number} index The index of the target node
12125 * @param {HTMLElement} node The target node
12126 * @param {Roo.EventObject} e The raw event object
12128 "beforeclick" : true,
12131 * Fires when a template node is clicked.
12132 * @param {Roo.View} this
12133 * @param {Number} index The index of the target node
12134 * @param {HTMLElement} node The target node
12135 * @param {Roo.EventObject} e The raw event object
12140 * Fires when a template node is double clicked.
12141 * @param {Roo.View} this
12142 * @param {Number} index The index of the target node
12143 * @param {HTMLElement} node The target node
12144 * @param {Roo.EventObject} e The raw event object
12148 * @event contextmenu
12149 * Fires when a template node is right clicked.
12150 * @param {Roo.View} this
12151 * @param {Number} index The index of the target node
12152 * @param {HTMLElement} node The target node
12153 * @param {Roo.EventObject} e The raw event object
12155 "contextmenu" : true,
12157 * @event selectionchange
12158 * Fires when the selected nodes change.
12159 * @param {Roo.View} this
12160 * @param {Array} selections Array of the selected nodes
12162 "selectionchange" : true,
12165 * @event beforeselect
12166 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12167 * @param {Roo.View} this
12168 * @param {HTMLElement} node The node to be selected
12169 * @param {Array} selections Array of currently selected nodes
12171 "beforeselect" : true,
12173 * @event preparedata
12174 * Fires on every row to render, to allow you to change the data.
12175 * @param {Roo.View} this
12176 * @param {Object} data to be rendered (change this)
12178 "preparedata" : true
12186 "click": this.onClick,
12187 "dblclick": this.onDblClick,
12188 "contextmenu": this.onContextMenu,
12192 this.selections = [];
12194 this.cmp = new Roo.CompositeElementLite([]);
12196 this.store = Roo.factory(this.store, Roo.data);
12197 this.setStore(this.store, true);
12200 if ( this.footer && this.footer.xtype) {
12202 var fctr = this.wrapEl.appendChild(document.createElement("div"));
12204 this.footer.dataSource = this.store
12205 this.footer.container = fctr;
12206 this.footer = Roo.factory(this.footer, Roo);
12207 fctr.insertFirst(this.el);
12209 // this is a bit insane - as the paging toolbar seems to detach the el..
12210 // dom.parentNode.parentNode.parentNode
12211 // they get detached?
12215 Roo.View.superclass.constructor.call(this);
12220 Roo.extend(Roo.View, Roo.util.Observable, {
12223 * @cfg {Roo.data.Store} store Data store to load data from.
12228 * @cfg {String|Roo.Element} el The container element.
12233 * @cfg {String|Roo.Template} tpl The template used by this View
12237 * @cfg {String} dataName the named area of the template to use as the data area
12238 * Works with domtemplates roo-name="name"
12242 * @cfg {String} selectedClass The css class to add to selected nodes
12244 selectedClass : "x-view-selected",
12246 * @cfg {String} emptyText The empty text to show when nothing is loaded.
12251 * @cfg {String} text to display on mask (default Loading)
12255 * @cfg {Boolean} multiSelect Allow multiple selection
12257 multiSelect : false,
12259 * @cfg {Boolean} singleSelect Allow single selection
12261 singleSelect: false,
12264 * @cfg {Boolean} toggleSelect - selecting
12266 toggleSelect : false,
12269 * @cfg {Boolean} tickable - selecting
12274 * Returns the element this view is bound to.
12275 * @return {Roo.Element}
12277 getEl : function(){
12278 return this.wrapEl;
12284 * Refreshes the view. - called by datachanged on the store. - do not call directly.
12286 refresh : function(){
12287 Roo.log('refresh');
12290 // if we are using something like 'domtemplate', then
12291 // the what gets used is:
12292 // t.applySubtemplate(NAME, data, wrapping data..)
12293 // the outer template then get' applied with
12294 // the store 'extra data'
12295 // and the body get's added to the
12296 // roo-name="data" node?
12297 // <span class='roo-tpl-{name}'></span> ?????
12301 this.clearSelections();
12302 this.el.update("");
12304 var records = this.store.getRange();
12305 if(records.length < 1) {
12307 // is this valid?? = should it render a template??
12309 this.el.update(this.emptyText);
12313 if (this.dataName) {
12314 this.el.update(t.apply(this.store.meta)); //????
12315 el = this.el.child('.roo-tpl-' + this.dataName);
12318 for(var i = 0, len = records.length; i < len; i++){
12319 var data = this.prepareData(records[i].data, i, records[i]);
12320 this.fireEvent("preparedata", this, data, i, records[i]);
12322 var d = Roo.apply({}, data);
12325 Roo.apply(d, {'roo-id' : Roo.id()});
12329 Roo.each(this.parent.item, function(item){
12330 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12333 Roo.apply(d, {'roo-data-checked' : 'checked'});
12337 html[html.length] = Roo.util.Format.trim(
12339 t.applySubtemplate(this.dataName, d, this.store.meta) :
12346 el.update(html.join(""));
12347 this.nodes = el.dom.childNodes;
12348 this.updateIndexes(0);
12353 * Function to override to reformat the data that is sent to
12354 * the template for each node.
12355 * DEPRICATED - use the preparedata event handler.
12356 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12357 * a JSON object for an UpdateManager bound view).
12359 prepareData : function(data, index, record)
12361 this.fireEvent("preparedata", this, data, index, record);
12365 onUpdate : function(ds, record){
12366 Roo.log('on update');
12367 this.clearSelections();
12368 var index = this.store.indexOf(record);
12369 var n = this.nodes[index];
12370 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12371 n.parentNode.removeChild(n);
12372 this.updateIndexes(index, index);
12378 onAdd : function(ds, records, index)
12380 Roo.log(['on Add', ds, records, index] );
12381 this.clearSelections();
12382 if(this.nodes.length == 0){
12386 var n = this.nodes[index];
12387 for(var i = 0, len = records.length; i < len; i++){
12388 var d = this.prepareData(records[i].data, i, records[i]);
12390 this.tpl.insertBefore(n, d);
12393 this.tpl.append(this.el, d);
12396 this.updateIndexes(index);
12399 onRemove : function(ds, record, index){
12400 Roo.log('onRemove');
12401 this.clearSelections();
12402 var el = this.dataName ?
12403 this.el.child('.roo-tpl-' + this.dataName) :
12406 el.dom.removeChild(this.nodes[index]);
12407 this.updateIndexes(index);
12411 * Refresh an individual node.
12412 * @param {Number} index
12414 refreshNode : function(index){
12415 this.onUpdate(this.store, this.store.getAt(index));
12418 updateIndexes : function(startIndex, endIndex){
12419 var ns = this.nodes;
12420 startIndex = startIndex || 0;
12421 endIndex = endIndex || ns.length - 1;
12422 for(var i = startIndex; i <= endIndex; i++){
12423 ns[i].nodeIndex = i;
12428 * Changes the data store this view uses and refresh the view.
12429 * @param {Store} store
12431 setStore : function(store, initial){
12432 if(!initial && this.store){
12433 this.store.un("datachanged", this.refresh);
12434 this.store.un("add", this.onAdd);
12435 this.store.un("remove", this.onRemove);
12436 this.store.un("update", this.onUpdate);
12437 this.store.un("clear", this.refresh);
12438 this.store.un("beforeload", this.onBeforeLoad);
12439 this.store.un("load", this.onLoad);
12440 this.store.un("loadexception", this.onLoad);
12444 store.on("datachanged", this.refresh, this);
12445 store.on("add", this.onAdd, this);
12446 store.on("remove", this.onRemove, this);
12447 store.on("update", this.onUpdate, this);
12448 store.on("clear", this.refresh, this);
12449 store.on("beforeload", this.onBeforeLoad, this);
12450 store.on("load", this.onLoad, this);
12451 store.on("loadexception", this.onLoad, this);
12459 * onbeforeLoad - masks the loading area.
12462 onBeforeLoad : function(store,opts)
12464 Roo.log('onBeforeLoad');
12466 this.el.update("");
12468 this.el.mask(this.mask ? this.mask : "Loading" );
12470 onLoad : function ()
12477 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12478 * @param {HTMLElement} node
12479 * @return {HTMLElement} The template node
12481 findItemFromChild : function(node){
12482 var el = this.dataName ?
12483 this.el.child('.roo-tpl-' + this.dataName,true) :
12486 if(!node || node.parentNode == el){
12489 var p = node.parentNode;
12490 while(p && p != el){
12491 if(p.parentNode == el){
12500 onClick : function(e){
12501 var item = this.findItemFromChild(e.getTarget());
12503 var index = this.indexOf(item);
12504 if(this.onItemClick(item, index, e) !== false){
12505 this.fireEvent("click", this, index, item, e);
12508 this.clearSelections();
12513 onContextMenu : function(e){
12514 var item = this.findItemFromChild(e.getTarget());
12516 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12521 onDblClick : function(e){
12522 var item = this.findItemFromChild(e.getTarget());
12524 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12528 onItemClick : function(item, index, e)
12530 if(this.fireEvent("beforeclick", this, index, item, e) === false){
12533 if (this.toggleSelect) {
12534 var m = this.isSelected(item) ? 'unselect' : 'select';
12537 _t[m](item, true, false);
12540 if(this.multiSelect || this.singleSelect){
12541 if(this.multiSelect && e.shiftKey && this.lastSelection){
12542 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12544 this.select(item, this.multiSelect && e.ctrlKey);
12545 this.lastSelection = item;
12548 if(!this.tickable){
12549 e.preventDefault();
12557 * Get the number of selected nodes.
12560 getSelectionCount : function(){
12561 return this.selections.length;
12565 * Get the currently selected nodes.
12566 * @return {Array} An array of HTMLElements
12568 getSelectedNodes : function(){
12569 return this.selections;
12573 * Get the indexes of the selected nodes.
12576 getSelectedIndexes : function(){
12577 var indexes = [], s = this.selections;
12578 for(var i = 0, len = s.length; i < len; i++){
12579 indexes.push(s[i].nodeIndex);
12585 * Clear all selections
12586 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12588 clearSelections : function(suppressEvent){
12589 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12590 this.cmp.elements = this.selections;
12591 this.cmp.removeClass(this.selectedClass);
12592 this.selections = [];
12593 if(!suppressEvent){
12594 this.fireEvent("selectionchange", this, this.selections);
12600 * Returns true if the passed node is selected
12601 * @param {HTMLElement/Number} node The node or node index
12602 * @return {Boolean}
12604 isSelected : function(node){
12605 var s = this.selections;
12609 node = this.getNode(node);
12610 return s.indexOf(node) !== -1;
12615 * @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
12616 * @param {Boolean} keepExisting (optional) true to keep existing selections
12617 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12619 select : function(nodeInfo, keepExisting, suppressEvent){
12620 if(nodeInfo instanceof Array){
12622 this.clearSelections(true);
12624 for(var i = 0, len = nodeInfo.length; i < len; i++){
12625 this.select(nodeInfo[i], true, true);
12629 var node = this.getNode(nodeInfo);
12630 if(!node || this.isSelected(node)){
12631 return; // already selected.
12634 this.clearSelections(true);
12636 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12637 Roo.fly(node).addClass(this.selectedClass);
12638 this.selections.push(node);
12639 if(!suppressEvent){
12640 this.fireEvent("selectionchange", this, this.selections);
12648 * @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
12649 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12650 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12652 unselect : function(nodeInfo, keepExisting, suppressEvent)
12654 if(nodeInfo instanceof Array){
12655 Roo.each(this.selections, function(s) {
12656 this.unselect(s, nodeInfo);
12660 var node = this.getNode(nodeInfo);
12661 if(!node || !this.isSelected(node)){
12662 Roo.log("not selected");
12663 return; // not selected.
12667 Roo.each(this.selections, function(s) {
12669 Roo.fly(node).removeClass(this.selectedClass);
12676 this.selections= ns;
12677 this.fireEvent("selectionchange", this, this.selections);
12681 * Gets a template node.
12682 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12683 * @return {HTMLElement} The node or null if it wasn't found
12685 getNode : function(nodeInfo){
12686 if(typeof nodeInfo == "string"){
12687 return document.getElementById(nodeInfo);
12688 }else if(typeof nodeInfo == "number"){
12689 return this.nodes[nodeInfo];
12695 * Gets a range template nodes.
12696 * @param {Number} startIndex
12697 * @param {Number} endIndex
12698 * @return {Array} An array of nodes
12700 getNodes : function(start, end){
12701 var ns = this.nodes;
12702 start = start || 0;
12703 end = typeof end == "undefined" ? ns.length - 1 : end;
12706 for(var i = start; i <= end; i++){
12710 for(var i = start; i >= end; i--){
12718 * Finds the index of the passed node
12719 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12720 * @return {Number} The index of the node or -1
12722 indexOf : function(node){
12723 node = this.getNode(node);
12724 if(typeof node.nodeIndex == "number"){
12725 return node.nodeIndex;
12727 var ns = this.nodes;
12728 for(var i = 0, len = ns.length; i < len; i++){
12739 * based on jquery fullcalendar
12743 Roo.bootstrap = Roo.bootstrap || {};
12745 * @class Roo.bootstrap.Calendar
12746 * @extends Roo.bootstrap.Component
12747 * Bootstrap Calendar class
12748 * @cfg {Boolean} loadMask (true|false) default false
12749 * @cfg {Object} header generate the user specific header of the calendar, default false
12752 * Create a new Container
12753 * @param {Object} config The config object
12758 Roo.bootstrap.Calendar = function(config){
12759 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12763 * Fires when a date is selected
12764 * @param {DatePicker} this
12765 * @param {Date} date The selected date
12769 * @event monthchange
12770 * Fires when the displayed month changes
12771 * @param {DatePicker} this
12772 * @param {Date} date The selected month
12774 'monthchange': true,
12776 * @event evententer
12777 * Fires when mouse over an event
12778 * @param {Calendar} this
12779 * @param {event} Event
12781 'evententer': true,
12783 * @event eventleave
12784 * Fires when the mouse leaves an
12785 * @param {Calendar} this
12788 'eventleave': true,
12790 * @event eventclick
12791 * Fires when the mouse click an
12792 * @param {Calendar} this
12801 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
12804 * @cfg {Number} startDay
12805 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12813 getAutoCreate : function(){
12816 var fc_button = function(name, corner, style, content ) {
12817 return Roo.apply({},{
12819 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
12821 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12824 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12835 style : 'width:100%',
12842 cls : 'fc-header-left',
12844 fc_button('prev', 'left', 'arrow', '‹' ),
12845 fc_button('next', 'right', 'arrow', '›' ),
12846 { tag: 'span', cls: 'fc-header-space' },
12847 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
12855 cls : 'fc-header-center',
12859 cls: 'fc-header-title',
12862 html : 'month / year'
12870 cls : 'fc-header-right',
12872 /* fc_button('month', 'left', '', 'month' ),
12873 fc_button('week', '', '', 'week' ),
12874 fc_button('day', 'right', '', 'day' )
12886 header = this.header;
12889 var cal_heads = function() {
12891 // fixme - handle this.
12893 for (var i =0; i < Date.dayNames.length; i++) {
12894 var d = Date.dayNames[i];
12897 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12898 html : d.substring(0,3)
12902 ret[0].cls += ' fc-first';
12903 ret[6].cls += ' fc-last';
12906 var cal_cell = function(n) {
12909 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12914 cls: 'fc-day-number',
12918 cls: 'fc-day-content',
12922 style: 'position: relative;' // height: 17px;
12934 var cal_rows = function() {
12937 for (var r = 0; r < 6; r++) {
12944 for (var i =0; i < Date.dayNames.length; i++) {
12945 var d = Date.dayNames[i];
12946 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12949 row.cn[0].cls+=' fc-first';
12950 row.cn[0].cn[0].style = 'min-height:90px';
12951 row.cn[6].cls+=' fc-last';
12955 ret[0].cls += ' fc-first';
12956 ret[4].cls += ' fc-prev-last';
12957 ret[5].cls += ' fc-last';
12964 cls: 'fc-border-separate',
12965 style : 'width:100%',
12973 cls : 'fc-first fc-last',
12991 cls : 'fc-content',
12992 style : "position: relative;",
12995 cls : 'fc-view fc-view-month fc-grid',
12996 style : 'position: relative',
12997 unselectable : 'on',
13000 cls : 'fc-event-container',
13001 style : 'position:absolute;z-index:8;top:0;left:0;'
13019 initEvents : function()
13022 throw "can not find store for calendar";
13028 style: "text-align:center",
13032 style: "background-color:white;width:50%;margin:250 auto",
13036 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
13047 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13049 var size = this.el.select('.fc-content', true).first().getSize();
13050 this.maskEl.setSize(size.width, size.height);
13051 this.maskEl.enableDisplayMode("block");
13052 if(!this.loadMask){
13053 this.maskEl.hide();
13056 this.store = Roo.factory(this.store, Roo.data);
13057 this.store.on('load', this.onLoad, this);
13058 this.store.on('beforeload', this.onBeforeLoad, this);
13062 this.cells = this.el.select('.fc-day',true);
13063 //Roo.log(this.cells);
13064 this.textNodes = this.el.query('.fc-day-number');
13065 this.cells.addClassOnOver('fc-state-hover');
13067 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13068 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13069 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13070 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13072 this.on('monthchange', this.onMonthChange, this);
13074 this.update(new Date().clearTime());
13077 resize : function() {
13078 var sz = this.el.getSize();
13080 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13081 this.el.select('.fc-day-content div',true).setHeight(34);
13086 showPrevMonth : function(e){
13087 this.update(this.activeDate.add("mo", -1));
13089 showToday : function(e){
13090 this.update(new Date().clearTime());
13093 showNextMonth : function(e){
13094 this.update(this.activeDate.add("mo", 1));
13098 showPrevYear : function(){
13099 this.update(this.activeDate.add("y", -1));
13103 showNextYear : function(){
13104 this.update(this.activeDate.add("y", 1));
13109 update : function(date)
13111 var vd = this.activeDate;
13112 this.activeDate = date;
13113 // if(vd && this.el){
13114 // var t = date.getTime();
13115 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13116 // Roo.log('using add remove');
13118 // this.fireEvent('monthchange', this, date);
13120 // this.cells.removeClass("fc-state-highlight");
13121 // this.cells.each(function(c){
13122 // if(c.dateValue == t){
13123 // c.addClass("fc-state-highlight");
13124 // setTimeout(function(){
13125 // try{c.dom.firstChild.focus();}catch(e){}
13135 var days = date.getDaysInMonth();
13137 var firstOfMonth = date.getFirstDateOfMonth();
13138 var startingPos = firstOfMonth.getDay()-this.startDay;
13140 if(startingPos < this.startDay){
13144 var pm = date.add(Date.MONTH, -1);
13145 var prevStart = pm.getDaysInMonth()-startingPos;
13147 this.cells = this.el.select('.fc-day',true);
13148 this.textNodes = this.el.query('.fc-day-number');
13149 this.cells.addClassOnOver('fc-state-hover');
13151 var cells = this.cells.elements;
13152 var textEls = this.textNodes;
13154 Roo.each(cells, function(cell){
13155 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13158 days += startingPos;
13160 // convert everything to numbers so it's fast
13161 var day = 86400000;
13162 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13165 //Roo.log(prevStart);
13167 var today = new Date().clearTime().getTime();
13168 var sel = date.clearTime().getTime();
13169 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13170 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13171 var ddMatch = this.disabledDatesRE;
13172 var ddText = this.disabledDatesText;
13173 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13174 var ddaysText = this.disabledDaysText;
13175 var format = this.format;
13177 var setCellClass = function(cal, cell){
13181 //Roo.log('set Cell Class');
13183 var t = d.getTime();
13187 cell.dateValue = t;
13189 cell.className += " fc-today";
13190 cell.className += " fc-state-highlight";
13191 cell.title = cal.todayText;
13194 // disable highlight in other month..
13195 //cell.className += " fc-state-highlight";
13200 cell.className = " fc-state-disabled";
13201 cell.title = cal.minText;
13205 cell.className = " fc-state-disabled";
13206 cell.title = cal.maxText;
13210 if(ddays.indexOf(d.getDay()) != -1){
13211 cell.title = ddaysText;
13212 cell.className = " fc-state-disabled";
13215 if(ddMatch && format){
13216 var fvalue = d.dateFormat(format);
13217 if(ddMatch.test(fvalue)){
13218 cell.title = ddText.replace("%0", fvalue);
13219 cell.className = " fc-state-disabled";
13223 if (!cell.initialClassName) {
13224 cell.initialClassName = cell.dom.className;
13227 cell.dom.className = cell.initialClassName + ' ' + cell.className;
13232 for(; i < startingPos; i++) {
13233 textEls[i].innerHTML = (++prevStart);
13234 d.setDate(d.getDate()+1);
13236 cells[i].className = "fc-past fc-other-month";
13237 setCellClass(this, cells[i]);
13242 for(; i < days; i++){
13243 intDay = i - startingPos + 1;
13244 textEls[i].innerHTML = (intDay);
13245 d.setDate(d.getDate()+1);
13247 cells[i].className = ''; // "x-date-active";
13248 setCellClass(this, cells[i]);
13252 for(; i < 42; i++) {
13253 textEls[i].innerHTML = (++extraDays);
13254 d.setDate(d.getDate()+1);
13256 cells[i].className = "fc-future fc-other-month";
13257 setCellClass(this, cells[i]);
13260 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13262 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13264 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13265 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13267 if(totalRows != 6){
13268 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13269 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13272 this.fireEvent('monthchange', this, date);
13276 if(!this.internalRender){
13277 var main = this.el.dom.firstChild;
13278 var w = main.offsetWidth;
13279 this.el.setWidth(w + this.el.getBorderWidth("lr"));
13280 Roo.fly(main).setWidth(w);
13281 this.internalRender = true;
13282 // opera does not respect the auto grow header center column
13283 // then, after it gets a width opera refuses to recalculate
13284 // without a second pass
13285 if(Roo.isOpera && !this.secondPass){
13286 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13287 this.secondPass = true;
13288 this.update.defer(10, this, [date]);
13295 findCell : function(dt) {
13296 dt = dt.clearTime().getTime();
13298 this.cells.each(function(c){
13299 //Roo.log("check " +c.dateValue + '?=' + dt);
13300 if(c.dateValue == dt){
13310 findCells : function(ev) {
13311 var s = ev.start.clone().clearTime().getTime();
13313 var e= ev.end.clone().clearTime().getTime();
13316 this.cells.each(function(c){
13317 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13319 if(c.dateValue > e){
13322 if(c.dateValue < s){
13331 // findBestRow: function(cells)
13335 // for (var i =0 ; i < cells.length;i++) {
13336 // ret = Math.max(cells[i].rows || 0,ret);
13343 addItem : function(ev)
13345 // look for vertical location slot in
13346 var cells = this.findCells(ev);
13348 // ev.row = this.findBestRow(cells);
13350 // work out the location.
13354 for(var i =0; i < cells.length; i++) {
13356 cells[i].row = cells[0].row;
13359 cells[i].row = cells[i].row + 1;
13369 if (crow.start.getY() == cells[i].getY()) {
13371 crow.end = cells[i];
13388 cells[0].events.push(ev);
13390 this.calevents.push(ev);
13393 clearEvents: function() {
13395 if(!this.calevents){
13399 Roo.each(this.cells.elements, function(c){
13405 Roo.each(this.calevents, function(e) {
13406 Roo.each(e.els, function(el) {
13407 el.un('mouseenter' ,this.onEventEnter, this);
13408 el.un('mouseleave' ,this.onEventLeave, this);
13413 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13419 renderEvents: function()
13423 this.cells.each(function(c) {
13432 if(c.row != c.events.length){
13433 r = 4 - (4 - (c.row - c.events.length));
13436 c.events = ev.slice(0, r);
13437 c.more = ev.slice(r);
13439 if(c.more.length && c.more.length == 1){
13440 c.events.push(c.more.pop());
13443 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13447 this.cells.each(function(c) {
13449 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13452 for (var e = 0; e < c.events.length; e++){
13453 var ev = c.events[e];
13454 var rows = ev.rows;
13456 for(var i = 0; i < rows.length; i++) {
13458 // how many rows should it span..
13461 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13462 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13464 unselectable : "on",
13467 cls: 'fc-event-inner',
13471 // cls: 'fc-event-time',
13472 // html : cells.length > 1 ? '' : ev.time
13476 cls: 'fc-event-title',
13477 html : String.format('{0}', ev.title)
13484 cls: 'ui-resizable-handle ui-resizable-e',
13485 html : '  '
13492 cfg.cls += ' fc-event-start';
13494 if ((i+1) == rows.length) {
13495 cfg.cls += ' fc-event-end';
13498 var ctr = _this.el.select('.fc-event-container',true).first();
13499 var cg = ctr.createChild(cfg);
13501 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13502 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13504 var r = (c.more.length) ? 1 : 0;
13505 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
13506 cg.setWidth(ebox.right - sbox.x -2);
13508 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13509 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13510 cg.on('click', _this.onEventClick, _this, ev);
13521 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13522 style : 'position: absolute',
13523 unselectable : "on",
13526 cls: 'fc-event-inner',
13530 cls: 'fc-event-title',
13538 cls: 'ui-resizable-handle ui-resizable-e',
13539 html : '  '
13545 var ctr = _this.el.select('.fc-event-container',true).first();
13546 var cg = ctr.createChild(cfg);
13548 var sbox = c.select('.fc-day-content',true).first().getBox();
13549 var ebox = c.select('.fc-day-content',true).first().getBox();
13551 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
13552 cg.setWidth(ebox.right - sbox.x -2);
13554 cg.on('click', _this.onMoreEventClick, _this, c.more);
13564 onEventEnter: function (e, el,event,d) {
13565 this.fireEvent('evententer', this, el, event);
13568 onEventLeave: function (e, el,event,d) {
13569 this.fireEvent('eventleave', this, el, event);
13572 onEventClick: function (e, el,event,d) {
13573 this.fireEvent('eventclick', this, el, event);
13576 onMonthChange: function () {
13580 onMoreEventClick: function(e, el, more)
13584 this.calpopover.placement = 'right';
13585 this.calpopover.setTitle('More');
13587 this.calpopover.setContent('');
13589 var ctr = this.calpopover.el.select('.popover-content', true).first();
13591 Roo.each(more, function(m){
13593 cls : 'fc-event-hori fc-event-draggable',
13596 var cg = ctr.createChild(cfg);
13598 cg.on('click', _this.onEventClick, _this, m);
13601 this.calpopover.show(el);
13606 onLoad: function ()
13608 this.calevents = [];
13611 if(this.store.getCount() > 0){
13612 this.store.data.each(function(d){
13615 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13616 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13617 time : d.data.start_time,
13618 title : d.data.title,
13619 description : d.data.description,
13620 venue : d.data.venue
13625 this.renderEvents();
13627 if(this.calevents.length && this.loadMask){
13628 this.maskEl.hide();
13632 onBeforeLoad: function()
13634 this.clearEvents();
13636 this.maskEl.show();
13650 * @class Roo.bootstrap.Popover
13651 * @extends Roo.bootstrap.Component
13652 * Bootstrap Popover class
13653 * @cfg {String} html contents of the popover (or false to use children..)
13654 * @cfg {String} title of popover (or false to hide)
13655 * @cfg {String} placement how it is placed
13656 * @cfg {String} trigger click || hover (or false to trigger manually)
13657 * @cfg {String} over what (parent or false to trigger manually.)
13658 * @cfg {Number} delay - delay before showing
13661 * Create a new Popover
13662 * @param {Object} config The config object
13665 Roo.bootstrap.Popover = function(config){
13666 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13669 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
13671 title: 'Fill in a title',
13674 placement : 'right',
13675 trigger : 'hover', // hover
13681 can_build_overlaid : false,
13683 getChildContainer : function()
13685 return this.el.select('.popover-content',true).first();
13688 getAutoCreate : function(){
13689 Roo.log('make popover?');
13691 cls : 'popover roo-dynamic',
13692 style: 'display:block',
13698 cls : 'popover-inner',
13702 cls: 'popover-title',
13706 cls : 'popover-content',
13717 setTitle: function(str)
13719 this.el.select('.popover-title',true).first().dom.innerHTML = str;
13721 setContent: function(str)
13723 this.el.select('.popover-content',true).first().dom.innerHTML = str;
13725 // as it get's added to the bottom of the page.
13726 onRender : function(ct, position)
13728 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13730 var cfg = Roo.apply({}, this.getAutoCreate());
13734 cfg.cls += ' ' + this.cls;
13737 cfg.style = this.style;
13739 Roo.log("adding to ")
13740 this.el = Roo.get(document.body).createChild(cfg, position);
13746 initEvents : function()
13748 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13749 this.el.enableDisplayMode('block');
13751 if (this.over === false) {
13754 if (this.triggers === false) {
13757 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13758 var triggers = this.trigger ? this.trigger.split(' ') : [];
13759 Roo.each(triggers, function(trigger) {
13761 if (trigger == 'click') {
13762 on_el.on('click', this.toggle, this);
13763 } else if (trigger != 'manual') {
13764 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
13765 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13767 on_el.on(eventIn ,this.enter, this);
13768 on_el.on(eventOut, this.leave, this);
13779 toggle : function () {
13780 this.hoverState == 'in' ? this.leave() : this.enter();
13783 enter : function () {
13786 clearTimeout(this.timeout);
13788 this.hoverState = 'in'
13790 if (!this.delay || !this.delay.show) {
13795 this.timeout = setTimeout(function () {
13796 if (_t.hoverState == 'in') {
13799 }, this.delay.show)
13801 leave : function() {
13802 clearTimeout(this.timeout);
13804 this.hoverState = 'out'
13806 if (!this.delay || !this.delay.hide) {
13811 this.timeout = setTimeout(function () {
13812 if (_t.hoverState == 'out') {
13815 }, this.delay.hide)
13818 show : function (on_el)
13821 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13824 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13825 if (this.html !== false) {
13826 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13828 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13829 if (!this.title.length) {
13830 this.el.select('.popover-title',true).hide();
13833 var placement = typeof this.placement == 'function' ?
13834 this.placement.call(this, this.el, on_el) :
13837 var autoToken = /\s?auto?\s?/i;
13838 var autoPlace = autoToken.test(placement);
13840 placement = placement.replace(autoToken, '') || 'top';
13844 //this.el.setXY([0,0]);
13846 this.el.dom.style.display='block';
13847 this.el.addClass(placement);
13849 //this.el.appendTo(on_el);
13851 var p = this.getPosition();
13852 var box = this.el.getBox();
13857 var align = Roo.bootstrap.Popover.alignment[placement]
13858 this.el.alignTo(on_el, align[0],align[1]);
13859 //var arrow = this.el.select('.arrow',true).first();
13860 //arrow.set(align[2],
13862 this.el.addClass('in');
13863 this.hoverState = null;
13865 if (this.el.hasClass('fade')) {
13872 this.el.setXY([0,0]);
13873 this.el.removeClass('in');
13880 Roo.bootstrap.Popover.alignment = {
13881 'left' : ['r-l', [-10,0], 'right'],
13882 'right' : ['l-r', [10,0], 'left'],
13883 'bottom' : ['t-b', [0,10], 'top'],
13884 'top' : [ 'b-t', [0,-10], 'bottom']
13895 * @class Roo.bootstrap.Progress
13896 * @extends Roo.bootstrap.Component
13897 * Bootstrap Progress class
13898 * @cfg {Boolean} striped striped of the progress bar
13899 * @cfg {Boolean} active animated of the progress bar
13903 * Create a new Progress
13904 * @param {Object} config The config object
13907 Roo.bootstrap.Progress = function(config){
13908 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13911 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
13916 getAutoCreate : function(){
13924 cfg.cls += ' progress-striped';
13928 cfg.cls += ' active';
13947 * @class Roo.bootstrap.ProgressBar
13948 * @extends Roo.bootstrap.Component
13949 * Bootstrap ProgressBar class
13950 * @cfg {Number} aria_valuenow aria-value now
13951 * @cfg {Number} aria_valuemin aria-value min
13952 * @cfg {Number} aria_valuemax aria-value max
13953 * @cfg {String} label label for the progress bar
13954 * @cfg {String} panel (success | info | warning | danger )
13955 * @cfg {String} role role of the progress bar
13956 * @cfg {String} sr_only text
13960 * Create a new ProgressBar
13961 * @param {Object} config The config object
13964 Roo.bootstrap.ProgressBar = function(config){
13965 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13968 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
13972 aria_valuemax : 100,
13978 getAutoCreate : function()
13983 cls: 'progress-bar',
13984 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13996 cfg.role = this.role;
13999 if(this.aria_valuenow){
14000 cfg['aria-valuenow'] = this.aria_valuenow;
14003 if(this.aria_valuemin){
14004 cfg['aria-valuemin'] = this.aria_valuemin;
14007 if(this.aria_valuemax){
14008 cfg['aria-valuemax'] = this.aria_valuemax;
14011 if(this.label && !this.sr_only){
14012 cfg.html = this.label;
14016 cfg.cls += ' progress-bar-' + this.panel;
14022 update : function(aria_valuenow)
14024 this.aria_valuenow = aria_valuenow;
14026 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14041 * @class Roo.bootstrap.TabGroup
14042 * @extends Roo.bootstrap.Column
14043 * Bootstrap Column class
14044 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14045 * @cfg {Boolean} carousel true to make the group behave like a carousel
14048 * Create a new TabGroup
14049 * @param {Object} config The config object
14052 Roo.bootstrap.TabGroup = function(config){
14053 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14055 this.navId = Roo.id();
14058 Roo.bootstrap.TabGroup.register(this);
14062 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
14065 transition : false,
14067 getAutoCreate : function()
14069 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14071 cfg.cls += ' tab-content';
14073 if (this.carousel) {
14074 cfg.cls += ' carousel slide';
14076 cls : 'carousel-inner'
14083 getChildContainer : function()
14085 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14089 * register a Navigation item
14090 * @param {Roo.bootstrap.NavItem} the navitem to add
14092 register : function(item)
14094 this.tabs.push( item);
14095 item.navId = this.navId; // not really needed..
14099 getActivePanel : function()
14102 Roo.each(this.tabs, function(t) {
14112 getPanelByName : function(n)
14115 Roo.each(this.tabs, function(t) {
14116 if (t.tabId == n) {
14124 indexOfPanel : function(p)
14127 Roo.each(this.tabs, function(t,i) {
14128 if (t.tabId == p.tabId) {
14137 * show a specific panel
14138 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14139 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14141 showPanel : function (pan)
14144 if (typeof(pan) == 'number') {
14145 pan = this.tabs[pan];
14147 if (typeof(pan) == 'string') {
14148 pan = this.getPanelByName(pan);
14150 if (pan.tabId == this.getActivePanel().tabId) {
14153 var cur = this.getActivePanel();
14155 if (false === cur.fireEvent('beforedeactivate')) {
14159 if (this.carousel) {
14160 this.transition = true;
14161 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
14162 var lr = dir == 'next' ? 'left' : 'right';
14163 pan.el.addClass(dir); // or prev
14164 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14165 cur.el.addClass(lr); // or right
14166 pan.el.addClass(lr);
14169 cur.el.on('transitionend', function() {
14170 Roo.log("trans end?");
14172 pan.el.removeClass([lr,dir]);
14173 pan.setActive(true);
14175 cur.el.removeClass([lr]);
14176 cur.setActive(false);
14178 _this.transition = false;
14180 }, this, { single: true } );
14184 cur.setActive(false);
14185 pan.setActive(true);
14189 showPanelNext : function()
14191 var i = this.indexOfPanel(this.getActivePanel());
14192 if (i > this.tabs.length) {
14195 this.showPanel(this.tabs[i+1]);
14197 showPanelPrev : function()
14199 var i = this.indexOfPanel(this.getActivePanel());
14203 this.showPanel(this.tabs[i-1]);
14214 Roo.apply(Roo.bootstrap.TabGroup, {
14218 * register a Navigation Group
14219 * @param {Roo.bootstrap.NavGroup} the navgroup to add
14221 register : function(navgrp)
14223 this.groups[navgrp.navId] = navgrp;
14227 * fetch a Navigation Group based on the navigation ID
14228 * if one does not exist , it will get created.
14229 * @param {string} the navgroup to add
14230 * @returns {Roo.bootstrap.NavGroup} the navgroup
14232 get: function(navId) {
14233 if (typeof(this.groups[navId]) == 'undefined') {
14234 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14236 return this.groups[navId] ;
14251 * @class Roo.bootstrap.TabPanel
14252 * @extends Roo.bootstrap.Component
14253 * Bootstrap TabPanel class
14254 * @cfg {Boolean} active panel active
14255 * @cfg {String} html panel content
14256 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14257 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14261 * Create a new TabPanel
14262 * @param {Object} config The config object
14265 Roo.bootstrap.TabPanel = function(config){
14266 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14270 * Fires when the active status changes
14271 * @param {Roo.bootstrap.TabPanel} this
14272 * @param {Boolean} state the new state
14277 * @event beforedeactivate
14278 * Fires before a tab is de-activated - can be used to do validation on a form.
14279 * @param {Roo.bootstrap.TabPanel} this
14280 * @return {Boolean} false if there is an error
14283 'beforedeactivate': true
14286 this.tabId = this.tabId || Roo.id();
14290 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
14297 getAutoCreate : function(){
14300 // item is needed for carousel - not sure if it has any effect otherwise
14301 cls: 'tab-pane item',
14302 html: this.html || ''
14306 cfg.cls += ' active';
14310 cfg.tabId = this.tabId;
14317 initEvents: function()
14319 Roo.log('-------- init events on tab panel ---------');
14321 var p = this.parent();
14322 this.navId = this.navId || p.navId;
14324 if (typeof(this.navId) != 'undefined') {
14325 // not really needed.. but just in case.. parent should be a NavGroup.
14326 var tg = Roo.bootstrap.TabGroup.get(this.navId);
14327 Roo.log(['register', tg, this]);
14333 onRender : function(ct, position)
14335 // Roo.log("Call onRender: " + this.xtype);
14337 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14345 setActive: function(state)
14347 Roo.log("panel - set active " + this.tabId + "=" + state);
14349 this.active = state;
14351 this.el.removeClass('active');
14353 } else if (!this.el.hasClass('active')) {
14354 this.el.addClass('active');
14356 this.fireEvent('changed', this, state);
14373 * @class Roo.bootstrap.DateField
14374 * @extends Roo.bootstrap.Input
14375 * Bootstrap DateField class
14376 * @cfg {Number} weekStart default 0
14377 * @cfg {Number} weekStart default 0
14378 * @cfg {Number} viewMode default empty, (months|years)
14379 * @cfg {Number} minViewMode default empty, (months|years)
14380 * @cfg {Number} startDate default -Infinity
14381 * @cfg {Number} endDate default Infinity
14382 * @cfg {Boolean} todayHighlight default false
14383 * @cfg {Boolean} todayBtn default false
14384 * @cfg {Boolean} calendarWeeks default false
14385 * @cfg {Object} daysOfWeekDisabled default empty
14387 * @cfg {Boolean} keyboardNavigation default true
14388 * @cfg {String} language default en
14391 * Create a new DateField
14392 * @param {Object} config The config object
14395 Roo.bootstrap.DateField = function(config){
14396 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14400 * Fires when this field show.
14401 * @param {Roo.bootstrap.DateField} this
14402 * @param {Mixed} date The date value
14407 * Fires when this field hide.
14408 * @param {Roo.bootstrap.DateField} this
14409 * @param {Mixed} date The date value
14414 * Fires when select a date.
14415 * @param {Roo.bootstrap.DateField} this
14416 * @param {Mixed} date The date value
14422 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
14425 * @cfg {String} format
14426 * The default date format string which can be overriden for localization support. The format must be
14427 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14431 * @cfg {String} altFormats
14432 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14433 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14435 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14443 todayHighlight : false,
14449 keyboardNavigation: true,
14451 calendarWeeks: false,
14453 startDate: -Infinity,
14457 daysOfWeekDisabled: [],
14461 UTCDate: function()
14463 return new Date(Date.UTC.apply(Date, arguments));
14466 UTCToday: function()
14468 var today = new Date();
14469 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14472 getDate: function() {
14473 var d = this.getUTCDate();
14474 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14477 getUTCDate: function() {
14481 setDate: function(d) {
14482 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14485 setUTCDate: function(d) {
14487 this.setValue(this.formatDate(this.date));
14490 onRender: function(ct, position)
14493 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14495 this.language = this.language || 'en';
14496 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14497 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14499 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14500 this.format = this.format || 'm/d/y';
14501 this.isInline = false;
14502 this.isInput = true;
14503 this.component = this.el.select('.add-on', true).first() || false;
14504 this.component = (this.component && this.component.length === 0) ? false : this.component;
14505 this.hasInput = this.component && this.inputEL().length;
14507 if (typeof(this.minViewMode === 'string')) {
14508 switch (this.minViewMode) {
14510 this.minViewMode = 1;
14513 this.minViewMode = 2;
14516 this.minViewMode = 0;
14521 if (typeof(this.viewMode === 'string')) {
14522 switch (this.viewMode) {
14535 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14537 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14539 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14541 this.picker().on('mousedown', this.onMousedown, this);
14542 this.picker().on('click', this.onClick, this);
14544 this.picker().addClass('datepicker-dropdown');
14546 this.startViewMode = this.viewMode;
14549 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14550 if(!this.calendarWeeks){
14555 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14556 v.attr('colspan', function(i, val){
14557 return parseInt(val) + 1;
14562 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14564 this.setStartDate(this.startDate);
14565 this.setEndDate(this.endDate);
14567 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14574 if(this.isInline) {
14579 picker : function()
14581 return this.pickerEl;
14582 // return this.el.select('.datepicker', true).first();
14585 fillDow: function()
14587 var dowCnt = this.weekStart;
14596 if(this.calendarWeeks){
14604 while (dowCnt < this.weekStart + 7) {
14608 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14612 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14615 fillMonths: function()
14618 var months = this.picker().select('>.datepicker-months td', true).first();
14620 months.dom.innerHTML = '';
14626 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14629 months.createChild(month);
14636 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;
14638 if (this.date < this.startDate) {
14639 this.viewDate = new Date(this.startDate);
14640 } else if (this.date > this.endDate) {
14641 this.viewDate = new Date(this.endDate);
14643 this.viewDate = new Date(this.date);
14651 var d = new Date(this.viewDate),
14652 year = d.getUTCFullYear(),
14653 month = d.getUTCMonth(),
14654 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14655 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14656 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14657 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14658 currentDate = this.date && this.date.valueOf(),
14659 today = this.UTCToday();
14661 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14663 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14665 // this.picker.select('>tfoot th.today').
14666 // .text(dates[this.language].today)
14667 // .toggle(this.todayBtn !== false);
14669 this.updateNavArrows();
14672 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14674 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14676 prevMonth.setUTCDate(day);
14678 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14680 var nextMonth = new Date(prevMonth);
14682 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14684 nextMonth = nextMonth.valueOf();
14686 var fillMonths = false;
14688 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14690 while(prevMonth.valueOf() < nextMonth) {
14693 if (prevMonth.getUTCDay() === this.weekStart) {
14695 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14703 if(this.calendarWeeks){
14704 // ISO 8601: First week contains first thursday.
14705 // ISO also states week starts on Monday, but we can be more abstract here.
14707 // Start of current week: based on weekstart/current date
14708 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14709 // Thursday of this week
14710 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14711 // First Thursday of year, year from thursday
14712 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14713 // Calendar week: ms between thursdays, div ms per day, div 7 days
14714 calWeek = (th - yth) / 864e5 / 7 + 1;
14716 fillMonths.cn.push({
14724 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14726 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14729 if (this.todayHighlight &&
14730 prevMonth.getUTCFullYear() == today.getFullYear() &&
14731 prevMonth.getUTCMonth() == today.getMonth() &&
14732 prevMonth.getUTCDate() == today.getDate()) {
14733 clsName += ' today';
14736 if (currentDate && prevMonth.valueOf() === currentDate) {
14737 clsName += ' active';
14740 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14741 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14742 clsName += ' disabled';
14745 fillMonths.cn.push({
14747 cls: 'day ' + clsName,
14748 html: prevMonth.getDate()
14751 prevMonth.setDate(prevMonth.getDate()+1);
14754 var currentYear = this.date && this.date.getUTCFullYear();
14755 var currentMonth = this.date && this.date.getUTCMonth();
14757 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14759 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14760 v.removeClass('active');
14762 if(currentYear === year && k === currentMonth){
14763 v.addClass('active');
14766 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14767 v.addClass('disabled');
14773 year = parseInt(year/10, 10) * 10;
14775 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14777 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14780 for (var i = -1; i < 11; i++) {
14781 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14783 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14791 showMode: function(dir)
14794 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14796 Roo.each(this.picker().select('>div',true).elements, function(v){
14797 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14800 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14805 if(this.isInline) return;
14807 this.picker().removeClass(['bottom', 'top']);
14809 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14811 * place to the top of element!
14815 this.picker().addClass('top');
14816 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14821 this.picker().addClass('bottom');
14823 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14826 parseDate : function(value)
14828 if(!value || value instanceof Date){
14831 var v = Date.parseDate(value, this.format);
14832 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
14833 v = Date.parseDate(value, 'Y-m-d');
14835 if(!v && this.altFormats){
14836 if(!this.altFormatsArray){
14837 this.altFormatsArray = this.altFormats.split("|");
14839 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14840 v = Date.parseDate(value, this.altFormatsArray[i]);
14846 formatDate : function(date, fmt)
14848 return (!date || !(date instanceof Date)) ?
14849 date : date.dateFormat(fmt || this.format);
14852 onFocus : function()
14854 Roo.bootstrap.DateField.superclass.onFocus.call(this);
14858 onBlur : function()
14860 Roo.bootstrap.DateField.superclass.onBlur.call(this);
14862 var d = this.inputEl().getValue();
14871 this.picker().show();
14875 this.fireEvent('show', this, this.date);
14880 if(this.isInline) return;
14881 this.picker().hide();
14882 this.viewMode = this.startViewMode;
14885 this.fireEvent('hide', this, this.date);
14889 onMousedown: function(e)
14891 e.stopPropagation();
14892 e.preventDefault();
14897 Roo.bootstrap.DateField.superclass.keyup.call(this);
14901 setValue: function(v)
14904 // v can be a string or a date..
14907 var d = new Date(this.parseDate(v) ).clearTime();
14911 if(isNaN(d.getTime())){
14912 this.date = this.viewDate = '';
14913 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14917 v = this.formatDate(d);
14919 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14921 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14925 this.fireEvent('select', this, this.date);
14929 getValue: function()
14931 return this.formatDate(this.date);
14934 fireKey: function(e)
14936 if (!this.picker().isVisible()){
14937 if (e.keyCode == 27) // allow escape to hide and re-show picker
14942 var dateChanged = false,
14944 newDate, newViewDate;
14949 e.preventDefault();
14953 if (!this.keyboardNavigation) break;
14954 dir = e.keyCode == 37 ? -1 : 1;
14957 newDate = this.moveYear(this.date, dir);
14958 newViewDate = this.moveYear(this.viewDate, dir);
14959 } else if (e.shiftKey){
14960 newDate = this.moveMonth(this.date, dir);
14961 newViewDate = this.moveMonth(this.viewDate, dir);
14963 newDate = new Date(this.date);
14964 newDate.setUTCDate(this.date.getUTCDate() + dir);
14965 newViewDate = new Date(this.viewDate);
14966 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14968 if (this.dateWithinRange(newDate)){
14969 this.date = newDate;
14970 this.viewDate = newViewDate;
14971 this.setValue(this.formatDate(this.date));
14973 e.preventDefault();
14974 dateChanged = true;
14979 if (!this.keyboardNavigation) break;
14980 dir = e.keyCode == 38 ? -1 : 1;
14982 newDate = this.moveYear(this.date, dir);
14983 newViewDate = this.moveYear(this.viewDate, dir);
14984 } else if (e.shiftKey){
14985 newDate = this.moveMonth(this.date, dir);
14986 newViewDate = this.moveMonth(this.viewDate, dir);
14988 newDate = new Date(this.date);
14989 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14990 newViewDate = new Date(this.viewDate);
14991 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14993 if (this.dateWithinRange(newDate)){
14994 this.date = newDate;
14995 this.viewDate = newViewDate;
14996 this.setValue(this.formatDate(this.date));
14998 e.preventDefault();
14999 dateChanged = true;
15003 this.setValue(this.formatDate(this.date));
15005 e.preventDefault();
15008 this.setValue(this.formatDate(this.date));
15022 onClick: function(e)
15024 e.stopPropagation();
15025 e.preventDefault();
15027 var target = e.getTarget();
15029 if(target.nodeName.toLowerCase() === 'i'){
15030 target = Roo.get(target).dom.parentNode;
15033 var nodeName = target.nodeName;
15034 var className = target.className;
15035 var html = target.innerHTML;
15036 //Roo.log(nodeName);
15038 switch(nodeName.toLowerCase()) {
15040 switch(className) {
15046 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15047 switch(this.viewMode){
15049 this.viewDate = this.moveMonth(this.viewDate, dir);
15053 this.viewDate = this.moveYear(this.viewDate, dir);
15059 var date = new Date();
15060 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15062 this.setValue(this.formatDate(this.date));
15069 if (className.indexOf('disabled') < 0) {
15070 this.viewDate.setUTCDate(1);
15071 if (className.indexOf('month') > -1) {
15072 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15074 var year = parseInt(html, 10) || 0;
15075 this.viewDate.setUTCFullYear(year);
15084 //Roo.log(className);
15085 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15086 var day = parseInt(html, 10) || 1;
15087 var year = this.viewDate.getUTCFullYear(),
15088 month = this.viewDate.getUTCMonth();
15090 if (className.indexOf('old') > -1) {
15097 } else if (className.indexOf('new') > -1) {
15105 //Roo.log([year,month,day]);
15106 this.date = this.UTCDate(year, month, day,0,0,0,0);
15107 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15109 //Roo.log(this.formatDate(this.date));
15110 this.setValue(this.formatDate(this.date));
15117 setStartDate: function(startDate)
15119 this.startDate = startDate || -Infinity;
15120 if (this.startDate !== -Infinity) {
15121 this.startDate = this.parseDate(this.startDate);
15124 this.updateNavArrows();
15127 setEndDate: function(endDate)
15129 this.endDate = endDate || Infinity;
15130 if (this.endDate !== Infinity) {
15131 this.endDate = this.parseDate(this.endDate);
15134 this.updateNavArrows();
15137 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15139 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15140 if (typeof(this.daysOfWeekDisabled) !== 'object') {
15141 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15143 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15144 return parseInt(d, 10);
15147 this.updateNavArrows();
15150 updateNavArrows: function()
15152 var d = new Date(this.viewDate),
15153 year = d.getUTCFullYear(),
15154 month = d.getUTCMonth();
15156 Roo.each(this.picker().select('.prev', true).elements, function(v){
15158 switch (this.viewMode) {
15161 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15167 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15174 Roo.each(this.picker().select('.next', true).elements, function(v){
15176 switch (this.viewMode) {
15179 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15185 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15193 moveMonth: function(date, dir)
15195 if (!dir) return date;
15196 var new_date = new Date(date.valueOf()),
15197 day = new_date.getUTCDate(),
15198 month = new_date.getUTCMonth(),
15199 mag = Math.abs(dir),
15201 dir = dir > 0 ? 1 : -1;
15204 // If going back one month, make sure month is not current month
15205 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15207 return new_date.getUTCMonth() == month;
15209 // If going forward one month, make sure month is as expected
15210 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15212 return new_date.getUTCMonth() != new_month;
15214 new_month = month + dir;
15215 new_date.setUTCMonth(new_month);
15216 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15217 if (new_month < 0 || new_month > 11)
15218 new_month = (new_month + 12) % 12;
15220 // For magnitudes >1, move one month at a time...
15221 for (var i=0; i<mag; i++)
15222 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15223 new_date = this.moveMonth(new_date, dir);
15224 // ...then reset the day, keeping it in the new month
15225 new_month = new_date.getUTCMonth();
15226 new_date.setUTCDate(day);
15228 return new_month != new_date.getUTCMonth();
15231 // Common date-resetting loop -- if date is beyond end of month, make it
15234 new_date.setUTCDate(--day);
15235 new_date.setUTCMonth(new_month);
15240 moveYear: function(date, dir)
15242 return this.moveMonth(date, dir*12);
15245 dateWithinRange: function(date)
15247 return date >= this.startDate && date <= this.endDate;
15253 this.picker().remove();
15258 Roo.apply(Roo.bootstrap.DateField, {
15269 html: '<i class="fa fa-arrow-left"/>'
15279 html: '<i class="fa fa-arrow-right"/>'
15321 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15322 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15323 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15324 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15325 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15338 navFnc: 'FullYear',
15343 navFnc: 'FullYear',
15348 Roo.apply(Roo.bootstrap.DateField, {
15352 cls: 'datepicker dropdown-menu',
15356 cls: 'datepicker-days',
15360 cls: 'table-condensed',
15362 Roo.bootstrap.DateField.head,
15366 Roo.bootstrap.DateField.footer
15373 cls: 'datepicker-months',
15377 cls: 'table-condensed',
15379 Roo.bootstrap.DateField.head,
15380 Roo.bootstrap.DateField.content,
15381 Roo.bootstrap.DateField.footer
15388 cls: 'datepicker-years',
15392 cls: 'table-condensed',
15394 Roo.bootstrap.DateField.head,
15395 Roo.bootstrap.DateField.content,
15396 Roo.bootstrap.DateField.footer
15415 * @class Roo.bootstrap.TimeField
15416 * @extends Roo.bootstrap.Input
15417 * Bootstrap DateField class
15421 * Create a new TimeField
15422 * @param {Object} config The config object
15425 Roo.bootstrap.TimeField = function(config){
15426 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15430 * Fires when this field show.
15431 * @param {Roo.bootstrap.DateField} this
15432 * @param {Mixed} date The date value
15437 * Fires when this field hide.
15438 * @param {Roo.bootstrap.DateField} this
15439 * @param {Mixed} date The date value
15444 * Fires when select a date.
15445 * @param {Roo.bootstrap.DateField} this
15446 * @param {Mixed} date The date value
15452 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
15455 * @cfg {String} format
15456 * The default time format string which can be overriden for localization support. The format must be
15457 * valid according to {@link Date#parseDate} (defaults to 'H:i').
15461 onRender: function(ct, position)
15464 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15466 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15468 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15470 this.pop = this.picker().select('>.datepicker-time',true).first();
15471 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
15473 this.picker().on('mousedown', this.onMousedown, this);
15474 this.picker().on('click', this.onClick, this);
15476 this.picker().addClass('datepicker-dropdown');
15481 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15482 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15483 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15484 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15485 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15486 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15490 fireKey: function(e){
15491 if (!this.picker().isVisible()){
15492 if (e.keyCode == 27) // allow escape to hide and re-show picker
15497 e.preventDefault();
15505 this.onTogglePeriod();
15508 this.onIncrementMinutes();
15511 this.onDecrementMinutes();
15520 onClick: function(e) {
15521 e.stopPropagation();
15522 e.preventDefault();
15525 picker : function()
15527 return this.el.select('.datepicker', true).first();
15530 fillTime: function()
15532 var time = this.pop.select('tbody', true).first();
15534 time.dom.innerHTML = '';
15549 cls: 'hours-up glyphicon glyphicon-chevron-up'
15569 cls: 'minutes-up glyphicon glyphicon-chevron-up'
15590 cls: 'timepicker-hour',
15605 cls: 'timepicker-minute',
15620 cls: 'btn btn-primary period',
15642 cls: 'hours-down glyphicon glyphicon-chevron-down'
15662 cls: 'minutes-down glyphicon glyphicon-chevron-down'
15680 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15687 var hours = this.time.getHours();
15688 var minutes = this.time.getMinutes();
15701 hours = hours - 12;
15705 hours = '0' + hours;
15709 minutes = '0' + minutes;
15712 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15713 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15714 this.pop.select('button', true).first().dom.innerHTML = period;
15720 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15722 var cls = ['bottom'];
15724 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15731 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15736 this.picker().addClass(cls.join('-'));
15740 Roo.each(cls, function(c){
15742 _this.picker().setTop(_this.inputEl().getHeight());
15746 _this.picker().setTop(0 - _this.picker().getHeight());
15751 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15755 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15762 onFocus : function()
15764 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15768 onBlur : function()
15770 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15776 this.picker().show();
15781 this.fireEvent('show', this, this.date);
15786 this.picker().hide();
15789 this.fireEvent('hide', this, this.date);
15792 setTime : function()
15795 this.setValue(this.time.format(this.format));
15797 this.fireEvent('select', this, this.date);
15802 onMousedown: function(e){
15803 e.stopPropagation();
15804 e.preventDefault();
15807 onIncrementHours: function()
15809 Roo.log('onIncrementHours');
15810 this.time = this.time.add(Date.HOUR, 1);
15815 onDecrementHours: function()
15817 Roo.log('onDecrementHours');
15818 this.time = this.time.add(Date.HOUR, -1);
15822 onIncrementMinutes: function()
15824 Roo.log('onIncrementMinutes');
15825 this.time = this.time.add(Date.MINUTE, 1);
15829 onDecrementMinutes: function()
15831 Roo.log('onDecrementMinutes');
15832 this.time = this.time.add(Date.MINUTE, -1);
15836 onTogglePeriod: function()
15838 Roo.log('onTogglePeriod');
15839 this.time = this.time.add(Date.HOUR, 12);
15846 Roo.apply(Roo.bootstrap.TimeField, {
15876 cls: 'btn btn-info ok',
15888 Roo.apply(Roo.bootstrap.TimeField, {
15892 cls: 'datepicker dropdown-menu',
15896 cls: 'datepicker-time',
15900 cls: 'table-condensed',
15902 Roo.bootstrap.TimeField.content,
15903 Roo.bootstrap.TimeField.footer
15922 * @class Roo.bootstrap.CheckBox
15923 * @extends Roo.bootstrap.Input
15924 * Bootstrap CheckBox class
15926 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15927 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15928 * @cfg {String} boxLabel The text that appears beside the checkbox
15929 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15930 * @cfg {Boolean} checked initnal the element
15934 * Create a new CheckBox
15935 * @param {Object} config The config object
15938 Roo.bootstrap.CheckBox = function(config){
15939 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15944 * Fires when the element is checked or unchecked.
15945 * @param {Roo.bootstrap.CheckBox} this This input
15946 * @param {Boolean} checked The new checked value
15952 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
15954 inputType: 'checkbox',
15961 getAutoCreate : function()
15963 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15969 cfg.cls = 'form-group checkbox' //input-group
15977 type : this.inputType,
15978 value : (!this.checked) ? this.valueOff : this.inputValue,
15979 cls : 'roo-checkbox', //'form-box',
15980 placeholder : this.placeholder || ''
15984 if (this.weight) { // Validity check?
15985 cfg.cls += " checkbox-" + this.weight;
15988 if (this.disabled) {
15989 input.disabled=true;
15993 input.checked = this.checked;
15997 input.name = this.name;
16001 input.cls += ' input-' + this.size;
16005 ['xs','sm','md','lg'].map(function(size){
16006 if (settings[size]) {
16007 cfg.cls += ' col-' + size + '-' + settings[size];
16013 var inputblock = input;
16018 if (this.before || this.after) {
16021 cls : 'input-group',
16025 inputblock.cn.push({
16027 cls : 'input-group-addon',
16031 inputblock.cn.push(input);
16033 inputblock.cn.push({
16035 cls : 'input-group-addon',
16042 if (align ==='left' && this.fieldLabel.length) {
16043 Roo.log("left and has label");
16049 cls : 'control-label col-md-' + this.labelWidth,
16050 html : this.fieldLabel
16054 cls : "col-md-" + (12 - this.labelWidth),
16061 } else if ( this.fieldLabel.length) {
16066 tag: this.boxLabel ? 'span' : 'label',
16068 cls: 'control-label box-input-label',
16069 //cls : 'input-group-addon',
16070 html : this.fieldLabel
16080 Roo.log(" no label && no align");
16081 cfg.cn = [ inputblock ] ;
16090 html: this.boxLabel
16102 * return the real input element.
16104 inputEl: function ()
16106 return this.el.select('input.roo-checkbox',true).first();
16111 return this.el.select('label.control-label',true).first();
16114 initEvents : function()
16116 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16118 this.inputEl().on('click', this.onClick, this);
16122 onClick : function()
16124 this.setChecked(!this.checked);
16127 setChecked : function(state,suppressEvent)
16129 this.checked = state;
16131 this.inputEl().dom.checked = state;
16133 if(suppressEvent !== true){
16134 this.fireEvent('check', this, state);
16137 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16141 setValue : function(v,suppressEvent)
16143 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16157 * @class Roo.bootstrap.Radio
16158 * @extends Roo.bootstrap.CheckBox
16159 * Bootstrap Radio class
16162 * Create a new Radio
16163 * @param {Object} config The config object
16166 Roo.bootstrap.Radio = function(config){
16167 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16171 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
16173 inputType: 'radio',
16177 getAutoCreate : function()
16179 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16185 cfg.cls = 'form-group radio' //input-group
16190 type : this.inputType,
16191 value : (!this.checked) ? this.valueOff : this.inputValue,
16193 placeholder : this.placeholder || ''
16196 if (this.weight) { // Validity check?
16197 cfg.cls += " radio-" + this.weight;
16199 if (this.disabled) {
16200 input.disabled=true;
16204 input.checked = this.checked;
16208 input.name = this.name;
16212 input.cls += ' input-' + this.size;
16216 ['xs','sm','md','lg'].map(function(size){
16217 if (settings[size]) {
16218 cfg.cls += ' col-' + size + '-' + settings[size];
16222 var inputblock = input;
16224 if (this.before || this.after) {
16227 cls : 'input-group',
16231 inputblock.cn.push({
16233 cls : 'input-group-addon',
16237 inputblock.cn.push(input);
16239 inputblock.cn.push({
16241 cls : 'input-group-addon',
16248 if (align ==='left' && this.fieldLabel.length) {
16249 Roo.log("left and has label");
16255 cls : 'control-label col-md-' + this.labelWidth,
16256 html : this.fieldLabel
16260 cls : "col-md-" + (12 - this.labelWidth),
16267 } else if ( this.fieldLabel.length) {
16274 cls: 'control-label box-input-label',
16275 //cls : 'input-group-addon',
16276 html : this.fieldLabel
16286 Roo.log(" no label && no align");
16301 html: this.boxLabel
16308 inputEl: function ()
16310 return this.el.select('input.roo-radio',true).first();
16312 onClick : function()
16314 this.setChecked(true);
16317 setChecked : function(state,suppressEvent)
16320 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16321 v.dom.checked = false;
16325 this.checked = state;
16326 this.inputEl().dom.checked = state;
16328 if(suppressEvent !== true){
16329 this.fireEvent('check', this, state);
16332 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16336 getGroupValue : function()
16339 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16340 if(v.dom.checked == true){
16341 value = v.dom.value;
16349 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
16350 * @return {Mixed} value The field value
16352 getValue : function(){
16353 return this.getGroupValue();
16359 //<script type="text/javascript">
16362 * Based Ext JS Library 1.1.1
16363 * Copyright(c) 2006-2007, Ext JS, LLC.
16369 * @class Roo.HtmlEditorCore
16370 * @extends Roo.Component
16371 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16373 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16376 Roo.HtmlEditorCore = function(config){
16379 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16384 * @event initialize
16385 * Fires when the editor is fully initialized (including the iframe)
16386 * @param {Roo.HtmlEditorCore} this
16391 * Fires when the editor is first receives the focus. Any insertion must wait
16392 * until after this event.
16393 * @param {Roo.HtmlEditorCore} this
16397 * @event beforesync
16398 * Fires before the textarea is updated with content from the editor iframe. Return false
16399 * to cancel the sync.
16400 * @param {Roo.HtmlEditorCore} this
16401 * @param {String} html
16405 * @event beforepush
16406 * Fires before the iframe editor is updated with content from the textarea. Return false
16407 * to cancel the push.
16408 * @param {Roo.HtmlEditorCore} this
16409 * @param {String} html
16414 * Fires when the textarea is updated with content from the editor iframe.
16415 * @param {Roo.HtmlEditorCore} this
16416 * @param {String} html
16421 * Fires when the iframe editor is updated with content from the textarea.
16422 * @param {Roo.HtmlEditorCore} this
16423 * @param {String} html
16428 * @event editorevent
16429 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16430 * @param {Roo.HtmlEditorCore} this
16435 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16437 // defaults : white / black...
16438 this.applyBlacklists();
16445 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
16449 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
16455 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
16460 * @cfg {Number} height (in pixels)
16464 * @cfg {Number} width (in pixels)
16469 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16472 stylesheets: false,
16477 // private properties
16478 validationEvent : false,
16480 initialized : false,
16482 sourceEditMode : false,
16483 onFocus : Roo.emptyFn,
16485 hideMode:'offsets',
16489 // blacklist + whitelisted elements..
16496 * Protected method that will not generally be called directly. It
16497 * is called when the editor initializes the iframe with HTML contents. Override this method if you
16498 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16500 getDocMarkup : function(){
16503 Roo.log(this.stylesheets);
16505 // inherit styels from page...??
16506 if (this.stylesheets === false) {
16508 Roo.get(document.head).select('style').each(function(node) {
16509 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16512 Roo.get(document.head).select('link').each(function(node) {
16513 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16516 } else if (!this.stylesheets.length) {
16518 st = '<style type="text/css">' +
16519 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16522 Roo.each(this.stylesheets, function(s) {
16523 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16528 st += '<style type="text/css">' +
16529 'IMG { cursor: pointer } ' +
16533 return '<html><head>' + st +
16534 //<style type="text/css">' +
16535 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16537 ' </head><body class="roo-htmleditor-body"></body></html>';
16541 onRender : function(ct, position)
16544 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16545 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16548 this.el.dom.style.border = '0 none';
16549 this.el.dom.setAttribute('tabIndex', -1);
16550 this.el.addClass('x-hidden hide');
16554 if(Roo.isIE){ // fix IE 1px bogus margin
16555 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16559 this.frameId = Roo.id();
16563 var iframe = this.owner.wrap.createChild({
16565 cls: 'form-control', // bootstrap..
16567 name: this.frameId,
16568 frameBorder : 'no',
16569 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
16574 this.iframe = iframe.dom;
16576 this.assignDocWin();
16578 this.doc.designMode = 'on';
16581 this.doc.write(this.getDocMarkup());
16585 var task = { // must defer to wait for browser to be ready
16587 //console.log("run task?" + this.doc.readyState);
16588 this.assignDocWin();
16589 if(this.doc.body || this.doc.readyState == 'complete'){
16591 this.doc.designMode="on";
16595 Roo.TaskMgr.stop(task);
16596 this.initEditor.defer(10, this);
16603 Roo.TaskMgr.start(task);
16610 onResize : function(w, h)
16612 Roo.log('resize: ' +w + ',' + h );
16613 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16617 if(typeof w == 'number'){
16619 this.iframe.style.width = w + 'px';
16621 if(typeof h == 'number'){
16623 this.iframe.style.height = h + 'px';
16625 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16632 * Toggles the editor between standard and source edit mode.
16633 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16635 toggleSourceEdit : function(sourceEditMode){
16637 this.sourceEditMode = sourceEditMode === true;
16639 if(this.sourceEditMode){
16641 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
16644 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16645 //this.iframe.className = '';
16648 //this.setSize(this.owner.wrap.getSize());
16649 //this.fireEvent('editmodechange', this, this.sourceEditMode);
16656 * Protected method that will not generally be called directly. If you need/want
16657 * custom HTML cleanup, this is the method you should override.
16658 * @param {String} html The HTML to be cleaned
16659 * return {String} The cleaned HTML
16661 cleanHtml : function(html){
16662 html = String(html);
16663 if(html.length > 5){
16664 if(Roo.isSafari){ // strip safari nonsense
16665 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16668 if(html == ' '){
16675 * HTML Editor -> Textarea
16676 * Protected method that will not generally be called directly. Syncs the contents
16677 * of the editor iframe with the textarea.
16679 syncValue : function(){
16680 if(this.initialized){
16681 var bd = (this.doc.body || this.doc.documentElement);
16682 //this.cleanUpPaste(); -- this is done else where and causes havoc..
16683 var html = bd.innerHTML;
16685 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16686 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16688 html = '<div style="'+m[0]+'">' + html + '</div>';
16691 html = this.cleanHtml(html);
16692 // fix up the special chars.. normaly like back quotes in word...
16693 // however we do not want to do this with chinese..
16694 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16695 var cc = b.charCodeAt();
16697 (cc >= 0x4E00 && cc < 0xA000 ) ||
16698 (cc >= 0x3400 && cc < 0x4E00 ) ||
16699 (cc >= 0xf900 && cc < 0xfb00 )
16705 if(this.owner.fireEvent('beforesync', this, html) !== false){
16706 this.el.dom.value = html;
16707 this.owner.fireEvent('sync', this, html);
16713 * Protected method that will not generally be called directly. Pushes the value of the textarea
16714 * into the iframe editor.
16716 pushValue : function(){
16717 if(this.initialized){
16718 var v = this.el.dom.value.trim();
16720 // if(v.length < 1){
16724 if(this.owner.fireEvent('beforepush', this, v) !== false){
16725 var d = (this.doc.body || this.doc.documentElement);
16727 this.cleanUpPaste();
16728 this.el.dom.value = d.innerHTML;
16729 this.owner.fireEvent('push', this, v);
16735 deferFocus : function(){
16736 this.focus.defer(10, this);
16740 focus : function(){
16741 if(this.win && !this.sourceEditMode){
16748 assignDocWin: function()
16750 var iframe = this.iframe;
16753 this.doc = iframe.contentWindow.document;
16754 this.win = iframe.contentWindow;
16756 // if (!Roo.get(this.frameId)) {
16759 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16760 // this.win = Roo.get(this.frameId).dom.contentWindow;
16762 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16766 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16767 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16772 initEditor : function(){
16773 //console.log("INIT EDITOR");
16774 this.assignDocWin();
16778 this.doc.designMode="on";
16780 this.doc.write(this.getDocMarkup());
16783 var dbody = (this.doc.body || this.doc.documentElement);
16784 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16785 // this copies styles from the containing element into thsi one..
16786 // not sure why we need all of this..
16787 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16789 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16790 //ss['background-attachment'] = 'fixed'; // w3c
16791 dbody.bgProperties = 'fixed'; // ie
16792 //Roo.DomHelper.applyStyles(dbody, ss);
16793 Roo.EventManager.on(this.doc, {
16794 //'mousedown': this.onEditorEvent,
16795 'mouseup': this.onEditorEvent,
16796 'dblclick': this.onEditorEvent,
16797 'click': this.onEditorEvent,
16798 'keyup': this.onEditorEvent,
16803 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16805 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16806 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16808 this.initialized = true;
16810 this.owner.fireEvent('initialize', this);
16815 onDestroy : function(){
16821 //for (var i =0; i < this.toolbars.length;i++) {
16822 // // fixme - ask toolbars for heights?
16823 // this.toolbars[i].onDestroy();
16826 //this.wrap.dom.innerHTML = '';
16827 //this.wrap.remove();
16832 onFirstFocus : function(){
16834 this.assignDocWin();
16837 this.activated = true;
16840 if(Roo.isGecko){ // prevent silly gecko errors
16842 var s = this.win.getSelection();
16843 if(!s.focusNode || s.focusNode.nodeType != 3){
16844 var r = s.getRangeAt(0);
16845 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16850 this.execCmd('useCSS', true);
16851 this.execCmd('styleWithCSS', false);
16854 this.owner.fireEvent('activate', this);
16858 adjustFont: function(btn){
16859 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16860 //if(Roo.isSafari){ // safari
16863 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16864 if(Roo.isSafari){ // safari
16865 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16866 v = (v < 10) ? 10 : v;
16867 v = (v > 48) ? 48 : v;
16868 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16873 v = Math.max(1, v+adjust);
16875 this.execCmd('FontSize', v );
16878 onEditorEvent : function(e){
16879 this.owner.fireEvent('editorevent', this, e);
16880 // this.updateToolbar();
16881 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16884 insertTag : function(tg)
16886 // could be a bit smarter... -> wrap the current selected tRoo..
16887 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16889 range = this.createRange(this.getSelection());
16890 var wrappingNode = this.doc.createElement(tg.toLowerCase());
16891 wrappingNode.appendChild(range.extractContents());
16892 range.insertNode(wrappingNode);
16899 this.execCmd("formatblock", tg);
16903 insertText : function(txt)
16907 var range = this.createRange();
16908 range.deleteContents();
16909 //alert(Sender.getAttribute('label'));
16911 range.insertNode(this.doc.createTextNode(txt));
16917 * Executes a Midas editor command on the editor document and performs necessary focus and
16918 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16919 * @param {String} cmd The Midas command
16920 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16922 relayCmd : function(cmd, value){
16924 this.execCmd(cmd, value);
16925 this.owner.fireEvent('editorevent', this);
16926 //this.updateToolbar();
16927 this.owner.deferFocus();
16931 * Executes a Midas editor command directly on the editor document.
16932 * For visual commands, you should use {@link #relayCmd} instead.
16933 * <b>This should only be called after the editor is initialized.</b>
16934 * @param {String} cmd The Midas command
16935 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16937 execCmd : function(cmd, value){
16938 this.doc.execCommand(cmd, false, value === undefined ? null : value);
16945 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16947 * @param {String} text | dom node..
16949 insertAtCursor : function(text)
16954 if(!this.activated){
16960 var r = this.doc.selection.createRange();
16971 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16975 // from jquery ui (MIT licenced)
16977 var win = this.win;
16979 if (win.getSelection && win.getSelection().getRangeAt) {
16980 range = win.getSelection().getRangeAt(0);
16981 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16982 range.insertNode(node);
16983 } else if (win.document.selection && win.document.selection.createRange) {
16984 // no firefox support
16985 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16986 win.document.selection.createRange().pasteHTML(txt);
16988 // no firefox support
16989 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16990 this.execCmd('InsertHTML', txt);
16999 mozKeyPress : function(e){
17001 var c = e.getCharCode(), cmd;
17004 c = String.fromCharCode(c).toLowerCase();
17018 this.cleanUpPaste.defer(100, this);
17026 e.preventDefault();
17034 fixKeys : function(){ // load time branching for fastest keydown performance
17036 return function(e){
17037 var k = e.getKey(), r;
17040 r = this.doc.selection.createRange();
17043 r.pasteHTML('    ');
17050 r = this.doc.selection.createRange();
17052 var target = r.parentElement();
17053 if(!target || target.tagName.toLowerCase() != 'li'){
17055 r.pasteHTML('<br />');
17061 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17062 this.cleanUpPaste.defer(100, this);
17068 }else if(Roo.isOpera){
17069 return function(e){
17070 var k = e.getKey();
17074 this.execCmd('InsertHTML','    ');
17077 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17078 this.cleanUpPaste.defer(100, this);
17083 }else if(Roo.isSafari){
17084 return function(e){
17085 var k = e.getKey();
17089 this.execCmd('InsertText','\t');
17093 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17094 this.cleanUpPaste.defer(100, this);
17102 getAllAncestors: function()
17104 var p = this.getSelectedNode();
17107 a.push(p); // push blank onto stack..
17108 p = this.getParentElement();
17112 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17116 a.push(this.doc.body);
17120 lastSelNode : false,
17123 getSelection : function()
17125 this.assignDocWin();
17126 return Roo.isIE ? this.doc.selection : this.win.getSelection();
17129 getSelectedNode: function()
17131 // this may only work on Gecko!!!
17133 // should we cache this!!!!
17138 var range = this.createRange(this.getSelection()).cloneRange();
17141 var parent = range.parentElement();
17143 var testRange = range.duplicate();
17144 testRange.moveToElementText(parent);
17145 if (testRange.inRange(range)) {
17148 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17151 parent = parent.parentElement;
17156 // is ancestor a text element.
17157 var ac = range.commonAncestorContainer;
17158 if (ac.nodeType == 3) {
17159 ac = ac.parentNode;
17162 var ar = ac.childNodes;
17165 var other_nodes = [];
17166 var has_other_nodes = false;
17167 for (var i=0;i<ar.length;i++) {
17168 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
17171 // fullly contained node.
17173 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17178 // probably selected..
17179 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17180 other_nodes.push(ar[i]);
17184 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
17189 has_other_nodes = true;
17191 if (!nodes.length && other_nodes.length) {
17192 nodes= other_nodes;
17194 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17200 createRange: function(sel)
17202 // this has strange effects when using with
17203 // top toolbar - not sure if it's a great idea.
17204 //this.editor.contentWindow.focus();
17205 if (typeof sel != "undefined") {
17207 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17209 return this.doc.createRange();
17212 return this.doc.createRange();
17215 getParentElement: function()
17218 this.assignDocWin();
17219 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17221 var range = this.createRange(sel);
17224 var p = range.commonAncestorContainer;
17225 while (p.nodeType == 3) { // text node
17236 * Range intersection.. the hard stuff...
17240 * [ -- selected range --- ]
17244 * if end is before start or hits it. fail.
17245 * if start is after end or hits it fail.
17247 * if either hits (but other is outside. - then it's not
17253 // @see http://www.thismuchiknow.co.uk/?p=64.
17254 rangeIntersectsNode : function(range, node)
17256 var nodeRange = node.ownerDocument.createRange();
17258 nodeRange.selectNode(node);
17260 nodeRange.selectNodeContents(node);
17263 var rangeStartRange = range.cloneRange();
17264 rangeStartRange.collapse(true);
17266 var rangeEndRange = range.cloneRange();
17267 rangeEndRange.collapse(false);
17269 var nodeStartRange = nodeRange.cloneRange();
17270 nodeStartRange.collapse(true);
17272 var nodeEndRange = nodeRange.cloneRange();
17273 nodeEndRange.collapse(false);
17275 return rangeStartRange.compareBoundaryPoints(
17276 Range.START_TO_START, nodeEndRange) == -1 &&
17277 rangeEndRange.compareBoundaryPoints(
17278 Range.START_TO_START, nodeStartRange) == 1;
17282 rangeCompareNode : function(range, node)
17284 var nodeRange = node.ownerDocument.createRange();
17286 nodeRange.selectNode(node);
17288 nodeRange.selectNodeContents(node);
17292 range.collapse(true);
17294 nodeRange.collapse(true);
17296 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17297 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
17299 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17301 var nodeIsBefore = ss == 1;
17302 var nodeIsAfter = ee == -1;
17304 if (nodeIsBefore && nodeIsAfter)
17306 if (!nodeIsBefore && nodeIsAfter)
17307 return 1; //right trailed.
17309 if (nodeIsBefore && !nodeIsAfter)
17310 return 2; // left trailed.
17315 // private? - in a new class?
17316 cleanUpPaste : function()
17318 // cleans up the whole document..
17319 Roo.log('cleanuppaste');
17321 this.cleanUpChildren(this.doc.body);
17322 var clean = this.cleanWordChars(this.doc.body.innerHTML);
17323 if (clean != this.doc.body.innerHTML) {
17324 this.doc.body.innerHTML = clean;
17329 cleanWordChars : function(input) {// change the chars to hex code
17330 var he = Roo.HtmlEditorCore;
17332 var output = input;
17333 Roo.each(he.swapCodes, function(sw) {
17334 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17336 output = output.replace(swapper, sw[1]);
17343 cleanUpChildren : function (n)
17345 if (!n.childNodes.length) {
17348 for (var i = n.childNodes.length-1; i > -1 ; i--) {
17349 this.cleanUpChild(n.childNodes[i]);
17356 cleanUpChild : function (node)
17359 //console.log(node);
17360 if (node.nodeName == "#text") {
17361 // clean up silly Windows -- stuff?
17364 if (node.nodeName == "#comment") {
17365 node.parentNode.removeChild(node);
17366 // clean up silly Windows -- stuff?
17369 var lcname = node.tagName.toLowerCase();
17370 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17371 // whitelist of tags..
17373 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17375 node.parentNode.removeChild(node);
17380 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17382 // remove <a name=....> as rendering on yahoo mailer is borked with this.
17383 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17385 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17386 // remove_keep_children = true;
17389 if (remove_keep_children) {
17390 this.cleanUpChildren(node);
17391 // inserts everything just before this node...
17392 while (node.childNodes.length) {
17393 var cn = node.childNodes[0];
17394 node.removeChild(cn);
17395 node.parentNode.insertBefore(cn, node);
17397 node.parentNode.removeChild(node);
17401 if (!node.attributes || !node.attributes.length) {
17402 this.cleanUpChildren(node);
17406 function cleanAttr(n,v)
17409 if (v.match(/^\./) || v.match(/^\//)) {
17412 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17415 if (v.match(/^#/)) {
17418 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17419 node.removeAttribute(n);
17423 var cwhite = this.cwhite;
17424 var cblack = this.cblack;
17426 function cleanStyle(n,v)
17428 if (v.match(/expression/)) { //XSS?? should we even bother..
17429 node.removeAttribute(n);
17433 var parts = v.split(/;/);
17436 Roo.each(parts, function(p) {
17437 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17441 var l = p.split(':').shift().replace(/\s+/g,'');
17442 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17444 if ( cwhite.length && cblack.indexOf(l) > -1) {
17445 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17446 //node.removeAttribute(n);
17450 // only allow 'c whitelisted system attributes'
17451 if ( cwhite.length && cwhite.indexOf(l) < 0) {
17452 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17453 //node.removeAttribute(n);
17463 if (clean.length) {
17464 node.setAttribute(n, clean.join(';'));
17466 node.removeAttribute(n);
17472 for (var i = node.attributes.length-1; i > -1 ; i--) {
17473 var a = node.attributes[i];
17476 if (a.name.toLowerCase().substr(0,2)=='on') {
17477 node.removeAttribute(a.name);
17480 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17481 node.removeAttribute(a.name);
17484 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17485 cleanAttr(a.name,a.value); // fixme..
17488 if (a.name == 'style') {
17489 cleanStyle(a.name,a.value);
17492 /// clean up MS crap..
17493 // tecnically this should be a list of valid class'es..
17496 if (a.name == 'class') {
17497 if (a.value.match(/^Mso/)) {
17498 node.className = '';
17501 if (a.value.match(/body/)) {
17502 node.className = '';
17513 this.cleanUpChildren(node);
17518 * Clean up MS wordisms...
17520 cleanWord : function(node)
17523 var cleanWordChildren = function()
17525 if (!node.childNodes.length) {
17528 for (var i = node.childNodes.length-1; i > -1 ; i--) {
17529 _t.cleanWord(node.childNodes[i]);
17535 this.cleanWord(this.doc.body);
17538 if (node.nodeName == "#text") {
17539 // clean up silly Windows -- stuff?
17542 if (node.nodeName == "#comment") {
17543 node.parentNode.removeChild(node);
17544 // clean up silly Windows -- stuff?
17548 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17549 node.parentNode.removeChild(node);
17553 // remove - but keep children..
17554 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17555 while (node.childNodes.length) {
17556 var cn = node.childNodes[0];
17557 node.removeChild(cn);
17558 node.parentNode.insertBefore(cn, node);
17560 node.parentNode.removeChild(node);
17561 cleanWordChildren();
17565 if (node.className.length) {
17567 var cn = node.className.split(/\W+/);
17569 Roo.each(cn, function(cls) {
17570 if (cls.match(/Mso[a-zA-Z]+/)) {
17575 node.className = cna.length ? cna.join(' ') : '';
17577 node.removeAttribute("class");
17581 if (node.hasAttribute("lang")) {
17582 node.removeAttribute("lang");
17585 if (node.hasAttribute("style")) {
17587 var styles = node.getAttribute("style").split(";");
17589 Roo.each(styles, function(s) {
17590 if (!s.match(/:/)) {
17593 var kv = s.split(":");
17594 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17597 // what ever is left... we allow.
17600 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17601 if (!nstyle.length) {
17602 node.removeAttribute('style');
17606 cleanWordChildren();
17610 domToHTML : function(currentElement, depth, nopadtext) {
17612 depth = depth || 0;
17613 nopadtext = nopadtext || false;
17615 if (!currentElement) {
17616 return this.domToHTML(this.doc.body);
17619 //Roo.log(currentElement);
17621 var allText = false;
17622 var nodeName = currentElement.nodeName;
17623 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17625 if (nodeName == '#text') {
17626 return currentElement.nodeValue;
17631 if (nodeName != 'BODY') {
17634 // Prints the node tagName, such as <A>, <IMG>, etc
17637 for(i = 0; i < currentElement.attributes.length;i++) {
17639 var aname = currentElement.attributes.item(i).name;
17640 if (!currentElement.attributes.item(i).value.length) {
17643 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17646 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17655 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17658 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17663 // Traverse the tree
17665 var currentElementChild = currentElement.childNodes.item(i);
17666 var allText = true;
17667 var innerHTML = '';
17669 while (currentElementChild) {
17670 // Formatting code (indent the tree so it looks nice on the screen)
17671 var nopad = nopadtext;
17672 if (lastnode == 'SPAN') {
17676 if (currentElementChild.nodeName == '#text') {
17677 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17678 if (!nopad && toadd.length > 80) {
17679 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
17681 innerHTML += toadd;
17684 currentElementChild = currentElement.childNodes.item(i);
17690 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
17692 // Recursively traverse the tree structure of the child node
17693 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
17694 lastnode = currentElementChild.nodeName;
17696 currentElementChild=currentElement.childNodes.item(i);
17702 // The remaining code is mostly for formatting the tree
17703 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
17708 ret+= "</"+tagName+">";
17714 applyBlacklists : function()
17716 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
17717 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
17721 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17722 if (b.indexOf(tag) > -1) {
17725 this.white.push(tag);
17729 Roo.each(w, function(tag) {
17730 if (b.indexOf(tag) > -1) {
17733 if (this.white.indexOf(tag) > -1) {
17736 this.white.push(tag);
17741 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17742 if (w.indexOf(tag) > -1) {
17745 this.black.push(tag);
17749 Roo.each(b, function(tag) {
17750 if (w.indexOf(tag) > -1) {
17753 if (this.black.indexOf(tag) > -1) {
17756 this.black.push(tag);
17761 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
17762 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
17766 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17767 if (b.indexOf(tag) > -1) {
17770 this.cwhite.push(tag);
17774 Roo.each(w, function(tag) {
17775 if (b.indexOf(tag) > -1) {
17778 if (this.cwhite.indexOf(tag) > -1) {
17781 this.cwhite.push(tag);
17786 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17787 if (w.indexOf(tag) > -1) {
17790 this.cblack.push(tag);
17794 Roo.each(b, function(tag) {
17795 if (w.indexOf(tag) > -1) {
17798 if (this.cblack.indexOf(tag) > -1) {
17801 this.cblack.push(tag);
17806 // hide stuff that is not compatible
17820 * @event specialkey
17824 * @cfg {String} fieldClass @hide
17827 * @cfg {String} focusClass @hide
17830 * @cfg {String} autoCreate @hide
17833 * @cfg {String} inputType @hide
17836 * @cfg {String} invalidClass @hide
17839 * @cfg {String} invalidText @hide
17842 * @cfg {String} msgFx @hide
17845 * @cfg {String} validateOnBlur @hide
17849 Roo.HtmlEditorCore.white = [
17850 'area', 'br', 'img', 'input', 'hr', 'wbr',
17852 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
17853 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
17854 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
17855 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
17856 'table', 'ul', 'xmp',
17858 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
17861 'dir', 'menu', 'ol', 'ul', 'dl',
17867 Roo.HtmlEditorCore.black = [
17868 // 'embed', 'object', // enable - backend responsiblity to clean thiese
17870 'base', 'basefont', 'bgsound', 'blink', 'body',
17871 'frame', 'frameset', 'head', 'html', 'ilayer',
17872 'iframe', 'layer', 'link', 'meta', 'object',
17873 'script', 'style' ,'title', 'xml' // clean later..
17875 Roo.HtmlEditorCore.clean = [
17876 'script', 'style', 'title', 'xml'
17878 Roo.HtmlEditorCore.remove = [
17883 Roo.HtmlEditorCore.ablack = [
17887 Roo.HtmlEditorCore.aclean = [
17888 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
17892 Roo.HtmlEditorCore.pwhite= [
17893 'http', 'https', 'mailto'
17896 // white listed style attributes.
17897 Roo.HtmlEditorCore.cwhite= [
17898 // 'text-align', /// default is to allow most things..
17904 // black listed style attributes.
17905 Roo.HtmlEditorCore.cblack= [
17906 // 'font-size' -- this can be set by the project
17910 Roo.HtmlEditorCore.swapCodes =[
17929 * @class Roo.bootstrap.HtmlEditor
17930 * @extends Roo.bootstrap.TextArea
17931 * Bootstrap HtmlEditor class
17934 * Create a new HtmlEditor
17935 * @param {Object} config The config object
17938 Roo.bootstrap.HtmlEditor = function(config){
17939 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17940 if (!this.toolbars) {
17941 this.toolbars = [];
17943 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17946 * @event initialize
17947 * Fires when the editor is fully initialized (including the iframe)
17948 * @param {HtmlEditor} this
17953 * Fires when the editor is first receives the focus. Any insertion must wait
17954 * until after this event.
17955 * @param {HtmlEditor} this
17959 * @event beforesync
17960 * Fires before the textarea is updated with content from the editor iframe. Return false
17961 * to cancel the sync.
17962 * @param {HtmlEditor} this
17963 * @param {String} html
17967 * @event beforepush
17968 * Fires before the iframe editor is updated with content from the textarea. Return false
17969 * to cancel the push.
17970 * @param {HtmlEditor} this
17971 * @param {String} html
17976 * Fires when the textarea is updated with content from the editor iframe.
17977 * @param {HtmlEditor} this
17978 * @param {String} html
17983 * Fires when the iframe editor is updated with content from the textarea.
17984 * @param {HtmlEditor} this
17985 * @param {String} html
17989 * @event editmodechange
17990 * Fires when the editor switches edit modes
17991 * @param {HtmlEditor} this
17992 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17994 editmodechange: true,
17996 * @event editorevent
17997 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17998 * @param {HtmlEditor} this
18002 * @event firstfocus
18003 * Fires when on first focus - needed by toolbars..
18004 * @param {HtmlEditor} this
18009 * Auto save the htmlEditor value as a file into Events
18010 * @param {HtmlEditor} this
18014 * @event savedpreview
18015 * preview the saved version of htmlEditor
18016 * @param {HtmlEditor} this
18023 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
18027 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
18032 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
18037 * @cfg {Number} height (in pixels)
18041 * @cfg {Number} width (in pixels)
18046 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18049 stylesheets: false,
18054 // private properties
18055 validationEvent : false,
18057 initialized : false,
18060 onFocus : Roo.emptyFn,
18062 hideMode:'offsets',
18065 tbContainer : false,
18067 toolbarContainer :function() {
18068 return this.wrap.select('.x-html-editor-tb',true).first();
18072 * Protected method that will not generally be called directly. It
18073 * is called when the editor creates its toolbar. Override this method if you need to
18074 * add custom toolbar buttons.
18075 * @param {HtmlEditor} editor
18077 createToolbar : function(){
18079 Roo.log("create toolbars");
18081 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18082 this.toolbars[0].render(this.toolbarContainer());
18086 // if (!editor.toolbars || !editor.toolbars.length) {
18087 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18090 // for (var i =0 ; i < editor.toolbars.length;i++) {
18091 // editor.toolbars[i] = Roo.factory(
18092 // typeof(editor.toolbars[i]) == 'string' ?
18093 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
18094 // Roo.bootstrap.HtmlEditor);
18095 // editor.toolbars[i].init(editor);
18101 onRender : function(ct, position)
18103 // Roo.log("Call onRender: " + this.xtype);
18105 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18107 this.wrap = this.inputEl().wrap({
18108 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18111 this.editorcore.onRender(ct, position);
18113 if (this.resizable) {
18114 this.resizeEl = new Roo.Resizable(this.wrap, {
18118 minHeight : this.height,
18119 height: this.height,
18120 handles : this.resizable,
18123 resize : function(r, w, h) {
18124 _t.onResize(w,h); // -something
18130 this.createToolbar(this);
18133 if(!this.width && this.resizable){
18134 this.setSize(this.wrap.getSize());
18136 if (this.resizeEl) {
18137 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18138 // should trigger onReize..
18144 onResize : function(w, h)
18146 Roo.log('resize: ' +w + ',' + h );
18147 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18151 if(this.inputEl() ){
18152 if(typeof w == 'number'){
18153 var aw = w - this.wrap.getFrameWidth('lr');
18154 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18157 if(typeof h == 'number'){
18158 var tbh = -11; // fixme it needs to tool bar size!
18159 for (var i =0; i < this.toolbars.length;i++) {
18160 // fixme - ask toolbars for heights?
18161 tbh += this.toolbars[i].el.getHeight();
18162 //if (this.toolbars[i].footer) {
18163 // tbh += this.toolbars[i].footer.el.getHeight();
18171 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18172 ah -= 5; // knock a few pixes off for look..
18173 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18177 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18178 this.editorcore.onResize(ew,eh);
18183 * Toggles the editor between standard and source edit mode.
18184 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18186 toggleSourceEdit : function(sourceEditMode)
18188 this.editorcore.toggleSourceEdit(sourceEditMode);
18190 if(this.editorcore.sourceEditMode){
18191 Roo.log('editor - showing textarea');
18194 // Roo.log(this.syncValue());
18196 this.inputEl().removeClass(['hide', 'x-hidden']);
18197 this.inputEl().dom.removeAttribute('tabIndex');
18198 this.inputEl().focus();
18200 Roo.log('editor - hiding textarea');
18202 // Roo.log(this.pushValue());
18205 this.inputEl().addClass(['hide', 'x-hidden']);
18206 this.inputEl().dom.setAttribute('tabIndex', -1);
18207 //this.deferFocus();
18210 if(this.resizable){
18211 this.setSize(this.wrap.getSize());
18214 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18217 // private (for BoxComponent)
18218 adjustSize : Roo.BoxComponent.prototype.adjustSize,
18220 // private (for BoxComponent)
18221 getResizeEl : function(){
18225 // private (for BoxComponent)
18226 getPositionEl : function(){
18231 initEvents : function(){
18232 this.originalValue = this.getValue();
18236 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18239 // markInvalid : Roo.emptyFn,
18241 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18244 // clearInvalid : Roo.emptyFn,
18246 setValue : function(v){
18247 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18248 this.editorcore.pushValue();
18253 deferFocus : function(){
18254 this.focus.defer(10, this);
18258 focus : function(){
18259 this.editorcore.focus();
18265 onDestroy : function(){
18271 for (var i =0; i < this.toolbars.length;i++) {
18272 // fixme - ask toolbars for heights?
18273 this.toolbars[i].onDestroy();
18276 this.wrap.dom.innerHTML = '';
18277 this.wrap.remove();
18282 onFirstFocus : function(){
18283 //Roo.log("onFirstFocus");
18284 this.editorcore.onFirstFocus();
18285 for (var i =0; i < this.toolbars.length;i++) {
18286 this.toolbars[i].onFirstFocus();
18292 syncValue : function()
18294 this.editorcore.syncValue();
18297 pushValue : function()
18299 this.editorcore.pushValue();
18303 // hide stuff that is not compatible
18317 * @event specialkey
18321 * @cfg {String} fieldClass @hide
18324 * @cfg {String} focusClass @hide
18327 * @cfg {String} autoCreate @hide
18330 * @cfg {String} inputType @hide
18333 * @cfg {String} invalidClass @hide
18336 * @cfg {String} invalidText @hide
18339 * @cfg {String} msgFx @hide
18342 * @cfg {String} validateOnBlur @hide
18351 Roo.namespace('Roo.bootstrap.htmleditor');
18353 * @class Roo.bootstrap.HtmlEditorToolbar1
18358 new Roo.bootstrap.HtmlEditor({
18361 new Roo.bootstrap.HtmlEditorToolbar1({
18362 disable : { fonts: 1 , format: 1, ..., ... , ...],
18368 * @cfg {Object} disable List of elements to disable..
18369 * @cfg {Array} btns List of additional buttons.
18373 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18376 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18379 Roo.apply(this, config);
18381 // default disabled, based on 'good practice'..
18382 this.disable = this.disable || {};
18383 Roo.applyIf(this.disable, {
18386 specialElements : true
18388 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18390 this.editor = config.editor;
18391 this.editorcore = config.editor.editorcore;
18393 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18395 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18396 // dont call parent... till later.
18398 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
18403 editorcore : false,
18408 "h1","h2","h3","h4","h5","h6",
18410 "abbr", "acronym", "address", "cite", "samp", "var",
18414 onRender : function(ct, position)
18416 // Roo.log("Call onRender: " + this.xtype);
18418 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18420 this.el.dom.style.marginBottom = '0';
18422 var editorcore = this.editorcore;
18423 var editor= this.editor;
18426 var btn = function(id,cmd , toggle, handler){
18428 var event = toggle ? 'toggle' : 'click';
18433 xns: Roo.bootstrap,
18436 enableToggle:toggle !== false,
18438 pressed : toggle ? false : null,
18441 a.listeners[toggle ? 'toggle' : 'click'] = function() {
18442 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
18451 xns: Roo.bootstrap,
18452 glyphicon : 'font',
18456 xns: Roo.bootstrap,
18460 Roo.each(this.formats, function(f) {
18461 style.menu.items.push({
18463 xns: Roo.bootstrap,
18464 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18469 editorcore.insertTag(this.tagname);
18476 children.push(style);
18479 btn('bold',false,true);
18480 btn('italic',false,true);
18481 btn('align-left', 'justifyleft',true);
18482 btn('align-center', 'justifycenter',true);
18483 btn('align-right' , 'justifyright',true);
18484 btn('link', false, false, function(btn) {
18485 //Roo.log("create link?");
18486 var url = prompt(this.createLinkText, this.defaultLinkValue);
18487 if(url && url != 'http:/'+'/'){
18488 this.editorcore.relayCmd('createlink', url);
18491 btn('list','insertunorderedlist',true);
18492 btn('pencil', false,true, function(btn){
18495 this.toggleSourceEdit(btn.pressed);
18501 xns: Roo.bootstrap,
18506 xns: Roo.bootstrap,
18511 cog.menu.items.push({
18513 xns: Roo.bootstrap,
18514 html : Clean styles,
18519 editorcore.insertTag(this.tagname);
18528 this.xtype = 'NavSimplebar';
18530 for(var i=0;i< children.length;i++) {
18532 this.buttons.add(this.addxtypeChild(children[i]));
18536 editor.on('editorevent', this.updateToolbar, this);
18538 onBtnClick : function(id)
18540 this.editorcore.relayCmd(id);
18541 this.editorcore.focus();
18545 * Protected method that will not generally be called directly. It triggers
18546 * a toolbar update by reading the markup state of the current selection in the editor.
18548 updateToolbar: function(){
18550 if(!this.editorcore.activated){
18551 this.editor.onFirstFocus(); // is this neeed?
18555 var btns = this.buttons;
18556 var doc = this.editorcore.doc;
18557 btns.get('bold').setActive(doc.queryCommandState('bold'));
18558 btns.get('italic').setActive(doc.queryCommandState('italic'));
18559 //btns.get('underline').setActive(doc.queryCommandState('underline'));
18561 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18562 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18563 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18565 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18566 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18569 var ans = this.editorcore.getAllAncestors();
18570 if (this.formatCombo) {
18573 var store = this.formatCombo.store;
18574 this.formatCombo.setValue("");
18575 for (var i =0; i < ans.length;i++) {
18576 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18578 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18586 // hides menus... - so this cant be on a menu...
18587 Roo.bootstrap.MenuMgr.hideAll();
18589 Roo.bootstrap.MenuMgr.hideAll();
18590 //this.editorsyncValue();
18592 onFirstFocus: function() {
18593 this.buttons.each(function(item){
18597 toggleSourceEdit : function(sourceEditMode){
18600 if(sourceEditMode){
18601 Roo.log("disabling buttons");
18602 this.buttons.each( function(item){
18603 if(item.cmd != 'pencil'){
18609 Roo.log("enabling buttons");
18610 if(this.editorcore.initialized){
18611 this.buttons.each( function(item){
18617 Roo.log("calling toggole on editor");
18618 // tell the editor that it's been pressed..
18619 this.editor.toggleSourceEdit(sourceEditMode);
18629 * @class Roo.bootstrap.Table.AbstractSelectionModel
18630 * @extends Roo.util.Observable
18631 * Abstract base class for grid SelectionModels. It provides the interface that should be
18632 * implemented by descendant classes. This class should not be directly instantiated.
18635 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18636 this.locked = false;
18637 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18641 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
18642 /** @ignore Called by the grid automatically. Do not call directly. */
18643 init : function(grid){
18649 * Locks the selections.
18652 this.locked = true;
18656 * Unlocks the selections.
18658 unlock : function(){
18659 this.locked = false;
18663 * Returns true if the selections are locked.
18664 * @return {Boolean}
18666 isLocked : function(){
18667 return this.locked;
18671 * @extends Roo.bootstrap.Table.AbstractSelectionModel
18672 * @class Roo.bootstrap.Table.RowSelectionModel
18673 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18674 * It supports multiple selections and keyboard selection/navigation.
18676 * @param {Object} config
18679 Roo.bootstrap.Table.RowSelectionModel = function(config){
18680 Roo.apply(this, config);
18681 this.selections = new Roo.util.MixedCollection(false, function(o){
18686 this.lastActive = false;
18690 * @event selectionchange
18691 * Fires when the selection changes
18692 * @param {SelectionModel} this
18694 "selectionchange" : true,
18696 * @event afterselectionchange
18697 * Fires after the selection changes (eg. by key press or clicking)
18698 * @param {SelectionModel} this
18700 "afterselectionchange" : true,
18702 * @event beforerowselect
18703 * Fires when a row is selected being selected, return false to cancel.
18704 * @param {SelectionModel} this
18705 * @param {Number} rowIndex The selected index
18706 * @param {Boolean} keepExisting False if other selections will be cleared
18708 "beforerowselect" : true,
18711 * Fires when a row is selected.
18712 * @param {SelectionModel} this
18713 * @param {Number} rowIndex The selected index
18714 * @param {Roo.data.Record} r The record
18716 "rowselect" : true,
18718 * @event rowdeselect
18719 * Fires when a row is deselected.
18720 * @param {SelectionModel} this
18721 * @param {Number} rowIndex The selected index
18723 "rowdeselect" : true
18725 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18726 this.locked = false;
18729 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
18731 * @cfg {Boolean} singleSelect
18732 * True to allow selection of only one row at a time (defaults to false)
18734 singleSelect : false,
18737 initEvents : function(){
18739 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18740 this.grid.on("mousedown", this.handleMouseDown, this);
18741 }else{ // allow click to work like normal
18742 this.grid.on("rowclick", this.handleDragableRowClick, this);
18745 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18746 "up" : function(e){
18748 this.selectPrevious(e.shiftKey);
18749 }else if(this.last !== false && this.lastActive !== false){
18750 var last = this.last;
18751 this.selectRange(this.last, this.lastActive-1);
18752 this.grid.getView().focusRow(this.lastActive);
18753 if(last !== false){
18757 this.selectFirstRow();
18759 this.fireEvent("afterselectionchange", this);
18761 "down" : function(e){
18763 this.selectNext(e.shiftKey);
18764 }else if(this.last !== false && this.lastActive !== false){
18765 var last = this.last;
18766 this.selectRange(this.last, this.lastActive+1);
18767 this.grid.getView().focusRow(this.lastActive);
18768 if(last !== false){
18772 this.selectFirstRow();
18774 this.fireEvent("afterselectionchange", this);
18779 var view = this.grid.view;
18780 view.on("refresh", this.onRefresh, this);
18781 view.on("rowupdated", this.onRowUpdated, this);
18782 view.on("rowremoved", this.onRemove, this);
18786 onRefresh : function(){
18787 var ds = this.grid.dataSource, i, v = this.grid.view;
18788 var s = this.selections;
18789 s.each(function(r){
18790 if((i = ds.indexOfId(r.id)) != -1){
18799 onRemove : function(v, index, r){
18800 this.selections.remove(r);
18804 onRowUpdated : function(v, index, r){
18805 if(this.isSelected(r)){
18806 v.onRowSelect(index);
18812 * @param {Array} records The records to select
18813 * @param {Boolean} keepExisting (optional) True to keep existing selections
18815 selectRecords : function(records, keepExisting){
18817 this.clearSelections();
18819 var ds = this.grid.dataSource;
18820 for(var i = 0, len = records.length; i < len; i++){
18821 this.selectRow(ds.indexOf(records[i]), true);
18826 * Gets the number of selected rows.
18829 getCount : function(){
18830 return this.selections.length;
18834 * Selects the first row in the grid.
18836 selectFirstRow : function(){
18841 * Select the last row.
18842 * @param {Boolean} keepExisting (optional) True to keep existing selections
18844 selectLastRow : function(keepExisting){
18845 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18849 * Selects the row immediately following the last selected row.
18850 * @param {Boolean} keepExisting (optional) True to keep existing selections
18852 selectNext : function(keepExisting){
18853 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18854 this.selectRow(this.last+1, keepExisting);
18855 this.grid.getView().focusRow(this.last);
18860 * Selects the row that precedes the last selected row.
18861 * @param {Boolean} keepExisting (optional) True to keep existing selections
18863 selectPrevious : function(keepExisting){
18865 this.selectRow(this.last-1, keepExisting);
18866 this.grid.getView().focusRow(this.last);
18871 * Returns the selected records
18872 * @return {Array} Array of selected records
18874 getSelections : function(){
18875 return [].concat(this.selections.items);
18879 * Returns the first selected record.
18882 getSelected : function(){
18883 return this.selections.itemAt(0);
18888 * Clears all selections.
18890 clearSelections : function(fast){
18891 if(this.locked) return;
18893 var ds = this.grid.dataSource;
18894 var s = this.selections;
18895 s.each(function(r){
18896 this.deselectRow(ds.indexOfId(r.id));
18900 this.selections.clear();
18907 * Selects all rows.
18909 selectAll : function(){
18910 if(this.locked) return;
18911 this.selections.clear();
18912 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18913 this.selectRow(i, true);
18918 * Returns True if there is a selection.
18919 * @return {Boolean}
18921 hasSelection : function(){
18922 return this.selections.length > 0;
18926 * Returns True if the specified row is selected.
18927 * @param {Number/Record} record The record or index of the record to check
18928 * @return {Boolean}
18930 isSelected : function(index){
18931 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18932 return (r && this.selections.key(r.id) ? true : false);
18936 * Returns True if the specified record id is selected.
18937 * @param {String} id The id of record to check
18938 * @return {Boolean}
18940 isIdSelected : function(id){
18941 return (this.selections.key(id) ? true : false);
18945 handleMouseDown : function(e, t){
18946 var view = this.grid.getView(), rowIndex;
18947 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18950 if(e.shiftKey && this.last !== false){
18951 var last = this.last;
18952 this.selectRange(last, rowIndex, e.ctrlKey);
18953 this.last = last; // reset the last
18954 view.focusRow(rowIndex);
18956 var isSelected = this.isSelected(rowIndex);
18957 if(e.button !== 0 && isSelected){
18958 view.focusRow(rowIndex);
18959 }else if(e.ctrlKey && isSelected){
18960 this.deselectRow(rowIndex);
18961 }else if(!isSelected){
18962 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18963 view.focusRow(rowIndex);
18966 this.fireEvent("afterselectionchange", this);
18969 handleDragableRowClick : function(grid, rowIndex, e)
18971 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18972 this.selectRow(rowIndex, false);
18973 grid.view.focusRow(rowIndex);
18974 this.fireEvent("afterselectionchange", this);
18979 * Selects multiple rows.
18980 * @param {Array} rows Array of the indexes of the row to select
18981 * @param {Boolean} keepExisting (optional) True to keep existing selections
18983 selectRows : function(rows, keepExisting){
18985 this.clearSelections();
18987 for(var i = 0, len = rows.length; i < len; i++){
18988 this.selectRow(rows[i], true);
18993 * Selects a range of rows. All rows in between startRow and endRow are also selected.
18994 * @param {Number} startRow The index of the first row in the range
18995 * @param {Number} endRow The index of the last row in the range
18996 * @param {Boolean} keepExisting (optional) True to retain existing selections
18998 selectRange : function(startRow, endRow, keepExisting){
18999 if(this.locked) return;
19001 this.clearSelections();
19003 if(startRow <= endRow){
19004 for(var i = startRow; i <= endRow; i++){
19005 this.selectRow(i, true);
19008 for(var i = startRow; i >= endRow; i--){
19009 this.selectRow(i, true);
19015 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
19016 * @param {Number} startRow The index of the first row in the range
19017 * @param {Number} endRow The index of the last row in the range
19019 deselectRange : function(startRow, endRow, preventViewNotify){
19020 if(this.locked) return;
19021 for(var i = startRow; i <= endRow; i++){
19022 this.deselectRow(i, preventViewNotify);
19028 * @param {Number} row The index of the row to select
19029 * @param {Boolean} keepExisting (optional) True to keep existing selections
19031 selectRow : function(index, keepExisting, preventViewNotify){
19032 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
19033 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
19034 if(!keepExisting || this.singleSelect){
19035 this.clearSelections();
19037 var r = this.grid.dataSource.getAt(index);
19038 this.selections.add(r);
19039 this.last = this.lastActive = index;
19040 if(!preventViewNotify){
19041 this.grid.getView().onRowSelect(index);
19043 this.fireEvent("rowselect", this, index, r);
19044 this.fireEvent("selectionchange", this);
19050 * @param {Number} row The index of the row to deselect
19052 deselectRow : function(index, preventViewNotify){
19053 if(this.locked) return;
19054 if(this.last == index){
19057 if(this.lastActive == index){
19058 this.lastActive = false;
19060 var r = this.grid.dataSource.getAt(index);
19061 this.selections.remove(r);
19062 if(!preventViewNotify){
19063 this.grid.getView().onRowDeselect(index);
19065 this.fireEvent("rowdeselect", this, index);
19066 this.fireEvent("selectionchange", this);
19070 restoreLast : function(){
19072 this.last = this._last;
19077 acceptsNav : function(row, col, cm){
19078 return !cm.isHidden(col) && cm.isCellEditable(col, row);
19082 onEditorKey : function(field, e){
19083 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19088 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19090 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19092 }else if(k == e.ENTER && !e.ctrlKey){
19096 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19098 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19100 }else if(k == e.ESC){
19104 g.startEditing(newCell[0], newCell[1]);
19109 * Ext JS Library 1.1.1
19110 * Copyright(c) 2006-2007, Ext JS, LLC.
19112 * Originally Released Under LGPL - original licence link has changed is not relivant.
19115 * <script type="text/javascript">
19119 * @class Roo.bootstrap.PagingToolbar
19121 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19123 * Create a new PagingToolbar
19124 * @param {Object} config The config object
19126 Roo.bootstrap.PagingToolbar = function(config)
19128 // old args format still supported... - xtype is prefered..
19129 // created from xtype...
19130 var ds = config.dataSource;
19131 this.toolbarItems = [];
19132 if (config.items) {
19133 this.toolbarItems = config.items;
19134 // config.items = [];
19137 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19144 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19148 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19150 * @cfg {Roo.data.Store} dataSource
19151 * The underlying data store providing the paged data
19154 * @cfg {String/HTMLElement/Element} container
19155 * container The id or element that will contain the toolbar
19158 * @cfg {Boolean} displayInfo
19159 * True to display the displayMsg (defaults to false)
19162 * @cfg {Number} pageSize
19163 * The number of records to display per page (defaults to 20)
19167 * @cfg {String} displayMsg
19168 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19170 displayMsg : 'Displaying {0} - {1} of {2}',
19172 * @cfg {String} emptyMsg
19173 * The message to display when no records are found (defaults to "No data to display")
19175 emptyMsg : 'No data to display',
19177 * Customizable piece of the default paging text (defaults to "Page")
19180 beforePageText : "Page",
19182 * Customizable piece of the default paging text (defaults to "of %0")
19185 afterPageText : "of {0}",
19187 * Customizable piece of the default paging text (defaults to "First Page")
19190 firstText : "First Page",
19192 * Customizable piece of the default paging text (defaults to "Previous Page")
19195 prevText : "Previous Page",
19197 * Customizable piece of the default paging text (defaults to "Next Page")
19200 nextText : "Next Page",
19202 * Customizable piece of the default paging text (defaults to "Last Page")
19205 lastText : "Last Page",
19207 * Customizable piece of the default paging text (defaults to "Refresh")
19210 refreshText : "Refresh",
19214 onRender : function(ct, position)
19216 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19217 this.navgroup.parentId = this.id;
19218 this.navgroup.onRender(this.el, null);
19219 // add the buttons to the navgroup
19221 if(this.displayInfo){
19222 Roo.log(this.el.select('ul.navbar-nav',true).first());
19223 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19224 this.displayEl = this.el.select('.x-paging-info', true).first();
19225 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19226 // this.displayEl = navel.el.select('span',true).first();
19232 Roo.each(_this.buttons, function(e){
19233 Roo.factory(e).onRender(_this.el, null);
19237 Roo.each(_this.toolbarItems, function(e) {
19238 _this.navgroup.addItem(e);
19241 this.first = this.navgroup.addItem({
19242 tooltip: this.firstText,
19244 icon : 'fa fa-backward',
19246 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19249 this.prev = this.navgroup.addItem({
19250 tooltip: this.prevText,
19252 icon : 'fa fa-step-backward',
19254 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
19256 //this.addSeparator();
19259 var field = this.navgroup.addItem( {
19261 cls : 'x-paging-position',
19263 html : this.beforePageText +
19264 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19265 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
19268 this.field = field.el.select('input', true).first();
19269 this.field.on("keydown", this.onPagingKeydown, this);
19270 this.field.on("focus", function(){this.dom.select();});
19273 this.afterTextEl = field.el.select('.x-paging-after',true).first();
19274 //this.field.setHeight(18);
19275 //this.addSeparator();
19276 this.next = this.navgroup.addItem({
19277 tooltip: this.nextText,
19279 html : ' <i class="fa fa-step-forward">',
19281 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
19283 this.last = this.navgroup.addItem({
19284 tooltip: this.lastText,
19285 icon : 'fa fa-forward',
19288 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
19290 //this.addSeparator();
19291 this.loading = this.navgroup.addItem({
19292 tooltip: this.refreshText,
19293 icon: 'fa fa-refresh',
19295 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19301 updateInfo : function(){
19302 if(this.displayEl){
19303 var count = this.ds.getCount();
19304 var msg = count == 0 ?
19308 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
19310 this.displayEl.update(msg);
19315 onLoad : function(ds, r, o){
19316 this.cursor = o.params ? o.params.start : 0;
19317 var d = this.getPageData(),
19321 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19322 this.field.dom.value = ap;
19323 this.first.setDisabled(ap == 1);
19324 this.prev.setDisabled(ap == 1);
19325 this.next.setDisabled(ap == ps);
19326 this.last.setDisabled(ap == ps);
19327 this.loading.enable();
19332 getPageData : function(){
19333 var total = this.ds.getTotalCount();
19336 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19337 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19342 onLoadError : function(){
19343 this.loading.enable();
19347 onPagingKeydown : function(e){
19348 var k = e.getKey();
19349 var d = this.getPageData();
19351 var v = this.field.dom.value, pageNum;
19352 if(!v || isNaN(pageNum = parseInt(v, 10))){
19353 this.field.dom.value = d.activePage;
19356 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19357 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19360 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))
19362 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19363 this.field.dom.value = pageNum;
19364 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19367 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19369 var v = this.field.dom.value, pageNum;
19370 var increment = (e.shiftKey) ? 10 : 1;
19371 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19373 if(!v || isNaN(pageNum = parseInt(v, 10))) {
19374 this.field.dom.value = d.activePage;
19377 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19379 this.field.dom.value = parseInt(v, 10) + increment;
19380 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19381 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19388 beforeLoad : function(){
19390 this.loading.disable();
19395 onClick : function(which){
19402 ds.load({params:{start: 0, limit: this.pageSize}});
19405 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19408 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19411 var total = ds.getTotalCount();
19412 var extra = total % this.pageSize;
19413 var lastStart = extra ? (total - extra) : total-this.pageSize;
19414 ds.load({params:{start: lastStart, limit: this.pageSize}});
19417 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19423 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19424 * @param {Roo.data.Store} store The data store to unbind
19426 unbind : function(ds){
19427 ds.un("beforeload", this.beforeLoad, this);
19428 ds.un("load", this.onLoad, this);
19429 ds.un("loadexception", this.onLoadError, this);
19430 ds.un("remove", this.updateInfo, this);
19431 ds.un("add", this.updateInfo, this);
19432 this.ds = undefined;
19436 * Binds the paging toolbar to the specified {@link Roo.data.Store}
19437 * @param {Roo.data.Store} store The data store to bind
19439 bind : function(ds){
19440 ds.on("beforeload", this.beforeLoad, this);
19441 ds.on("load", this.onLoad, this);
19442 ds.on("loadexception", this.onLoadError, this);
19443 ds.on("remove", this.updateInfo, this);
19444 ds.on("add", this.updateInfo, this);
19455 * @class Roo.bootstrap.MessageBar
19456 * @extends Roo.bootstrap.Component
19457 * Bootstrap MessageBar class
19458 * @cfg {String} html contents of the MessageBar
19459 * @cfg {String} weight (info | success | warning | danger) default info
19460 * @cfg {String} beforeClass insert the bar before the given class
19461 * @cfg {Boolean} closable (true | false) default false
19462 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19465 * Create a new Element
19466 * @param {Object} config The config object
19469 Roo.bootstrap.MessageBar = function(config){
19470 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19473 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
19479 beforeClass: 'bootstrap-sticky-wrap',
19481 getAutoCreate : function(){
19485 cls: 'alert alert-dismissable alert-' + this.weight,
19490 html: this.html || ''
19496 cfg.cls += ' alert-messages-fixed';
19510 onRender : function(ct, position)
19512 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19515 var cfg = Roo.apply({}, this.getAutoCreate());
19519 cfg.cls += ' ' + this.cls;
19522 cfg.style = this.style;
19524 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19526 this.el.setVisibilityMode(Roo.Element.DISPLAY);
19529 this.el.select('>button.close').on('click', this.hide, this);
19535 if (!this.rendered) {
19541 this.fireEvent('show', this);
19547 if (!this.rendered) {
19553 this.fireEvent('hide', this);
19556 update : function()
19558 // var e = this.el.dom.firstChild;
19560 // if(this.closable){
19561 // e = e.nextSibling;
19564 // e.data = this.html || '';
19566 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19582 * @class Roo.bootstrap.Graph
19583 * @extends Roo.bootstrap.Component
19584 * Bootstrap Graph class
19588 @cfg {String} graphtype bar | vbar | pie
19589 @cfg {number} g_x coodinator | centre x (pie)
19590 @cfg {number} g_y coodinator | centre y (pie)
19591 @cfg {number} g_r radius (pie)
19592 @cfg {number} g_height height of the chart (respected by all elements in the set)
19593 @cfg {number} g_width width of the chart (respected by all elements in the set)
19594 @cfg {Object} title The title of the chart
19597 -opts (object) options for the chart
19599 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19600 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19602 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.
19603 o stacked (boolean) whether or not to tread values as in a stacked bar chart
19605 o stretch (boolean)
19607 -opts (object) options for the pie
19610 o startAngle (number)
19611 o endAngle (number)
19615 * Create a new Input
19616 * @param {Object} config The config object
19619 Roo.bootstrap.Graph = function(config){
19620 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19626 * The img click event for the img.
19627 * @param {Roo.EventObject} e
19633 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
19644 //g_colors: this.colors,
19651 getAutoCreate : function(){
19662 onRender : function(ct,position){
19663 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19664 this.raphael = Raphael(this.el.dom);
19666 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19667 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19668 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19669 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19671 r.text(160, 10, "Single Series Chart").attr(txtattr);
19672 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19673 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19674 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19676 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19677 r.barchart(330, 10, 300, 220, data1);
19678 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19679 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19682 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19683 // r.barchart(30, 30, 560, 250, xdata, {
19684 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19685 // axis : "0 0 1 1",
19686 // axisxlabels : xdata
19687 // //yvalues : cols,
19690 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19692 // this.load(null,xdata,{
19693 // axis : "0 0 1 1",
19694 // axisxlabels : xdata
19699 load : function(graphtype,xdata,opts){
19700 this.raphael.clear();
19702 graphtype = this.graphtype;
19707 var r = this.raphael,
19708 fin = function () {
19709 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19711 fout = function () {
19712 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19714 pfin = function() {
19715 this.sector.stop();
19716 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19719 this.label[0].stop();
19720 this.label[0].attr({ r: 7.5 });
19721 this.label[1].attr({ "font-weight": 800 });
19724 pfout = function() {
19725 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19728 this.label[0].animate({ r: 5 }, 500, "bounce");
19729 this.label[1].attr({ "font-weight": 400 });
19735 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19738 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19741 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
19742 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19744 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19751 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19756 setTitle: function(o)
19761 initEvents: function() {
19764 this.el.on('click', this.onClick, this);
19768 onClick : function(e)
19770 Roo.log('img onclick');
19771 this.fireEvent('click', this, e);
19783 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19786 * @class Roo.bootstrap.dash.NumberBox
19787 * @extends Roo.bootstrap.Component
19788 * Bootstrap NumberBox class
19789 * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19790 * @cfg {String} headline Box headline
19791 * @cfg {String} content Box content
19792 * @cfg {String} icon Box icon
19793 * @cfg {String} footer Footer text
19794 * @cfg {String} fhref Footer href
19797 * Create a new NumberBox
19798 * @param {Object} config The config object
19802 Roo.bootstrap.dash.NumberBox = function(config){
19803 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19807 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
19817 getAutoCreate : function(){
19821 cls : 'small-box bg-' + this.bgcolor,
19829 cls : 'roo-headline',
19830 html : this.headline
19834 cls : 'roo-content',
19835 html : this.content
19849 cls : 'ion ' + this.icon
19858 cls : 'small-box-footer',
19859 href : this.fhref || '#',
19863 cfg.cn.push(footer);
19870 onRender : function(ct,position){
19871 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19878 setHeadline: function (value)
19880 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19883 setFooter: function (value, href)
19885 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19888 this.el.select('a.small-box-footer',true).first().attr('href', href);
19893 setContent: function (value)
19895 this.el.select('.roo-content',true).first().dom.innerHTML = value;
19898 initEvents: function()
19912 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19915 * @class Roo.bootstrap.dash.TabBox
19916 * @extends Roo.bootstrap.Component
19917 * Bootstrap TabBox class
19918 * @cfg {String} title Title of the TabBox
19919 * @cfg {String} icon Icon of the TabBox
19920 * @cfg {Boolean} showtabs (true|false) show the tabs default true
19921 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
19924 * Create a new TabBox
19925 * @param {Object} config The config object
19929 Roo.bootstrap.dash.TabBox = function(config){
19930 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19935 * When a pane is added
19936 * @param {Roo.bootstrap.dash.TabPane} pane
19940 * @event activatepane
19941 * When a pane is activated
19942 * @param {Roo.bootstrap.dash.TabPane} pane
19944 "activatepane" : true
19952 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
19957 tabScrollable : false,
19959 getChildContainer : function()
19961 return this.el.select('.tab-content', true).first();
19964 getAutoCreate : function(){
19968 cls: 'pull-left header',
19976 cls: 'fa ' + this.icon
19982 cls: 'nav nav-tabs pull-right',
19988 if(this.tabScrollable){
19995 cls: 'nav nav-tabs pull-right',
20006 cls: 'nav-tabs-custom',
20011 cls: 'tab-content no-padding',
20019 initEvents : function()
20021 //Roo.log('add add pane handler');
20022 this.on('addpane', this.onAddPane, this);
20025 * Updates the box title
20026 * @param {String} html to set the title to.
20028 setTitle : function(value)
20030 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
20032 onAddPane : function(pane)
20034 this.panes.push(pane);
20035 //Roo.log('addpane');
20037 // tabs are rendere left to right..
20038 if(!this.showtabs){
20042 var ctr = this.el.select('.nav-tabs', true).first();
20045 var existing = ctr.select('.nav-tab',true);
20046 var qty = existing.getCount();;
20049 var tab = ctr.createChild({
20051 cls : 'nav-tab' + (qty ? '' : ' active'),
20059 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
20062 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
20064 pane.el.addClass('active');
20069 onTabClick : function(ev,un,ob,pane)
20071 //Roo.log('tab - prev default');
20072 ev.preventDefault();
20075 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20076 pane.tab.addClass('active');
20077 //Roo.log(pane.title);
20078 this.getChildContainer().select('.tab-pane',true).removeClass('active');
20079 // technically we should have a deactivate event.. but maybe add later.
20080 // and it should not de-activate the selected tab...
20081 this.fireEvent('activatepane', pane);
20082 pane.el.addClass('active');
20083 pane.fireEvent('activate');
20088 getActivePane : function()
20091 Roo.each(this.panes, function(p) {
20092 if(p.el.hasClass('active')){
20113 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20115 * @class Roo.bootstrap.TabPane
20116 * @extends Roo.bootstrap.Component
20117 * Bootstrap TabPane class
20118 * @cfg {Boolean} active (false | true) Default false
20119 * @cfg {String} title title of panel
20123 * Create a new TabPane
20124 * @param {Object} config The config object
20127 Roo.bootstrap.dash.TabPane = function(config){
20128 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20134 * When a pane is activated
20135 * @param {Roo.bootstrap.dash.TabPane} pane
20142 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
20147 // the tabBox that this is attached to.
20150 getAutoCreate : function()
20158 cfg.cls += ' active';
20163 initEvents : function()
20165 //Roo.log('trigger add pane handler');
20166 this.parent().fireEvent('addpane', this)
20170 * Updates the tab title
20171 * @param {String} html to set the title to.
20173 setTitle: function(str)
20179 this.tab.select('a', true).first().dom.innerHTML = str;
20196 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20199 * @class Roo.bootstrap.menu.Menu
20200 * @extends Roo.bootstrap.Component
20201 * Bootstrap Menu class - container for Menu
20202 * @cfg {String} html Text of the menu
20203 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20204 * @cfg {String} icon Font awesome icon
20205 * @cfg {String} pos Menu align to (top | bottom) default bottom
20209 * Create a new Menu
20210 * @param {Object} config The config object
20214 Roo.bootstrap.menu.Menu = function(config){
20215 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20219 * @event beforeshow
20220 * Fires before this menu is displayed
20221 * @param {Roo.bootstrap.menu.Menu} this
20225 * @event beforehide
20226 * Fires before this menu is hidden
20227 * @param {Roo.bootstrap.menu.Menu} this
20232 * Fires after this menu is displayed
20233 * @param {Roo.bootstrap.menu.Menu} this
20238 * Fires after this menu is hidden
20239 * @param {Roo.bootstrap.menu.Menu} this
20244 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20245 * @param {Roo.bootstrap.menu.Menu} this
20246 * @param {Roo.EventObject} e
20253 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
20257 weight : 'default',
20262 getChildContainer : function() {
20263 if(this.isSubMenu){
20267 return this.el.select('ul.dropdown-menu', true).first();
20270 getAutoCreate : function()
20275 cls : 'roo-menu-text',
20283 cls : 'fa ' + this.icon
20294 cls : 'dropdown-button btn btn-' + this.weight,
20299 cls : 'dropdown-toggle btn btn-' + this.weight,
20309 cls : 'dropdown-menu'
20315 if(this.pos == 'top'){
20316 cfg.cls += ' dropup';
20319 if(this.isSubMenu){
20322 cls : 'dropdown-menu'
20329 onRender : function(ct, position)
20331 this.isSubMenu = ct.hasClass('dropdown-submenu');
20333 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20336 initEvents : function()
20338 if(this.isSubMenu){
20342 this.hidden = true;
20344 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20345 this.triggerEl.on('click', this.onTriggerPress, this);
20347 this.buttonEl = this.el.select('button.dropdown-button', true).first();
20348 this.buttonEl.on('click', this.onClick, this);
20354 if(this.isSubMenu){
20358 return this.el.select('ul.dropdown-menu', true).first();
20361 onClick : function(e)
20363 this.fireEvent("click", this, e);
20366 onTriggerPress : function(e)
20368 if (this.isVisible()) {
20375 isVisible : function(){
20376 return !this.hidden;
20381 this.fireEvent("beforeshow", this);
20383 this.hidden = false;
20384 this.el.addClass('open');
20386 Roo.get(document).on("mouseup", this.onMouseUp, this);
20388 this.fireEvent("show", this);
20395 this.fireEvent("beforehide", this);
20397 this.hidden = true;
20398 this.el.removeClass('open');
20400 Roo.get(document).un("mouseup", this.onMouseUp);
20402 this.fireEvent("hide", this);
20405 onMouseUp : function()
20419 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20422 * @class Roo.bootstrap.menu.Item
20423 * @extends Roo.bootstrap.Component
20424 * Bootstrap MenuItem class
20425 * @cfg {Boolean} submenu (true | false) default false
20426 * @cfg {String} html text of the item
20427 * @cfg {String} href the link
20428 * @cfg {Boolean} disable (true | false) default false
20429 * @cfg {Boolean} preventDefault (true | false) default true
20430 * @cfg {String} icon Font awesome icon
20431 * @cfg {String} pos Submenu align to (left | right) default right
20435 * Create a new Item
20436 * @param {Object} config The config object
20440 Roo.bootstrap.menu.Item = function(config){
20441 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20445 * Fires when the mouse is hovering over this menu
20446 * @param {Roo.bootstrap.menu.Item} this
20447 * @param {Roo.EventObject} e
20452 * Fires when the mouse exits this menu
20453 * @param {Roo.bootstrap.menu.Item} this
20454 * @param {Roo.EventObject} e
20460 * The raw click event for the entire grid.
20461 * @param {Roo.EventObject} e
20467 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
20472 preventDefault: true,
20477 getAutoCreate : function()
20482 cls : 'roo-menu-item-text',
20490 cls : 'fa ' + this.icon
20499 href : this.href || '#',
20506 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20510 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20512 if(this.pos == 'left'){
20513 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20520 initEvents : function()
20522 this.el.on('mouseover', this.onMouseOver, this);
20523 this.el.on('mouseout', this.onMouseOut, this);
20525 this.el.select('a', true).first().on('click', this.onClick, this);
20529 onClick : function(e)
20531 if(this.preventDefault){
20532 e.preventDefault();
20535 this.fireEvent("click", this, e);
20538 onMouseOver : function(e)
20540 if(this.submenu && this.pos == 'left'){
20541 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20544 this.fireEvent("mouseover", this, e);
20547 onMouseOut : function(e)
20549 this.fireEvent("mouseout", this, e);
20561 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20564 * @class Roo.bootstrap.menu.Separator
20565 * @extends Roo.bootstrap.Component
20566 * Bootstrap Separator class
20569 * Create a new Separator
20570 * @param {Object} config The config object
20574 Roo.bootstrap.menu.Separator = function(config){
20575 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20578 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
20580 getAutoCreate : function(){