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
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
33 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
36 allowDomMove : false, // to stop relocations in parent onRender...
46 * Initialize Events for the element
48 initEvents : function() { },
54 can_build_overlaid : true,
56 container_method : false,
63 // returns the parent component..
64 return Roo.ComponentMgr.get(this.parentId)
70 onRender : function(ct, position)
72 // Roo.log("Call onRender: " + this.xtype);
74 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
77 if (this.el.attr('xtype')) {
78 this.el.attr('xtypex', this.el.attr('xtype'));
79 this.el.dom.removeAttribute('xtype');
89 var cfg = Roo.apply({}, this.getAutoCreate());
92 // fill in the extra attributes
93 if (this.xattr && typeof(this.xattr) =='object') {
94 for (var i in this.xattr) {
95 cfg[i] = this.xattr[i];
100 cfg.dataId = this.dataId;
104 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
107 if (this.style) { // fixme needs to support more complex style data.
108 cfg.style = this.style;
112 cfg.name = this.name;
117 this.el = ct.createChild(cfg, position);
120 this.tooltipEl().attr('tooltip', this.tooltip);
123 if(this.tabIndex !== undefined){
124 this.el.dom.setAttribute('tabIndex', this.tabIndex);
131 * Fetch the element to add children to
132 * @return {Roo.Element} defaults to this.el
134 getChildContainer : function()
139 * Fetch the element to display the tooltip on.
140 * @return {Roo.Element} defaults to this.el
142 tooltipEl : function()
147 addxtype : function(tree,cntr)
151 cn = Roo.factory(tree);
153 cn.parentType = this.xtype; //??
154 cn.parentId = this.id;
156 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
157 if (typeof(cn.container_method) == 'string') {
158 cntr = cn.container_method;
162 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
164 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
166 var build_from_html = Roo.XComponent.build_from_html;
168 var is_body = (tree.xtype == 'Body') ;
170 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
172 var self_cntr_el = Roo.get(this[cntr](false));
174 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
175 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
176 return this.addxtypeChild(tree,cntr);
179 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
182 return this.addxtypeChild(Roo.apply({}, tree),cntr);
185 Roo.log('skipping render');
193 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
199 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
203 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
208 addxtypeChild : function (tree, cntr)
210 Roo.debug && Roo.log('addxtypeChild:' + cntr);
212 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
215 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
216 (typeof(tree['flexy:foreach']) != 'undefined');
220 skip_children = false;
221 // render the element if it's not BODY.
222 if (tree.xtype != 'Body') {
224 cn = Roo.factory(tree);
226 cn.parentType = this.xtype; //??
227 cn.parentId = this.id;
229 var build_from_html = Roo.XComponent.build_from_html;
232 // does the container contain child eleemnts with 'xtype' attributes.
233 // that match this xtype..
234 // note - when we render we create these as well..
235 // so we should check to see if body has xtype set.
236 if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
238 var self_cntr_el = Roo.get(this[cntr](false));
239 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
242 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
243 // and are not displayed -this causes this to use up the wrong element when matching.
244 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
247 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
248 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
254 //echild.dom.removeAttribute('xtype');
256 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
257 Roo.debug && Roo.log(self_cntr_el);
258 Roo.debug && Roo.log(echild);
259 Roo.debug && Roo.log(cn);
265 // if object has flexy:if - then it may or may not be rendered.
266 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
267 // skip a flexy if element.
268 Roo.debug && Roo.log('skipping render');
269 Roo.debug && Roo.log(tree);
271 Roo.debug && Roo.log('skipping all children');
272 skip_children = true;
277 // actually if flexy:foreach is found, we really want to create
278 // multiple copies here...
280 //Roo.log(this[cntr]());
281 cn.render(this[cntr](true));
283 // then add the element..
291 if (typeof (tree.menu) != 'undefined') {
292 tree.menu.parentType = cn.xtype;
293 tree.menu.triggerEl = cn.el;
294 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
298 if (!tree.items || !tree.items.length) {
302 var items = tree.items;
305 //Roo.log(items.length);
307 if (!skip_children) {
308 for(var i =0;i < items.length;i++) {
309 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
331 * @class Roo.bootstrap.Body
332 * @extends Roo.bootstrap.Component
333 * Bootstrap Body class
337 * @param {Object} config The config object
340 Roo.bootstrap.Body = function(config){
341 Roo.bootstrap.Body.superclass.constructor.call(this, config);
342 this.el = Roo.get(document.body);
343 if (this.cls && this.cls.length) {
344 Roo.get(document.body).addClass(this.cls);
348 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
353 onRender : function(ct, position)
355 /* Roo.log("Roo.bootstrap.Body - onRender");
356 if (this.cls && this.cls.length) {
357 Roo.get(document.body).addClass(this.cls);
377 * @class Roo.bootstrap.ButtonGroup
378 * @extends Roo.bootstrap.Component
379 * Bootstrap ButtonGroup class
380 * @cfg {String} size lg | sm | xs (default empty normal)
381 * @cfg {String} align vertical | justified (default none)
382 * @cfg {String} direction up | down (default down)
383 * @cfg {Boolean} toolbar false | true
384 * @cfg {Boolean} btn true | false
389 * @param {Object} config The config object
392 Roo.bootstrap.ButtonGroup = function(config){
393 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
396 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
404 getAutoCreate : function(){
410 cfg.html = this.html || cfg.html;
421 if (['vertical','justified'].indexOf(this.align)!==-1) {
422 cfg.cls = 'btn-group-' + this.align;
424 if (this.align == 'justified') {
425 console.log(this.items);
429 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
430 cfg.cls += ' btn-group-' + this.size;
433 if (this.direction == 'up') {
434 cfg.cls += ' dropup' ;
450 * @class Roo.bootstrap.Button
451 * @extends Roo.bootstrap.Component
452 * Bootstrap Button class
453 * @cfg {String} html The button content
454 * @cfg {String} weight ( primary | success | info | warning | danger | link ) default
455 * @cfg {String} size ( lg | sm | xs)
456 * @cfg {String} tag ( a | input | submit)
457 * @cfg {String} href empty or href
458 * @cfg {Boolean} disabled default false;
459 * @cfg {Boolean} isClose default false;
460 * @cfg {String} glyphicon (| 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)
461 * @cfg {String} badge text for badge
462 * @cfg {String} theme default
463 * @cfg {Boolean} inverse
464 * @cfg {Boolean} toggle
465 * @cfg {String} ontext text for on toggle state
466 * @cfg {String} offtext text for off toggle state
467 * @cfg {Boolean} defaulton
468 * @cfg {Boolean} preventDefault default true
469 * @cfg {Boolean} removeClass remove the standard class..
470 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
473 * Create a new button
474 * @param {Object} config The config object
478 Roo.bootstrap.Button = function(config){
479 Roo.bootstrap.Button.superclass.constructor.call(this, config);
484 * When a butotn is pressed
485 * @param {Roo.EventObject} e
490 * After the button has been toggles
491 * @param {Roo.EventObject} e
492 * @param {boolean} pressed (also available as button.pressed)
498 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
516 preventDefault: true,
525 getAutoCreate : function(){
533 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
534 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
539 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
541 if (this.toggle == true) {
544 cls: 'slider-frame roo-button',
549 'data-off-text':'OFF',
550 cls: 'slider-button',
556 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
557 cfg.cls += ' '+this.weight;
566 cfg["aria-hidden"] = true;
568 cfg.html = "×";
574 if (this.theme==='default') {
575 cfg.cls = 'btn roo-button';
577 //if (this.parentType != 'Navbar') {
578 this.weight = this.weight.length ? this.weight : 'default';
580 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
582 cfg.cls += ' btn-' + this.weight;
584 } else if (this.theme==='glow') {
587 cfg.cls = 'btn-glow roo-button';
589 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
591 cfg.cls += ' ' + this.weight;
597 this.cls += ' inverse';
602 cfg.cls += ' active';
606 cfg.disabled = 'disabled';
610 Roo.log('changing to ul' );
612 this.glyphicon = 'caret';
615 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
617 //gsRoo.log(this.parentType);
618 if (this.parentType === 'Navbar' && !this.parent().bar) {
619 Roo.log('changing to li?');
628 href : this.href || '#'
631 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
632 cfg.cls += ' dropdown';
639 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
641 if (this.glyphicon) {
642 cfg.html = ' ' + cfg.html;
647 cls: 'glyphicon glyphicon-' + this.glyphicon
657 // cfg.cls='btn roo-button';
661 var value = cfg.html;
666 cls: 'glyphicon glyphicon-' + this.glyphicon,
685 cfg.cls += ' dropdown';
686 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
689 if (cfg.tag !== 'a' && this.href !== '') {
690 throw "Tag must be a to set href.";
691 } else if (this.href.length > 0) {
692 cfg.href = this.href;
695 if(this.removeClass){
700 cfg.target = this.target;
705 initEvents: function() {
706 // Roo.log('init events?');
707 // Roo.log(this.el.dom);
710 if (typeof (this.menu) != 'undefined') {
711 this.menu.parentType = this.xtype;
712 this.menu.triggerEl = this.el;
713 this.addxtype(Roo.apply({}, this.menu));
717 if (this.el.hasClass('roo-button')) {
718 this.el.on('click', this.onClick, this);
720 this.el.select('.roo-button').on('click', this.onClick, this);
723 if(this.removeClass){
724 this.el.on('click', this.onClick, this);
727 this.el.enableDisplayMode();
730 onClick : function(e)
736 Roo.log('button on click ');
737 if(this.preventDefault){
740 if (this.pressed === true || this.pressed === false) {
741 this.pressed = !this.pressed;
742 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
743 this.fireEvent('toggle', this, e, this.pressed);
747 this.fireEvent('click', this, e);
751 * Enables this button
755 this.disabled = false;
756 this.el.removeClass('disabled');
760 * Disable this button
764 this.disabled = true;
765 this.el.addClass('disabled');
768 * sets the active state on/off,
769 * @param {Boolean} state (optional) Force a particular state
771 setActive : function(v) {
773 this.el[v ? 'addClass' : 'removeClass']('active');
776 * toggles the current active state
778 toggleActive : function()
780 var active = this.el.hasClass('active');
781 this.setActive(!active);
785 setText : function(str)
787 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
791 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
814 * @class Roo.bootstrap.Column
815 * @extends Roo.bootstrap.Component
816 * Bootstrap Column class
817 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
818 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
819 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
820 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
821 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
822 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
823 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
824 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
827 * @cfg {Boolean} hidden (true|false) hide the element
828 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
829 * @cfg {String} fa (ban|check|...) font awesome icon
830 * @cfg {Number} fasize (1|2|....) font awsome size
832 * @cfg {String} icon (info-sign|check|...) glyphicon name
834 * @cfg {String} html content of column.
837 * Create a new Column
838 * @param {Object} config The config object
841 Roo.bootstrap.Column = function(config){
842 Roo.bootstrap.Column.superclass.constructor.call(this, config);
845 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
863 getAutoCreate : function(){
864 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
872 ['xs','sm','md','lg'].map(function(size){
873 //Roo.log( size + ':' + settings[size]);
875 if (settings[size+'off'] !== false) {
876 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
879 if (settings[size] === false) {
882 Roo.log(settings[size]);
883 if (!settings[size]) { // 0 = hidden
884 cfg.cls += ' hidden-' + size;
887 cfg.cls += ' col-' + size + '-' + settings[size];
892 cfg.cls += ' hidden';
895 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
896 cfg.cls +=' alert alert-' + this.alert;
900 if (this.html.length) {
901 cfg.html = this.html;
905 if (this.fasize > 1) {
906 fasize = ' fa-' + this.fasize + 'x';
908 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
913 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
932 * @class Roo.bootstrap.Container
933 * @extends Roo.bootstrap.Component
934 * Bootstrap Container class
935 * @cfg {Boolean} jumbotron is it a jumbotron element
936 * @cfg {String} html content of element
937 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
938 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
939 * @cfg {String} header content of header (for panel)
940 * @cfg {String} footer content of footer (for panel)
941 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
942 * @cfg {String} tag (header|aside|section) type of HTML tag.
943 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
944 * @cfg {String} fa (ban|check|...) font awesome icon
945 * @cfg {String} icon (info-sign|check|...) glyphicon name
946 * @cfg {Boolean} hidden (true|false) hide the element
950 * Create a new Container
951 * @param {Object} config The config object
954 Roo.bootstrap.Container = function(config){
955 Roo.bootstrap.Container.superclass.constructor.call(this, config);
958 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
972 getChildContainer : function() {
978 if (this.panel.length) {
979 return this.el.select('.panel-body',true).first();
986 getAutoCreate : function(){
989 tag : this.tag || 'div',
993 if (this.jumbotron) {
994 cfg.cls = 'jumbotron';
999 // - this is applied by the parent..
1001 // cfg.cls = this.cls + '';
1004 if (this.sticky.length) {
1006 var bd = Roo.get(document.body);
1007 if (!bd.hasClass('bootstrap-sticky')) {
1008 bd.addClass('bootstrap-sticky');
1009 Roo.select('html',true).setStyle('height', '100%');
1012 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1016 if (this.well.length) {
1017 switch (this.well) {
1020 cfg.cls +=' well well-' +this.well;
1029 cfg.cls += ' hidden';
1033 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1034 cfg.cls +=' alert alert-' + this.alert;
1039 if (this.panel.length) {
1040 cfg.cls += ' panel panel-' + this.panel;
1042 if (this.header.length) {
1045 cls : 'panel-heading',
1048 cls : 'panel-title',
1061 if (this.footer.length) {
1063 cls : 'panel-footer',
1072 body.html = this.html || cfg.html;
1073 // prefix with the icons..
1075 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1078 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1083 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1084 cfg.cls = 'container';
1090 titleEl : function()
1092 if(!this.el || !this.panel.length || !this.header.length){
1096 return this.el.select('.panel-title',true).first();
1099 setTitle : function(v)
1101 var titleEl = this.titleEl();
1107 titleEl.dom.innerHTML = v;
1110 getTitle : function()
1113 var titleEl = this.titleEl();
1119 return titleEl.dom.innerHTML;
1133 * @class Roo.bootstrap.Img
1134 * @extends Roo.bootstrap.Component
1135 * Bootstrap Img class
1136 * @cfg {Boolean} imgResponsive false | true
1137 * @cfg {String} border rounded | circle | thumbnail
1138 * @cfg {String} src image source
1139 * @cfg {String} alt image alternative text
1140 * @cfg {String} href a tag href
1141 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1144 * Create a new Input
1145 * @param {Object} config The config object
1148 Roo.bootstrap.Img = function(config){
1149 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1155 * The img click event for the img.
1156 * @param {Roo.EventObject} e
1162 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1164 imgResponsive: true,
1170 getAutoCreate : function(){
1174 cls: (this.imgResponsive) ? 'img-responsive' : '',
1178 cfg.html = this.html || cfg.html;
1180 cfg.src = this.src || cfg.src;
1182 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1183 cfg.cls += ' img-' + this.border;
1200 a.target = this.target;
1206 return (this.href) ? a : cfg;
1209 initEvents: function() {
1212 this.el.on('click', this.onClick, this);
1216 onClick : function(e)
1218 Roo.log('img onclick');
1219 this.fireEvent('click', this, e);
1233 * @class Roo.bootstrap.Link
1234 * @extends Roo.bootstrap.Component
1235 * Bootstrap Link Class
1236 * @cfg {String} alt image alternative text
1237 * @cfg {String} href a tag href
1238 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1239 * @cfg {String} html the content of the link.
1240 * @cfg {String} anchor name for the anchor link
1242 * @cfg {Boolean} preventDefault (true | false) default false
1246 * Create a new Input
1247 * @param {Object} config The config object
1250 Roo.bootstrap.Link = function(config){
1251 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1257 * The img click event for the img.
1258 * @param {Roo.EventObject} e
1264 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1268 preventDefault: false,
1272 getAutoCreate : function()
1278 // anchor's do not require html/href...
1279 if (this.anchor === false) {
1280 cfg.html = this.html || 'html-missing';
1281 cfg.href = this.href || '#';
1283 cfg.name = this.anchor;
1284 if (this.html !== false) {
1285 cfg.html = this.html;
1287 if (this.href !== false) {
1288 cfg.href = this.href;
1292 if(this.alt !== false){
1297 if(this.target !== false) {
1298 cfg.target = this.target;
1304 initEvents: function() {
1306 if(!this.href || this.preventDefault){
1307 this.el.on('click', this.onClick, this);
1311 onClick : function(e)
1313 if(this.preventDefault){
1316 //Roo.log('img onclick');
1317 this.fireEvent('click', this, e);
1330 * @class Roo.bootstrap.Header
1331 * @extends Roo.bootstrap.Component
1332 * Bootstrap Header class
1333 * @cfg {String} html content of header
1334 * @cfg {Number} level (1|2|3|4|5|6) default 1
1337 * Create a new Header
1338 * @param {Object} config The config object
1342 Roo.bootstrap.Header = function(config){
1343 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1346 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1354 getAutoCreate : function(){
1357 tag: 'h' + (1 *this.level),
1358 html: this.html || 'fill in html'
1370 * Ext JS Library 1.1.1
1371 * Copyright(c) 2006-2007, Ext JS, LLC.
1373 * Originally Released Under LGPL - original licence link has changed is not relivant.
1376 * <script type="text/javascript">
1380 * @class Roo.bootstrap.MenuMgr
1381 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1384 Roo.bootstrap.MenuMgr = function(){
1385 var menus, active, groups = {}, attached = false, lastShow = new Date();
1387 // private - called when first menu is created
1390 active = new Roo.util.MixedCollection();
1391 Roo.get(document).addKeyListener(27, function(){
1392 if(active.length > 0){
1400 if(active && active.length > 0){
1401 var c = active.clone();
1411 if(active.length < 1){
1412 Roo.get(document).un("mouseup", onMouseDown);
1420 var last = active.last();
1421 lastShow = new Date();
1424 Roo.get(document).on("mouseup", onMouseDown);
1429 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1430 m.parentMenu.activeChild = m;
1431 }else if(last && last.isVisible()){
1432 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1437 function onBeforeHide(m){
1439 m.activeChild.hide();
1441 if(m.autoHideTimer){
1442 clearTimeout(m.autoHideTimer);
1443 delete m.autoHideTimer;
1448 function onBeforeShow(m){
1449 var pm = m.parentMenu;
1450 if(!pm && !m.allowOtherMenus){
1452 }else if(pm && pm.activeChild && active != m){
1453 pm.activeChild.hide();
1458 function onMouseDown(e){
1459 Roo.log("on MouseDown");
1460 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1468 function onBeforeCheck(mi, state){
1470 var g = groups[mi.group];
1471 for(var i = 0, l = g.length; i < l; i++){
1473 g[i].setChecked(false);
1482 * Hides all menus that are currently visible
1484 hideAll : function(){
1489 register : function(menu){
1493 menus[menu.id] = menu;
1494 menu.on("beforehide", onBeforeHide);
1495 menu.on("hide", onHide);
1496 menu.on("beforeshow", onBeforeShow);
1497 menu.on("show", onShow);
1499 if(g && menu.events["checkchange"]){
1503 groups[g].push(menu);
1504 menu.on("checkchange", onCheck);
1509 * Returns a {@link Roo.menu.Menu} object
1510 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1511 * be used to generate and return a new Menu instance.
1513 get : function(menu){
1514 if(typeof menu == "string"){ // menu id
1516 }else if(menu.events){ // menu instance
1519 /*else if(typeof menu.length == 'number'){ // array of menu items?
1520 return new Roo.bootstrap.Menu({items:menu});
1521 }else{ // otherwise, must be a config
1522 return new Roo.bootstrap.Menu(menu);
1529 unregister : function(menu){
1530 delete menus[menu.id];
1531 menu.un("beforehide", onBeforeHide);
1532 menu.un("hide", onHide);
1533 menu.un("beforeshow", onBeforeShow);
1534 menu.un("show", onShow);
1536 if(g && menu.events["checkchange"]){
1537 groups[g].remove(menu);
1538 menu.un("checkchange", onCheck);
1543 registerCheckable : function(menuItem){
1544 var g = menuItem.group;
1549 groups[g].push(menuItem);
1550 menuItem.on("beforecheckchange", onBeforeCheck);
1555 unregisterCheckable : function(menuItem){
1556 var g = menuItem.group;
1558 groups[g].remove(menuItem);
1559 menuItem.un("beforecheckchange", onBeforeCheck);
1571 * @class Roo.bootstrap.Menu
1572 * @extends Roo.bootstrap.Component
1573 * Bootstrap Menu class - container for MenuItems
1574 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1578 * @param {Object} config The config object
1582 Roo.bootstrap.Menu = function(config){
1583 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1584 if (this.registerMenu) {
1585 Roo.bootstrap.MenuMgr.register(this);
1590 * Fires before this menu is displayed
1591 * @param {Roo.menu.Menu} this
1596 * Fires before this menu is hidden
1597 * @param {Roo.menu.Menu} this
1602 * Fires after this menu is displayed
1603 * @param {Roo.menu.Menu} this
1608 * Fires after this menu is hidden
1609 * @param {Roo.menu.Menu} this
1614 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1615 * @param {Roo.menu.Menu} this
1616 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1617 * @param {Roo.EventObject} e
1622 * Fires when the mouse is hovering over 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 the mouse exits this menu
1631 * @param {Roo.menu.Menu} this
1632 * @param {Roo.EventObject} e
1633 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1638 * Fires when a menu item contained in this menu is clicked
1639 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1640 * @param {Roo.EventObject} e
1644 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1647 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1651 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1654 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1656 registerMenu : true,
1658 menuItems :false, // stores the menu items..
1664 getChildContainer : function() {
1668 getAutoCreate : function(){
1670 //if (['right'].indexOf(this.align)!==-1) {
1671 // cfg.cn[1].cls += ' pull-right'
1677 cls : 'dropdown-menu' ,
1678 style : 'z-index:1000'
1682 if (this.type === 'submenu') {
1683 cfg.cls = 'submenu active';
1685 if (this.type === 'treeview') {
1686 cfg.cls = 'treeview-menu';
1691 initEvents : function() {
1693 // Roo.log("ADD event");
1694 // Roo.log(this.triggerEl.dom);
1695 this.triggerEl.on('click', this.onTriggerPress, this);
1696 this.triggerEl.addClass('dropdown-toggle');
1697 this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1699 this.el.on("mouseover", this.onMouseOver, this);
1700 this.el.on("mouseout", this.onMouseOut, this);
1704 findTargetItem : function(e){
1705 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1709 //Roo.log(t); Roo.log(t.id);
1711 //Roo.log(this.menuitems);
1712 return this.menuitems.get(t.id);
1714 //return this.items.get(t.menuItemId);
1719 onClick : function(e){
1720 Roo.log("menu.onClick");
1721 var t = this.findTargetItem(e);
1722 if(!t || t.isContainer){
1727 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
1728 if(t == this.activeItem && t.shouldDeactivate(e)){
1729 this.activeItem.deactivate();
1730 delete this.activeItem;
1734 this.setActiveItem(t, true);
1742 Roo.log('pass click event');
1746 this.fireEvent("click", this, t, e);
1750 onMouseOver : function(e){
1751 var t = this.findTargetItem(e);
1754 // if(t.canActivate && !t.disabled){
1755 // this.setActiveItem(t, true);
1759 this.fireEvent("mouseover", this, e, t);
1761 isVisible : function(){
1762 return !this.hidden;
1764 onMouseOut : function(e){
1765 var t = this.findTargetItem(e);
1768 // if(t == this.activeItem && t.shouldDeactivate(e)){
1769 // this.activeItem.deactivate();
1770 // delete this.activeItem;
1773 this.fireEvent("mouseout", this, e, t);
1778 * Displays this menu relative to another element
1779 * @param {String/HTMLElement/Roo.Element} element The element to align to
1780 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1781 * the element (defaults to this.defaultAlign)
1782 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1784 show : function(el, pos, parentMenu){
1785 this.parentMenu = parentMenu;
1789 this.fireEvent("beforeshow", this);
1790 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1793 * Displays this menu at a specific xy position
1794 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1795 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1797 showAt : function(xy, parentMenu, /* private: */_e){
1798 this.parentMenu = parentMenu;
1803 this.fireEvent("beforeshow", this);
1805 //xy = this.el.adjustForConstraints(xy);
1807 //this.el.setXY(xy);
1809 this.hideMenuItems();
1810 this.hidden = false;
1811 this.triggerEl.addClass('open');
1813 this.fireEvent("show", this);
1819 this.doFocus.defer(50, this);
1823 doFocus : function(){
1825 this.focusEl.focus();
1830 * Hides this menu and optionally all parent menus
1831 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1833 hide : function(deep){
1835 this.hideMenuItems();
1836 if(this.el && this.isVisible()){
1837 this.fireEvent("beforehide", this);
1838 if(this.activeItem){
1839 this.activeItem.deactivate();
1840 this.activeItem = null;
1842 this.triggerEl.removeClass('open');;
1844 this.fireEvent("hide", this);
1846 if(deep === true && this.parentMenu){
1847 this.parentMenu.hide(true);
1851 onTriggerPress : function(e)
1854 Roo.log('trigger press');
1855 //Roo.log(e.getTarget());
1856 // Roo.log(this.triggerEl.dom);
1857 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1860 if (this.isVisible()) {
1864 this.show(this.triggerEl, false, false);
1873 hideMenuItems : function()
1875 //$(backdrop).remove()
1876 Roo.select('.open',true).each(function(aa) {
1878 aa.removeClass('open');
1879 //var parent = getParent($(this))
1880 //var relatedTarget = { relatedTarget: this }
1882 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1883 //if (e.isDefaultPrevented()) return
1884 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1887 addxtypeChild : function (tree, cntr) {
1888 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1890 this.menuitems.add(comp);
1911 * @class Roo.bootstrap.MenuItem
1912 * @extends Roo.bootstrap.Component
1913 * Bootstrap MenuItem class
1914 * @cfg {String} html the menu label
1915 * @cfg {String} href the link
1916 * @cfg {Boolean} preventDefault (true | false) default true
1917 * @cfg {Boolean} isContainer (true | false) default false
1921 * Create a new MenuItem
1922 * @param {Object} config The config object
1926 Roo.bootstrap.MenuItem = function(config){
1927 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1932 * The raw click event for the entire grid.
1933 * @param {Roo.EventObject} e
1939 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
1943 preventDefault: true,
1944 isContainer : false,
1946 getAutoCreate : function(){
1948 if(this.isContainer){
1951 cls: 'dropdown-menu-item'
1957 cls: 'dropdown-menu-item',
1966 if (this.parent().type == 'treeview') {
1967 cfg.cls = 'treeview-menu';
1970 cfg.cn[0].href = this.href || cfg.cn[0].href ;
1971 cfg.cn[0].html = this.html || cfg.cn[0].html ;
1975 initEvents: function() {
1977 //this.el.select('a').on('click', this.onClick, this);
1980 onClick : function(e)
1982 Roo.log('item on click ');
1983 //if(this.preventDefault){
1984 // e.preventDefault();
1986 //this.parent().hideMenuItems();
1988 this.fireEvent('click', this, e);
2007 * @class Roo.bootstrap.MenuSeparator
2008 * @extends Roo.bootstrap.Component
2009 * Bootstrap MenuSeparator class
2012 * Create a new MenuItem
2013 * @param {Object} config The config object
2017 Roo.bootstrap.MenuSeparator = function(config){
2018 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2021 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2023 getAutoCreate : function(){
2038 <div class="modal fade">
2039 <div class="modal-dialog">
2040 <div class="modal-content">
2041 <div class="modal-header">
2042 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
2043 <h4 class="modal-title">Modal title</h4>
2045 <div class="modal-body">
2046 <p>One fine body…</p>
2048 <div class="modal-footer">
2049 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2050 <button type="button" class="btn btn-primary">Save changes</button>
2052 </div><!-- /.modal-content -->
2053 </div><!-- /.modal-dialog -->
2054 </div><!-- /.modal -->
2064 * @class Roo.bootstrap.Modal
2065 * @extends Roo.bootstrap.Component
2066 * Bootstrap Modal class
2067 * @cfg {String} title Title of dialog
2068 * @cfg {Boolean} specificTitle default false
2069 * @cfg {Array} buttons Array of buttons or standard button set..
2070 * @cfg {String} buttonPosition (left|right|center) default right
2071 * @cfg {Boolean} animate default true
2072 * @cfg {Boolean} allow_close default true
2075 * Create a new Modal Dialog
2076 * @param {Object} config The config object
2079 Roo.bootstrap.Modal = function(config){
2080 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2085 * The raw btnclick event for the button
2086 * @param {Roo.EventObject} e
2090 this.buttons = this.buttons || [];
2093 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2095 title : 'test dialog',
2102 specificTitle: false,
2104 buttonPosition: 'right',
2110 onRender : function(ct, position)
2112 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2115 var cfg = Roo.apply({}, this.getAutoCreate());
2118 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2120 //if (!cfg.name.length) {
2124 cfg.cls += ' ' + this.cls;
2127 cfg.style = this.style;
2129 this.el = Roo.get(document.body).createChild(cfg, position);
2131 //var type = this.el.dom.type;
2133 if(this.tabIndex !== undefined){
2134 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2139 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2140 this.maskEl.enableDisplayMode("block");
2142 //this.el.addClass("x-dlg-modal");
2144 if (this.buttons.length) {
2145 Roo.each(this.buttons, function(bb) {
2146 b = Roo.apply({}, bb);
2147 b.xns = b.xns || Roo.bootstrap;
2148 b.xtype = b.xtype || 'Button';
2149 if (typeof(b.listeners) == 'undefined') {
2150 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2153 var btn = Roo.factory(b);
2155 btn.onRender(this.el.select('.modal-footer div').first());
2159 // render the children.
2162 if(typeof(this.items) != 'undefined'){
2163 var items = this.items;
2166 for(var i =0;i < items.length;i++) {
2167 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2171 this.items = nitems;
2173 this.body = this.el.select('.modal-body',true).first();
2174 this.close = this.el.select('.modal-header .close', true).first();
2175 this.footer = this.el.select('.modal-footer',true).first();
2177 //this.el.addClass([this.fieldClass, this.cls]);
2180 getAutoCreate : function(){
2185 html : this.html || ''
2190 cls : 'modal-title',
2194 if(this.specificTitle){
2200 if (this.allow_close) {
2211 style : 'display: none',
2214 cls: "modal-dialog",
2217 cls : "modal-content",
2220 cls : 'modal-header',
2225 cls : 'modal-footer',
2229 cls: 'btn-' + this.buttonPosition
2246 modal.cls += ' fade';
2252 getChildContainer : function() {
2254 return this.el.select('.modal-body',true).first();
2257 getButtonContainer : function() {
2258 return this.el.select('.modal-footer div',true).first();
2261 initEvents : function()
2263 this.el.select('.modal-header .close').on('click', this.hide, this);
2265 // this.addxtype(this);
2269 if (!this.rendered) {
2273 this.el.setStyle('display', 'block');
2277 (function(){ _this.el.addClass('in'); }).defer(50);
2279 this.el.addClass('in');
2282 Roo.get(document.body).addClass("x-body-masked");
2283 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2285 this.el.setStyle('zIndex', '10001');
2286 this.fireEvent('show', this);
2293 Roo.get(document.body).removeClass("x-body-masked");
2294 this.el.removeClass('in');
2298 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2300 this.el.setStyle('display', 'none');
2303 this.fireEvent('hide', this);
2306 addButton : function(str, cb)
2310 var b = Roo.apply({}, { html : str } );
2311 b.xns = b.xns || Roo.bootstrap;
2312 b.xtype = b.xtype || 'Button';
2313 if (typeof(b.listeners) == 'undefined') {
2314 b.listeners = { click : cb.createDelegate(this) };
2317 var btn = Roo.factory(b);
2319 btn.onRender(this.el.select('.modal-footer div').first());
2325 setDefaultButton : function(btn)
2327 //this.el.select('.modal-footer').()
2329 resizeTo: function(w,h)
2333 setContentSize : function(w, h)
2337 onButtonClick: function(btn,e)
2340 this.fireEvent('btnclick', btn.name, e);
2342 setTitle: function(str) {
2343 this.el.select('.modal-title',true).first().dom.innerHTML = str;
2349 Roo.apply(Roo.bootstrap.Modal, {
2351 * Button config that displays a single OK button
2360 * Button config that displays Yes and No buttons
2376 * Button config that displays OK and Cancel buttons
2391 * Button config that displays Yes, No and Cancel buttons
2413 * messagebox - can be used as a replace
2417 * @class Roo.MessageBox
2418 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2422 Roo.Msg.alert('Status', 'Changes saved successfully.');
2424 // Prompt for user data:
2425 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2427 // process text value...
2431 // Show a dialog using config options:
2433 title:'Save Changes?',
2434 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2435 buttons: Roo.Msg.YESNOCANCEL,
2442 Roo.bootstrap.MessageBox = function(){
2443 var dlg, opt, mask, waitTimer;
2444 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2445 var buttons, activeTextEl, bwidth;
2449 var handleButton = function(button){
2451 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2455 var handleHide = function(){
2457 dlg.el.removeClass(opt.cls);
2460 // Roo.TaskMgr.stop(waitTimer);
2461 // waitTimer = null;
2466 var updateButtons = function(b){
2469 buttons["ok"].hide();
2470 buttons["cancel"].hide();
2471 buttons["yes"].hide();
2472 buttons["no"].hide();
2473 //dlg.footer.dom.style.display = 'none';
2476 dlg.footer.dom.style.display = '';
2477 for(var k in buttons){
2478 if(typeof buttons[k] != "function"){
2481 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2482 width += buttons[k].el.getWidth()+15;
2492 var handleEsc = function(d, k, e){
2493 if(opt && opt.closable !== false){
2503 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2504 * @return {Roo.BasicDialog} The BasicDialog element
2506 getDialog : function(){
2508 dlg = new Roo.bootstrap.Modal( {
2511 //constraintoviewport:false,
2513 //collapsible : false,
2518 //buttonAlign:"center",
2519 closeClick : function(){
2520 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2523 handleButton("cancel");
2528 dlg.on("hide", handleHide);
2530 //dlg.addKeyListener(27, handleEsc);
2532 this.buttons = buttons;
2533 var bt = this.buttonText;
2534 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2535 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2536 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2537 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2539 bodyEl = dlg.body.createChild({
2541 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2542 '<textarea class="roo-mb-textarea"></textarea>' +
2543 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2545 msgEl = bodyEl.dom.firstChild;
2546 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2547 textboxEl.enableDisplayMode();
2548 textboxEl.addKeyListener([10,13], function(){
2549 if(dlg.isVisible() && opt && opt.buttons){
2552 }else if(opt.buttons.yes){
2553 handleButton("yes");
2557 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2558 textareaEl.enableDisplayMode();
2559 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2560 progressEl.enableDisplayMode();
2561 var pf = progressEl.dom.firstChild;
2563 pp = Roo.get(pf.firstChild);
2564 pp.setHeight(pf.offsetHeight);
2572 * Updates the message box body text
2573 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2574 * the XHTML-compliant non-breaking space character '&#160;')
2575 * @return {Roo.MessageBox} This message box
2577 updateText : function(text){
2578 if(!dlg.isVisible() && !opt.width){
2579 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2581 msgEl.innerHTML = text || ' ';
2583 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2584 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2586 Math.min(opt.width || cw , this.maxWidth),
2587 Math.max(opt.minWidth || this.minWidth, bwidth)
2590 activeTextEl.setWidth(w);
2592 if(dlg.isVisible()){
2593 dlg.fixedcenter = false;
2595 // to big, make it scroll. = But as usual stupid IE does not support
2598 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2599 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2600 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2602 bodyEl.dom.style.height = '';
2603 bodyEl.dom.style.overflowY = '';
2606 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2608 bodyEl.dom.style.overflowX = '';
2611 dlg.setContentSize(w, bodyEl.getHeight());
2612 if(dlg.isVisible()){
2613 dlg.fixedcenter = true;
2619 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2620 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2621 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2622 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2623 * @return {Roo.MessageBox} This message box
2625 updateProgress : function(value, text){
2627 this.updateText(text);
2629 if (pp) { // weird bug on my firefox - for some reason this is not defined
2630 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2636 * Returns true if the message box is currently displayed
2637 * @return {Boolean} True if the message box is visible, else false
2639 isVisible : function(){
2640 return dlg && dlg.isVisible();
2644 * Hides the message box if it is displayed
2647 if(this.isVisible()){
2653 * Displays a new message box, or reinitializes an existing message box, based on the config options
2654 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2655 * The following config object properties are supported:
2657 Property Type Description
2658 ---------- --------------- ------------------------------------------------------------------------------------
2659 animEl String/Element An id or Element from which the message box should animate as it opens and
2660 closes (defaults to undefined)
2661 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2662 cancel:'Bar'}), or false to not show any buttons (defaults to false)
2663 closable Boolean False to hide the top-right close button (defaults to true). Note that
2664 progress and wait dialogs will ignore this property and always hide the
2665 close button as they can only be closed programmatically.
2666 cls String A custom CSS class to apply to the message box element
2667 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
2668 displayed (defaults to 75)
2669 fn Function A callback function to execute after closing the dialog. The arguments to the
2670 function will be btn (the name of the button that was clicked, if applicable,
2671 e.g. "ok"), and text (the value of the active text field, if applicable).
2672 Progress and wait dialogs will ignore this option since they do not respond to
2673 user actions and can only be closed programmatically, so any required function
2674 should be called by the same code after it closes the dialog.
2675 icon String A CSS class that provides a background image to be used as an icon for
2676 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2677 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
2678 minWidth Number The minimum width in pixels of the message box (defaults to 100)
2679 modal Boolean False to allow user interaction with the page while the message box is
2680 displayed (defaults to true)
2681 msg String A string that will replace the existing message box body text (defaults
2682 to the XHTML-compliant non-breaking space character ' ')
2683 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
2684 progress Boolean True to display a progress bar (defaults to false)
2685 progressText String The text to display inside the progress bar if progress = true (defaults to '')
2686 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
2687 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
2688 title String The title text
2689 value String The string value to set into the active textbox element if displayed
2690 wait Boolean True to display a progress bar (defaults to false)
2691 width Number The width of the dialog in pixels
2698 msg: 'Please enter your address:',
2700 buttons: Roo.MessageBox.OKCANCEL,
2703 animEl: 'addAddressBtn'
2706 * @param {Object} config Configuration options
2707 * @return {Roo.MessageBox} This message box
2709 show : function(options)
2712 // this causes nightmares if you show one dialog after another
2713 // especially on callbacks..
2715 if(this.isVisible()){
2718 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2719 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
2720 Roo.log("New Dialog Message:" + options.msg )
2721 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2722 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2725 var d = this.getDialog();
2727 d.setTitle(opt.title || " ");
2728 d.close.setDisplayed(opt.closable !== false);
2729 activeTextEl = textboxEl;
2730 opt.prompt = opt.prompt || (opt.multiline ? true : false);
2735 textareaEl.setHeight(typeof opt.multiline == "number" ?
2736 opt.multiline : this.defaultTextHeight);
2737 activeTextEl = textareaEl;
2746 progressEl.setDisplayed(opt.progress === true);
2747 this.updateProgress(0);
2748 activeTextEl.dom.value = opt.value || "";
2750 dlg.setDefaultButton(activeTextEl);
2752 var bs = opt.buttons;
2756 }else if(bs && bs.yes){
2757 db = buttons["yes"];
2759 dlg.setDefaultButton(db);
2761 bwidth = updateButtons(opt.buttons);
2762 this.updateText(opt.msg);
2764 d.el.addClass(opt.cls);
2766 d.proxyDrag = opt.proxyDrag === true;
2767 d.modal = opt.modal !== false;
2768 d.mask = opt.modal !== false ? mask : false;
2770 // force it to the end of the z-index stack so it gets a cursor in FF
2771 document.body.appendChild(dlg.el.dom);
2772 d.animateTarget = null;
2773 d.show(options.animEl);
2779 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
2780 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2781 * and closing the message box when the process is complete.
2782 * @param {String} title The title bar text
2783 * @param {String} msg The message box body text
2784 * @return {Roo.MessageBox} This message box
2786 progress : function(title, msg){
2793 minWidth: this.minProgressWidth,
2800 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2801 * If a callback function is passed it will be called after the user clicks the button, and the
2802 * id of the button that was clicked will be passed as the only parameter to the callback
2803 * (could also be the top-right close button).
2804 * @param {String} title The title bar text
2805 * @param {String} msg The message box body text
2806 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2807 * @param {Object} scope (optional) The scope of the callback function
2808 * @return {Roo.MessageBox} This message box
2810 alert : function(title, msg, fn, scope){
2823 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
2824 * interaction while waiting for a long-running process to complete that does not have defined intervals.
2825 * You are responsible for closing the message box when the process is complete.
2826 * @param {String} msg The message box body text
2827 * @param {String} title (optional) The title bar text
2828 * @return {Roo.MessageBox} This message box
2830 wait : function(msg, title){
2841 waitTimer = Roo.TaskMgr.start({
2843 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2851 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2852 * If a callback function is passed it will be called after the user clicks either button, and the id of the
2853 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2854 * @param {String} title The title bar text
2855 * @param {String} msg The message box body text
2856 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2857 * @param {Object} scope (optional) The scope of the callback function
2858 * @return {Roo.MessageBox} This message box
2860 confirm : function(title, msg, fn, scope){
2864 buttons: this.YESNO,
2873 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2874 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
2875 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2876 * (could also be the top-right close button) and the text that was entered will be passed as the two
2877 * parameters to the callback.
2878 * @param {String} title The title bar text
2879 * @param {String} msg The message box body text
2880 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2881 * @param {Object} scope (optional) The scope of the callback function
2882 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2883 * property, or the height in pixels to create the textbox (defaults to false / single-line)
2884 * @return {Roo.MessageBox} This message box
2886 prompt : function(title, msg, fn, scope, multiline){
2890 buttons: this.OKCANCEL,
2895 multiline: multiline,
2902 * Button config that displays a single OK button
2907 * Button config that displays Yes and No buttons
2910 YESNO : {yes:true, no:true},
2912 * Button config that displays OK and Cancel buttons
2915 OKCANCEL : {ok:true, cancel:true},
2917 * Button config that displays Yes, No and Cancel buttons
2920 YESNOCANCEL : {yes:true, no:true, cancel:true},
2923 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2926 defaultTextHeight : 75,
2928 * The maximum width in pixels of the message box (defaults to 600)
2933 * The minimum width in pixels of the message box (defaults to 100)
2938 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
2939 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2942 minProgressWidth : 250,
2944 * An object containing the default button text strings that can be overriden for localized language support.
2945 * Supported properties are: ok, cancel, yes and no.
2946 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2959 * Shorthand for {@link Roo.MessageBox}
2961 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox
2962 Roo.Msg = Roo.Msg || Roo.MessageBox;
2971 * @class Roo.bootstrap.Navbar
2972 * @extends Roo.bootstrap.Component
2973 * Bootstrap Navbar class
2976 * Create a new Navbar
2977 * @param {Object} config The config object
2981 Roo.bootstrap.Navbar = function(config){
2982 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2986 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
2995 getAutoCreate : function(){
2998 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3002 initEvents :function ()
3004 //Roo.log(this.el.select('.navbar-toggle',true));
3005 this.el.select('.navbar-toggle',true).on('click', function() {
3006 // Roo.log('click');
3007 this.el.select('.navbar-collapse',true).toggleClass('in');
3015 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3017 var size = this.el.getSize();
3018 this.maskEl.setSize(size.width, size.height);
3019 this.maskEl.enableDisplayMode("block");
3028 getChildContainer : function()
3030 if (this.el.select('.collapse').getCount()) {
3031 return this.el.select('.collapse',true).first();
3064 * @class Roo.bootstrap.NavSimplebar
3065 * @extends Roo.bootstrap.Navbar
3066 * Bootstrap Sidebar class
3068 * @cfg {Boolean} inverse is inverted color
3070 * @cfg {String} type (nav | pills | tabs)
3071 * @cfg {Boolean} arrangement stacked | justified
3072 * @cfg {String} align (left | right) alignment
3074 * @cfg {Boolean} main (true|false) main nav bar? default false
3075 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3077 * @cfg {String} tag (header|footer|nav|div) default is nav
3083 * Create a new Sidebar
3084 * @param {Object} config The config object
3088 Roo.bootstrap.NavSimplebar = function(config){
3089 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3092 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3108 getAutoCreate : function(){
3112 tag : this.tag || 'div',
3125 this.type = this.type || 'nav';
3126 if (['tabs','pills'].indexOf(this.type)!==-1) {
3127 cfg.cn[0].cls += ' nav-' + this.type
3131 if (this.type!=='nav') {
3132 Roo.log('nav type must be nav/tabs/pills')
3134 cfg.cn[0].cls += ' navbar-nav'
3140 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3141 cfg.cn[0].cls += ' nav-' + this.arrangement;
3145 if (this.align === 'right') {
3146 cfg.cn[0].cls += ' navbar-right';
3150 cfg.cls += ' navbar-inverse';
3177 * @class Roo.bootstrap.NavHeaderbar
3178 * @extends Roo.bootstrap.NavSimplebar
3179 * Bootstrap Sidebar class
3181 * @cfg {String} brand what is brand
3182 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3183 * @cfg {String} brand_href href of the brand
3184 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3185 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3186 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3189 * Create a new Sidebar
3190 * @param {Object} config The config object
3194 Roo.bootstrap.NavHeaderbar = function(config){
3195 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3199 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3208 getAutoCreate : function(){
3211 tag: this.nav || 'nav',
3220 cls: 'navbar-header',
3225 cls: 'navbar-toggle',
3226 'data-toggle': 'collapse',
3231 html: 'Toggle navigation'
3253 cls: 'collapse navbar-collapse',
3257 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3259 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3260 cfg.cls += ' navbar-' + this.position;
3262 // tag can override this..
3264 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3267 if (this.brand !== '') {
3270 href: this.brand_href ? this.brand_href : '#',
3271 cls: 'navbar-brand',
3279 cfg.cls += ' main-nav';
3287 getHeaderChildContainer : function()
3289 if (this.el.select('.navbar-header').getCount()) {
3290 return this.el.select('.navbar-header',true).first();
3293 return this.getChildContainer();
3297 initEvents : function()
3299 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3301 if (this.autohide) {
3306 Roo.get(document).on('scroll',function(e) {
3307 var ns = Roo.get(document).getScroll().top;
3308 var os = prevScroll;
3312 ft.removeClass('slideDown');
3313 ft.addClass('slideUp');
3316 ft.removeClass('slideUp');
3317 ft.addClass('slideDown');
3341 * @class Roo.bootstrap.NavSidebar
3342 * @extends Roo.bootstrap.Navbar
3343 * Bootstrap Sidebar class
3346 * Create a new Sidebar
3347 * @param {Object} config The config object
3351 Roo.bootstrap.NavSidebar = function(config){
3352 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3355 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3357 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3359 getAutoCreate : function(){
3364 cls: 'sidebar sidebar-nav'
3386 * @class Roo.bootstrap.NavGroup
3387 * @extends Roo.bootstrap.Component
3388 * Bootstrap NavGroup class
3389 * @cfg {String} align left | right
3390 * @cfg {Boolean} inverse false | true
3391 * @cfg {String} type (nav|pills|tab) default nav
3392 * @cfg {String} navId - reference Id for navbar.
3396 * Create a new nav group
3397 * @param {Object} config The config object
3400 Roo.bootstrap.NavGroup = function(config){
3401 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3404 Roo.bootstrap.NavGroup.register(this);
3408 * Fires when the active item changes
3409 * @param {Roo.bootstrap.NavGroup} this
3410 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3411 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3418 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3429 getAutoCreate : function()
3431 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3438 if (['tabs','pills'].indexOf(this.type)!==-1) {
3439 cfg.cls += ' nav-' + this.type
3441 if (this.type!=='nav') {
3442 Roo.log('nav type must be nav/tabs/pills')
3444 cfg.cls += ' navbar-nav'
3447 if (this.parent().sidebar) {
3450 cls: 'dashboard-menu sidebar-menu'
3456 if (this.form === true) {
3462 if (this.align === 'right') {
3463 cfg.cls += ' navbar-right';
3465 cfg.cls += ' navbar-left';
3469 if (this.align === 'right') {
3470 cfg.cls += ' navbar-right';
3474 cfg.cls += ' navbar-inverse';
3482 * sets the active Navigation item
3483 * @param {Roo.bootstrap.NavItem} the new current navitem
3485 setActiveItem : function(item)
3488 Roo.each(this.navItems, function(v){
3493 v.setActive(false, true);
3500 item.setActive(true, true);
3501 this.fireEvent('changed', this, item, prev);
3506 * gets the active Navigation item
3507 * @return {Roo.bootstrap.NavItem} the current navitem
3509 getActive : function()
3513 Roo.each(this.navItems, function(v){
3524 indexOfNav : function()
3528 Roo.each(this.navItems, function(v,i){
3539 * adds a Navigation item
3540 * @param {Roo.bootstrap.NavItem} the navitem to add
3542 addItem : function(cfg)
3544 var cn = new Roo.bootstrap.NavItem(cfg);
3546 cn.parentId = this.id;
3547 cn.onRender(this.el, null);
3551 * register a Navigation item
3552 * @param {Roo.bootstrap.NavItem} the navitem to add
3554 register : function(item)
3556 this.navItems.push( item);
3557 item.navId = this.navId;
3562 * clear all the Navigation item
3565 clearAll : function()
3568 this.el.dom.innerHTML = '';
3571 getNavItem: function(tabId)
3574 Roo.each(this.navItems, function(e) {
3575 if (e.tabId == tabId) {
3585 setActiveNext : function()
3587 var i = this.indexOfNav(this.getActive());
3588 if (i > this.navItems.length) {
3591 this.setActiveItem(this.navItems[i+1]);
3593 setActivePrev : function()
3595 var i = this.indexOfNav(this.getActive());
3599 this.setActiveItem(this.navItems[i-1]);
3601 clearWasActive : function(except) {
3602 Roo.each(this.navItems, function(e) {
3603 if (e.tabId != except.tabId && e.was_active) {
3604 e.was_active = false;
3611 getWasActive : function ()
3614 Roo.each(this.navItems, function(e) {
3629 Roo.apply(Roo.bootstrap.NavGroup, {
3633 * register a Navigation Group
3634 * @param {Roo.bootstrap.NavGroup} the navgroup to add
3636 register : function(navgrp)
3638 this.groups[navgrp.navId] = navgrp;
3642 * fetch a Navigation Group based on the navigation ID
3643 * @param {string} the navgroup to add
3644 * @returns {Roo.bootstrap.NavGroup} the navgroup
3646 get: function(navId) {
3647 if (typeof(this.groups[navId]) == 'undefined') {
3649 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3651 return this.groups[navId] ;
3666 * @class Roo.bootstrap.NavItem
3667 * @extends Roo.bootstrap.Component
3668 * Bootstrap Navbar.NavItem class
3669 * @cfg {String} href link to
3670 * @cfg {String} html content of button
3671 * @cfg {String} badge text inside badge
3672 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3673 * @cfg {String} glyphicon name of glyphicon
3674 * @cfg {String} icon name of font awesome icon
3675 * @cfg {Boolean} active Is item active
3676 * @cfg {Boolean} disabled Is item disabled
3678 * @cfg {Boolean} preventDefault (true | false) default false
3679 * @cfg {String} tabId the tab that this item activates.
3680 * @cfg {String} tagtype (a|span) render as a href or span?
3683 * Create a new Navbar Item
3684 * @param {Object} config The config object
3686 Roo.bootstrap.NavItem = function(config){
3687 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3692 * The raw click event for the entire grid.
3693 * @param {Roo.EventObject} e
3698 * Fires when the active item active state changes
3699 * @param {Roo.bootstrap.NavItem} this
3700 * @param {boolean} state the new state
3708 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
3716 preventDefault : false,
3723 getAutoCreate : function(){
3731 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3733 if (this.disabled) {
3734 cfg.cls += ' disabled';
3737 if (this.href || this.html || this.glyphicon || this.icon) {
3741 href : this.href || "#",
3742 html: this.html || ''
3747 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3750 if(this.glyphicon) {
3751 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
3756 cfg.cn[0].html += " <span class='caret'></span>";
3760 if (this.badge !== '') {
3762 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3770 initEvents: function()
3772 if (typeof (this.menu) != 'undefined') {
3773 this.menu.parentType = this.xtype;
3774 this.menu.triggerEl = this.el;
3775 this.addxtype(Roo.apply({}, this.menu));
3778 this.el.select('a',true).on('click', this.onClick, this);
3780 if(this.tagtype == 'span'){
3781 this.el.select('span',true).on('click', this.onClick, this);
3784 // at this point parent should be available..
3785 this.parent().register(this);
3788 onClick : function(e)
3791 if(this.preventDefault){
3794 if (this.disabled) {
3798 var tg = Roo.bootstrap.TabGroup.get(this.navId);
3799 if (tg && tg.transition) {
3800 Roo.log("waiting for the transitionend");
3804 Roo.log("fire event clicked");
3805 if(this.fireEvent('click', this, e) === false){
3809 if(this.tagtype == 'span'){
3813 var p = this.parent();
3814 if (['tabs','pills'].indexOf(p.type)!==-1) {
3815 if (typeof(p.setActiveItem) !== 'undefined') {
3816 p.setActiveItem(this);
3819 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3820 if (p.parentType == 'NavHeaderbar' && !this.menu) {
3821 // remove the collapsed menu expand...
3822 p.parent().el.select('.navbar-collapse',true).removeClass('in');
3827 isActive: function () {
3830 setActive : function(state, fire, is_was_active)
3832 if (this.active && !state & this.navId) {
3833 this.was_active = true;
3834 var nv = Roo.bootstrap.NavGroup.get(this.navId);
3836 nv.clearWasActive(this);
3840 this.active = state;
3843 this.el.removeClass('active');
3844 } else if (!this.el.hasClass('active')) {
3845 this.el.addClass('active');
3848 this.fireEvent('changed', this, state);
3851 // show a panel if it's registered and related..
3853 if (!this.navId || !this.tabId || !state || is_was_active) {
3857 var tg = Roo.bootstrap.TabGroup.get(this.navId);
3861 var pan = tg.getPanelByName(this.tabId);
3865 // if we can not flip to new panel - go back to old nav highlight..
3866 if (false == tg.showPanel(pan)) {
3867 var nv = Roo.bootstrap.NavGroup.get(this.navId);
3869 var onav = nv.getWasActive();
3871 onav.setActive(true, false, true);
3880 // this should not be here...
3881 setDisabled : function(state)
3883 this.disabled = state;
3885 this.el.removeClass('disabled');
3886 } else if (!this.el.hasClass('disabled')) {
3887 this.el.addClass('disabled');
3893 * Fetch the element to display the tooltip on.
3894 * @return {Roo.Element} defaults to this.el
3896 tooltipEl : function()
3898 return this.el.select('' + this.tagtype + '', true).first();
3909 * <span> icon </span>
3910 * <span> text </span>
3911 * <span>badge </span>
3915 * @class Roo.bootstrap.NavSidebarItem
3916 * @extends Roo.bootstrap.NavItem
3917 * Bootstrap Navbar.NavSidebarItem class
3919 * Create a new Navbar Button
3920 * @param {Object} config The config object
3922 Roo.bootstrap.NavSidebarItem = function(config){
3923 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3928 * The raw click event for the entire grid.
3929 * @param {Roo.EventObject} e
3934 * Fires when the active item active state changes
3935 * @param {Roo.bootstrap.NavSidebarItem} this
3936 * @param {boolean} state the new state
3944 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
3947 getAutoCreate : function(){
3952 href : this.href || '#',
3964 html : this.html || ''
3969 cfg.cls += ' active';
3973 if (this.glyphicon || this.icon) {
3974 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
3975 a.cn.push({ tag : 'i', cls : c }) ;
3980 if (this.badge !== '') {
3981 a.cn.push({ tag: 'span', cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge });
3985 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3986 a.cls += 'dropdown-toggle treeview' ;
4010 * @class Roo.bootstrap.Row
4011 * @extends Roo.bootstrap.Component
4012 * Bootstrap Row class (contains columns...)
4016 * @param {Object} config The config object
4019 Roo.bootstrap.Row = function(config){
4020 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4023 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4025 getAutoCreate : function(){
4044 * @class Roo.bootstrap.Element
4045 * @extends Roo.bootstrap.Component
4046 * Bootstrap Element class
4047 * @cfg {String} html contents of the element
4048 * @cfg {String} tag tag of the element
4049 * @cfg {String} cls class of the element
4052 * Create a new Element
4053 * @param {Object} config The config object
4056 Roo.bootstrap.Element = function(config){
4057 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4060 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4067 getAutoCreate : function(){
4092 * @class Roo.bootstrap.Pagination
4093 * @extends Roo.bootstrap.Component
4094 * Bootstrap Pagination class
4095 * @cfg {String} size xs | sm | md | lg
4096 * @cfg {Boolean} inverse false | true
4099 * Create a new Pagination
4100 * @param {Object} config The config object
4103 Roo.bootstrap.Pagination = function(config){
4104 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4107 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4113 getAutoCreate : function(){
4119 cfg.cls += ' inverse';
4125 cfg.cls += " " + this.cls;
4143 * @class Roo.bootstrap.PaginationItem
4144 * @extends Roo.bootstrap.Component
4145 * Bootstrap PaginationItem class
4146 * @cfg {String} html text
4147 * @cfg {String} href the link
4148 * @cfg {Boolean} preventDefault (true | false) default true
4149 * @cfg {Boolean} active (true | false) default false
4153 * Create a new PaginationItem
4154 * @param {Object} config The config object
4158 Roo.bootstrap.PaginationItem = function(config){
4159 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4164 * The raw click event for the entire grid.
4165 * @param {Roo.EventObject} e
4171 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4175 preventDefault: true,
4179 getAutoCreate : function(){
4185 href : this.href ? this.href : '#',
4186 html : this.html ? this.html : ''
4196 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4202 initEvents: function() {
4204 this.el.on('click', this.onClick, this);
4207 onClick : function(e)
4209 Roo.log('PaginationItem on click ');
4210 if(this.preventDefault){
4214 this.fireEvent('click', this, e);
4230 * @class Roo.bootstrap.Slider
4231 * @extends Roo.bootstrap.Component
4232 * Bootstrap Slider class
4235 * Create a new Slider
4236 * @param {Object} config The config object
4239 Roo.bootstrap.Slider = function(config){
4240 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4243 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4245 getAutoCreate : function(){
4249 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4253 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4265 * Ext JS Library 1.1.1
4266 * Copyright(c) 2006-2007, Ext JS, LLC.
4268 * Originally Released Under LGPL - original licence link has changed is not relivant.
4271 * <script type="text/javascript">
4276 * @class Roo.grid.ColumnModel
4277 * @extends Roo.util.Observable
4278 * This is the default implementation of a ColumnModel used by the Grid. It defines
4279 * the columns in the grid.
4282 var colModel = new Roo.grid.ColumnModel([
4283 {header: "Ticker", width: 60, sortable: true, locked: true},
4284 {header: "Company Name", width: 150, sortable: true},
4285 {header: "Market Cap.", width: 100, sortable: true},
4286 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4287 {header: "Employees", width: 100, sortable: true, resizable: false}
4292 * The config options listed for this class are options which may appear in each
4293 * individual column definition.
4294 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4296 * @param {Object} config An Array of column config objects. See this class's
4297 * config objects for details.
4299 Roo.grid.ColumnModel = function(config){
4301 * The config passed into the constructor
4303 this.config = config;
4306 // if no id, create one
4307 // if the column does not have a dataIndex mapping,
4308 // map it to the order it is in the config
4309 for(var i = 0, len = config.length; i < len; i++){
4311 if(typeof c.dataIndex == "undefined"){
4314 if(typeof c.renderer == "string"){
4315 c.renderer = Roo.util.Format[c.renderer];
4317 if(typeof c.id == "undefined"){
4320 if(c.editor && c.editor.xtype){
4321 c.editor = Roo.factory(c.editor, Roo.grid);
4323 if(c.editor && c.editor.isFormField){
4324 c.editor = new Roo.grid.GridEditor(c.editor);
4326 this.lookup[c.id] = c;
4330 * The width of columns which have no width specified (defaults to 100)
4333 this.defaultWidth = 100;
4336 * Default sortable of columns which have no sortable specified (defaults to false)
4339 this.defaultSortable = false;
4343 * @event widthchange
4344 * Fires when the width of a column changes.
4345 * @param {ColumnModel} this
4346 * @param {Number} columnIndex The column index
4347 * @param {Number} newWidth The new width
4349 "widthchange": true,
4351 * @event headerchange
4352 * Fires when the text of a header changes.
4353 * @param {ColumnModel} this
4354 * @param {Number} columnIndex The column index
4355 * @param {Number} newText The new header text
4357 "headerchange": true,
4359 * @event hiddenchange
4360 * Fires when a column is hidden or "unhidden".
4361 * @param {ColumnModel} this
4362 * @param {Number} columnIndex The column index
4363 * @param {Boolean} hidden true if hidden, false otherwise
4365 "hiddenchange": true,
4367 * @event columnmoved
4368 * Fires when a column is moved.
4369 * @param {ColumnModel} this
4370 * @param {Number} oldIndex
4371 * @param {Number} newIndex
4373 "columnmoved" : true,
4375 * @event columlockchange
4376 * Fires when a column's locked state is changed
4377 * @param {ColumnModel} this
4378 * @param {Number} colIndex
4379 * @param {Boolean} locked true if locked
4381 "columnlockchange" : true
4383 Roo.grid.ColumnModel.superclass.constructor.call(this);
4385 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4387 * @cfg {String} header The header text to display in the Grid view.
4390 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4391 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4392 * specified, the column's index is used as an index into the Record's data Array.
4395 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4396 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4399 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4400 * Defaults to the value of the {@link #defaultSortable} property.
4401 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4404 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4407 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4410 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4413 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4416 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4417 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4418 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4419 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4422 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4425 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4428 * @cfg {String} cursor (Optional)
4431 * Returns the id of the column at the specified index.
4432 * @param {Number} index The column index
4433 * @return {String} the id
4435 getColumnId : function(index){
4436 return this.config[index].id;
4440 * Returns the column for a specified id.
4441 * @param {String} id The column id
4442 * @return {Object} the column
4444 getColumnById : function(id){
4445 return this.lookup[id];
4450 * Returns the column for a specified dataIndex.
4451 * @param {String} dataIndex The column dataIndex
4452 * @return {Object|Boolean} the column or false if not found
4454 getColumnByDataIndex: function(dataIndex){
4455 var index = this.findColumnIndex(dataIndex);
4456 return index > -1 ? this.config[index] : false;
4460 * Returns the index for a specified column id.
4461 * @param {String} id The column id
4462 * @return {Number} the index, or -1 if not found
4464 getIndexById : function(id){
4465 for(var i = 0, len = this.config.length; i < len; i++){
4466 if(this.config[i].id == id){
4474 * Returns the index for a specified column dataIndex.
4475 * @param {String} dataIndex The column dataIndex
4476 * @return {Number} the index, or -1 if not found
4479 findColumnIndex : function(dataIndex){
4480 for(var i = 0, len = this.config.length; i < len; i++){
4481 if(this.config[i].dataIndex == dataIndex){
4489 moveColumn : function(oldIndex, newIndex){
4490 var c = this.config[oldIndex];
4491 this.config.splice(oldIndex, 1);
4492 this.config.splice(newIndex, 0, c);
4493 this.dataMap = null;
4494 this.fireEvent("columnmoved", this, oldIndex, newIndex);
4497 isLocked : function(colIndex){
4498 return this.config[colIndex].locked === true;
4501 setLocked : function(colIndex, value, suppressEvent){
4502 if(this.isLocked(colIndex) == value){
4505 this.config[colIndex].locked = value;
4507 this.fireEvent("columnlockchange", this, colIndex, value);
4511 getTotalLockedWidth : function(){
4513 for(var i = 0; i < this.config.length; i++){
4514 if(this.isLocked(i) && !this.isHidden(i)){
4515 this.totalWidth += this.getColumnWidth(i);
4521 getLockedCount : function(){
4522 for(var i = 0, len = this.config.length; i < len; i++){
4523 if(!this.isLocked(i)){
4530 * Returns the number of columns.
4533 getColumnCount : function(visibleOnly){
4534 if(visibleOnly === true){
4536 for(var i = 0, len = this.config.length; i < len; i++){
4537 if(!this.isHidden(i)){
4543 return this.config.length;
4547 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4548 * @param {Function} fn
4549 * @param {Object} scope (optional)
4550 * @return {Array} result
4552 getColumnsBy : function(fn, scope){
4554 for(var i = 0, len = this.config.length; i < len; i++){
4555 var c = this.config[i];
4556 if(fn.call(scope||this, c, i) === true){
4564 * Returns true if the specified column is sortable.
4565 * @param {Number} col The column index
4568 isSortable : function(col){
4569 if(typeof this.config[col].sortable == "undefined"){
4570 return this.defaultSortable;
4572 return this.config[col].sortable;
4576 * Returns the rendering (formatting) function defined for the column.
4577 * @param {Number} col The column index.
4578 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4580 getRenderer : function(col){
4581 if(!this.config[col].renderer){
4582 return Roo.grid.ColumnModel.defaultRenderer;
4584 return this.config[col].renderer;
4588 * Sets the rendering (formatting) function for a column.
4589 * @param {Number} col The column index
4590 * @param {Function} fn The function to use to process the cell's raw data
4591 * to return HTML markup for the grid view. The render function is called with
4592 * the following parameters:<ul>
4593 * <li>Data value.</li>
4594 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4595 * <li>css A CSS style string to apply to the table cell.</li>
4596 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4597 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4598 * <li>Row index</li>
4599 * <li>Column index</li>
4600 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4602 setRenderer : function(col, fn){
4603 this.config[col].renderer = fn;
4607 * Returns the width for the specified column.
4608 * @param {Number} col The column index
4611 getColumnWidth : function(col){
4612 return this.config[col].width * 1 || this.defaultWidth;
4616 * Sets the width for a column.
4617 * @param {Number} col The column index
4618 * @param {Number} width The new width
4620 setColumnWidth : function(col, width, suppressEvent){
4621 this.config[col].width = width;
4622 this.totalWidth = null;
4624 this.fireEvent("widthchange", this, col, width);
4629 * Returns the total width of all columns.
4630 * @param {Boolean} includeHidden True to include hidden column widths
4633 getTotalWidth : function(includeHidden){
4634 if(!this.totalWidth){
4635 this.totalWidth = 0;
4636 for(var i = 0, len = this.config.length; i < len; i++){
4637 if(includeHidden || !this.isHidden(i)){
4638 this.totalWidth += this.getColumnWidth(i);
4642 return this.totalWidth;
4646 * Returns the header for the specified column.
4647 * @param {Number} col The column index
4650 getColumnHeader : function(col){
4651 return this.config[col].header;
4655 * Sets the header for a column.
4656 * @param {Number} col The column index
4657 * @param {String} header The new header
4659 setColumnHeader : function(col, header){
4660 this.config[col].header = header;
4661 this.fireEvent("headerchange", this, col, header);
4665 * Returns the tooltip for the specified column.
4666 * @param {Number} col The column index
4669 getColumnTooltip : function(col){
4670 return this.config[col].tooltip;
4673 * Sets the tooltip for a column.
4674 * @param {Number} col The column index
4675 * @param {String} tooltip The new tooltip
4677 setColumnTooltip : function(col, tooltip){
4678 this.config[col].tooltip = tooltip;
4682 * Returns the dataIndex for the specified column.
4683 * @param {Number} col The column index
4686 getDataIndex : function(col){
4687 return this.config[col].dataIndex;
4691 * Sets the dataIndex for a column.
4692 * @param {Number} col The column index
4693 * @param {Number} dataIndex The new dataIndex
4695 setDataIndex : function(col, dataIndex){
4696 this.config[col].dataIndex = dataIndex;
4702 * Returns true if the cell is editable.
4703 * @param {Number} colIndex The column index
4704 * @param {Number} rowIndex The row index
4707 isCellEditable : function(colIndex, rowIndex){
4708 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4712 * Returns the editor defined for the cell/column.
4713 * return false or null to disable editing.
4714 * @param {Number} colIndex The column index
4715 * @param {Number} rowIndex The row index
4718 getCellEditor : function(colIndex, rowIndex){
4719 return this.config[colIndex].editor;
4723 * Sets if a column is editable.
4724 * @param {Number} col The column index
4725 * @param {Boolean} editable True if the column is editable
4727 setEditable : function(col, editable){
4728 this.config[col].editable = editable;
4733 * Returns true if the column is hidden.
4734 * @param {Number} colIndex The column index
4737 isHidden : function(colIndex){
4738 return this.config[colIndex].hidden;
4743 * Returns true if the column width cannot be changed
4745 isFixed : function(colIndex){
4746 return this.config[colIndex].fixed;
4750 * Returns true if the column can be resized
4753 isResizable : function(colIndex){
4754 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4757 * Sets if a column is hidden.
4758 * @param {Number} colIndex The column index
4759 * @param {Boolean} hidden True if the column is hidden
4761 setHidden : function(colIndex, hidden){
4762 this.config[colIndex].hidden = hidden;
4763 this.totalWidth = null;
4764 this.fireEvent("hiddenchange", this, colIndex, hidden);
4768 * Sets the editor for a column.
4769 * @param {Number} col The column index
4770 * @param {Object} editor The editor object
4772 setEditor : function(col, editor){
4773 this.config[col].editor = editor;
4777 Roo.grid.ColumnModel.defaultRenderer = function(value){
4778 if(typeof value == "string" && value.length < 1){
4784 // Alias for backwards compatibility
4785 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4788 * Ext JS Library 1.1.1
4789 * Copyright(c) 2006-2007, Ext JS, LLC.
4791 * Originally Released Under LGPL - original licence link has changed is not relivant.
4794 * <script type="text/javascript">
4798 * @class Roo.LoadMask
4799 * A simple utility class for generically masking elements while loading data. If the element being masked has
4800 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4801 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
4802 * element's UpdateManager load indicator and will be destroyed after the initial load.
4804 * Create a new LoadMask
4805 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4806 * @param {Object} config The config object
4808 Roo.LoadMask = function(el, config){
4809 this.el = Roo.get(el);
4810 Roo.apply(this, config);
4812 this.store.on('beforeload', this.onBeforeLoad, this);
4813 this.store.on('load', this.onLoad, this);
4814 this.store.on('loadexception', this.onLoadException, this);
4815 this.removeMask = false;
4817 var um = this.el.getUpdateManager();
4818 um.showLoadIndicator = false; // disable the default indicator
4819 um.on('beforeupdate', this.onBeforeLoad, this);
4820 um.on('update', this.onLoad, this);
4821 um.on('failure', this.onLoad, this);
4822 this.removeMask = true;
4826 Roo.LoadMask.prototype = {
4828 * @cfg {Boolean} removeMask
4829 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4830 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
4834 * The text to display in a centered loading message box (defaults to 'Loading...')
4838 * @cfg {String} msgCls
4839 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4841 msgCls : 'x-mask-loading',
4844 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4850 * Disables the mask to prevent it from being displayed
4852 disable : function(){
4853 this.disabled = true;
4857 * Enables the mask so that it can be displayed
4859 enable : function(){
4860 this.disabled = false;
4863 onLoadException : function()
4867 if (typeof(arguments[3]) != 'undefined') {
4868 Roo.MessageBox.alert("Error loading",arguments[3]);
4872 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4873 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4882 this.el.unmask(this.removeMask);
4887 this.el.unmask(this.removeMask);
4891 onBeforeLoad : function(){
4893 this.el.mask(this.msg, this.msgCls);
4898 destroy : function(){
4900 this.store.un('beforeload', this.onBeforeLoad, this);
4901 this.store.un('load', this.onLoad, this);
4902 this.store.un('loadexception', this.onLoadException, this);
4904 var um = this.el.getUpdateManager();
4905 um.un('beforeupdate', this.onBeforeLoad, this);
4906 um.un('update', this.onLoad, this);
4907 um.un('failure', this.onLoad, this);
4918 * @class Roo.bootstrap.Table
4919 * @extends Roo.bootstrap.Component
4920 * Bootstrap Table class
4921 * @cfg {String} cls table class
4922 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4923 * @cfg {String} bgcolor Specifies the background color for a table
4924 * @cfg {Number} border Specifies whether the table cells should have borders or not
4925 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4926 * @cfg {Number} cellspacing Specifies the space between cells
4927 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4928 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4929 * @cfg {String} sortable Specifies that the table should be sortable
4930 * @cfg {String} summary Specifies a summary of the content of a table
4931 * @cfg {Number} width Specifies the width of a table
4932 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4934 * @cfg {boolean} striped Should the rows be alternative striped
4935 * @cfg {boolean} bordered Add borders to the table
4936 * @cfg {boolean} hover Add hover highlighting
4937 * @cfg {boolean} condensed Format condensed
4938 * @cfg {boolean} responsive Format condensed
4939 * @cfg {Boolean} loadMask (true|false) default false
4940 * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4941 * @cfg {Boolean} thead (true|false) generate thead, default true
4942 * @cfg {Boolean} RowSelection (true|false) default false
4943 * @cfg {Boolean} CellSelection (true|false) default false
4944 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
4948 * Create a new Table
4949 * @param {Object} config The config object
4952 Roo.bootstrap.Table = function(config){
4953 Roo.bootstrap.Table.superclass.constructor.call(this, config);
4956 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4957 this.sm = this.selModel;
4958 this.sm.xmodule = this.xmodule || false;
4960 if (this.cm && typeof(this.cm.config) == 'undefined') {
4961 this.colModel = new Roo.grid.ColumnModel(this.cm);
4962 this.cm = this.colModel;
4963 this.cm.xmodule = this.xmodule || false;
4966 this.store= Roo.factory(this.store, Roo.data);
4967 this.ds = this.store;
4968 this.ds.xmodule = this.xmodule || false;
4971 if (this.footer && this.store) {
4972 this.footer.dataSource = this.ds;
4973 this.footer = Roo.factory(this.footer);
4980 * Fires when a cell is clicked
4981 * @param {Roo.bootstrap.Table} this
4982 * @param {Roo.Element} el
4983 * @param {Number} rowIndex
4984 * @param {Number} columnIndex
4985 * @param {Roo.EventObject} e
4989 * @event celldblclick
4990 * Fires when a cell is double clicked
4991 * @param {Roo.bootstrap.Table} this
4992 * @param {Roo.Element} el
4993 * @param {Number} rowIndex
4994 * @param {Number} columnIndex
4995 * @param {Roo.EventObject} e
4997 "celldblclick" : true,
5000 * Fires when a row is clicked
5001 * @param {Roo.bootstrap.Table} this
5002 * @param {Roo.Element} el
5003 * @param {Number} rowIndex
5004 * @param {Roo.EventObject} e
5008 * @event rowdblclick
5009 * Fires when a row is double clicked
5010 * @param {Roo.bootstrap.Table} this
5011 * @param {Roo.Element} el
5012 * @param {Number} rowIndex
5013 * @param {Roo.EventObject} e
5015 "rowdblclick" : true,
5018 * Fires when a mouseover occur
5019 * @param {Roo.bootstrap.Table} this
5020 * @param {Roo.Element} el
5021 * @param {Number} rowIndex
5022 * @param {Number} columnIndex
5023 * @param {Roo.EventObject} e
5028 * Fires when a mouseout occur
5029 * @param {Roo.bootstrap.Table} this
5030 * @param {Roo.Element} el
5031 * @param {Number} rowIndex
5032 * @param {Number} columnIndex
5033 * @param {Roo.EventObject} e
5038 * Fires when a row is rendered, so you can change add a style to it.
5039 * @param {Roo.bootstrap.Table} this
5040 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5047 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5071 RowSelection : false,
5072 CellSelection : false,
5075 // Roo.Element - the tbody
5078 getAutoCreate : function(){
5079 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5088 cfg.cls += ' table-striped';
5092 cfg.cls += ' table-hover';
5094 if (this.bordered) {
5095 cfg.cls += ' table-bordered';
5097 if (this.condensed) {
5098 cfg.cls += ' table-condensed';
5100 if (this.responsive) {
5101 cfg.cls += ' table-responsive';
5105 cfg.cls+= ' ' +this.cls;
5108 // this lot should be simplifed...
5111 cfg.align=this.align;
5114 cfg.bgcolor=this.bgcolor;
5117 cfg.border=this.border;
5119 if (this.cellpadding) {
5120 cfg.cellpadding=this.cellpadding;
5122 if (this.cellspacing) {
5123 cfg.cellspacing=this.cellspacing;
5126 cfg.frame=this.frame;
5129 cfg.rules=this.rules;
5131 if (this.sortable) {
5132 cfg.sortable=this.sortable;
5135 cfg.summary=this.summary;
5138 cfg.width=this.width;
5141 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5144 if(this.store || this.cm){
5146 cfg.cn.push(this.renderHeader());
5149 cfg.cn.push(this.renderBody());
5152 cfg.cn.push(this.renderFooter());
5155 cfg.cls+= ' TableGrid';
5158 return { cn : [ cfg ] };
5161 initEvents : function()
5163 if(!this.store || !this.cm){
5167 //Roo.log('initEvents with ds!!!!');
5169 this.mainBody = this.el.select('tbody', true).first();
5174 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5175 e.on('click', _this.sort, _this);
5178 this.el.on("click", this.onClick, this);
5179 this.el.on("dblclick", this.onDblClick, this);
5181 this.parent().el.setStyle('position', 'relative');
5183 this.footer.parentId = this.id;
5184 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5187 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5189 this.store.on('load', this.onLoad, this);
5190 this.store.on('beforeload', this.onBeforeLoad, this);
5191 this.store.on('update', this.onUpdate, this);
5195 onMouseover : function(e, el)
5197 var cell = Roo.get(el);
5203 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5204 cell = cell.findParent('td', false, true);
5207 var row = cell.findParent('tr', false, true);
5208 var cellIndex = cell.dom.cellIndex;
5209 var rowIndex = row.dom.rowIndex - 1; // start from 0
5211 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5215 onMouseout : function(e, el)
5217 var cell = Roo.get(el);
5223 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5224 cell = cell.findParent('td', false, true);
5227 var row = cell.findParent('tr', false, true);
5228 var cellIndex = cell.dom.cellIndex;
5229 var rowIndex = row.dom.rowIndex - 1; // start from 0
5231 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5235 onClick : function(e, el)
5237 var cell = Roo.get(el);
5239 if(!cell || (!this.CellSelection && !this.RowSelection)){
5244 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5245 cell = cell.findParent('td', false, true);
5248 var row = cell.findParent('tr', false, true);
5249 var cellIndex = cell.dom.cellIndex;
5250 var rowIndex = row.dom.rowIndex - 1;
5252 if(this.CellSelection){
5253 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5256 if(this.RowSelection){
5257 this.fireEvent('rowclick', this, row, rowIndex, e);
5263 onDblClick : function(e,el)
5265 var cell = Roo.get(el);
5267 if(!cell || (!this.CellSelection && !this.RowSelection)){
5271 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5272 cell = cell.findParent('td', false, true);
5275 var row = cell.findParent('tr', false, true);
5276 var cellIndex = cell.dom.cellIndex;
5277 var rowIndex = row.dom.rowIndex - 1;
5279 if(this.CellSelection){
5280 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5283 if(this.RowSelection){
5284 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5288 sort : function(e,el)
5290 var col = Roo.get(el)
5292 if(!col.hasClass('sortable')){
5296 var sort = col.attr('sort');
5299 if(col.hasClass('glyphicon-arrow-up')){
5303 this.store.sortInfo = {field : sort, direction : dir};
5306 Roo.log("calling footer first");
5307 this.footer.onClick('first');
5310 this.store.load({ params : { start : 0 } });
5314 renderHeader : function()
5323 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5325 var config = cm.config[i];
5330 html: cm.getColumnHeader(i)
5333 if(typeof(config.hidden) != 'undefined' && config.hidden){
5334 c.style += ' display:none;';
5337 if(typeof(config.dataIndex) != 'undefined'){
5338 c.sort = config.dataIndex;
5341 if(typeof(config.sortable) != 'undefined' && config.sortable){
5345 if(typeof(config.align) != 'undefined' && config.align.length){
5346 c.style += ' text-align:' + config.align + ';';
5349 if(typeof(config.width) != 'undefined'){
5350 c.style += ' width:' + config.width + 'px;';
5359 renderBody : function()
5369 colspan : this.cm.getColumnCount()
5379 renderFooter : function()
5389 colspan : this.cm.getColumnCount()
5403 Roo.log('ds onload');
5408 var ds = this.store;
5410 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5411 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5413 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5414 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5417 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5418 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5422 var tbody = this.mainBody;
5424 if(ds.getCount() > 0){
5425 ds.data.each(function(d,rowIndex){
5426 var row = this.renderRow(cm, ds, rowIndex);
5428 tbody.createChild(row);
5432 if(row.cellObjects.length){
5433 Roo.each(row.cellObjects, function(r){
5434 _this.renderCellObject(r);
5441 Roo.each(this.el.select('tbody td', true).elements, function(e){
5442 e.on('mouseover', _this.onMouseover, _this);
5445 Roo.each(this.el.select('tbody td', true).elements, function(e){
5446 e.on('mouseout', _this.onMouseout, _this);
5449 //if(this.loadMask){
5450 // this.maskEl.hide();
5455 onUpdate : function(ds,record)
5457 this.refreshRow(record);
5459 onRemove : function(ds, record, index, isUpdate){
5460 if(isUpdate !== true){
5461 this.fireEvent("beforerowremoved", this, index, record);
5463 var bt = this.mainBody.dom;
5465 bt.removeChild(bt.rows[index]);
5468 if(isUpdate !== true){
5469 //this.stripeRows(index);
5470 //this.syncRowHeights(index, index);
5472 this.fireEvent("rowremoved", this, index, record);
5477 refreshRow : function(record){
5478 var ds = this.store, index;
5479 if(typeof record == 'number'){
5481 record = ds.getAt(index);
5483 index = ds.indexOf(record);
5485 this.insertRow(ds, index, true);
5486 this.onRemove(ds, record, index+1, true);
5487 //this.syncRowHeights(index, index);
5489 this.fireEvent("rowupdated", this, index, record);
5492 insertRow : function(dm, rowIndex, isUpdate){
5495 this.fireEvent("beforerowsinserted", this, rowIndex);
5497 //var s = this.getScrollState();
5498 var row = this.renderRow(this.cm, this.store, rowIndex);
5499 // insert before rowIndex..
5500 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5504 if(row.cellObjects.length){
5505 Roo.each(row.cellObjects, function(r){
5506 _this.renderCellObject(r);
5511 this.fireEvent("rowsinserted", this, rowIndex);
5512 //this.syncRowHeights(firstRow, lastRow);
5513 //this.stripeRows(firstRow);
5520 getRowDom : function(rowIndex)
5522 // not sure if I need to check this.. but let's do it anyway..
5523 return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5524 this.mainBody.dom.rows[rowIndex] : false
5526 // returns the object tree for a tr..
5529 renderRow : function(cm, ds, rowIndex) {
5531 var d = ds.getAt(rowIndex);
5538 var cellObjects = [];
5540 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5541 var config = cm.config[i];
5543 var renderer = cm.getRenderer(i);
5547 if(typeof(renderer) !== 'undefined'){
5548 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5550 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5551 // and are rendered into the cells after the row is rendered - using the id for the element.
5553 if(typeof(value) === 'object'){
5563 rowIndex : rowIndex,
5568 this.fireEvent('rowclass', this, rowcfg);
5572 cls : rowcfg.rowClass,
5574 html: (typeof(value) === 'object') ? '' : value
5581 if(typeof(config.hidden) != 'undefined' && config.hidden){
5582 td.style += ' display:none;';
5585 if(typeof(config.align) != 'undefined' && config.align.length){
5586 td.style += ' text-align:' + config.align + ';';
5589 if(typeof(config.width) != 'undefined'){
5590 td.style += ' width:' + config.width + 'px;';
5593 if(typeof(config.cursor) != 'undefined'){
5594 td.style += ' cursor:' + config.cursor + ';';
5601 row.cellObjects = cellObjects;
5609 onBeforeLoad : function()
5611 //Roo.log('ds onBeforeLoad');
5615 //if(this.loadMask){
5616 // this.maskEl.show();
5622 this.el.select('tbody', true).first().dom.innerHTML = '';
5625 getSelectionModel : function(){
5627 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5629 return this.selModel;
5632 * Render the Roo.bootstrap object from renderder
5634 renderCellObject : function(r)
5638 var t = r.cfg.render(r.container);
5641 Roo.each(r.cfg.cn, function(c){
5643 container: t.getChildContainer(),
5646 _this.renderCellObject(child);
5663 * @class Roo.bootstrap.TableCell
5664 * @extends Roo.bootstrap.Component
5665 * Bootstrap TableCell class
5666 * @cfg {String} html cell contain text
5667 * @cfg {String} cls cell class
5668 * @cfg {String} tag cell tag (td|th) default td
5669 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5670 * @cfg {String} align Aligns the content in a cell
5671 * @cfg {String} axis Categorizes cells
5672 * @cfg {String} bgcolor Specifies the background color of a cell
5673 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5674 * @cfg {Number} colspan Specifies the number of columns a cell should span
5675 * @cfg {String} headers Specifies one or more header cells a cell is related to
5676 * @cfg {Number} height Sets the height of a cell
5677 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5678 * @cfg {Number} rowspan Sets the number of rows a cell should span
5679 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5680 * @cfg {String} valign Vertical aligns the content in a cell
5681 * @cfg {Number} width Specifies the width of a cell
5684 * Create a new TableCell
5685 * @param {Object} config The config object
5688 Roo.bootstrap.TableCell = function(config){
5689 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5692 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
5712 getAutoCreate : function(){
5713 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5733 cfg.align=this.align
5739 cfg.bgcolor=this.bgcolor
5742 cfg.charoff=this.charoff
5745 cfg.colspan=this.colspan
5748 cfg.headers=this.headers
5751 cfg.height=this.height
5754 cfg.nowrap=this.nowrap
5757 cfg.rowspan=this.rowspan
5760 cfg.scope=this.scope
5763 cfg.valign=this.valign
5766 cfg.width=this.width
5785 * @class Roo.bootstrap.TableRow
5786 * @extends Roo.bootstrap.Component
5787 * Bootstrap TableRow class
5788 * @cfg {String} cls row class
5789 * @cfg {String} align Aligns the content in a table row
5790 * @cfg {String} bgcolor Specifies a background color for a table row
5791 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5792 * @cfg {String} valign Vertical aligns the content in a table row
5795 * Create a new TableRow
5796 * @param {Object} config The config object
5799 Roo.bootstrap.TableRow = function(config){
5800 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5803 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
5811 getAutoCreate : function(){
5812 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5822 cfg.align = this.align;
5825 cfg.bgcolor = this.bgcolor;
5828 cfg.charoff = this.charoff;
5831 cfg.valign = this.valign;
5849 * @class Roo.bootstrap.TableBody
5850 * @extends Roo.bootstrap.Component
5851 * Bootstrap TableBody class
5852 * @cfg {String} cls element class
5853 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5854 * @cfg {String} align Aligns the content inside the element
5855 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5856 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5859 * Create a new TableBody
5860 * @param {Object} config The config object
5863 Roo.bootstrap.TableBody = function(config){
5864 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5867 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
5875 getAutoCreate : function(){
5876 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5890 cfg.align = this.align;
5893 cfg.charoff = this.charoff;
5896 cfg.valign = this.valign;
5903 // initEvents : function()
5910 // this.store = Roo.factory(this.store, Roo.data);
5911 // this.store.on('load', this.onLoad, this);
5913 // this.store.load();
5917 // onLoad: function ()
5919 // this.fireEvent('load', this);
5929 * Ext JS Library 1.1.1
5930 * Copyright(c) 2006-2007, Ext JS, LLC.
5932 * Originally Released Under LGPL - original licence link has changed is not relivant.
5935 * <script type="text/javascript">
5938 // as we use this in bootstrap.
5939 Roo.namespace('Roo.form');
5941 * @class Roo.form.Action
5942 * Internal Class used to handle form actions
5944 * @param {Roo.form.BasicForm} el The form element or its id
5945 * @param {Object} config Configuration options
5950 // define the action interface
5951 Roo.form.Action = function(form, options){
5953 this.options = options || {};
5956 * Client Validation Failed
5959 Roo.form.Action.CLIENT_INVALID = 'client';
5961 * Server Validation Failed
5964 Roo.form.Action.SERVER_INVALID = 'server';
5966 * Connect to Server Failed
5969 Roo.form.Action.CONNECT_FAILURE = 'connect';
5971 * Reading Data from Server Failed
5974 Roo.form.Action.LOAD_FAILURE = 'load';
5976 Roo.form.Action.prototype = {
5978 failureType : undefined,
5979 response : undefined,
5983 run : function(options){
5988 success : function(response){
5993 handleResponse : function(response){
5997 // default connection failure
5998 failure : function(response){
6000 this.response = response;
6001 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6002 this.form.afterAction(this, false);
6005 processResponse : function(response){
6006 this.response = response;
6007 if(!response.responseText){
6010 this.result = this.handleResponse(response);
6014 // utility functions used internally
6015 getUrl : function(appendParams){
6016 var url = this.options.url || this.form.url || this.form.el.dom.action;
6018 var p = this.getParams();
6020 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6026 getMethod : function(){
6027 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6030 getParams : function(){
6031 var bp = this.form.baseParams;
6032 var p = this.options.params;
6034 if(typeof p == "object"){
6035 p = Roo.urlEncode(Roo.applyIf(p, bp));
6036 }else if(typeof p == 'string' && bp){
6037 p += '&' + Roo.urlEncode(bp);
6040 p = Roo.urlEncode(bp);
6045 createCallback : function(){
6047 success: this.success,
6048 failure: this.failure,
6050 timeout: (this.form.timeout*1000),
6051 upload: this.form.fileUpload ? this.success : undefined
6056 Roo.form.Action.Submit = function(form, options){
6057 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6060 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6063 haveProgress : false,
6064 uploadComplete : false,
6066 // uploadProgress indicator.
6067 uploadProgress : function()
6069 if (!this.form.progressUrl) {
6073 if (!this.haveProgress) {
6074 Roo.MessageBox.progress("Uploading", "Uploading");
6076 if (this.uploadComplete) {
6077 Roo.MessageBox.hide();
6081 this.haveProgress = true;
6083 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6085 var c = new Roo.data.Connection();
6087 url : this.form.progressUrl,
6092 success : function(req){
6093 //console.log(data);
6097 rdata = Roo.decode(req.responseText)
6099 Roo.log("Invalid data from server..");
6103 if (!rdata || !rdata.success) {
6105 Roo.MessageBox.alert(Roo.encode(rdata));
6108 var data = rdata.data;
6110 if (this.uploadComplete) {
6111 Roo.MessageBox.hide();
6116 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6117 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6120 this.uploadProgress.defer(2000,this);
6123 failure: function(data) {
6124 Roo.log('progress url failed ');
6135 // run get Values on the form, so it syncs any secondary forms.
6136 this.form.getValues();
6138 var o = this.options;
6139 var method = this.getMethod();
6140 var isPost = method == 'POST';
6141 if(o.clientValidation === false || this.form.isValid()){
6143 if (this.form.progressUrl) {
6144 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6145 (new Date() * 1) + '' + Math.random());
6150 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6151 form:this.form.el.dom,
6152 url:this.getUrl(!isPost),
6154 params:isPost ? this.getParams() : null,
6155 isUpload: this.form.fileUpload
6158 this.uploadProgress();
6160 }else if (o.clientValidation !== false){ // client validation failed
6161 this.failureType = Roo.form.Action.CLIENT_INVALID;
6162 this.form.afterAction(this, false);
6166 success : function(response)
6168 this.uploadComplete= true;
6169 if (this.haveProgress) {
6170 Roo.MessageBox.hide();
6174 var result = this.processResponse(response);
6175 if(result === true || result.success){
6176 this.form.afterAction(this, true);
6180 this.form.markInvalid(result.errors);
6181 this.failureType = Roo.form.Action.SERVER_INVALID;
6183 this.form.afterAction(this, false);
6185 failure : function(response)
6187 this.uploadComplete= true;
6188 if (this.haveProgress) {
6189 Roo.MessageBox.hide();
6192 this.response = response;
6193 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6194 this.form.afterAction(this, false);
6197 handleResponse : function(response){
6198 if(this.form.errorReader){
6199 var rs = this.form.errorReader.read(response);
6202 for(var i = 0, len = rs.records.length; i < len; i++) {
6203 var r = rs.records[i];
6207 if(errors.length < 1){
6211 success : rs.success,
6217 ret = Roo.decode(response.responseText);
6221 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6231 Roo.form.Action.Load = function(form, options){
6232 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6233 this.reader = this.form.reader;
6236 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6241 Roo.Ajax.request(Roo.apply(
6242 this.createCallback(), {
6243 method:this.getMethod(),
6244 url:this.getUrl(false),
6245 params:this.getParams()
6249 success : function(response){
6251 var result = this.processResponse(response);
6252 if(result === true || !result.success || !result.data){
6253 this.failureType = Roo.form.Action.LOAD_FAILURE;
6254 this.form.afterAction(this, false);
6257 this.form.clearInvalid();
6258 this.form.setValues(result.data);
6259 this.form.afterAction(this, true);
6262 handleResponse : function(response){
6263 if(this.form.reader){
6264 var rs = this.form.reader.read(response);
6265 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6267 success : rs.success,
6271 return Roo.decode(response.responseText);
6275 Roo.form.Action.ACTION_TYPES = {
6276 'load' : Roo.form.Action.Load,
6277 'submit' : Roo.form.Action.Submit
6286 * @class Roo.bootstrap.Form
6287 * @extends Roo.bootstrap.Component
6288 * Bootstrap Form class
6289 * @cfg {String} method GET | POST (default POST)
6290 * @cfg {String} labelAlign top | left (default top)
6291 * @cfg {String} align left | right - for navbars
6292 * @cfg {Boolean} loadMask load mask when submit (default true)
6297 * @param {Object} config The config object
6301 Roo.bootstrap.Form = function(config){
6302 Roo.bootstrap.Form.superclass.constructor.call(this, config);
6305 * @event clientvalidation
6306 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6307 * @param {Form} this
6308 * @param {Boolean} valid true if the form has passed client-side validation
6310 clientvalidation: true,
6312 * @event beforeaction
6313 * Fires before any action is performed. Return false to cancel the action.
6314 * @param {Form} this
6315 * @param {Action} action The action to be performed
6319 * @event actionfailed
6320 * Fires when an action fails.
6321 * @param {Form} this
6322 * @param {Action} action The action that failed
6324 actionfailed : true,
6326 * @event actioncomplete
6327 * Fires when an action is completed.
6328 * @param {Form} this
6329 * @param {Action} action The action that completed
6331 actioncomplete : true
6336 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
6339 * @cfg {String} method
6340 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6345 * The URL to use for form actions if one isn't supplied in the action options.
6348 * @cfg {Boolean} fileUpload
6349 * Set to true if this form is a file upload.
6353 * @cfg {Object} baseParams
6354 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6358 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6362 * @cfg {Sting} align (left|right) for navbar forms
6367 activeAction : null,
6370 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6371 * element by passing it or its id or mask the form itself by passing in true.
6374 waitMsgTarget : false,
6378 getAutoCreate : function(){
6382 method : this.method || 'POST',
6383 id : this.id || Roo.id(),
6386 if (this.parent().xtype.match(/^Nav/)) {
6387 cfg.cls = 'navbar-form navbar-' + this.align;
6391 if (this.labelAlign == 'left' ) {
6392 cfg.cls += ' form-horizontal';
6398 initEvents : function()
6400 this.el.on('submit', this.onSubmit, this);
6401 // this was added as random key presses on the form where triggering form submit.
6402 this.el.on('keypress', function(e) {
6403 if (e.getCharCode() != 13) {
6406 // we might need to allow it for textareas.. and some other items.
6407 // check e.getTarget().
6409 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6413 Roo.log("keypress blocked");
6421 onSubmit : function(e){
6426 * Returns true if client-side validation on the form is successful.
6429 isValid : function(){
6430 var items = this.getItems();
6432 items.each(function(f){
6441 * Returns true if any fields in this form have changed since their original load.
6444 isDirty : function(){
6446 var items = this.getItems();
6447 items.each(function(f){
6457 * Performs a predefined action (submit or load) or custom actions you define on this form.
6458 * @param {String} actionName The name of the action type
6459 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
6460 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6461 * accept other config options):
6463 Property Type Description
6464 ---------------- --------------- ----------------------------------------------------------------------------------
6465 url String The url for the action (defaults to the form's url)
6466 method String The form method to use (defaults to the form's method, or POST if not defined)
6467 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
6468 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
6469 validate the form on the client (defaults to false)
6471 * @return {BasicForm} this
6473 doAction : function(action, options){
6474 if(typeof action == 'string'){
6475 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6477 if(this.fireEvent('beforeaction', this, action) !== false){
6478 this.beforeAction(action);
6479 action.run.defer(100, action);
6485 beforeAction : function(action){
6486 var o = action.options;
6489 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6491 // not really supported yet.. ??
6493 //if(this.waitMsgTarget === true){
6494 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6495 //}else if(this.waitMsgTarget){
6496 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6497 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6499 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6505 afterAction : function(action, success){
6506 this.activeAction = null;
6507 var o = action.options;
6509 //if(this.waitMsgTarget === true){
6511 //}else if(this.waitMsgTarget){
6512 // this.waitMsgTarget.unmask();
6514 // Roo.MessageBox.updateProgress(1);
6515 // Roo.MessageBox.hide();
6522 Roo.callback(o.success, o.scope, [this, action]);
6523 this.fireEvent('actioncomplete', this, action);
6527 // failure condition..
6528 // we have a scenario where updates need confirming.
6529 // eg. if a locking scenario exists..
6530 // we look for { errors : { needs_confirm : true }} in the response.
6532 (typeof(action.result) != 'undefined') &&
6533 (typeof(action.result.errors) != 'undefined') &&
6534 (typeof(action.result.errors.needs_confirm) != 'undefined')
6537 Roo.log("not supported yet");
6540 Roo.MessageBox.confirm(
6541 "Change requires confirmation",
6542 action.result.errorMsg,
6547 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
6557 Roo.callback(o.failure, o.scope, [this, action]);
6558 // show an error message if no failed handler is set..
6559 if (!this.hasListener('actionfailed')) {
6560 Roo.log("need to add dialog support");
6562 Roo.MessageBox.alert("Error",
6563 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6564 action.result.errorMsg :
6565 "Saving Failed, please check your entries or try again"
6570 this.fireEvent('actionfailed', this, action);
6575 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6576 * @param {String} id The value to search for
6579 findField : function(id){
6580 var items = this.getItems();
6581 var field = items.get(id);
6583 items.each(function(f){
6584 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6591 return field || null;
6594 * Mark fields in this form invalid in bulk.
6595 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6596 * @return {BasicForm} this
6598 markInvalid : function(errors){
6599 if(errors instanceof Array){
6600 for(var i = 0, len = errors.length; i < len; i++){
6601 var fieldError = errors[i];
6602 var f = this.findField(fieldError.id);
6604 f.markInvalid(fieldError.msg);
6610 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6611 field.markInvalid(errors[id]);
6615 //Roo.each(this.childForms || [], function (f) {
6616 // f.markInvalid(errors);
6623 * Set values for fields in this form in bulk.
6624 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6625 * @return {BasicForm} this
6627 setValues : function(values){
6628 if(values instanceof Array){ // array of objects
6629 for(var i = 0, len = values.length; i < len; i++){
6631 var f = this.findField(v.id);
6633 f.setValue(v.value);
6634 if(this.trackResetOnLoad){
6635 f.originalValue = f.getValue();
6639 }else{ // object hash
6642 if(typeof values[id] != 'function' && (field = this.findField(id))){
6644 if (field.setFromData &&
6646 field.displayField &&
6647 // combos' with local stores can
6648 // be queried via setValue()
6649 // to set their value..
6650 (field.store && !field.store.isLocal)
6654 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6655 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6656 field.setFromData(sd);
6659 field.setValue(values[id]);
6663 if(this.trackResetOnLoad){
6664 field.originalValue = field.getValue();
6670 //Roo.each(this.childForms || [], function (f) {
6671 // f.setValues(values);
6678 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6679 * they are returned as an array.
6680 * @param {Boolean} asString
6683 getValues : function(asString){
6684 //if (this.childForms) {
6685 // copy values from the child forms
6686 // Roo.each(this.childForms, function (f) {
6687 // this.setValues(f.getValues());
6693 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6694 if(asString === true){
6697 return Roo.urlDecode(fs);
6701 * Returns the fields in this form as an object with key/value pairs.
6702 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6705 getFieldValues : function(with_hidden)
6707 var items = this.getItems();
6709 items.each(function(f){
6713 var v = f.getValue();
6714 if (f.inputType =='radio') {
6715 if (typeof(ret[f.getName()]) == 'undefined') {
6716 ret[f.getName()] = ''; // empty..
6719 if (!f.el.dom.checked) {
6727 // not sure if this supported any more..
6728 if ((typeof(v) == 'object') && f.getRawValue) {
6729 v = f.getRawValue() ; // dates..
6731 // combo boxes where name != hiddenName...
6732 if (f.name != f.getName()) {
6733 ret[f.name] = f.getRawValue();
6735 ret[f.getName()] = v;
6742 * Clears all invalid messages in this form.
6743 * @return {BasicForm} this
6745 clearInvalid : function(){
6746 var items = this.getItems();
6748 items.each(function(f){
6759 * @return {BasicForm} this
6762 var items = this.getItems();
6763 items.each(function(f){
6767 Roo.each(this.childForms || [], function (f) {
6774 getItems : function()
6776 var r=new Roo.util.MixedCollection(false, function(o){
6777 return o.id || (o.id = Roo.id());
6779 var iter = function(el) {
6786 Roo.each(el.items,function(e) {
6805 * Ext JS Library 1.1.1
6806 * Copyright(c) 2006-2007, Ext JS, LLC.
6808 * Originally Released Under LGPL - original licence link has changed is not relivant.
6811 * <script type="text/javascript">
6814 * @class Roo.form.VTypes
6815 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6818 Roo.form.VTypes = function(){
6819 // closure these in so they are only created once.
6820 var alpha = /^[a-zA-Z_]+$/;
6821 var alphanum = /^[a-zA-Z0-9_]+$/;
6822 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6823 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6825 // All these messages and functions are configurable
6828 * The function used to validate email addresses
6829 * @param {String} value The email address
6831 'email' : function(v){
6832 return email.test(v);
6835 * The error text to display when the email validation function returns false
6838 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6840 * The keystroke filter mask to be applied on email input
6843 'emailMask' : /[a-z0-9_\.\-@]/i,
6846 * The function used to validate URLs
6847 * @param {String} value The URL
6849 'url' : function(v){
6853 * The error text to display when the url validation function returns false
6856 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6859 * The function used to validate alpha values
6860 * @param {String} value The value
6862 'alpha' : function(v){
6863 return alpha.test(v);
6866 * The error text to display when the alpha validation function returns false
6869 'alphaText' : 'This field should only contain letters and _',
6871 * The keystroke filter mask to be applied on alpha input
6874 'alphaMask' : /[a-z_]/i,
6877 * The function used to validate alphanumeric values
6878 * @param {String} value The value
6880 'alphanum' : function(v){
6881 return alphanum.test(v);
6884 * The error text to display when the alphanumeric validation function returns false
6887 'alphanumText' : 'This field should only contain letters, numbers and _',
6889 * The keystroke filter mask to be applied on alphanumeric input
6892 'alphanumMask' : /[a-z0-9_]/i
6902 * @class Roo.bootstrap.Input
6903 * @extends Roo.bootstrap.Component
6904 * Bootstrap Input class
6905 * @cfg {Boolean} disabled is it disabled
6906 * @cfg {String} fieldLabel - the label associated
6907 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6908 * @cfg {String} name name of the input
6909 * @cfg {string} fieldLabel - the label associated
6910 * @cfg {string} inputType - input / file submit ...
6911 * @cfg {string} placeholder - placeholder to put in text.
6912 * @cfg {string} before - input group add on before
6913 * @cfg {string} after - input group add on after
6914 * @cfg {string} size - (lg|sm) or leave empty..
6915 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6916 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6917 * @cfg {Number} md colspan out of 12 for computer-sized screens
6918 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6919 * @cfg {string} value default value of the input
6920 * @cfg {Number} labelWidth set the width of label (0-12)
6921 * @cfg {String} labelAlign (top|left)
6922 * @cfg {Boolean} readOnly Specifies that the field should be read-only
6923 * @cfg {String} align (left|center|right) Default left
6927 * Create a new Input
6928 * @param {Object} config The config object
6931 Roo.bootstrap.Input = function(config){
6932 Roo.bootstrap.Input.superclass.constructor.call(this, config);
6937 * Fires when this field receives input focus.
6938 * @param {Roo.form.Field} this
6943 * Fires when this field loses input focus.
6944 * @param {Roo.form.Field} this
6949 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
6950 * {@link Roo.EventObject#getKey} to determine which key was pressed.
6951 * @param {Roo.form.Field} this
6952 * @param {Roo.EventObject} e The event object
6957 * Fires just before the field blurs if the field value has changed.
6958 * @param {Roo.form.Field} this
6959 * @param {Mixed} newValue The new value
6960 * @param {Mixed} oldValue The original value
6965 * Fires after the field has been marked as invalid.
6966 * @param {Roo.form.Field} this
6967 * @param {String} msg The validation message
6972 * Fires after the field has been validated with no errors.
6973 * @param {Roo.form.Field} this
6978 * Fires after the key up
6979 * @param {Roo.form.Field} this
6980 * @param {Roo.EventObject} e The event Object
6986 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
6988 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6989 automatic validation (defaults to "keyup").
6991 validationEvent : "keyup",
6993 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6995 validateOnBlur : true,
6997 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6999 validationDelay : 250,
7001 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7003 focusClass : "x-form-focus", // not needed???
7007 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7009 invalidClass : "has-error",
7012 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7014 selectOnFocus : false,
7017 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7021 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7026 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7028 disableKeyFilter : false,
7031 * @cfg {Boolean} disabled True to disable the field (defaults to false).
7035 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7039 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7041 blankText : "This field is required",
7044 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7048 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7050 maxLength : Number.MAX_VALUE,
7052 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7054 minLengthText : "The minimum length for this field is {0}",
7056 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7058 maxLengthText : "The maximum length for this field is {0}",
7062 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7063 * If available, this function will be called only after the basic validators all return true, and will be passed the
7064 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7068 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7069 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7070 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7074 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7097 formatedValue : false,
7099 parentLabelAlign : function()
7102 while (parent.parent()) {
7103 parent = parent.parent();
7104 if (typeof(parent.labelAlign) !='undefined') {
7105 return parent.labelAlign;
7112 getAutoCreate : function(){
7114 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7120 if(this.inputType != 'hidden'){
7121 cfg.cls = 'form-group' //input-group
7127 type : this.inputType,
7129 cls : 'form-control',
7130 placeholder : this.placeholder || ''
7135 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7138 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7139 input.maxLength = this.maxLength;
7142 if (this.disabled) {
7143 input.disabled=true;
7146 if (this.readOnly) {
7147 input.readonly=true;
7151 input.name = this.name;
7154 input.cls += ' input-' + this.size;
7157 ['xs','sm','md','lg'].map(function(size){
7158 if (settings[size]) {
7159 cfg.cls += ' col-' + size + '-' + settings[size];
7163 var inputblock = input;
7165 if (this.before || this.after) {
7168 cls : 'input-group',
7171 if (this.before && typeof(this.before) == 'string') {
7173 inputblock.cn.push({
7175 cls : 'roo-input-before input-group-addon',
7179 if (this.before && typeof(this.before) == 'object') {
7180 this.before = Roo.factory(this.before);
7181 Roo.log(this.before);
7182 inputblock.cn.push({
7184 cls : 'roo-input-before input-group-' +
7185 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7189 inputblock.cn.push(input);
7191 if (this.after && typeof(this.after) == 'string') {
7192 inputblock.cn.push({
7194 cls : 'roo-input-after input-group-addon',
7198 if (this.after && typeof(this.after) == 'object') {
7199 this.after = Roo.factory(this.after);
7200 Roo.log(this.after);
7201 inputblock.cn.push({
7203 cls : 'roo-input-after input-group-' +
7204 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7209 if (align ==='left' && this.fieldLabel.length) {
7210 Roo.log("left and has label");
7216 cls : 'control-label col-sm-' + this.labelWidth,
7217 html : this.fieldLabel
7221 cls : "col-sm-" + (12 - this.labelWidth),
7228 } else if ( this.fieldLabel.length) {
7234 //cls : 'input-group-addon',
7235 html : this.fieldLabel
7245 Roo.log(" no label && no align");
7254 Roo.log('input-parentType: ' + this.parentType);
7256 if (this.parentType === 'Navbar' && this.parent().bar) {
7257 cfg.cls += ' navbar-form';
7265 * return the real input element.
7267 inputEl: function ()
7269 return this.el.select('input.form-control',true).first();
7272 tooltipEl : function()
7274 return this.inputEl();
7277 setDisabled : function(v)
7279 var i = this.inputEl().dom;
7281 i.removeAttribute('disabled');
7285 i.setAttribute('disabled','true');
7287 initEvents : function()
7290 this.inputEl().on("keydown" , this.fireKey, this);
7291 this.inputEl().on("focus", this.onFocus, this);
7292 this.inputEl().on("blur", this.onBlur, this);
7294 this.inputEl().relayEvent('keyup', this);
7296 // reference to original value for reset
7297 this.originalValue = this.getValue();
7298 //Roo.form.TextField.superclass.initEvents.call(this);
7299 if(this.validationEvent == 'keyup'){
7300 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7301 this.inputEl().on('keyup', this.filterValidation, this);
7303 else if(this.validationEvent !== false){
7304 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7307 if(this.selectOnFocus){
7308 this.on("focus", this.preFocus, this);
7311 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7312 this.inputEl().on("keypress", this.filterKeys, this);
7315 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
7316 this.el.on("click", this.autoSize, this);
7319 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7320 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7323 if (typeof(this.before) == 'object') {
7324 this.before.render(this.el.select('.roo-input-before',true).first());
7326 if (typeof(this.after) == 'object') {
7327 this.after.render(this.el.select('.roo-input-after',true).first());
7332 filterValidation : function(e){
7333 if(!e.isNavKeyPress()){
7334 this.validationTask.delay(this.validationDelay);
7338 * Validates the field value
7339 * @return {Boolean} True if the value is valid, else false
7341 validate : function(){
7342 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7343 if(this.disabled || this.validateValue(this.getRawValue())){
7344 this.clearInvalid();
7352 * Validates a value according to the field's validation rules and marks the field as invalid
7353 * if the validation fails
7354 * @param {Mixed} value The value to validate
7355 * @return {Boolean} True if the value is valid, else false
7357 validateValue : function(value){
7358 if(value.length < 1) { // if it's blank
7359 if(this.allowBlank){
7360 this.clearInvalid();
7363 this.markInvalid(this.blankText);
7367 if(value.length < this.minLength){
7368 this.markInvalid(String.format(this.minLengthText, this.minLength));
7371 if(value.length > this.maxLength){
7372 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7376 var vt = Roo.form.VTypes;
7377 if(!vt[this.vtype](value, this)){
7378 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7382 if(typeof this.validator == "function"){
7383 var msg = this.validator(value);
7385 this.markInvalid(msg);
7389 if(this.regex && !this.regex.test(value)){
7390 this.markInvalid(this.regexText);
7399 fireKey : function(e){
7400 //Roo.log('field ' + e.getKey());
7401 if(e.isNavKeyPress()){
7402 this.fireEvent("specialkey", this, e);
7405 focus : function (selectText){
7407 this.inputEl().focus();
7408 if(selectText === true){
7409 this.inputEl().dom.select();
7415 onFocus : function(){
7416 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7417 // this.el.addClass(this.focusClass);
7420 this.hasFocus = true;
7421 this.startValue = this.getValue();
7422 this.fireEvent("focus", this);
7426 beforeBlur : Roo.emptyFn,
7430 onBlur : function(){
7432 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7433 //this.el.removeClass(this.focusClass);
7435 this.hasFocus = false;
7436 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7439 var v = this.getValue();
7440 if(String(v) !== String(this.startValue)){
7441 this.fireEvent('change', this, v, this.startValue);
7443 this.fireEvent("blur", this);
7447 * Resets the current field value to the originally loaded value and clears any validation messages
7450 this.setValue(this.originalValue);
7451 this.clearInvalid();
7454 * Returns the name of the field
7455 * @return {Mixed} name The name field
7457 getName: function(){
7461 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
7462 * @return {Mixed} value The field value
7464 getValue : function(){
7466 var v = this.inputEl().getValue();
7471 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
7472 * @return {Mixed} value The field value
7474 getRawValue : function(){
7475 var v = this.inputEl().getValue();
7481 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
7482 * @param {Mixed} value The value to set
7484 setRawValue : function(v){
7485 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7488 selectText : function(start, end){
7489 var v = this.getRawValue();
7491 start = start === undefined ? 0 : start;
7492 end = end === undefined ? v.length : end;
7493 var d = this.inputEl().dom;
7494 if(d.setSelectionRange){
7495 d.setSelectionRange(start, end);
7496 }else if(d.createTextRange){
7497 var range = d.createTextRange();
7498 range.moveStart("character", start);
7499 range.moveEnd("character", v.length-end);
7506 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
7507 * @param {Mixed} value The value to set
7509 setValue : function(v){
7512 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7518 processValue : function(value){
7519 if(this.stripCharsRe){
7520 var newValue = value.replace(this.stripCharsRe, '');
7521 if(newValue !== value){
7522 this.setRawValue(newValue);
7529 preFocus : function(){
7531 if(this.selectOnFocus){
7532 this.inputEl().dom.select();
7535 filterKeys : function(e){
7537 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7540 var c = e.getCharCode(), cc = String.fromCharCode(c);
7541 if(Roo.isIE && (e.isSpecialKey() || !cc)){
7544 if(!this.maskRe.test(cc)){
7549 * Clear any invalid styles/messages for this field
7551 clearInvalid : function(){
7553 if(!this.el || this.preventMark){ // not rendered
7556 this.el.removeClass(this.invalidClass);
7558 switch(this.msgTarget){
7560 this.el.dom.qtip = '';
7563 this.el.dom.title = '';
7567 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7572 this.errorIcon.dom.qtip = '';
7573 this.errorIcon.hide();
7574 this.un('resize', this.alignErrorIcon, this);
7578 var t = Roo.getDom(this.msgTarget);
7580 t.style.display = 'none';
7584 this.fireEvent('valid', this);
7587 * Mark this field as invalid
7588 * @param {String} msg The validation message
7590 markInvalid : function(msg){
7591 if(!this.el || this.preventMark){ // not rendered
7594 this.el.addClass(this.invalidClass);
7596 msg = msg || this.invalidText;
7597 switch(this.msgTarget){
7599 this.el.dom.qtip = msg;
7600 this.el.dom.qclass = 'x-form-invalid-tip';
7601 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7602 Roo.QuickTips.enable();
7606 this.el.dom.title = msg;
7610 var elp = this.el.findParent('.x-form-element', 5, true);
7611 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7612 this.errorEl.setWidth(elp.getWidth(true)-20);
7614 this.errorEl.update(msg);
7615 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7618 if(!this.errorIcon){
7619 var elp = this.el.findParent('.x-form-element', 5, true);
7620 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7622 this.alignErrorIcon();
7623 this.errorIcon.dom.qtip = msg;
7624 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7625 this.errorIcon.show();
7626 this.on('resize', this.alignErrorIcon, this);
7629 var t = Roo.getDom(this.msgTarget);
7631 t.style.display = this.msgDisplay;
7635 this.fireEvent('invalid', this, msg);
7638 SafariOnKeyDown : function(event)
7640 // this is a workaround for a password hang bug on chrome/ webkit.
7642 var isSelectAll = false;
7644 if(this.inputEl().dom.selectionEnd > 0){
7645 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7647 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7648 event.preventDefault();
7653 if(isSelectAll){ // backspace and delete key
7655 event.preventDefault();
7656 // this is very hacky as keydown always get's upper case.
7658 var cc = String.fromCharCode(event.getCharCode());
7659 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
7663 adjustWidth : function(tag, w){
7664 tag = tag.toLowerCase();
7665 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7666 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7670 if(tag == 'textarea'){
7673 }else if(Roo.isOpera){
7677 if(tag == 'textarea'){
7696 * @class Roo.bootstrap.TextArea
7697 * @extends Roo.bootstrap.Input
7698 * Bootstrap TextArea class
7699 * @cfg {Number} cols Specifies the visible width of a text area
7700 * @cfg {Number} rows Specifies the visible number of lines in a text area
7701 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7702 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7703 * @cfg {string} html text
7706 * Create a new TextArea
7707 * @param {Object} config The config object
7710 Roo.bootstrap.TextArea = function(config){
7711 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7715 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
7725 getAutoCreate : function(){
7727 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7738 value : this.value || '',
7739 html: this.html || '',
7740 cls : 'form-control',
7741 placeholder : this.placeholder || ''
7745 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7746 input.maxLength = this.maxLength;
7750 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7754 input.cols = this.cols;
7757 if (this.readOnly) {
7758 input.readonly = true;
7762 input.name = this.name;
7766 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7770 ['xs','sm','md','lg'].map(function(size){
7771 if (settings[size]) {
7772 cfg.cls += ' col-' + size + '-' + settings[size];
7776 var inputblock = input;
7778 if (this.before || this.after) {
7781 cls : 'input-group',
7785 inputblock.cn.push({
7787 cls : 'input-group-addon',
7791 inputblock.cn.push(input);
7793 inputblock.cn.push({
7795 cls : 'input-group-addon',
7802 if (align ==='left' && this.fieldLabel.length) {
7803 Roo.log("left and has label");
7809 cls : 'control-label col-sm-' + this.labelWidth,
7810 html : this.fieldLabel
7814 cls : "col-sm-" + (12 - this.labelWidth),
7821 } else if ( this.fieldLabel.length) {
7827 //cls : 'input-group-addon',
7828 html : this.fieldLabel
7838 Roo.log(" no label && no align");
7848 if (this.disabled) {
7849 input.disabled=true;
7856 * return the real textarea element.
7858 inputEl: function ()
7860 return this.el.select('textarea.form-control',true).first();
7868 * trigger field - base class for combo..
7873 * @class Roo.bootstrap.TriggerField
7874 * @extends Roo.bootstrap.Input
7875 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7876 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7877 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7878 * for which you can provide a custom implementation. For example:
7880 var trigger = new Roo.bootstrap.TriggerField();
7881 trigger.onTriggerClick = myTriggerFn;
7882 trigger.applyTo('my-field');
7885 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7886 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7887 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
7888 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7890 * Create a new TriggerField.
7891 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7892 * to the base TextField)
7894 Roo.bootstrap.TriggerField = function(config){
7895 this.mimicing = false;
7896 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7899 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
7901 * @cfg {String} triggerClass A CSS class to apply to the trigger
7904 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7908 /** @cfg {Boolean} grow @hide */
7909 /** @cfg {Number} growMin @hide */
7910 /** @cfg {Number} growMax @hide */
7916 autoSize: Roo.emptyFn,
7923 actionMode : 'wrap',
7927 getAutoCreate : function(){
7929 var align = this.labelAlign || this.parentLabelAlign();
7934 cls: 'form-group' //input-group
7941 type : this.inputType,
7942 cls : 'form-control',
7943 autocomplete: 'false',
7944 placeholder : this.placeholder || ''
7948 input.name = this.name;
7951 input.cls += ' input-' + this.size;
7954 if (this.disabled) {
7955 input.disabled=true;
7958 var inputblock = input;
7960 if (this.before || this.after) {
7963 cls : 'input-group',
7967 inputblock.cn.push({
7969 cls : 'input-group-addon',
7973 inputblock.cn.push(input);
7975 inputblock.cn.push({
7977 cls : 'input-group-addon',
7990 cls: 'form-hidden-field'
7998 Roo.log('multiple');
8006 cls: 'form-hidden-field'
8010 cls: 'select2-choices',
8014 cls: 'select2-search-field',
8027 cls: 'select2-container input-group',
8032 // cls: 'typeahead typeahead-long dropdown-menu',
8033 // style: 'display:none'
8038 if(!this.multiple && this.showToggleBtn){
8041 cls : 'input-group-addon btn dropdown-toggle',
8049 cls: 'combobox-clear',
8063 combobox.cls += ' select2-container-multi';
8066 if (align ==='left' && this.fieldLabel.length) {
8068 Roo.log("left and has label");
8074 cls : 'control-label col-sm-' + this.labelWidth,
8075 html : this.fieldLabel
8079 cls : "col-sm-" + (12 - this.labelWidth),
8086 } else if ( this.fieldLabel.length) {
8092 //cls : 'input-group-addon',
8093 html : this.fieldLabel
8103 Roo.log(" no label && no align");
8110 ['xs','sm','md','lg'].map(function(size){
8111 if (settings[size]) {
8112 cfg.cls += ' col-' + size + '-' + settings[size];
8123 onResize : function(w, h){
8124 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8125 // if(typeof w == 'number'){
8126 // var x = w - this.trigger.getWidth();
8127 // this.inputEl().setWidth(this.adjustWidth('input', x));
8128 // this.trigger.setStyle('left', x+'px');
8133 adjustSize : Roo.BoxComponent.prototype.adjustSize,
8136 getResizeEl : function(){
8137 return this.inputEl();
8141 getPositionEl : function(){
8142 return this.inputEl();
8146 alignErrorIcon : function(){
8147 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8151 initEvents : function(){
8155 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8156 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8157 if(!this.multiple && this.showToggleBtn){
8158 this.trigger = this.el.select('span.dropdown-toggle',true).first();
8159 if(this.hideTrigger){
8160 this.trigger.setDisplayed(false);
8162 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8166 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8169 //this.trigger.addClassOnOver('x-form-trigger-over');
8170 //this.trigger.addClassOnClick('x-form-trigger-click');
8173 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8177 createList : function()
8179 this.list = Roo.get(document.body).createChild({
8181 cls: 'typeahead typeahead-long dropdown-menu',
8182 style: 'display:none'
8185 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8190 initTrigger : function(){
8195 onDestroy : function(){
8197 this.trigger.removeAllListeners();
8198 // this.trigger.remove();
8201 // this.wrap.remove();
8203 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8207 onFocus : function(){
8208 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8211 this.wrap.addClass('x-trigger-wrap-focus');
8212 this.mimicing = true;
8213 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8214 if(this.monitorTab){
8215 this.el.on("keydown", this.checkTab, this);
8222 checkTab : function(e){
8223 if(e.getKey() == e.TAB){
8229 onBlur : function(){
8234 mimicBlur : function(e, t){
8236 if(!this.wrap.contains(t) && this.validateBlur()){
8243 triggerBlur : function(){
8244 this.mimicing = false;
8245 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8246 if(this.monitorTab){
8247 this.el.un("keydown", this.checkTab, this);
8249 //this.wrap.removeClass('x-trigger-wrap-focus');
8250 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8254 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8255 validateBlur : function(e, t){
8260 onDisable : function(){
8261 this.inputEl().dom.disabled = true;
8262 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8264 // this.wrap.addClass('x-item-disabled');
8269 onEnable : function(){
8270 this.inputEl().dom.disabled = false;
8271 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8273 // this.el.removeClass('x-item-disabled');
8278 onShow : function(){
8279 var ae = this.getActionEl();
8282 ae.dom.style.display = '';
8283 ae.dom.style.visibility = 'visible';
8289 onHide : function(){
8290 var ae = this.getActionEl();
8291 ae.dom.style.display = 'none';
8295 * The function that should handle the trigger's click event. This method does nothing by default until overridden
8296 * by an implementing function.
8298 * @param {EventObject} e
8300 onTriggerClick : Roo.emptyFn
8304 * Ext JS Library 1.1.1
8305 * Copyright(c) 2006-2007, Ext JS, LLC.
8307 * Originally Released Under LGPL - original licence link has changed is not relivant.
8310 * <script type="text/javascript">
8315 * @class Roo.data.SortTypes
8317 * Defines the default sorting (casting?) comparison functions used when sorting data.
8319 Roo.data.SortTypes = {
8321 * Default sort that does nothing
8322 * @param {Mixed} s The value being converted
8323 * @return {Mixed} The comparison value
8330 * The regular expression used to strip tags
8334 stripTagsRE : /<\/?[^>]+>/gi,
8337 * Strips all HTML tags to sort on text only
8338 * @param {Mixed} s The value being converted
8339 * @return {String} The comparison value
8341 asText : function(s){
8342 return String(s).replace(this.stripTagsRE, "");
8346 * Strips all HTML tags to sort on text only - Case insensitive
8347 * @param {Mixed} s The value being converted
8348 * @return {String} The comparison value
8350 asUCText : function(s){
8351 return String(s).toUpperCase().replace(this.stripTagsRE, "");
8355 * Case insensitive string
8356 * @param {Mixed} s The value being converted
8357 * @return {String} The comparison value
8359 asUCString : function(s) {
8360 return String(s).toUpperCase();
8365 * @param {Mixed} s The value being converted
8366 * @return {Number} The comparison value
8368 asDate : function(s) {
8372 if(s instanceof Date){
8375 return Date.parse(String(s));
8380 * @param {Mixed} s The value being converted
8381 * @return {Float} The comparison value
8383 asFloat : function(s) {
8384 var val = parseFloat(String(s).replace(/,/g, ""));
8385 if(isNaN(val)) val = 0;
8391 * @param {Mixed} s The value being converted
8392 * @return {Number} The comparison value
8394 asInt : function(s) {
8395 var val = parseInt(String(s).replace(/,/g, ""));
8396 if(isNaN(val)) val = 0;
8401 * Ext JS Library 1.1.1
8402 * Copyright(c) 2006-2007, Ext JS, LLC.
8404 * Originally Released Under LGPL - original licence link has changed is not relivant.
8407 * <script type="text/javascript">
8411 * @class Roo.data.Record
8412 * Instances of this class encapsulate both record <em>definition</em> information, and record
8413 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8414 * to access Records cached in an {@link Roo.data.Store} object.<br>
8416 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8417 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8420 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8422 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8423 * {@link #create}. The parameters are the same.
8424 * @param {Array} data An associative Array of data values keyed by the field name.
8425 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8426 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8427 * not specified an integer id is generated.
8429 Roo.data.Record = function(data, id){
8430 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8435 * Generate a constructor for a specific record layout.
8436 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8437 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8438 * Each field definition object may contain the following properties: <ul>
8439 * <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,
8440 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8441 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8442 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8443 * is being used, then this is a string containing the javascript expression to reference the data relative to
8444 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8445 * to the data item relative to the record element. If the mapping expression is the same as the field name,
8446 * this may be omitted.</p></li>
8447 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8448 * <ul><li>auto (Default, implies no conversion)</li>
8453 * <li>date</li></ul></p></li>
8454 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8455 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8456 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8457 * by the Reader into an object that will be stored in the Record. It is passed the
8458 * following parameters:<ul>
8459 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8461 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8463 * <br>usage:<br><pre><code>
8464 var TopicRecord = Roo.data.Record.create(
8465 {name: 'title', mapping: 'topic_title'},
8466 {name: 'author', mapping: 'username'},
8467 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8468 {name: 'lastPost', mapping: 'post_time', type: 'date'},
8469 {name: 'lastPoster', mapping: 'user2'},
8470 {name: 'excerpt', mapping: 'post_text'}
8473 var myNewRecord = new TopicRecord({
8474 title: 'Do my job please',
8477 lastPost: new Date(),
8478 lastPoster: 'Animal',
8479 excerpt: 'No way dude!'
8481 myStore.add(myNewRecord);
8486 Roo.data.Record.create = function(o){
8488 f.superclass.constructor.apply(this, arguments);
8490 Roo.extend(f, Roo.data.Record);
8491 var p = f.prototype;
8492 p.fields = new Roo.util.MixedCollection(false, function(field){
8495 for(var i = 0, len = o.length; i < len; i++){
8496 p.fields.add(new Roo.data.Field(o[i]));
8498 f.getField = function(name){
8499 return p.fields.get(name);
8504 Roo.data.Record.AUTO_ID = 1000;
8505 Roo.data.Record.EDIT = 'edit';
8506 Roo.data.Record.REJECT = 'reject';
8507 Roo.data.Record.COMMIT = 'commit';
8509 Roo.data.Record.prototype = {
8511 * Readonly flag - true if this record has been modified.
8520 join : function(store){
8525 * Set the named field to the specified value.
8526 * @param {String} name The name of the field to set.
8527 * @param {Object} value The value to set the field to.
8529 set : function(name, value){
8530 if(this.data[name] == value){
8537 if(typeof this.modified[name] == 'undefined'){
8538 this.modified[name] = this.data[name];
8540 this.data[name] = value;
8541 if(!this.editing && this.store){
8542 this.store.afterEdit(this);
8547 * Get the value of the named field.
8548 * @param {String} name The name of the field to get the value of.
8549 * @return {Object} The value of the field.
8551 get : function(name){
8552 return this.data[name];
8556 beginEdit : function(){
8557 this.editing = true;
8562 cancelEdit : function(){
8563 this.editing = false;
8564 delete this.modified;
8568 endEdit : function(){
8569 this.editing = false;
8570 if(this.dirty && this.store){
8571 this.store.afterEdit(this);
8576 * Usually called by the {@link Roo.data.Store} which owns the Record.
8577 * Rejects all changes made to the Record since either creation, or the last commit operation.
8578 * Modified fields are reverted to their original values.
8580 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8581 * of reject operations.
8583 reject : function(){
8584 var m = this.modified;
8586 if(typeof m[n] != "function"){
8587 this.data[n] = m[n];
8591 delete this.modified;
8592 this.editing = false;
8594 this.store.afterReject(this);
8599 * Usually called by the {@link Roo.data.Store} which owns the Record.
8600 * Commits all changes made to the Record since either creation, or the last commit operation.
8602 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8603 * of commit operations.
8605 commit : function(){
8607 delete this.modified;
8608 this.editing = false;
8610 this.store.afterCommit(this);
8615 hasError : function(){
8616 return this.error != null;
8620 clearError : function(){
8625 * Creates a copy of this record.
8626 * @param {String} id (optional) A new record id if you don't want to use this record's id
8629 copy : function(newId) {
8630 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8634 * Ext JS Library 1.1.1
8635 * Copyright(c) 2006-2007, Ext JS, LLC.
8637 * Originally Released Under LGPL - original licence link has changed is not relivant.
8640 * <script type="text/javascript">
8646 * @class Roo.data.Store
8647 * @extends Roo.util.Observable
8648 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8649 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8651 * 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
8652 * has no knowledge of the format of the data returned by the Proxy.<br>
8654 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8655 * instances from the data object. These records are cached and made available through accessor functions.
8657 * Creates a new Store.
8658 * @param {Object} config A config object containing the objects needed for the Store to access data,
8659 * and read the data into Records.
8661 Roo.data.Store = function(config){
8662 this.data = new Roo.util.MixedCollection(false);
8663 this.data.getKey = function(o){
8666 this.baseParams = {};
8673 "multisort" : "_multisort"
8676 if(config && config.data){
8677 this.inlineData = config.data;
8681 Roo.apply(this, config);
8683 if(this.reader){ // reader passed
8684 this.reader = Roo.factory(this.reader, Roo.data);
8685 this.reader.xmodule = this.xmodule || false;
8686 if(!this.recordType){
8687 this.recordType = this.reader.recordType;
8689 if(this.reader.onMetaChange){
8690 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8694 if(this.recordType){
8695 this.fields = this.recordType.prototype.fields;
8701 * @event datachanged
8702 * Fires when the data cache has changed, and a widget which is using this Store
8703 * as a Record cache should refresh its view.
8704 * @param {Store} this
8709 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8710 * @param {Store} this
8711 * @param {Object} meta The JSON metadata
8716 * Fires when Records have been added to the Store
8717 * @param {Store} this
8718 * @param {Roo.data.Record[]} records The array of Records added
8719 * @param {Number} index The index at which the record(s) were added
8724 * Fires when a Record has been removed from the Store
8725 * @param {Store} this
8726 * @param {Roo.data.Record} record The Record that was removed
8727 * @param {Number} index The index at which the record was removed
8732 * Fires when a Record has been updated
8733 * @param {Store} this
8734 * @param {Roo.data.Record} record The Record that was updated
8735 * @param {String} operation The update operation being performed. Value may be one of:
8737 Roo.data.Record.EDIT
8738 Roo.data.Record.REJECT
8739 Roo.data.Record.COMMIT
8745 * Fires when the data cache has been cleared.
8746 * @param {Store} this
8751 * Fires before a request is made for a new data object. If the beforeload handler returns false
8752 * the load action will be canceled.
8753 * @param {Store} this
8754 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8758 * @event beforeloadadd
8759 * Fires after a new set of Records has been loaded.
8760 * @param {Store} this
8761 * @param {Roo.data.Record[]} records The Records that were loaded
8762 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8764 beforeloadadd : true,
8767 * Fires after a new set of Records has been loaded, before they are added to the store.
8768 * @param {Store} this
8769 * @param {Roo.data.Record[]} records The Records that were loaded
8770 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8771 * @params {Object} return from reader
8775 * @event loadexception
8776 * Fires if an exception occurs in the Proxy during loading.
8777 * Called with the signature of the Proxy's "loadexception" event.
8778 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8781 * @param {Object} return from JsonData.reader() - success, totalRecords, records
8782 * @param {Object} load options
8783 * @param {Object} jsonData from your request (normally this contains the Exception)
8785 loadexception : true
8789 this.proxy = Roo.factory(this.proxy, Roo.data);
8790 this.proxy.xmodule = this.xmodule || false;
8791 this.relayEvents(this.proxy, ["loadexception"]);
8793 this.sortToggle = {};
8794 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8796 Roo.data.Store.superclass.constructor.call(this);
8798 if(this.inlineData){
8799 this.loadData(this.inlineData);
8800 delete this.inlineData;
8804 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8806 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
8807 * without a remote query - used by combo/forms at present.
8811 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8814 * @cfg {Array} data Inline data to be loaded when the store is initialized.
8817 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8818 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8821 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8822 * on any HTTP request
8825 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8828 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8832 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8833 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8838 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8839 * loaded or when a record is removed. (defaults to false).
8841 pruneModifiedRecords : false,
8847 * Add Records to the Store and fires the add event.
8848 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8850 add : function(records){
8851 records = [].concat(records);
8852 for(var i = 0, len = records.length; i < len; i++){
8853 records[i].join(this);
8855 var index = this.data.length;
8856 this.data.addAll(records);
8857 this.fireEvent("add", this, records, index);
8861 * Remove a Record from the Store and fires the remove event.
8862 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8864 remove : function(record){
8865 var index = this.data.indexOf(record);
8866 this.data.removeAt(index);
8867 if(this.pruneModifiedRecords){
8868 this.modified.remove(record);
8870 this.fireEvent("remove", this, record, index);
8874 * Remove all Records from the Store and fires the clear event.
8876 removeAll : function(){
8878 if(this.pruneModifiedRecords){
8881 this.fireEvent("clear", this);
8885 * Inserts Records to the Store at the given index and fires the add event.
8886 * @param {Number} index The start index at which to insert the passed Records.
8887 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8889 insert : function(index, records){
8890 records = [].concat(records);
8891 for(var i = 0, len = records.length; i < len; i++){
8892 this.data.insert(index, records[i]);
8893 records[i].join(this);
8895 this.fireEvent("add", this, records, index);
8899 * Get the index within the cache of the passed Record.
8900 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8901 * @return {Number} The index of the passed Record. Returns -1 if not found.
8903 indexOf : function(record){
8904 return this.data.indexOf(record);
8908 * Get the index within the cache of the Record with the passed id.
8909 * @param {String} id The id of the Record to find.
8910 * @return {Number} The index of the Record. Returns -1 if not found.
8912 indexOfId : function(id){
8913 return this.data.indexOfKey(id);
8917 * Get the Record with the specified id.
8918 * @param {String} id The id of the Record to find.
8919 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8921 getById : function(id){
8922 return this.data.key(id);
8926 * Get the Record at the specified index.
8927 * @param {Number} index The index of the Record to find.
8928 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8930 getAt : function(index){
8931 return this.data.itemAt(index);
8935 * Returns a range of Records between specified indices.
8936 * @param {Number} startIndex (optional) The starting index (defaults to 0)
8937 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8938 * @return {Roo.data.Record[]} An array of Records
8940 getRange : function(start, end){
8941 return this.data.getRange(start, end);
8945 storeOptions : function(o){
8946 o = Roo.apply({}, o);
8949 this.lastOptions = o;
8953 * Loads the Record cache from the configured Proxy using the configured Reader.
8955 * If using remote paging, then the first load call must specify the <em>start</em>
8956 * and <em>limit</em> properties in the options.params property to establish the initial
8957 * position within the dataset, and the number of Records to cache on each read from the Proxy.
8959 * <strong>It is important to note that for remote data sources, loading is asynchronous,
8960 * and this call will return before the new data has been loaded. Perform any post-processing
8961 * in a callback function, or in a "load" event handler.</strong>
8963 * @param {Object} options An object containing properties which control loading options:<ul>
8964 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8965 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8966 * passed the following arguments:<ul>
8967 * <li>r : Roo.data.Record[]</li>
8968 * <li>options: Options object from the load call</li>
8969 * <li>success: Boolean success indicator</li></ul></li>
8970 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8971 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8974 load : function(options){
8975 options = options || {};
8976 if(this.fireEvent("beforeload", this, options) !== false){
8977 this.storeOptions(options);
8978 var p = Roo.apply(options.params || {}, this.baseParams);
8979 // if meta was not loaded from remote source.. try requesting it.
8980 if (!this.reader.metaFromRemote) {
8983 if(this.sortInfo && this.remoteSort){
8984 var pn = this.paramNames;
8985 p[pn["sort"]] = this.sortInfo.field;
8986 p[pn["dir"]] = this.sortInfo.direction;
8988 if (this.multiSort) {
8989 var pn = this.paramNames;
8990 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8993 this.proxy.load(p, this.reader, this.loadRecords, this, options);
8998 * Reloads the Record cache from the configured Proxy using the configured Reader and
8999 * the options from the last load operation performed.
9000 * @param {Object} options (optional) An object containing properties which may override the options
9001 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9002 * the most recently used options are reused).
9004 reload : function(options){
9005 this.load(Roo.applyIf(options||{}, this.lastOptions));
9009 // Called as a callback by the Reader during a load operation.
9010 loadRecords : function(o, options, success){
9011 if(!o || success === false){
9012 if(success !== false){
9013 this.fireEvent("load", this, [], options, o);
9015 if(options.callback){
9016 options.callback.call(options.scope || this, [], options, false);
9020 // if data returned failure - throw an exception.
9021 if (o.success === false) {
9022 // show a message if no listener is registered.
9023 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9024 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9026 // loadmask wil be hooked into this..
9027 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9030 var r = o.records, t = o.totalRecords || r.length;
9032 this.fireEvent("beforeloadadd", this, r, options, o);
9034 if(!options || options.add !== true){
9035 if(this.pruneModifiedRecords){
9038 for(var i = 0, len = r.length; i < len; i++){
9042 this.data = this.snapshot;
9043 delete this.snapshot;
9046 this.data.addAll(r);
9047 this.totalLength = t;
9049 this.fireEvent("datachanged", this);
9051 this.totalLength = Math.max(t, this.data.length+r.length);
9054 this.fireEvent("load", this, r, options, o);
9055 if(options.callback){
9056 options.callback.call(options.scope || this, r, options, true);
9062 * Loads data from a passed data block. A Reader which understands the format of the data
9063 * must have been configured in the constructor.
9064 * @param {Object} data The data block from which to read the Records. The format of the data expected
9065 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9066 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9068 loadData : function(o, append){
9069 var r = this.reader.readRecords(o);
9070 this.loadRecords(r, {add: append}, true);
9074 * Gets the number of cached records.
9076 * <em>If using paging, this may not be the total size of the dataset. If the data object
9077 * used by the Reader contains the dataset size, then the getTotalCount() function returns
9078 * the data set size</em>
9080 getCount : function(){
9081 return this.data.length || 0;
9085 * Gets the total number of records in the dataset as returned by the server.
9087 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9088 * the dataset size</em>
9090 getTotalCount : function(){
9091 return this.totalLength || 0;
9095 * Returns the sort state of the Store as an object with two properties:
9097 field {String} The name of the field by which the Records are sorted
9098 direction {String} The sort order, "ASC" or "DESC"
9101 getSortState : function(){
9102 return this.sortInfo;
9106 applySort : function(){
9107 if(this.sortInfo && !this.remoteSort){
9108 var s = this.sortInfo, f = s.field;
9109 var st = this.fields.get(f).sortType;
9110 var fn = function(r1, r2){
9111 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9112 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9114 this.data.sort(s.direction, fn);
9115 if(this.snapshot && this.snapshot != this.data){
9116 this.snapshot.sort(s.direction, fn);
9122 * Sets the default sort column and order to be used by the next load operation.
9123 * @param {String} fieldName The name of the field to sort by.
9124 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9126 setDefaultSort : function(field, dir){
9127 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9132 * If remote sorting is used, the sort is performed on the server, and the cache is
9133 * reloaded. If local sorting is used, the cache is sorted internally.
9134 * @param {String} fieldName The name of the field to sort by.
9135 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9137 sort : function(fieldName, dir){
9138 var f = this.fields.get(fieldName);
9140 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9142 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9143 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9148 this.sortToggle[f.name] = dir;
9149 this.sortInfo = {field: f.name, direction: dir};
9150 if(!this.remoteSort){
9152 this.fireEvent("datachanged", this);
9154 this.load(this.lastOptions);
9159 * Calls the specified function for each of the Records in the cache.
9160 * @param {Function} fn The function to call. The Record is passed as the first parameter.
9161 * Returning <em>false</em> aborts and exits the iteration.
9162 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9164 each : function(fn, scope){
9165 this.data.each(fn, scope);
9169 * Gets all records modified since the last commit. Modified records are persisted across load operations
9170 * (e.g., during paging).
9171 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9173 getModifiedRecords : function(){
9174 return this.modified;
9178 createFilterFn : function(property, value, anyMatch){
9179 if(!value.exec){ // not a regex
9180 value = String(value);
9181 if(value.length == 0){
9184 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9187 return value.test(r.data[property]);
9192 * Sums the value of <i>property</i> for each record between start and end and returns the result.
9193 * @param {String} property A field on your records
9194 * @param {Number} start The record index to start at (defaults to 0)
9195 * @param {Number} end The last record index to include (defaults to length - 1)
9196 * @return {Number} The sum
9198 sum : function(property, start, end){
9199 var rs = this.data.items, v = 0;
9201 end = (end || end === 0) ? end : rs.length-1;
9203 for(var i = start; i <= end; i++){
9204 v += (rs[i].data[property] || 0);
9210 * Filter the records by a specified property.
9211 * @param {String} field A field on your records
9212 * @param {String/RegExp} value Either a string that the field
9213 * should start with or a RegExp to test against the field
9214 * @param {Boolean} anyMatch True to match any part not just the beginning
9216 filter : function(property, value, anyMatch){
9217 var fn = this.createFilterFn(property, value, anyMatch);
9218 return fn ? this.filterBy(fn) : this.clearFilter();
9222 * Filter by a function. The specified function will be called with each
9223 * record in this data source. If the function returns true the record is included,
9224 * otherwise it is filtered.
9225 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9226 * @param {Object} scope (optional) The scope of the function (defaults to this)
9228 filterBy : function(fn, scope){
9229 this.snapshot = this.snapshot || this.data;
9230 this.data = this.queryBy(fn, scope||this);
9231 this.fireEvent("datachanged", this);
9235 * Query the records by a specified property.
9236 * @param {String} field A field on your records
9237 * @param {String/RegExp} value Either a string that the field
9238 * should start with or a RegExp to test against the field
9239 * @param {Boolean} anyMatch True to match any part not just the beginning
9240 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9242 query : function(property, value, anyMatch){
9243 var fn = this.createFilterFn(property, value, anyMatch);
9244 return fn ? this.queryBy(fn) : this.data.clone();
9248 * Query by a function. The specified function will be called with each
9249 * record in this data source. If the function returns true the record is included
9251 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9252 * @param {Object} scope (optional) The scope of the function (defaults to this)
9253 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9255 queryBy : function(fn, scope){
9256 var data = this.snapshot || this.data;
9257 return data.filterBy(fn, scope||this);
9261 * Collects unique values for a particular dataIndex from this store.
9262 * @param {String} dataIndex The property to collect
9263 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9264 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9265 * @return {Array} An array of the unique values
9267 collect : function(dataIndex, allowNull, bypassFilter){
9268 var d = (bypassFilter === true && this.snapshot) ?
9269 this.snapshot.items : this.data.items;
9270 var v, sv, r = [], l = {};
9271 for(var i = 0, len = d.length; i < len; i++){
9272 v = d[i].data[dataIndex];
9274 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9283 * Revert to a view of the Record cache with no filtering applied.
9284 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9286 clearFilter : function(suppressEvent){
9287 if(this.snapshot && this.snapshot != this.data){
9288 this.data = this.snapshot;
9289 delete this.snapshot;
9290 if(suppressEvent !== true){
9291 this.fireEvent("datachanged", this);
9297 afterEdit : function(record){
9298 if(this.modified.indexOf(record) == -1){
9299 this.modified.push(record);
9301 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9305 afterReject : function(record){
9306 this.modified.remove(record);
9307 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9311 afterCommit : function(record){
9312 this.modified.remove(record);
9313 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9317 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9318 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9320 commitChanges : function(){
9321 var m = this.modified.slice(0);
9323 for(var i = 0, len = m.length; i < len; i++){
9329 * Cancel outstanding changes on all changed records.
9331 rejectChanges : function(){
9332 var m = this.modified.slice(0);
9334 for(var i = 0, len = m.length; i < len; i++){
9339 onMetaChange : function(meta, rtype, o){
9340 this.recordType = rtype;
9341 this.fields = rtype.prototype.fields;
9342 delete this.snapshot;
9343 this.sortInfo = meta.sortInfo || this.sortInfo;
9345 this.fireEvent('metachange', this, this.reader.meta);
9348 moveIndex : function(data, type)
9350 var index = this.indexOf(data);
9352 var newIndex = index + type;
9356 this.insert(newIndex, data);
9361 * Ext JS Library 1.1.1
9362 * Copyright(c) 2006-2007, Ext JS, LLC.
9364 * Originally Released Under LGPL - original licence link has changed is not relivant.
9367 * <script type="text/javascript">
9371 * @class Roo.data.SimpleStore
9372 * @extends Roo.data.Store
9373 * Small helper class to make creating Stores from Array data easier.
9374 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9375 * @cfg {Array} fields An array of field definition objects, or field name strings.
9376 * @cfg {Array} data The multi-dimensional array of data
9378 * @param {Object} config
9380 Roo.data.SimpleStore = function(config){
9381 Roo.data.SimpleStore.superclass.constructor.call(this, {
9383 reader: new Roo.data.ArrayReader({
9386 Roo.data.Record.create(config.fields)
9388 proxy : new Roo.data.MemoryProxy(config.data)
9392 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9394 * Ext JS Library 1.1.1
9395 * Copyright(c) 2006-2007, Ext JS, LLC.
9397 * Originally Released Under LGPL - original licence link has changed is not relivant.
9400 * <script type="text/javascript">
9405 * @extends Roo.data.Store
9406 * @class Roo.data.JsonStore
9407 * Small helper class to make creating Stores for JSON data easier. <br/>
9409 var store = new Roo.data.JsonStore({
9410 url: 'get-images.php',
9412 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9415 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9416 * JsonReader and HttpProxy (unless inline data is provided).</b>
9417 * @cfg {Array} fields An array of field definition objects, or field name strings.
9419 * @param {Object} config
9421 Roo.data.JsonStore = function(c){
9422 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9423 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9424 reader: new Roo.data.JsonReader(c, c.fields)
9427 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9429 * Ext JS Library 1.1.1
9430 * Copyright(c) 2006-2007, Ext JS, LLC.
9432 * Originally Released Under LGPL - original licence link has changed is not relivant.
9435 * <script type="text/javascript">
9439 Roo.data.Field = function(config){
9440 if(typeof config == "string"){
9441 config = {name: config};
9443 Roo.apply(this, config);
9449 var st = Roo.data.SortTypes;
9450 // named sortTypes are supported, here we look them up
9451 if(typeof this.sortType == "string"){
9452 this.sortType = st[this.sortType];
9455 // set default sortType for strings and dates
9459 this.sortType = st.asUCString;
9462 this.sortType = st.asDate;
9465 this.sortType = st.none;
9470 var stripRe = /[\$,%]/g;
9472 // prebuilt conversion function for this field, instead of
9473 // switching every time we're reading a value
9475 var cv, dateFormat = this.dateFormat;
9480 cv = function(v){ return v; };
9483 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9487 return v !== undefined && v !== null && v !== '' ?
9488 parseInt(String(v).replace(stripRe, ""), 10) : '';
9493 return v !== undefined && v !== null && v !== '' ?
9494 parseFloat(String(v).replace(stripRe, ""), 10) : '';
9499 cv = function(v){ return v === true || v === "true" || v == 1; };
9506 if(v instanceof Date){
9510 if(dateFormat == "timestamp"){
9511 return new Date(v*1000);
9513 return Date.parseDate(v, dateFormat);
9515 var parsed = Date.parse(v);
9516 return parsed ? new Date(parsed) : null;
9525 Roo.data.Field.prototype = {
9533 * Ext JS Library 1.1.1
9534 * Copyright(c) 2006-2007, Ext JS, LLC.
9536 * Originally Released Under LGPL - original licence link has changed is not relivant.
9539 * <script type="text/javascript">
9542 // Base class for reading structured data from a data source. This class is intended to be
9543 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9546 * @class Roo.data.DataReader
9547 * Base class for reading structured data from a data source. This class is intended to be
9548 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9551 Roo.data.DataReader = function(meta, recordType){
9555 this.recordType = recordType instanceof Array ?
9556 Roo.data.Record.create(recordType) : recordType;
9559 Roo.data.DataReader.prototype = {
9561 * Create an empty record
9562 * @param {Object} data (optional) - overlay some values
9563 * @return {Roo.data.Record} record created.
9565 newRow : function(d) {
9567 this.recordType.prototype.fields.each(function(c) {
9569 case 'int' : da[c.name] = 0; break;
9570 case 'date' : da[c.name] = new Date(); break;
9571 case 'float' : da[c.name] = 0.0; break;
9572 case 'boolean' : da[c.name] = false; break;
9573 default : da[c.name] = ""; break;
9577 return new this.recordType(Roo.apply(da, d));
9582 * Ext JS Library 1.1.1
9583 * Copyright(c) 2006-2007, Ext JS, LLC.
9585 * Originally Released Under LGPL - original licence link has changed is not relivant.
9588 * <script type="text/javascript">
9592 * @class Roo.data.DataProxy
9593 * @extends Roo.data.Observable
9594 * This class is an abstract base class for implementations which provide retrieval of
9595 * unformatted data objects.<br>
9597 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9598 * (of the appropriate type which knows how to parse the data object) to provide a block of
9599 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9601 * Custom implementations must implement the load method as described in
9602 * {@link Roo.data.HttpProxy#load}.
9604 Roo.data.DataProxy = function(){
9608 * Fires before a network request is made to retrieve a data object.
9609 * @param {Object} This DataProxy object.
9610 * @param {Object} params The params parameter to the load function.
9615 * Fires before the load method's callback is called.
9616 * @param {Object} This DataProxy object.
9617 * @param {Object} o The data object.
9618 * @param {Object} arg The callback argument object passed to the load function.
9622 * @event loadexception
9623 * Fires if an Exception occurs during data retrieval.
9624 * @param {Object} This DataProxy object.
9625 * @param {Object} o The data object.
9626 * @param {Object} arg The callback argument object passed to the load function.
9627 * @param {Object} e The Exception.
9629 loadexception : true
9631 Roo.data.DataProxy.superclass.constructor.call(this);
9634 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9637 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9641 * Ext JS Library 1.1.1
9642 * Copyright(c) 2006-2007, Ext JS, LLC.
9644 * Originally Released Under LGPL - original licence link has changed is not relivant.
9647 * <script type="text/javascript">
9650 * @class Roo.data.MemoryProxy
9651 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9652 * to the Reader when its load method is called.
9654 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9656 Roo.data.MemoryProxy = function(data){
9660 Roo.data.MemoryProxy.superclass.constructor.call(this);
9664 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9666 * Load data from the requested source (in this case an in-memory
9667 * data object passed to the constructor), read the data object into
9668 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9669 * process that block using the passed callback.
9670 * @param {Object} params This parameter is not used by the MemoryProxy class.
9671 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9672 * object into a block of Roo.data.Records.
9673 * @param {Function} callback The function into which to pass the block of Roo.data.records.
9674 * The function must be passed <ul>
9675 * <li>The Record block object</li>
9676 * <li>The "arg" argument from the load function</li>
9677 * <li>A boolean success indicator</li>
9679 * @param {Object} scope The scope in which to call the callback
9680 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9682 load : function(params, reader, callback, scope, arg){
9683 params = params || {};
9686 result = reader.readRecords(this.data);
9688 this.fireEvent("loadexception", this, arg, null, e);
9689 callback.call(scope, null, arg, false);
9692 callback.call(scope, result, arg, true);
9696 update : function(params, records){
9701 * Ext JS Library 1.1.1
9702 * Copyright(c) 2006-2007, Ext JS, LLC.
9704 * Originally Released Under LGPL - original licence link has changed is not relivant.
9707 * <script type="text/javascript">
9710 * @class Roo.data.HttpProxy
9711 * @extends Roo.data.DataProxy
9712 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9713 * configured to reference a certain URL.<br><br>
9715 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9716 * from which the running page was served.<br><br>
9718 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9720 * Be aware that to enable the browser to parse an XML document, the server must set
9721 * the Content-Type header in the HTTP response to "text/xml".
9723 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9724 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
9725 * will be used to make the request.
9727 Roo.data.HttpProxy = function(conn){
9728 Roo.data.HttpProxy.superclass.constructor.call(this);
9729 // is conn a conn config or a real conn?
9731 this.useAjax = !conn || !conn.events;
9735 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9736 // thse are take from connection...
9739 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9742 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9743 * extra parameters to each request made by this object. (defaults to undefined)
9746 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9747 * to each request made by this object. (defaults to undefined)
9750 * @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)
9753 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9756 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9762 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9766 * Return the {@link Roo.data.Connection} object being used by this Proxy.
9767 * @return {Connection} The Connection object. This object may be used to subscribe to events on
9768 * a finer-grained basis than the DataProxy events.
9770 getConnection : function(){
9771 return this.useAjax ? Roo.Ajax : this.conn;
9775 * Load data from the configured {@link Roo.data.Connection}, read the data object into
9776 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9777 * process that block using the passed callback.
9778 * @param {Object} params An object containing properties which are to be used as HTTP parameters
9779 * for the request to the remote server.
9780 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9781 * object into a block of Roo.data.Records.
9782 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9783 * The function must be passed <ul>
9784 * <li>The Record block object</li>
9785 * <li>The "arg" argument from the load function</li>
9786 * <li>A boolean success indicator</li>
9788 * @param {Object} scope The scope in which to call the callback
9789 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9791 load : function(params, reader, callback, scope, arg){
9792 if(this.fireEvent("beforeload", this, params) !== false){
9794 params : params || {},
9796 callback : callback,
9801 callback : this.loadResponse,
9805 Roo.applyIf(o, this.conn);
9806 if(this.activeRequest){
9807 Roo.Ajax.abort(this.activeRequest);
9809 this.activeRequest = Roo.Ajax.request(o);
9811 this.conn.request(o);
9814 callback.call(scope||this, null, arg, false);
9819 loadResponse : function(o, success, response){
9820 delete this.activeRequest;
9822 this.fireEvent("loadexception", this, o, response);
9823 o.request.callback.call(o.request.scope, null, o.request.arg, false);
9828 result = o.reader.read(response);
9830 this.fireEvent("loadexception", this, o, response, e);
9831 o.request.callback.call(o.request.scope, null, o.request.arg, false);
9835 this.fireEvent("load", this, o, o.request.arg);
9836 o.request.callback.call(o.request.scope, result, o.request.arg, true);
9840 update : function(dataSet){
9845 updateResponse : function(dataSet){
9850 * Ext JS Library 1.1.1
9851 * Copyright(c) 2006-2007, Ext JS, LLC.
9853 * Originally Released Under LGPL - original licence link has changed is not relivant.
9856 * <script type="text/javascript">
9860 * @class Roo.data.ScriptTagProxy
9861 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9862 * other than the originating domain of the running page.<br><br>
9864 * <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
9865 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9867 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9868 * source code that is used as the source inside a <script> tag.<br><br>
9870 * In order for the browser to process the returned data, the server must wrap the data object
9871 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9872 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9873 * depending on whether the callback name was passed:
9876 boolean scriptTag = false;
9877 String cb = request.getParameter("callback");
9880 response.setContentType("text/javascript");
9882 response.setContentType("application/x-json");
9884 Writer out = response.getWriter();
9886 out.write(cb + "(");
9888 out.print(dataBlock.toJsonString());
9895 * @param {Object} config A configuration object.
9897 Roo.data.ScriptTagProxy = function(config){
9898 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9899 Roo.apply(this, config);
9900 this.head = document.getElementsByTagName("head")[0];
9903 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9905 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9907 * @cfg {String} url The URL from which to request the data object.
9910 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9914 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9915 * the server the name of the callback function set up by the load call to process the returned data object.
9916 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9917 * javascript output which calls this named function passing the data object as its only parameter.
9919 callbackParam : "callback",
9921 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9922 * name to the request.
9927 * Load data from the configured URL, read the data object into
9928 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9929 * process that block using the passed callback.
9930 * @param {Object} params An object containing properties which are to be used as HTTP parameters
9931 * for the request to the remote server.
9932 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9933 * object into a block of Roo.data.Records.
9934 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9935 * The function must be passed <ul>
9936 * <li>The Record block object</li>
9937 * <li>The "arg" argument from the load function</li>
9938 * <li>A boolean success indicator</li>
9940 * @param {Object} scope The scope in which to call the callback
9941 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9943 load : function(params, reader, callback, scope, arg){
9944 if(this.fireEvent("beforeload", this, params) !== false){
9946 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9949 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9951 url += "&_dc=" + (new Date().getTime());
9953 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9956 cb : "stcCallback"+transId,
9957 scriptId : "stcScript"+transId,
9961 callback : callback,
9967 window[trans.cb] = function(o){
9968 conn.handleResponse(o, trans);
9971 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9973 if(this.autoAbort !== false){
9977 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9979 var script = document.createElement("script");
9980 script.setAttribute("src", url);
9981 script.setAttribute("type", "text/javascript");
9982 script.setAttribute("id", trans.scriptId);
9983 this.head.appendChild(script);
9987 callback.call(scope||this, null, arg, false);
9992 isLoading : function(){
9993 return this.trans ? true : false;
9997 * Abort the current server request.
10000 if(this.isLoading()){
10001 this.destroyTrans(this.trans);
10006 destroyTrans : function(trans, isLoaded){
10007 this.head.removeChild(document.getElementById(trans.scriptId));
10008 clearTimeout(trans.timeoutId);
10010 window[trans.cb] = undefined;
10012 delete window[trans.cb];
10015 // if hasn't been loaded, wait for load to remove it to prevent script error
10016 window[trans.cb] = function(){
10017 window[trans.cb] = undefined;
10019 delete window[trans.cb];
10026 handleResponse : function(o, trans){
10027 this.trans = false;
10028 this.destroyTrans(trans, true);
10031 result = trans.reader.readRecords(o);
10033 this.fireEvent("loadexception", this, o, trans.arg, e);
10034 trans.callback.call(trans.scope||window, null, trans.arg, false);
10037 this.fireEvent("load", this, o, trans.arg);
10038 trans.callback.call(trans.scope||window, result, trans.arg, true);
10042 handleFailure : function(trans){
10043 this.trans = false;
10044 this.destroyTrans(trans, false);
10045 this.fireEvent("loadexception", this, null, trans.arg);
10046 trans.callback.call(trans.scope||window, null, trans.arg, false);
10050 * Ext JS Library 1.1.1
10051 * Copyright(c) 2006-2007, Ext JS, LLC.
10053 * Originally Released Under LGPL - original licence link has changed is not relivant.
10056 * <script type="text/javascript">
10060 * @class Roo.data.JsonReader
10061 * @extends Roo.data.DataReader
10062 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10063 * based on mappings in a provided Roo.data.Record constructor.
10065 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10066 * in the reply previously.
10071 var RecordDef = Roo.data.Record.create([
10072 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
10073 {name: 'occupation'} // This field will use "occupation" as the mapping.
10075 var myReader = new Roo.data.JsonReader({
10076 totalProperty: "results", // The property which contains the total dataset size (optional)
10077 root: "rows", // The property which contains an Array of row objects
10078 id: "id" // The property within each row object that provides an ID for the record (optional)
10082 * This would consume a JSON file like this:
10084 { 'results': 2, 'rows': [
10085 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10086 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10089 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10090 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10091 * paged from the remote server.
10092 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10093 * @cfg {String} root name of the property which contains the Array of row objects.
10094 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10096 * Create a new JsonReader
10097 * @param {Object} meta Metadata configuration options
10098 * @param {Object} recordType Either an Array of field definition objects,
10099 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10101 Roo.data.JsonReader = function(meta, recordType){
10104 // set some defaults:
10105 Roo.applyIf(meta, {
10106 totalProperty: 'total',
10107 successProperty : 'success',
10112 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10114 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10117 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
10118 * Used by Store query builder to append _requestMeta to params.
10121 metaFromRemote : false,
10123 * This method is only used by a DataProxy which has retrieved data from a remote server.
10124 * @param {Object} response The XHR object which contains the JSON data in its responseText.
10125 * @return {Object} data A data block which is used by an Roo.data.Store object as
10126 * a cache of Roo.data.Records.
10128 read : function(response){
10129 var json = response.responseText;
10131 var o = /* eval:var:o */ eval("("+json+")");
10133 throw {message: "JsonReader.read: Json object not found"};
10139 this.metaFromRemote = true;
10140 this.meta = o.metaData;
10141 this.recordType = Roo.data.Record.create(o.metaData.fields);
10142 this.onMetaChange(this.meta, this.recordType, o);
10144 return this.readRecords(o);
10147 // private function a store will implement
10148 onMetaChange : function(meta, recordType, o){
10155 simpleAccess: function(obj, subsc) {
10162 getJsonAccessor: function(){
10164 return function(expr) {
10166 return(re.test(expr))
10167 ? new Function("obj", "return obj." + expr)
10172 return Roo.emptyFn;
10177 * Create a data block containing Roo.data.Records from an XML document.
10178 * @param {Object} o An object which contains an Array of row objects in the property specified
10179 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10180 * which contains the total size of the dataset.
10181 * @return {Object} data A data block which is used by an Roo.data.Store object as
10182 * a cache of Roo.data.Records.
10184 readRecords : function(o){
10186 * After any data loads, the raw JSON data is available for further custom processing.
10190 var s = this.meta, Record = this.recordType,
10191 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10193 // Generate extraction functions for the totalProperty, the root, the id, and for each field
10195 if(s.totalProperty) {
10196 this.getTotal = this.getJsonAccessor(s.totalProperty);
10198 if(s.successProperty) {
10199 this.getSuccess = this.getJsonAccessor(s.successProperty);
10201 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10203 var g = this.getJsonAccessor(s.id);
10204 this.getId = function(rec) {
10206 return (r === undefined || r === "") ? null : r;
10209 this.getId = function(){return null;};
10212 for(var jj = 0; jj < fl; jj++){
10214 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10215 this.ef[jj] = this.getJsonAccessor(map);
10219 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10220 if(s.totalProperty){
10221 var vt = parseInt(this.getTotal(o), 10);
10226 if(s.successProperty){
10227 var vs = this.getSuccess(o);
10228 if(vs === false || vs === 'false'){
10233 for(var i = 0; i < c; i++){
10236 var id = this.getId(n);
10237 for(var j = 0; j < fl; j++){
10239 var v = this.ef[j](n);
10241 Roo.log('missing convert for ' + f.name);
10245 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10247 var record = new Record(values, id);
10249 records[i] = record;
10255 totalRecords : totalRecords
10260 * Ext JS Library 1.1.1
10261 * Copyright(c) 2006-2007, Ext JS, LLC.
10263 * Originally Released Under LGPL - original licence link has changed is not relivant.
10266 * <script type="text/javascript">
10270 * @class Roo.data.ArrayReader
10271 * @extends Roo.data.DataReader
10272 * Data reader class to create an Array of Roo.data.Record objects from an Array.
10273 * Each element of that Array represents a row of data fields. The
10274 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10275 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10279 var RecordDef = Roo.data.Record.create([
10280 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
10281 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
10283 var myReader = new Roo.data.ArrayReader({
10284 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
10288 * This would consume an Array like this:
10290 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10292 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10294 * Create a new JsonReader
10295 * @param {Object} meta Metadata configuration options.
10296 * @param {Object} recordType Either an Array of field definition objects
10297 * as specified to {@link Roo.data.Record#create},
10298 * or an {@link Roo.data.Record} object
10299 * created using {@link Roo.data.Record#create}.
10301 Roo.data.ArrayReader = function(meta, recordType){
10302 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10305 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10307 * Create a data block containing Roo.data.Records from an XML document.
10308 * @param {Object} o An Array of row objects which represents the dataset.
10309 * @return {Object} data A data block which is used by an Roo.data.Store object as
10310 * a cache of Roo.data.Records.
10312 readRecords : function(o){
10313 var sid = this.meta ? this.meta.id : null;
10314 var recordType = this.recordType, fields = recordType.prototype.fields;
10317 for(var i = 0; i < root.length; i++){
10320 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10321 for(var j = 0, jlen = fields.length; j < jlen; j++){
10322 var f = fields.items[j];
10323 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10324 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10326 values[f.name] = v;
10328 var record = new recordType(values, id);
10330 records[records.length] = record;
10334 totalRecords : records.length
10343 * @class Roo.bootstrap.ComboBox
10344 * @extends Roo.bootstrap.TriggerField
10345 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10346 * @cfg {Boolean} append (true|false) default false
10347 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10348 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10349 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10350 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10351 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10353 * Create a new ComboBox.
10354 * @param {Object} config Configuration options
10356 Roo.bootstrap.ComboBox = function(config){
10357 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10361 * Fires when the dropdown list is expanded
10362 * @param {Roo.bootstrap.ComboBox} combo This combo box
10367 * Fires when the dropdown list is collapsed
10368 * @param {Roo.bootstrap.ComboBox} combo This combo box
10372 * @event beforeselect
10373 * Fires before a list item is selected. Return false to cancel the selection.
10374 * @param {Roo.bootstrap.ComboBox} combo This combo box
10375 * @param {Roo.data.Record} record The data record returned from the underlying store
10376 * @param {Number} index The index of the selected item in the dropdown list
10378 'beforeselect' : true,
10381 * Fires when a list item is selected
10382 * @param {Roo.bootstrap.ComboBox} combo This combo box
10383 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10384 * @param {Number} index The index of the selected item in the dropdown list
10388 * @event beforequery
10389 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10390 * The event object passed has these properties:
10391 * @param {Roo.bootstrap.ComboBox} combo This combo box
10392 * @param {String} query The query
10393 * @param {Boolean} forceAll true to force "all" query
10394 * @param {Boolean} cancel true to cancel the query
10395 * @param {Object} e The query event object
10397 'beforequery': true,
10400 * Fires when the 'add' icon is pressed (add a listener to enable add button)
10401 * @param {Roo.bootstrap.ComboBox} combo This combo box
10406 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10407 * @param {Roo.bootstrap.ComboBox} combo This combo box
10408 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10413 * Fires when the remove value from the combobox array
10414 * @param {Roo.bootstrap.ComboBox} combo This combo box
10421 this.tickItems = [];
10423 this.selectedIndex = -1;
10424 if(this.mode == 'local'){
10425 if(config.queryDelay === undefined){
10426 this.queryDelay = 10;
10428 if(config.minChars === undefined){
10434 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10437 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10438 * rendering into an Roo.Editor, defaults to false)
10441 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10442 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10445 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10448 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10449 * the dropdown list (defaults to undefined, with no header element)
10453 * @cfg {String/Roo.Template} tpl The template to use to render the output
10457 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10459 listWidth: undefined,
10461 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10462 * mode = 'remote' or 'text' if mode = 'local')
10464 displayField: undefined,
10466 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10467 * mode = 'remote' or 'value' if mode = 'local').
10468 * Note: use of a valueField requires the user make a selection
10469 * in order for a value to be mapped.
10471 valueField: undefined,
10475 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10476 * field's data value (defaults to the underlying DOM element's name)
10478 hiddenName: undefined,
10480 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10484 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10486 selectedClass: 'active',
10489 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10493 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10494 * anchor positions (defaults to 'tl-bl')
10496 listAlign: 'tl-bl?',
10498 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10502 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
10503 * query specified by the allQuery config option (defaults to 'query')
10505 triggerAction: 'query',
10507 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10508 * (defaults to 4, does not apply if editable = false)
10512 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10513 * delay (typeAheadDelay) if it matches a known value (defaults to false)
10517 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10518 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10522 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10523 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
10527 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
10528 * when editable = true (defaults to false)
10530 selectOnFocus:false,
10532 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10534 queryParam: 'query',
10536 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
10537 * when mode = 'remote' (defaults to 'Loading...')
10539 loadingText: 'Loading...',
10541 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10545 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10549 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10550 * traditional select (defaults to true)
10554 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10558 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10562 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10563 * listWidth has a higher value)
10567 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10568 * allow the user to set arbitrary text into the field (defaults to false)
10570 forceSelection:false,
10572 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10573 * if typeAhead = true (defaults to 250)
10575 typeAheadDelay : 250,
10577 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10578 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10580 valueNotFoundText : undefined,
10582 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10584 blockFocus : false,
10587 * @cfg {Boolean} disableClear Disable showing of clear button.
10589 disableClear : false,
10591 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
10593 alwaysQuery : false,
10596 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
10610 btnPosition : 'right',
10611 triggerList : true,
10612 showToggleBtn : true,
10613 // element that contains real text value.. (when hidden is used..)
10615 getAutoCreate : function()
10622 if(!this.tickable){
10623 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10628 * ComboBox with tickable selections
10631 var align = this.labelAlign || this.parentLabelAlign();
10634 cls : 'form-group roo-combobox-tickable' //input-group
10640 cls : 'tickable-buttons',
10645 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10652 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10659 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10666 Roo.each(buttons.cn, function(c){
10668 c.cls += ' btn-' + _this.size;
10671 if (_this.disabled) {
10682 cls: 'form-hidden-field'
10686 cls: 'select2-choices',
10690 cls: 'select2-search-field',
10702 cls: 'select2-container input-group select2-container-multi',
10707 // cls: 'typeahead typeahead-long dropdown-menu',
10708 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
10713 if (align ==='left' && this.fieldLabel.length) {
10715 Roo.log("left and has label");
10721 cls : 'control-label col-sm-' + this.labelWidth,
10722 html : this.fieldLabel
10726 cls : "col-sm-" + (12 - this.labelWidth),
10733 } else if ( this.fieldLabel.length) {
10739 //cls : 'input-group-addon',
10740 html : this.fieldLabel
10750 Roo.log(" no label && no align");
10757 ['xs','sm','md','lg'].map(function(size){
10758 if (settings[size]) {
10759 cfg.cls += ' col-' + size + '-' + settings[size];
10768 initEvents: function()
10772 throw "can not find store for combo";
10774 this.store = Roo.factory(this.store, Roo.data);
10777 this.initTickableEvents();
10781 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10783 if(this.hiddenName){
10785 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10787 this.hiddenField.dom.value =
10788 this.hiddenValue !== undefined ? this.hiddenValue :
10789 this.value !== undefined ? this.value : '';
10791 // prevent input submission
10792 this.el.dom.removeAttribute('name');
10793 this.hiddenField.dom.setAttribute('name', this.hiddenName);
10798 // this.el.dom.setAttribute('autocomplete', 'off');
10801 var cls = 'x-combo-list';
10803 //this.list = new Roo.Layer({
10804 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10810 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10811 _this.list.setWidth(lw);
10814 this.list.on('mouseover', this.onViewOver, this);
10815 this.list.on('mousemove', this.onViewMove, this);
10817 this.list.on('scroll', this.onViewScroll, this);
10820 this.list.swallowEvent('mousewheel');
10821 this.assetHeight = 0;
10824 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10825 this.assetHeight += this.header.getHeight();
10828 this.innerList = this.list.createChild({cls:cls+'-inner'});
10829 this.innerList.on('mouseover', this.onViewOver, this);
10830 this.innerList.on('mousemove', this.onViewMove, this);
10831 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10833 if(this.allowBlank && !this.pageSize && !this.disableClear){
10834 this.footer = this.list.createChild({cls:cls+'-ft'});
10835 this.pageTb = new Roo.Toolbar(this.footer);
10839 this.footer = this.list.createChild({cls:cls+'-ft'});
10840 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10841 {pageSize: this.pageSize});
10845 if (this.pageTb && this.allowBlank && !this.disableClear) {
10847 this.pageTb.add(new Roo.Toolbar.Fill(), {
10848 cls: 'x-btn-icon x-btn-clear',
10850 handler: function()
10853 _this.clearValue();
10854 _this.onSelect(false, -1);
10859 this.assetHeight += this.footer.getHeight();
10864 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10867 this.view = new Roo.View(this.list, this.tpl, {
10868 singleSelect:true, store: this.store, selectedClass: this.selectedClass
10870 //this.view.wrapEl.setDisplayed(false);
10871 this.view.on('click', this.onViewClick, this);
10875 this.store.on('beforeload', this.onBeforeLoad, this);
10876 this.store.on('load', this.onLoad, this);
10877 this.store.on('loadexception', this.onLoadException, this);
10879 if(this.resizable){
10880 this.resizer = new Roo.Resizable(this.list, {
10881 pinned:true, handles:'se'
10883 this.resizer.on('resize', function(r, w, h){
10884 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10885 this.listWidth = w;
10886 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10887 this.restrictHeight();
10889 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10892 if(!this.editable){
10893 this.editable = true;
10894 this.setEditable(false);
10899 if (typeof(this.events.add.listeners) != 'undefined') {
10901 this.addicon = this.wrap.createChild(
10902 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
10904 this.addicon.on('click', function(e) {
10905 this.fireEvent('add', this);
10908 if (typeof(this.events.edit.listeners) != 'undefined') {
10910 this.editicon = this.wrap.createChild(
10911 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
10912 if (this.addicon) {
10913 this.editicon.setStyle('margin-left', '40px');
10915 this.editicon.on('click', function(e) {
10917 // we fire even if inothing is selected..
10918 this.fireEvent('edit', this, this.lastData );
10924 this.keyNav = new Roo.KeyNav(this.inputEl(), {
10925 "up" : function(e){
10926 this.inKeyMode = true;
10930 "down" : function(e){
10931 if(!this.isExpanded()){
10932 this.onTriggerClick();
10934 this.inKeyMode = true;
10939 "enter" : function(e){
10940 // this.onViewClick();
10944 if(this.fireEvent("specialkey", this, e)){
10945 this.onViewClick(false);
10951 "esc" : function(e){
10955 "tab" : function(e){
10958 if(this.fireEvent("specialkey", this, e)){
10959 this.onViewClick(false);
10967 doRelay : function(foo, bar, hname){
10968 if(hname == 'down' || this.scope.isExpanded()){
10969 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10978 this.queryDelay = Math.max(this.queryDelay || 10,
10979 this.mode == 'local' ? 10 : 250);
10982 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10984 if(this.typeAhead){
10985 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10987 if(this.editable !== false){
10988 this.inputEl().on("keyup", this.onKeyUp, this);
10990 if(this.forceSelection){
10991 this.inputEl().on('blur', this.doForce, this);
10995 this.choices = this.el.select('ul.select2-choices', true).first();
10996 this.searchField = this.el.select('ul li.select2-search-field', true).first();
11000 initTickableEvents: function()
11004 if(this.hiddenName){
11006 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11008 this.hiddenField.dom.value =
11009 this.hiddenValue !== undefined ? this.hiddenValue :
11010 this.value !== undefined ? this.value : '';
11012 // prevent input submission
11013 this.el.dom.removeAttribute('name');
11014 this.hiddenField.dom.setAttribute('name', this.hiddenName);
11019 // this.list = this.el.select('ul.dropdown-menu',true).first();
11021 this.choices = this.el.select('ul.select2-choices', true).first();
11022 this.searchField = this.el.select('ul li.select2-search-field', true).first();
11023 if(this.triggerList){
11024 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11027 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11028 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11030 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11031 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11033 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11034 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11036 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11037 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11038 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11041 this.cancelBtn.hide();
11046 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11047 _this.list.setWidth(lw);
11050 this.list.on('mouseover', this.onViewOver, this);
11051 this.list.on('mousemove', this.onViewMove, this);
11053 this.list.on('scroll', this.onViewScroll, this);
11056 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>';
11059 this.view = new Roo.View(this.list, this.tpl, {
11060 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11063 //this.view.wrapEl.setDisplayed(false);
11064 this.view.on('click', this.onViewClick, this);
11068 this.store.on('beforeload', this.onBeforeLoad, this);
11069 this.store.on('load', this.onLoad, this);
11070 this.store.on('loadexception', this.onLoadException, this);
11072 // this.keyNav = new Roo.KeyNav(this.inputEl(), {
11073 // "up" : function(e){
11074 // this.inKeyMode = true;
11075 // this.selectPrev();
11078 // "down" : function(e){
11079 // if(!this.isExpanded()){
11080 // this.onTriggerClick();
11082 // this.inKeyMode = true;
11083 // this.selectNext();
11087 // "enter" : function(e){
11088 //// this.onViewClick();
11090 // this.collapse();
11092 // if(this.fireEvent("specialkey", this, e)){
11093 // this.onViewClick(false);
11099 // "esc" : function(e){
11100 // this.collapse();
11103 // "tab" : function(e){
11104 // this.collapse();
11106 // if(this.fireEvent("specialkey", this, e)){
11107 // this.onViewClick(false);
11115 // doRelay : function(foo, bar, hname){
11116 // if(hname == 'down' || this.scope.isExpanded()){
11117 // return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11122 // forceKeyDown: true
11126 this.queryDelay = Math.max(this.queryDelay || 10,
11127 this.mode == 'local' ? 10 : 250);
11130 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11132 if(this.typeAhead){
11133 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11137 onDestroy : function(){
11139 this.view.setStore(null);
11140 this.view.el.removeAllListeners();
11141 this.view.el.remove();
11142 this.view.purgeListeners();
11145 this.list.dom.innerHTML = '';
11149 this.store.un('beforeload', this.onBeforeLoad, this);
11150 this.store.un('load', this.onLoad, this);
11151 this.store.un('loadexception', this.onLoadException, this);
11153 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11157 fireKey : function(e){
11158 if(e.isNavKeyPress() && !this.list.isVisible()){
11159 this.fireEvent("specialkey", this, e);
11164 onResize: function(w, h){
11165 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11167 // if(typeof w != 'number'){
11168 // // we do not handle it!?!?
11171 // var tw = this.trigger.getWidth();
11172 // // tw += this.addicon ? this.addicon.getWidth() : 0;
11173 // // tw += this.editicon ? this.editicon.getWidth() : 0;
11175 // this.inputEl().setWidth( this.adjustWidth('input', x));
11177 // //this.trigger.setStyle('left', x+'px');
11179 // if(this.list && this.listWidth === undefined){
11180 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11181 // this.list.setWidth(lw);
11182 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11190 * Allow or prevent the user from directly editing the field text. If false is passed,
11191 * the user will only be able to select from the items defined in the dropdown list. This method
11192 * is the runtime equivalent of setting the 'editable' config option at config time.
11193 * @param {Boolean} value True to allow the user to directly edit the field text
11195 setEditable : function(value){
11196 if(value == this.editable){
11199 this.editable = value;
11201 this.inputEl().dom.setAttribute('readOnly', true);
11202 this.inputEl().on('mousedown', this.onTriggerClick, this);
11203 this.inputEl().addClass('x-combo-noedit');
11205 this.inputEl().dom.setAttribute('readOnly', false);
11206 this.inputEl().un('mousedown', this.onTriggerClick, this);
11207 this.inputEl().removeClass('x-combo-noedit');
11213 onBeforeLoad : function(combo,opts){
11214 if(!this.hasFocus){
11218 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11220 this.restrictHeight();
11221 this.selectedIndex = -1;
11225 onLoad : function(){
11227 this.hasQuery = false;
11229 if(!this.hasFocus){
11233 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11234 this.loading.hide();
11237 if(this.store.getCount() > 0){
11239 // this.restrictHeight();
11240 if(this.lastQuery == this.allQuery){
11241 if(this.editable && !this.tickable){
11242 this.inputEl().dom.select();
11246 !this.selectByValue(this.value, true) &&
11247 this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' ||
11248 this.store.lastOptions.add != true)
11250 this.select(0, true);
11253 if(this.autoFocus){
11256 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11257 this.taTask.delay(this.typeAheadDelay);
11261 this.onEmptyResults();
11267 onLoadException : function()
11269 this.hasQuery = false;
11271 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11272 this.loading.hide();
11276 Roo.log(this.store.reader.jsonData);
11277 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11279 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11285 onTypeAhead : function(){
11286 if(this.store.getCount() > 0){
11287 var r = this.store.getAt(0);
11288 var newValue = r.data[this.displayField];
11289 var len = newValue.length;
11290 var selStart = this.getRawValue().length;
11292 if(selStart != len){
11293 this.setRawValue(newValue);
11294 this.selectText(selStart, newValue.length);
11300 onSelect : function(record, index){
11302 if(this.fireEvent('beforeselect', this, record, index) !== false){
11304 this.setFromData(index > -1 ? record.data : false);
11307 this.fireEvent('select', this, record, index);
11312 * Returns the currently selected field value or empty string if no value is set.
11313 * @return {String} value The selected value
11315 getValue : function(){
11318 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11321 if(this.valueField){
11322 return typeof this.value != 'undefined' ? this.value : '';
11324 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11329 * Clears any text/value currently set in the field
11331 clearValue : function(){
11332 if(this.hiddenField){
11333 this.hiddenField.dom.value = '';
11336 this.setRawValue('');
11337 this.lastSelectionText = '';
11342 * Sets the specified value into the field. If the value finds a match, the corresponding record text
11343 * will be displayed in the field. If the value does not match the data value of an existing item,
11344 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11345 * Otherwise the field will be blank (although the value will still be set).
11346 * @param {String} value The value to match
11348 setValue : function(v){
11355 if(this.valueField){
11356 var r = this.findRecord(this.valueField, v);
11358 text = r.data[this.displayField];
11359 }else if(this.valueNotFoundText !== undefined){
11360 text = this.valueNotFoundText;
11363 this.lastSelectionText = text;
11364 if(this.hiddenField){
11365 this.hiddenField.dom.value = v;
11367 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11371 * @property {Object} the last set data for the element
11376 * Sets the value of the field based on a object which is related to the record format for the store.
11377 * @param {Object} value the value to set as. or false on reset?
11379 setFromData : function(o){
11386 var dv = ''; // display value
11387 var vv = ''; // value value..
11389 if (this.displayField) {
11390 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11392 // this is an error condition!!!
11393 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
11396 if(this.valueField){
11397 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11400 if(this.hiddenField){
11401 this.hiddenField.dom.value = vv;
11403 this.lastSelectionText = dv;
11404 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11408 // no hidden field.. - we store the value in 'value', but still display
11409 // display field!!!!
11410 this.lastSelectionText = dv;
11411 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11417 reset : function(){
11418 // overridden so that last data is reset..
11419 this.setValue(this.originalValue);
11420 this.clearInvalid();
11421 this.lastData = false;
11423 this.view.clearSelections();
11427 findRecord : function(prop, value){
11429 if(this.store.getCount() > 0){
11430 this.store.each(function(r){
11431 if(r.data[prop] == value){
11441 getName: function()
11443 // returns hidden if it's set..
11444 if (!this.rendered) {return ''};
11445 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
11449 onViewMove : function(e, t){
11450 this.inKeyMode = false;
11454 onViewOver : function(e, t){
11455 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11458 var item = this.view.findItemFromChild(t);
11461 var index = this.view.indexOf(item);
11462 this.select(index, false);
11467 onViewClick : function(view, doFocus, el, e)
11469 var index = this.view.getSelectedIndexes()[0];
11471 var r = this.store.getAt(index);
11475 if(e.getTarget().nodeName.toLowerCase() != 'input'){
11482 Roo.each(this.tickItems, function(v,k){
11484 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11485 _this.tickItems.splice(k, 1);
11495 this.tickItems.push(r.data);
11500 this.onSelect(r, index);
11502 if(doFocus !== false && !this.blockFocus){
11503 this.inputEl().focus();
11508 restrictHeight : function(){
11509 //this.innerList.dom.style.height = '';
11510 //var inner = this.innerList.dom;
11511 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11512 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11513 //this.list.beginUpdate();
11514 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11515 this.list.alignTo(this.inputEl(), this.listAlign);
11516 this.list.alignTo(this.inputEl(), this.listAlign);
11517 //this.list.endUpdate();
11521 onEmptyResults : function(){
11526 * Returns true if the dropdown list is expanded, else false.
11528 isExpanded : function(){
11529 return this.list.isVisible();
11533 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11534 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11535 * @param {String} value The data value of the item to select
11536 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11537 * selected item if it is not currently in view (defaults to true)
11538 * @return {Boolean} True if the value matched an item in the list, else false
11540 selectByValue : function(v, scrollIntoView){
11541 if(v !== undefined && v !== null){
11542 var r = this.findRecord(this.valueField || this.displayField, v);
11544 this.select(this.store.indexOf(r), scrollIntoView);
11552 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11553 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11554 * @param {Number} index The zero-based index of the list item to select
11555 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11556 * selected item if it is not currently in view (defaults to true)
11558 select : function(index, scrollIntoView){
11559 this.selectedIndex = index;
11560 this.view.select(index);
11561 if(scrollIntoView !== false){
11562 var el = this.view.getNode(index);
11563 if(el && !this.multiple && !this.tickable){
11564 this.list.scrollChildIntoView(el, false);
11570 selectNext : function(){
11571 var ct = this.store.getCount();
11573 if(this.selectedIndex == -1){
11575 }else if(this.selectedIndex < ct-1){
11576 this.select(this.selectedIndex+1);
11582 selectPrev : function(){
11583 var ct = this.store.getCount();
11585 if(this.selectedIndex == -1){
11587 }else if(this.selectedIndex != 0){
11588 this.select(this.selectedIndex-1);
11594 onKeyUp : function(e){
11595 if(this.editable !== false && !e.isSpecialKey()){
11596 this.lastKey = e.getKey();
11597 this.dqTask.delay(this.queryDelay);
11602 validateBlur : function(){
11603 return !this.list || !this.list.isVisible();
11607 initQuery : function(){
11608 this.doQuery(this.getRawValue());
11612 doForce : function(){
11613 if(this.inputEl().dom.value.length > 0){
11614 this.inputEl().dom.value =
11615 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11621 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
11622 * query allowing the query action to be canceled if needed.
11623 * @param {String} query The SQL query to execute
11624 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11625 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
11626 * saved in the current store (defaults to false)
11628 doQuery : function(q, forceAll){
11630 if(q === undefined || q === null){
11635 forceAll: forceAll,
11639 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11644 forceAll = qe.forceAll;
11645 if(forceAll === true || (q.length >= this.minChars)){
11647 this.hasQuery = true;
11649 if(this.lastQuery != q || this.alwaysQuery){
11650 this.lastQuery = q;
11651 if(this.mode == 'local'){
11652 this.selectedIndex = -1;
11654 this.store.clearFilter();
11656 this.store.filter(this.displayField, q);
11660 this.store.baseParams[this.queryParam] = q;
11662 var options = {params : this.getParams(q)};
11665 options.add = true;
11666 options.params.start = this.page * this.pageSize;
11669 this.store.load(options);
11671 * this code will make the page width larger, at the beginning, the list not align correctly,
11672 * we should expand the list on onLoad
11673 * so command out it
11678 this.selectedIndex = -1;
11683 this.loadNext = false;
11687 getParams : function(q){
11689 //p[this.queryParam] = q;
11693 p.limit = this.pageSize;
11699 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11701 collapse : function(){
11702 if(!this.isExpanded()){
11710 this.cancelBtn.hide();
11711 this.trigger.show();
11714 Roo.get(document).un('mousedown', this.collapseIf, this);
11715 Roo.get(document).un('mousewheel', this.collapseIf, this);
11716 if (!this.editable) {
11717 Roo.get(document).un('keydown', this.listKeyPress, this);
11719 this.fireEvent('collapse', this);
11723 collapseIf : function(e){
11724 var in_combo = e.within(this.el);
11725 var in_list = e.within(this.list);
11726 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11728 if (in_combo || in_list || is_list) {
11729 //e.stopPropagation();
11734 this.onTickableFooterButtonClick(e, false, false);
11742 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11744 expand : function(){
11746 if(this.isExpanded() || !this.hasFocus){
11750 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11751 this.list.setWidth(lw);
11758 this.restrictHeight();
11762 this.tickItems = Roo.apply([], this.item);
11765 this.cancelBtn.show();
11766 this.trigger.hide();
11770 Roo.get(document).on('mousedown', this.collapseIf, this);
11771 Roo.get(document).on('mousewheel', this.collapseIf, this);
11772 if (!this.editable) {
11773 Roo.get(document).on('keydown', this.listKeyPress, this);
11776 this.fireEvent('expand', this);
11780 // Implements the default empty TriggerField.onTriggerClick function
11781 onTriggerClick : function(e)
11783 Roo.log('trigger click');
11785 if(this.disabled || !this.triggerList){
11790 this.loadNext = false;
11792 if(this.isExpanded()){
11794 if (!this.blockFocus) {
11795 this.inputEl().focus();
11799 this.hasFocus = true;
11800 if(this.triggerAction == 'all') {
11801 this.doQuery(this.allQuery, true);
11803 this.doQuery(this.getRawValue());
11805 if (!this.blockFocus) {
11806 this.inputEl().focus();
11811 onTickableTriggerClick : function(e)
11818 this.loadNext = false;
11819 this.hasFocus = true;
11821 if(this.triggerAction == 'all') {
11822 this.doQuery(this.allQuery, true);
11824 this.doQuery(this.getRawValue());
11828 onSearchFieldClick : function(e)
11830 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11835 this.loadNext = false;
11836 this.hasFocus = true;
11838 if(this.triggerAction == 'all') {
11839 this.doQuery(this.allQuery, true);
11841 this.doQuery(this.getRawValue());
11845 listKeyPress : function(e)
11847 //Roo.log('listkeypress');
11848 // scroll to first matching element based on key pres..
11849 if (e.isSpecialKey()) {
11852 var k = String.fromCharCode(e.getKey()).toUpperCase();
11855 var csel = this.view.getSelectedNodes();
11856 var cselitem = false;
11858 var ix = this.view.indexOf(csel[0]);
11859 cselitem = this.store.getAt(ix);
11860 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11866 this.store.each(function(v) {
11868 // start at existing selection.
11869 if (cselitem.id == v.id) {
11875 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11876 match = this.store.indexOf(v);
11882 if (match === false) {
11883 return true; // no more action?
11886 this.view.select(match);
11887 var sn = Roo.get(this.view.getSelectedNodes()[0])
11888 sn.scrollIntoView(sn.dom.parentNode, false);
11891 onViewScroll : function(e, t){
11893 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){
11897 this.hasQuery = true;
11899 this.loading = this.list.select('.loading', true).first();
11901 if(this.loading === null){
11902 this.list.createChild({
11904 cls: 'loading select2-more-results select2-active',
11905 html: 'Loading more results...'
11908 this.loading = this.list.select('.loading', true).first();
11910 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11912 this.loading.hide();
11915 this.loading.show();
11920 this.loadNext = true;
11922 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11927 addItem : function(o)
11929 var dv = ''; // display value
11931 if (this.displayField) {
11932 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11934 // this is an error condition!!!
11935 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
11942 var choice = this.choices.createChild({
11944 cls: 'select2-search-choice',
11953 cls: 'select2-search-choice-close',
11958 }, this.searchField);
11960 var close = choice.select('a.select2-search-choice-close', true).first()
11962 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11970 this.inputEl().dom.value = '';
11974 onRemoveItem : function(e, _self, o)
11976 e.preventDefault();
11977 var index = this.item.indexOf(o.data) * 1;
11980 Roo.log('not this item?!');
11984 this.item.splice(index, 1);
11989 this.fireEvent('remove', this, e);
11993 syncValue : function()
11995 if(!this.item.length){
12002 Roo.each(this.item, function(i){
12003 if(_this.valueField){
12004 value.push(i[_this.valueField]);
12011 this.value = value.join(',');
12013 if(this.hiddenField){
12014 this.hiddenField.dom.value = this.value;
12018 clearItem : function()
12020 if(!this.multiple){
12026 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12033 inputEl: function ()
12036 return this.searchField;
12038 return this.el.select('input.form-control',true).first();
12042 onTickableFooterButtonClick : function(e, btn, el)
12044 e.preventDefault();
12046 if(btn && btn.name == 'cancel'){
12047 this.tickItems = Roo.apply([], this.item);
12056 Roo.each(this.tickItems, function(o){
12064 validate : function()
12066 var v = this.getRawValue();
12069 v = this.getValue();
12072 if(this.disabled || this.validateValue(v)){
12073 this.clearInvalid();
12082 * @cfg {Boolean} grow
12086 * @cfg {Number} growMin
12090 * @cfg {Number} growMax
12100 * Ext JS Library 1.1.1
12101 * Copyright(c) 2006-2007, Ext JS, LLC.
12103 * Originally Released Under LGPL - original licence link has changed is not relivant.
12106 * <script type="text/javascript">
12111 * @extends Roo.util.Observable
12112 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
12113 * This class also supports single and multi selection modes. <br>
12114 * Create a data model bound view:
12116 var store = new Roo.data.Store(...);
12118 var view = new Roo.View({
12120 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
12122 singleSelect: true,
12123 selectedClass: "ydataview-selected",
12127 // listen for node click?
12128 view.on("click", function(vw, index, node, e){
12129 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12133 dataModel.load("foobar.xml");
12135 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12137 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12138 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12140 * Note: old style constructor is still suported (container, template, config)
12143 * Create a new View
12144 * @param {Object} config The config object
12147 Roo.View = function(config, depreciated_tpl, depreciated_config){
12149 this.parent = false;
12151 if (typeof(depreciated_tpl) == 'undefined') {
12152 // new way.. - universal constructor.
12153 Roo.apply(this, config);
12154 this.el = Roo.get(this.el);
12157 this.el = Roo.get(config);
12158 this.tpl = depreciated_tpl;
12159 Roo.apply(this, depreciated_config);
12161 this.wrapEl = this.el.wrap().wrap();
12162 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12165 if(typeof(this.tpl) == "string"){
12166 this.tpl = new Roo.Template(this.tpl);
12168 // support xtype ctors..
12169 this.tpl = new Roo.factory(this.tpl, Roo);
12173 this.tpl.compile();
12178 * @event beforeclick
12179 * Fires before a click is processed. Returns false to cancel the default action.
12180 * @param {Roo.View} this
12181 * @param {Number} index The index of the target node
12182 * @param {HTMLElement} node The target node
12183 * @param {Roo.EventObject} e The raw event object
12185 "beforeclick" : true,
12188 * Fires when a template node is clicked.
12189 * @param {Roo.View} this
12190 * @param {Number} index The index of the target node
12191 * @param {HTMLElement} node The target node
12192 * @param {Roo.EventObject} e The raw event object
12197 * Fires when a template node is double clicked.
12198 * @param {Roo.View} this
12199 * @param {Number} index The index of the target node
12200 * @param {HTMLElement} node The target node
12201 * @param {Roo.EventObject} e The raw event object
12205 * @event contextmenu
12206 * Fires when a template node is right clicked.
12207 * @param {Roo.View} this
12208 * @param {Number} index The index of the target node
12209 * @param {HTMLElement} node The target node
12210 * @param {Roo.EventObject} e The raw event object
12212 "contextmenu" : true,
12214 * @event selectionchange
12215 * Fires when the selected nodes change.
12216 * @param {Roo.View} this
12217 * @param {Array} selections Array of the selected nodes
12219 "selectionchange" : true,
12222 * @event beforeselect
12223 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12224 * @param {Roo.View} this
12225 * @param {HTMLElement} node The node to be selected
12226 * @param {Array} selections Array of currently selected nodes
12228 "beforeselect" : true,
12230 * @event preparedata
12231 * Fires on every row to render, to allow you to change the data.
12232 * @param {Roo.View} this
12233 * @param {Object} data to be rendered (change this)
12235 "preparedata" : true
12243 "click": this.onClick,
12244 "dblclick": this.onDblClick,
12245 "contextmenu": this.onContextMenu,
12249 this.selections = [];
12251 this.cmp = new Roo.CompositeElementLite([]);
12253 this.store = Roo.factory(this.store, Roo.data);
12254 this.setStore(this.store, true);
12257 if ( this.footer && this.footer.xtype) {
12259 var fctr = this.wrapEl.appendChild(document.createElement("div"));
12261 this.footer.dataSource = this.store
12262 this.footer.container = fctr;
12263 this.footer = Roo.factory(this.footer, Roo);
12264 fctr.insertFirst(this.el);
12266 // this is a bit insane - as the paging toolbar seems to detach the el..
12267 // dom.parentNode.parentNode.parentNode
12268 // they get detached?
12272 Roo.View.superclass.constructor.call(this);
12277 Roo.extend(Roo.View, Roo.util.Observable, {
12280 * @cfg {Roo.data.Store} store Data store to load data from.
12285 * @cfg {String|Roo.Element} el The container element.
12290 * @cfg {String|Roo.Template} tpl The template used by this View
12294 * @cfg {String} dataName the named area of the template to use as the data area
12295 * Works with domtemplates roo-name="name"
12299 * @cfg {String} selectedClass The css class to add to selected nodes
12301 selectedClass : "x-view-selected",
12303 * @cfg {String} emptyText The empty text to show when nothing is loaded.
12308 * @cfg {String} text to display on mask (default Loading)
12312 * @cfg {Boolean} multiSelect Allow multiple selection
12314 multiSelect : false,
12316 * @cfg {Boolean} singleSelect Allow single selection
12318 singleSelect: false,
12321 * @cfg {Boolean} toggleSelect - selecting
12323 toggleSelect : false,
12326 * @cfg {Boolean} tickable - selecting
12331 * Returns the element this view is bound to.
12332 * @return {Roo.Element}
12334 getEl : function(){
12335 return this.wrapEl;
12341 * Refreshes the view. - called by datachanged on the store. - do not call directly.
12343 refresh : function(){
12344 Roo.log('refresh');
12347 // if we are using something like 'domtemplate', then
12348 // the what gets used is:
12349 // t.applySubtemplate(NAME, data, wrapping data..)
12350 // the outer template then get' applied with
12351 // the store 'extra data'
12352 // and the body get's added to the
12353 // roo-name="data" node?
12354 // <span class='roo-tpl-{name}'></span> ?????
12358 this.clearSelections();
12359 this.el.update("");
12361 var records = this.store.getRange();
12362 if(records.length < 1) {
12364 // is this valid?? = should it render a template??
12366 this.el.update(this.emptyText);
12370 if (this.dataName) {
12371 this.el.update(t.apply(this.store.meta)); //????
12372 el = this.el.child('.roo-tpl-' + this.dataName);
12375 for(var i = 0, len = records.length; i < len; i++){
12376 var data = this.prepareData(records[i].data, i, records[i]);
12377 this.fireEvent("preparedata", this, data, i, records[i]);
12379 var d = Roo.apply({}, data);
12382 Roo.apply(d, {'roo-id' : Roo.id()});
12386 Roo.each(this.parent.item, function(item){
12387 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12390 Roo.apply(d, {'roo-data-checked' : 'checked'});
12394 html[html.length] = Roo.util.Format.trim(
12396 t.applySubtemplate(this.dataName, d, this.store.meta) :
12403 el.update(html.join(""));
12404 this.nodes = el.dom.childNodes;
12405 this.updateIndexes(0);
12410 * Function to override to reformat the data that is sent to
12411 * the template for each node.
12412 * DEPRICATED - use the preparedata event handler.
12413 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12414 * a JSON object for an UpdateManager bound view).
12416 prepareData : function(data, index, record)
12418 this.fireEvent("preparedata", this, data, index, record);
12422 onUpdate : function(ds, record){
12423 Roo.log('on update');
12424 this.clearSelections();
12425 var index = this.store.indexOf(record);
12426 var n = this.nodes[index];
12427 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12428 n.parentNode.removeChild(n);
12429 this.updateIndexes(index, index);
12435 onAdd : function(ds, records, index)
12437 Roo.log(['on Add', ds, records, index] );
12438 this.clearSelections();
12439 if(this.nodes.length == 0){
12443 var n = this.nodes[index];
12444 for(var i = 0, len = records.length; i < len; i++){
12445 var d = this.prepareData(records[i].data, i, records[i]);
12447 this.tpl.insertBefore(n, d);
12450 this.tpl.append(this.el, d);
12453 this.updateIndexes(index);
12456 onRemove : function(ds, record, index){
12457 Roo.log('onRemove');
12458 this.clearSelections();
12459 var el = this.dataName ?
12460 this.el.child('.roo-tpl-' + this.dataName) :
12463 el.dom.removeChild(this.nodes[index]);
12464 this.updateIndexes(index);
12468 * Refresh an individual node.
12469 * @param {Number} index
12471 refreshNode : function(index){
12472 this.onUpdate(this.store, this.store.getAt(index));
12475 updateIndexes : function(startIndex, endIndex){
12476 var ns = this.nodes;
12477 startIndex = startIndex || 0;
12478 endIndex = endIndex || ns.length - 1;
12479 for(var i = startIndex; i <= endIndex; i++){
12480 ns[i].nodeIndex = i;
12485 * Changes the data store this view uses and refresh the view.
12486 * @param {Store} store
12488 setStore : function(store, initial){
12489 if(!initial && this.store){
12490 this.store.un("datachanged", this.refresh);
12491 this.store.un("add", this.onAdd);
12492 this.store.un("remove", this.onRemove);
12493 this.store.un("update", this.onUpdate);
12494 this.store.un("clear", this.refresh);
12495 this.store.un("beforeload", this.onBeforeLoad);
12496 this.store.un("load", this.onLoad);
12497 this.store.un("loadexception", this.onLoad);
12501 store.on("datachanged", this.refresh, this);
12502 store.on("add", this.onAdd, this);
12503 store.on("remove", this.onRemove, this);
12504 store.on("update", this.onUpdate, this);
12505 store.on("clear", this.refresh, this);
12506 store.on("beforeload", this.onBeforeLoad, this);
12507 store.on("load", this.onLoad, this);
12508 store.on("loadexception", this.onLoad, this);
12516 * onbeforeLoad - masks the loading area.
12519 onBeforeLoad : function(store,opts)
12521 Roo.log('onBeforeLoad');
12523 this.el.update("");
12525 this.el.mask(this.mask ? this.mask : "Loading" );
12527 onLoad : function ()
12534 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12535 * @param {HTMLElement} node
12536 * @return {HTMLElement} The template node
12538 findItemFromChild : function(node){
12539 var el = this.dataName ?
12540 this.el.child('.roo-tpl-' + this.dataName,true) :
12543 if(!node || node.parentNode == el){
12546 var p = node.parentNode;
12547 while(p && p != el){
12548 if(p.parentNode == el){
12557 onClick : function(e){
12558 var item = this.findItemFromChild(e.getTarget());
12560 var index = this.indexOf(item);
12561 if(this.onItemClick(item, index, e) !== false){
12562 this.fireEvent("click", this, index, item, e);
12565 this.clearSelections();
12570 onContextMenu : function(e){
12571 var item = this.findItemFromChild(e.getTarget());
12573 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12578 onDblClick : function(e){
12579 var item = this.findItemFromChild(e.getTarget());
12581 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12585 onItemClick : function(item, index, e)
12587 if(this.fireEvent("beforeclick", this, index, item, e) === false){
12590 if (this.toggleSelect) {
12591 var m = this.isSelected(item) ? 'unselect' : 'select';
12594 _t[m](item, true, false);
12597 if(this.multiSelect || this.singleSelect){
12598 if(this.multiSelect && e.shiftKey && this.lastSelection){
12599 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12601 this.select(item, this.multiSelect && e.ctrlKey);
12602 this.lastSelection = item;
12605 if(!this.tickable){
12606 e.preventDefault();
12614 * Get the number of selected nodes.
12617 getSelectionCount : function(){
12618 return this.selections.length;
12622 * Get the currently selected nodes.
12623 * @return {Array} An array of HTMLElements
12625 getSelectedNodes : function(){
12626 return this.selections;
12630 * Get the indexes of the selected nodes.
12633 getSelectedIndexes : function(){
12634 var indexes = [], s = this.selections;
12635 for(var i = 0, len = s.length; i < len; i++){
12636 indexes.push(s[i].nodeIndex);
12642 * Clear all selections
12643 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12645 clearSelections : function(suppressEvent){
12646 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12647 this.cmp.elements = this.selections;
12648 this.cmp.removeClass(this.selectedClass);
12649 this.selections = [];
12650 if(!suppressEvent){
12651 this.fireEvent("selectionchange", this, this.selections);
12657 * Returns true if the passed node is selected
12658 * @param {HTMLElement/Number} node The node or node index
12659 * @return {Boolean}
12661 isSelected : function(node){
12662 var s = this.selections;
12666 node = this.getNode(node);
12667 return s.indexOf(node) !== -1;
12672 * @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
12673 * @param {Boolean} keepExisting (optional) true to keep existing selections
12674 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12676 select : function(nodeInfo, keepExisting, suppressEvent){
12677 if(nodeInfo instanceof Array){
12679 this.clearSelections(true);
12681 for(var i = 0, len = nodeInfo.length; i < len; i++){
12682 this.select(nodeInfo[i], true, true);
12686 var node = this.getNode(nodeInfo);
12687 if(!node || this.isSelected(node)){
12688 return; // already selected.
12691 this.clearSelections(true);
12694 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12695 Roo.fly(node).addClass(this.selectedClass);
12696 this.selections.push(node);
12697 if(!suppressEvent){
12698 this.fireEvent("selectionchange", this, this.selections);
12706 * @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
12707 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12708 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12710 unselect : function(nodeInfo, keepExisting, suppressEvent)
12712 if(nodeInfo instanceof Array){
12713 Roo.each(this.selections, function(s) {
12714 this.unselect(s, nodeInfo);
12718 var node = this.getNode(nodeInfo);
12719 if(!node || !this.isSelected(node)){
12720 Roo.log("not selected");
12721 return; // not selected.
12725 Roo.each(this.selections, function(s) {
12727 Roo.fly(node).removeClass(this.selectedClass);
12734 this.selections= ns;
12735 this.fireEvent("selectionchange", this, this.selections);
12739 * Gets a template node.
12740 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12741 * @return {HTMLElement} The node or null if it wasn't found
12743 getNode : function(nodeInfo){
12744 if(typeof nodeInfo == "string"){
12745 return document.getElementById(nodeInfo);
12746 }else if(typeof nodeInfo == "number"){
12747 return this.nodes[nodeInfo];
12753 * Gets a range template nodes.
12754 * @param {Number} startIndex
12755 * @param {Number} endIndex
12756 * @return {Array} An array of nodes
12758 getNodes : function(start, end){
12759 var ns = this.nodes;
12760 start = start || 0;
12761 end = typeof end == "undefined" ? ns.length - 1 : end;
12764 for(var i = start; i <= end; i++){
12768 for(var i = start; i >= end; i--){
12776 * Finds the index of the passed node
12777 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12778 * @return {Number} The index of the node or -1
12780 indexOf : function(node){
12781 node = this.getNode(node);
12782 if(typeof node.nodeIndex == "number"){
12783 return node.nodeIndex;
12785 var ns = this.nodes;
12786 for(var i = 0, len = ns.length; i < len; i++){
12797 * based on jquery fullcalendar
12801 Roo.bootstrap = Roo.bootstrap || {};
12803 * @class Roo.bootstrap.Calendar
12804 * @extends Roo.bootstrap.Component
12805 * Bootstrap Calendar class
12806 * @cfg {Boolean} loadMask (true|false) default false
12807 * @cfg {Object} header generate the user specific header of the calendar, default false
12810 * Create a new Container
12811 * @param {Object} config The config object
12816 Roo.bootstrap.Calendar = function(config){
12817 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12821 * Fires when a date is selected
12822 * @param {DatePicker} this
12823 * @param {Date} date The selected date
12827 * @event monthchange
12828 * Fires when the displayed month changes
12829 * @param {DatePicker} this
12830 * @param {Date} date The selected month
12832 'monthchange': true,
12834 * @event evententer
12835 * Fires when mouse over an event
12836 * @param {Calendar} this
12837 * @param {event} Event
12839 'evententer': true,
12841 * @event eventleave
12842 * Fires when the mouse leaves an
12843 * @param {Calendar} this
12846 'eventleave': true,
12848 * @event eventclick
12849 * Fires when the mouse click an
12850 * @param {Calendar} this
12859 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
12862 * @cfg {Number} startDay
12863 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12871 getAutoCreate : function(){
12874 var fc_button = function(name, corner, style, content ) {
12875 return Roo.apply({},{
12877 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
12879 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12882 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12893 style : 'width:100%',
12900 cls : 'fc-header-left',
12902 fc_button('prev', 'left', 'arrow', '‹' ),
12903 fc_button('next', 'right', 'arrow', '›' ),
12904 { tag: 'span', cls: 'fc-header-space' },
12905 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
12913 cls : 'fc-header-center',
12917 cls: 'fc-header-title',
12920 html : 'month / year'
12928 cls : 'fc-header-right',
12930 /* fc_button('month', 'left', '', 'month' ),
12931 fc_button('week', '', '', 'week' ),
12932 fc_button('day', 'right', '', 'day' )
12944 header = this.header;
12947 var cal_heads = function() {
12949 // fixme - handle this.
12951 for (var i =0; i < Date.dayNames.length; i++) {
12952 var d = Date.dayNames[i];
12955 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12956 html : d.substring(0,3)
12960 ret[0].cls += ' fc-first';
12961 ret[6].cls += ' fc-last';
12964 var cal_cell = function(n) {
12967 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12972 cls: 'fc-day-number',
12976 cls: 'fc-day-content',
12980 style: 'position: relative;' // height: 17px;
12992 var cal_rows = function() {
12995 for (var r = 0; r < 6; r++) {
13002 for (var i =0; i < Date.dayNames.length; i++) {
13003 var d = Date.dayNames[i];
13004 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13007 row.cn[0].cls+=' fc-first';
13008 row.cn[0].cn[0].style = 'min-height:90px';
13009 row.cn[6].cls+=' fc-last';
13013 ret[0].cls += ' fc-first';
13014 ret[4].cls += ' fc-prev-last';
13015 ret[5].cls += ' fc-last';
13022 cls: 'fc-border-separate',
13023 style : 'width:100%',
13031 cls : 'fc-first fc-last',
13049 cls : 'fc-content',
13050 style : "position: relative;",
13053 cls : 'fc-view fc-view-month fc-grid',
13054 style : 'position: relative',
13055 unselectable : 'on',
13058 cls : 'fc-event-container',
13059 style : 'position:absolute;z-index:8;top:0;left:0;'
13077 initEvents : function()
13080 throw "can not find store for calendar";
13086 style: "text-align:center",
13090 style: "background-color:white;width:50%;margin:250 auto",
13094 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
13105 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13107 var size = this.el.select('.fc-content', true).first().getSize();
13108 this.maskEl.setSize(size.width, size.height);
13109 this.maskEl.enableDisplayMode("block");
13110 if(!this.loadMask){
13111 this.maskEl.hide();
13114 this.store = Roo.factory(this.store, Roo.data);
13115 this.store.on('load', this.onLoad, this);
13116 this.store.on('beforeload', this.onBeforeLoad, this);
13120 this.cells = this.el.select('.fc-day',true);
13121 //Roo.log(this.cells);
13122 this.textNodes = this.el.query('.fc-day-number');
13123 this.cells.addClassOnOver('fc-state-hover');
13125 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13126 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13127 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13128 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13130 this.on('monthchange', this.onMonthChange, this);
13132 this.update(new Date().clearTime());
13135 resize : function() {
13136 var sz = this.el.getSize();
13138 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13139 this.el.select('.fc-day-content div',true).setHeight(34);
13144 showPrevMonth : function(e){
13145 this.update(this.activeDate.add("mo", -1));
13147 showToday : function(e){
13148 this.update(new Date().clearTime());
13151 showNextMonth : function(e){
13152 this.update(this.activeDate.add("mo", 1));
13156 showPrevYear : function(){
13157 this.update(this.activeDate.add("y", -1));
13161 showNextYear : function(){
13162 this.update(this.activeDate.add("y", 1));
13167 update : function(date)
13169 var vd = this.activeDate;
13170 this.activeDate = date;
13171 // if(vd && this.el){
13172 // var t = date.getTime();
13173 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13174 // Roo.log('using add remove');
13176 // this.fireEvent('monthchange', this, date);
13178 // this.cells.removeClass("fc-state-highlight");
13179 // this.cells.each(function(c){
13180 // if(c.dateValue == t){
13181 // c.addClass("fc-state-highlight");
13182 // setTimeout(function(){
13183 // try{c.dom.firstChild.focus();}catch(e){}
13193 var days = date.getDaysInMonth();
13195 var firstOfMonth = date.getFirstDateOfMonth();
13196 var startingPos = firstOfMonth.getDay()-this.startDay;
13198 if(startingPos < this.startDay){
13202 var pm = date.add(Date.MONTH, -1);
13203 var prevStart = pm.getDaysInMonth()-startingPos;
13205 this.cells = this.el.select('.fc-day',true);
13206 this.textNodes = this.el.query('.fc-day-number');
13207 this.cells.addClassOnOver('fc-state-hover');
13209 var cells = this.cells.elements;
13210 var textEls = this.textNodes;
13212 Roo.each(cells, function(cell){
13213 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13216 days += startingPos;
13218 // convert everything to numbers so it's fast
13219 var day = 86400000;
13220 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13223 //Roo.log(prevStart);
13225 var today = new Date().clearTime().getTime();
13226 var sel = date.clearTime().getTime();
13227 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13228 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13229 var ddMatch = this.disabledDatesRE;
13230 var ddText = this.disabledDatesText;
13231 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13232 var ddaysText = this.disabledDaysText;
13233 var format = this.format;
13235 var setCellClass = function(cal, cell){
13239 //Roo.log('set Cell Class');
13241 var t = d.getTime();
13245 cell.dateValue = t;
13247 cell.className += " fc-today";
13248 cell.className += " fc-state-highlight";
13249 cell.title = cal.todayText;
13252 // disable highlight in other month..
13253 //cell.className += " fc-state-highlight";
13258 cell.className = " fc-state-disabled";
13259 cell.title = cal.minText;
13263 cell.className = " fc-state-disabled";
13264 cell.title = cal.maxText;
13268 if(ddays.indexOf(d.getDay()) != -1){
13269 cell.title = ddaysText;
13270 cell.className = " fc-state-disabled";
13273 if(ddMatch && format){
13274 var fvalue = d.dateFormat(format);
13275 if(ddMatch.test(fvalue)){
13276 cell.title = ddText.replace("%0", fvalue);
13277 cell.className = " fc-state-disabled";
13281 if (!cell.initialClassName) {
13282 cell.initialClassName = cell.dom.className;
13285 cell.dom.className = cell.initialClassName + ' ' + cell.className;
13290 for(; i < startingPos; i++) {
13291 textEls[i].innerHTML = (++prevStart);
13292 d.setDate(d.getDate()+1);
13294 cells[i].className = "fc-past fc-other-month";
13295 setCellClass(this, cells[i]);
13300 for(; i < days; i++){
13301 intDay = i - startingPos + 1;
13302 textEls[i].innerHTML = (intDay);
13303 d.setDate(d.getDate()+1);
13305 cells[i].className = ''; // "x-date-active";
13306 setCellClass(this, cells[i]);
13310 for(; i < 42; i++) {
13311 textEls[i].innerHTML = (++extraDays);
13312 d.setDate(d.getDate()+1);
13314 cells[i].className = "fc-future fc-other-month";
13315 setCellClass(this, cells[i]);
13318 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13320 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13322 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13323 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13325 if(totalRows != 6){
13326 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13327 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13330 this.fireEvent('monthchange', this, date);
13334 if(!this.internalRender){
13335 var main = this.el.dom.firstChild;
13336 var w = main.offsetWidth;
13337 this.el.setWidth(w + this.el.getBorderWidth("lr"));
13338 Roo.fly(main).setWidth(w);
13339 this.internalRender = true;
13340 // opera does not respect the auto grow header center column
13341 // then, after it gets a width opera refuses to recalculate
13342 // without a second pass
13343 if(Roo.isOpera && !this.secondPass){
13344 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13345 this.secondPass = true;
13346 this.update.defer(10, this, [date]);
13353 findCell : function(dt) {
13354 dt = dt.clearTime().getTime();
13356 this.cells.each(function(c){
13357 //Roo.log("check " +c.dateValue + '?=' + dt);
13358 if(c.dateValue == dt){
13368 findCells : function(ev) {
13369 var s = ev.start.clone().clearTime().getTime();
13371 var e= ev.end.clone().clearTime().getTime();
13374 this.cells.each(function(c){
13375 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13377 if(c.dateValue > e){
13380 if(c.dateValue < s){
13389 // findBestRow: function(cells)
13393 // for (var i =0 ; i < cells.length;i++) {
13394 // ret = Math.max(cells[i].rows || 0,ret);
13401 addItem : function(ev)
13403 // look for vertical location slot in
13404 var cells = this.findCells(ev);
13406 // ev.row = this.findBestRow(cells);
13408 // work out the location.
13412 for(var i =0; i < cells.length; i++) {
13414 cells[i].row = cells[0].row;
13417 cells[i].row = cells[i].row + 1;
13427 if (crow.start.getY() == cells[i].getY()) {
13429 crow.end = cells[i];
13446 cells[0].events.push(ev);
13448 this.calevents.push(ev);
13451 clearEvents: function() {
13453 if(!this.calevents){
13457 Roo.each(this.cells.elements, function(c){
13463 Roo.each(this.calevents, function(e) {
13464 Roo.each(e.els, function(el) {
13465 el.un('mouseenter' ,this.onEventEnter, this);
13466 el.un('mouseleave' ,this.onEventLeave, this);
13471 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13477 renderEvents: function()
13481 this.cells.each(function(c) {
13490 if(c.row != c.events.length){
13491 r = 4 - (4 - (c.row - c.events.length));
13494 c.events = ev.slice(0, r);
13495 c.more = ev.slice(r);
13497 if(c.more.length && c.more.length == 1){
13498 c.events.push(c.more.pop());
13501 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13505 this.cells.each(function(c) {
13507 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13510 for (var e = 0; e < c.events.length; e++){
13511 var ev = c.events[e];
13512 var rows = ev.rows;
13514 for(var i = 0; i < rows.length; i++) {
13516 // how many rows should it span..
13519 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13520 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13522 unselectable : "on",
13525 cls: 'fc-event-inner',
13529 // cls: 'fc-event-time',
13530 // html : cells.length > 1 ? '' : ev.time
13534 cls: 'fc-event-title',
13535 html : String.format('{0}', ev.title)
13542 cls: 'ui-resizable-handle ui-resizable-e',
13543 html : '  '
13550 cfg.cls += ' fc-event-start';
13552 if ((i+1) == rows.length) {
13553 cfg.cls += ' fc-event-end';
13556 var ctr = _this.el.select('.fc-event-container',true).first();
13557 var cg = ctr.createChild(cfg);
13559 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13560 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13562 var r = (c.more.length) ? 1 : 0;
13563 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
13564 cg.setWidth(ebox.right - sbox.x -2);
13566 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13567 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13568 cg.on('click', _this.onEventClick, _this, ev);
13579 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13580 style : 'position: absolute',
13581 unselectable : "on",
13584 cls: 'fc-event-inner',
13588 cls: 'fc-event-title',
13596 cls: 'ui-resizable-handle ui-resizable-e',
13597 html : '  '
13603 var ctr = _this.el.select('.fc-event-container',true).first();
13604 var cg = ctr.createChild(cfg);
13606 var sbox = c.select('.fc-day-content',true).first().getBox();
13607 var ebox = c.select('.fc-day-content',true).first().getBox();
13609 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
13610 cg.setWidth(ebox.right - sbox.x -2);
13612 cg.on('click', _this.onMoreEventClick, _this, c.more);
13622 onEventEnter: function (e, el,event,d) {
13623 this.fireEvent('evententer', this, el, event);
13626 onEventLeave: function (e, el,event,d) {
13627 this.fireEvent('eventleave', this, el, event);
13630 onEventClick: function (e, el,event,d) {
13631 this.fireEvent('eventclick', this, el, event);
13634 onMonthChange: function () {
13638 onMoreEventClick: function(e, el, more)
13642 this.calpopover.placement = 'right';
13643 this.calpopover.setTitle('More');
13645 this.calpopover.setContent('');
13647 var ctr = this.calpopover.el.select('.popover-content', true).first();
13649 Roo.each(more, function(m){
13651 cls : 'fc-event-hori fc-event-draggable',
13654 var cg = ctr.createChild(cfg);
13656 cg.on('click', _this.onEventClick, _this, m);
13659 this.calpopover.show(el);
13664 onLoad: function ()
13666 this.calevents = [];
13669 if(this.store.getCount() > 0){
13670 this.store.data.each(function(d){
13673 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13674 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13675 time : d.data.start_time,
13676 title : d.data.title,
13677 description : d.data.description,
13678 venue : d.data.venue
13683 this.renderEvents();
13685 if(this.calevents.length && this.loadMask){
13686 this.maskEl.hide();
13690 onBeforeLoad: function()
13692 this.clearEvents();
13694 this.maskEl.show();
13708 * @class Roo.bootstrap.Popover
13709 * @extends Roo.bootstrap.Component
13710 * Bootstrap Popover class
13711 * @cfg {String} html contents of the popover (or false to use children..)
13712 * @cfg {String} title of popover (or false to hide)
13713 * @cfg {String} placement how it is placed
13714 * @cfg {String} trigger click || hover (or false to trigger manually)
13715 * @cfg {String} over what (parent or false to trigger manually.)
13716 * @cfg {Number} delay - delay before showing
13719 * Create a new Popover
13720 * @param {Object} config The config object
13723 Roo.bootstrap.Popover = function(config){
13724 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13727 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
13729 title: 'Fill in a title',
13732 placement : 'right',
13733 trigger : 'hover', // hover
13739 can_build_overlaid : false,
13741 getChildContainer : function()
13743 return this.el.select('.popover-content',true).first();
13746 getAutoCreate : function(){
13747 Roo.log('make popover?');
13749 cls : 'popover roo-dynamic',
13750 style: 'display:block',
13756 cls : 'popover-inner',
13760 cls: 'popover-title',
13764 cls : 'popover-content',
13775 setTitle: function(str)
13777 this.el.select('.popover-title',true).first().dom.innerHTML = str;
13779 setContent: function(str)
13781 this.el.select('.popover-content',true).first().dom.innerHTML = str;
13783 // as it get's added to the bottom of the page.
13784 onRender : function(ct, position)
13786 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13788 var cfg = Roo.apply({}, this.getAutoCreate());
13792 cfg.cls += ' ' + this.cls;
13795 cfg.style = this.style;
13797 Roo.log("adding to ")
13798 this.el = Roo.get(document.body).createChild(cfg, position);
13804 initEvents : function()
13806 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13807 this.el.enableDisplayMode('block');
13809 if (this.over === false) {
13812 if (this.triggers === false) {
13815 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13816 var triggers = this.trigger ? this.trigger.split(' ') : [];
13817 Roo.each(triggers, function(trigger) {
13819 if (trigger == 'click') {
13820 on_el.on('click', this.toggle, this);
13821 } else if (trigger != 'manual') {
13822 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
13823 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13825 on_el.on(eventIn ,this.enter, this);
13826 on_el.on(eventOut, this.leave, this);
13837 toggle : function () {
13838 this.hoverState == 'in' ? this.leave() : this.enter();
13841 enter : function () {
13844 clearTimeout(this.timeout);
13846 this.hoverState = 'in'
13848 if (!this.delay || !this.delay.show) {
13853 this.timeout = setTimeout(function () {
13854 if (_t.hoverState == 'in') {
13857 }, this.delay.show)
13859 leave : function() {
13860 clearTimeout(this.timeout);
13862 this.hoverState = 'out'
13864 if (!this.delay || !this.delay.hide) {
13869 this.timeout = setTimeout(function () {
13870 if (_t.hoverState == 'out') {
13873 }, this.delay.hide)
13876 show : function (on_el)
13879 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13882 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13883 if (this.html !== false) {
13884 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13886 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13887 if (!this.title.length) {
13888 this.el.select('.popover-title',true).hide();
13891 var placement = typeof this.placement == 'function' ?
13892 this.placement.call(this, this.el, on_el) :
13895 var autoToken = /\s?auto?\s?/i;
13896 var autoPlace = autoToken.test(placement);
13898 placement = placement.replace(autoToken, '') || 'top';
13902 //this.el.setXY([0,0]);
13904 this.el.dom.style.display='block';
13905 this.el.addClass(placement);
13907 //this.el.appendTo(on_el);
13909 var p = this.getPosition();
13910 var box = this.el.getBox();
13915 var align = Roo.bootstrap.Popover.alignment[placement]
13916 this.el.alignTo(on_el, align[0],align[1]);
13917 //var arrow = this.el.select('.arrow',true).first();
13918 //arrow.set(align[2],
13920 this.el.addClass('in');
13921 this.hoverState = null;
13923 if (this.el.hasClass('fade')) {
13930 this.el.setXY([0,0]);
13931 this.el.removeClass('in');
13938 Roo.bootstrap.Popover.alignment = {
13939 'left' : ['r-l', [-10,0], 'right'],
13940 'right' : ['l-r', [10,0], 'left'],
13941 'bottom' : ['t-b', [0,10], 'top'],
13942 'top' : [ 'b-t', [0,-10], 'bottom']
13953 * @class Roo.bootstrap.Progress
13954 * @extends Roo.bootstrap.Component
13955 * Bootstrap Progress class
13956 * @cfg {Boolean} striped striped of the progress bar
13957 * @cfg {Boolean} active animated of the progress bar
13961 * Create a new Progress
13962 * @param {Object} config The config object
13965 Roo.bootstrap.Progress = function(config){
13966 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13969 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
13974 getAutoCreate : function(){
13982 cfg.cls += ' progress-striped';
13986 cfg.cls += ' active';
14005 * @class Roo.bootstrap.ProgressBar
14006 * @extends Roo.bootstrap.Component
14007 * Bootstrap ProgressBar class
14008 * @cfg {Number} aria_valuenow aria-value now
14009 * @cfg {Number} aria_valuemin aria-value min
14010 * @cfg {Number} aria_valuemax aria-value max
14011 * @cfg {String} label label for the progress bar
14012 * @cfg {String} panel (success | info | warning | danger )
14013 * @cfg {String} role role of the progress bar
14014 * @cfg {String} sr_only text
14018 * Create a new ProgressBar
14019 * @param {Object} config The config object
14022 Roo.bootstrap.ProgressBar = function(config){
14023 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14026 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
14030 aria_valuemax : 100,
14036 getAutoCreate : function()
14041 cls: 'progress-bar',
14042 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14054 cfg.role = this.role;
14057 if(this.aria_valuenow){
14058 cfg['aria-valuenow'] = this.aria_valuenow;
14061 if(this.aria_valuemin){
14062 cfg['aria-valuemin'] = this.aria_valuemin;
14065 if(this.aria_valuemax){
14066 cfg['aria-valuemax'] = this.aria_valuemax;
14069 if(this.label && !this.sr_only){
14070 cfg.html = this.label;
14074 cfg.cls += ' progress-bar-' + this.panel;
14080 update : function(aria_valuenow)
14082 this.aria_valuenow = aria_valuenow;
14084 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14099 * @class Roo.bootstrap.TabGroup
14100 * @extends Roo.bootstrap.Column
14101 * Bootstrap Column class
14102 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14103 * @cfg {Boolean} carousel true to make the group behave like a carousel
14106 * Create a new TabGroup
14107 * @param {Object} config The config object
14110 Roo.bootstrap.TabGroup = function(config){
14111 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14113 this.navId = Roo.id();
14116 Roo.bootstrap.TabGroup.register(this);
14120 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
14123 transition : false,
14125 getAutoCreate : function()
14127 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14129 cfg.cls += ' tab-content';
14131 if (this.carousel) {
14132 cfg.cls += ' carousel slide';
14134 cls : 'carousel-inner'
14141 getChildContainer : function()
14143 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14147 * register a Navigation item
14148 * @param {Roo.bootstrap.NavItem} the navitem to add
14150 register : function(item)
14152 this.tabs.push( item);
14153 item.navId = this.navId; // not really needed..
14157 getActivePanel : function()
14160 Roo.each(this.tabs, function(t) {
14170 getPanelByName : function(n)
14173 Roo.each(this.tabs, function(t) {
14174 if (t.tabId == n) {
14182 indexOfPanel : function(p)
14185 Roo.each(this.tabs, function(t,i) {
14186 if (t.tabId == p.tabId) {
14195 * show a specific panel
14196 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14197 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14199 showPanel : function (pan)
14202 if (typeof(pan) == 'number') {
14203 pan = this.tabs[pan];
14205 if (typeof(pan) == 'string') {
14206 pan = this.getPanelByName(pan);
14208 if (pan.tabId == this.getActivePanel().tabId) {
14211 var cur = this.getActivePanel();
14213 if (false === cur.fireEvent('beforedeactivate')) {
14217 if (this.carousel) {
14218 this.transition = true;
14219 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
14220 var lr = dir == 'next' ? 'left' : 'right';
14221 pan.el.addClass(dir); // or prev
14222 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14223 cur.el.addClass(lr); // or right
14224 pan.el.addClass(lr);
14227 cur.el.on('transitionend', function() {
14228 Roo.log("trans end?");
14230 pan.el.removeClass([lr,dir]);
14231 pan.setActive(true);
14233 cur.el.removeClass([lr]);
14234 cur.setActive(false);
14236 _this.transition = false;
14238 }, this, { single: true } );
14242 cur.setActive(false);
14243 pan.setActive(true);
14247 showPanelNext : function()
14249 var i = this.indexOfPanel(this.getActivePanel());
14250 if (i > this.tabs.length) {
14253 this.showPanel(this.tabs[i+1]);
14255 showPanelPrev : function()
14257 var i = this.indexOfPanel(this.getActivePanel());
14261 this.showPanel(this.tabs[i-1]);
14272 Roo.apply(Roo.bootstrap.TabGroup, {
14276 * register a Navigation Group
14277 * @param {Roo.bootstrap.NavGroup} the navgroup to add
14279 register : function(navgrp)
14281 this.groups[navgrp.navId] = navgrp;
14285 * fetch a Navigation Group based on the navigation ID
14286 * if one does not exist , it will get created.
14287 * @param {string} the navgroup to add
14288 * @returns {Roo.bootstrap.NavGroup} the navgroup
14290 get: function(navId) {
14291 if (typeof(this.groups[navId]) == 'undefined') {
14292 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14294 return this.groups[navId] ;
14309 * @class Roo.bootstrap.TabPanel
14310 * @extends Roo.bootstrap.Component
14311 * Bootstrap TabPanel class
14312 * @cfg {Boolean} active panel active
14313 * @cfg {String} html panel content
14314 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14315 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14319 * Create a new TabPanel
14320 * @param {Object} config The config object
14323 Roo.bootstrap.TabPanel = function(config){
14324 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14328 * Fires when the active status changes
14329 * @param {Roo.bootstrap.TabPanel} this
14330 * @param {Boolean} state the new state
14335 * @event beforedeactivate
14336 * Fires before a tab is de-activated - can be used to do validation on a form.
14337 * @param {Roo.bootstrap.TabPanel} this
14338 * @return {Boolean} false if there is an error
14341 'beforedeactivate': true
14344 this.tabId = this.tabId || Roo.id();
14348 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
14355 getAutoCreate : function(){
14358 // item is needed for carousel - not sure if it has any effect otherwise
14359 cls: 'tab-pane item',
14360 html: this.html || ''
14364 cfg.cls += ' active';
14368 cfg.tabId = this.tabId;
14375 initEvents: function()
14377 Roo.log('-------- init events on tab panel ---------');
14379 var p = this.parent();
14380 this.navId = this.navId || p.navId;
14382 if (typeof(this.navId) != 'undefined') {
14383 // not really needed.. but just in case.. parent should be a NavGroup.
14384 var tg = Roo.bootstrap.TabGroup.get(this.navId);
14385 Roo.log(['register', tg, this]);
14391 onRender : function(ct, position)
14393 // Roo.log("Call onRender: " + this.xtype);
14395 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14403 setActive: function(state)
14405 Roo.log("panel - set active " + this.tabId + "=" + state);
14407 this.active = state;
14409 this.el.removeClass('active');
14411 } else if (!this.el.hasClass('active')) {
14412 this.el.addClass('active');
14414 this.fireEvent('changed', this, state);
14431 * @class Roo.bootstrap.DateField
14432 * @extends Roo.bootstrap.Input
14433 * Bootstrap DateField class
14434 * @cfg {Number} weekStart default 0
14435 * @cfg {String} viewMode default empty, (months|years)
14436 * @cfg {String} minViewMode default empty, (months|years)
14437 * @cfg {Number} startDate default -Infinity
14438 * @cfg {Number} endDate default Infinity
14439 * @cfg {Boolean} todayHighlight default false
14440 * @cfg {Boolean} todayBtn default false
14441 * @cfg {Boolean} calendarWeeks default false
14442 * @cfg {Object} daysOfWeekDisabled default empty
14443 * @cfg {Boolean} singleMode default false (true | false)
14445 * @cfg {Boolean} keyboardNavigation default true
14446 * @cfg {String} language default en
14449 * Create a new DateField
14450 * @param {Object} config The config object
14453 Roo.bootstrap.DateField = function(config){
14454 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14458 * Fires when this field show.
14459 * @param {Roo.bootstrap.DateField} this
14460 * @param {Mixed} date The date value
14465 * Fires when this field hide.
14466 * @param {Roo.bootstrap.DateField} this
14467 * @param {Mixed} date The date value
14472 * Fires when select a date.
14473 * @param {Roo.bootstrap.DateField} this
14474 * @param {Mixed} date The date value
14480 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
14483 * @cfg {String} format
14484 * The default date format string which can be overriden for localization support. The format must be
14485 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14489 * @cfg {String} altFormats
14490 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14491 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14493 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14501 todayHighlight : false,
14507 keyboardNavigation: true,
14509 calendarWeeks: false,
14511 startDate: -Infinity,
14515 daysOfWeekDisabled: [],
14519 singleMode : false,
14521 UTCDate: function()
14523 return new Date(Date.UTC.apply(Date, arguments));
14526 UTCToday: function()
14528 var today = new Date();
14529 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14532 getDate: function() {
14533 var d = this.getUTCDate();
14534 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14537 getUTCDate: function() {
14541 setDate: function(d) {
14542 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14545 setUTCDate: function(d) {
14547 this.setValue(this.formatDate(this.date));
14550 onRender: function(ct, position)
14553 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14555 this.language = this.language || 'en';
14556 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14557 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14559 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14560 this.format = this.format || 'm/d/y';
14561 this.isInline = false;
14562 this.isInput = true;
14563 this.component = this.el.select('.add-on', true).first() || false;
14564 this.component = (this.component && this.component.length === 0) ? false : this.component;
14565 this.hasInput = this.component && this.inputEL().length;
14567 if (typeof(this.minViewMode === 'string')) {
14568 switch (this.minViewMode) {
14570 this.minViewMode = 1;
14573 this.minViewMode = 2;
14576 this.minViewMode = 0;
14581 if (typeof(this.viewMode === 'string')) {
14582 switch (this.viewMode) {
14595 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14597 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14599 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14601 this.picker().on('mousedown', this.onMousedown, this);
14602 this.picker().on('click', this.onClick, this);
14604 this.picker().addClass('datepicker-dropdown');
14606 this.startViewMode = this.viewMode;
14608 if(this.singleMode){
14609 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
14610 v.setVisibilityMode(Roo.Element.DISPLAY)
14614 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
14615 v.setStyle('width', '189px');
14619 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14620 if(!this.calendarWeeks){
14625 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14626 v.attr('colspan', function(i, val){
14627 return parseInt(val) + 1;
14632 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14634 this.setStartDate(this.startDate);
14635 this.setEndDate(this.endDate);
14637 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14644 if(this.isInline) {
14649 picker : function()
14651 return this.pickerEl;
14652 // return this.el.select('.datepicker', true).first();
14655 fillDow: function()
14657 var dowCnt = this.weekStart;
14666 if(this.calendarWeeks){
14674 while (dowCnt < this.weekStart + 7) {
14678 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14682 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14685 fillMonths: function()
14688 var months = this.picker().select('>.datepicker-months td', true).first();
14690 months.dom.innerHTML = '';
14696 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14699 months.createChild(month);
14706 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;
14708 if (this.date < this.startDate) {
14709 this.viewDate = new Date(this.startDate);
14710 } else if (this.date > this.endDate) {
14711 this.viewDate = new Date(this.endDate);
14713 this.viewDate = new Date(this.date);
14721 var d = new Date(this.viewDate),
14722 year = d.getUTCFullYear(),
14723 month = d.getUTCMonth(),
14724 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14725 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14726 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14727 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14728 currentDate = this.date && this.date.valueOf(),
14729 today = this.UTCToday();
14731 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14733 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14735 // this.picker.select('>tfoot th.today').
14736 // .text(dates[this.language].today)
14737 // .toggle(this.todayBtn !== false);
14739 this.updateNavArrows();
14742 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14744 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14746 prevMonth.setUTCDate(day);
14748 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14750 var nextMonth = new Date(prevMonth);
14752 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14754 nextMonth = nextMonth.valueOf();
14756 var fillMonths = false;
14758 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14760 while(prevMonth.valueOf() < nextMonth) {
14763 if (prevMonth.getUTCDay() === this.weekStart) {
14765 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14773 if(this.calendarWeeks){
14774 // ISO 8601: First week contains first thursday.
14775 // ISO also states week starts on Monday, but we can be more abstract here.
14777 // Start of current week: based on weekstart/current date
14778 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14779 // Thursday of this week
14780 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14781 // First Thursday of year, year from thursday
14782 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14783 // Calendar week: ms between thursdays, div ms per day, div 7 days
14784 calWeek = (th - yth) / 864e5 / 7 + 1;
14786 fillMonths.cn.push({
14794 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14796 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14799 if (this.todayHighlight &&
14800 prevMonth.getUTCFullYear() == today.getFullYear() &&
14801 prevMonth.getUTCMonth() == today.getMonth() &&
14802 prevMonth.getUTCDate() == today.getDate()) {
14803 clsName += ' today';
14806 if (currentDate && prevMonth.valueOf() === currentDate) {
14807 clsName += ' active';
14810 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14811 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14812 clsName += ' disabled';
14815 fillMonths.cn.push({
14817 cls: 'day ' + clsName,
14818 html: prevMonth.getDate()
14821 prevMonth.setDate(prevMonth.getDate()+1);
14824 var currentYear = this.date && this.date.getUTCFullYear();
14825 var currentMonth = this.date && this.date.getUTCMonth();
14827 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14829 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14830 v.removeClass('active');
14832 if(currentYear === year && k === currentMonth){
14833 v.addClass('active');
14836 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14837 v.addClass('disabled');
14843 year = parseInt(year/10, 10) * 10;
14845 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14847 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14850 for (var i = -1; i < 11; i++) {
14851 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14853 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14861 showMode: function(dir)
14864 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14867 Roo.each(this.picker().select('>div',true).elements, function(v){
14868 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14871 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14876 if(this.isInline) return;
14878 this.picker().removeClass(['bottom', 'top']);
14880 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14882 * place to the top of element!
14886 this.picker().addClass('top');
14887 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14892 this.picker().addClass('bottom');
14894 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14897 parseDate : function(value)
14899 if(!value || value instanceof Date){
14902 var v = Date.parseDate(value, this.format);
14903 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
14904 v = Date.parseDate(value, 'Y-m-d');
14906 if(!v && this.altFormats){
14907 if(!this.altFormatsArray){
14908 this.altFormatsArray = this.altFormats.split("|");
14910 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14911 v = Date.parseDate(value, this.altFormatsArray[i]);
14917 formatDate : function(date, fmt)
14919 return (!date || !(date instanceof Date)) ?
14920 date : date.dateFormat(fmt || this.format);
14923 onFocus : function()
14925 Roo.bootstrap.DateField.superclass.onFocus.call(this);
14929 onBlur : function()
14931 Roo.bootstrap.DateField.superclass.onBlur.call(this);
14933 var d = this.inputEl().getValue();
14942 this.picker().show();
14946 this.fireEvent('show', this, this.date);
14951 if(this.isInline) return;
14952 this.picker().hide();
14953 this.viewMode = this.startViewMode;
14956 this.fireEvent('hide', this, this.date);
14960 onMousedown: function(e)
14962 e.stopPropagation();
14963 e.preventDefault();
14968 Roo.bootstrap.DateField.superclass.keyup.call(this);
14972 setValue: function(v)
14975 // v can be a string or a date..
14978 var d = new Date(this.parseDate(v) ).clearTime();
14980 if(isNaN(d.getTime())){
14981 this.date = this.viewDate = '';
14982 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14986 v = this.formatDate(d);
14988 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14990 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14994 this.fireEvent('select', this, this.date);
14998 getValue: function()
15000 return this.formatDate(this.date);
15003 fireKey: function(e)
15005 if (!this.picker().isVisible()){
15006 if (e.keyCode == 27) // allow escape to hide and re-show picker
15011 var dateChanged = false,
15013 newDate, newViewDate;
15018 e.preventDefault();
15022 if (!this.keyboardNavigation) break;
15023 dir = e.keyCode == 37 ? -1 : 1;
15026 newDate = this.moveYear(this.date, dir);
15027 newViewDate = this.moveYear(this.viewDate, dir);
15028 } else if (e.shiftKey){
15029 newDate = this.moveMonth(this.date, dir);
15030 newViewDate = this.moveMonth(this.viewDate, dir);
15032 newDate = new Date(this.date);
15033 newDate.setUTCDate(this.date.getUTCDate() + dir);
15034 newViewDate = new Date(this.viewDate);
15035 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15037 if (this.dateWithinRange(newDate)){
15038 this.date = newDate;
15039 this.viewDate = newViewDate;
15040 this.setValue(this.formatDate(this.date));
15042 e.preventDefault();
15043 dateChanged = true;
15048 if (!this.keyboardNavigation) break;
15049 dir = e.keyCode == 38 ? -1 : 1;
15051 newDate = this.moveYear(this.date, dir);
15052 newViewDate = this.moveYear(this.viewDate, dir);
15053 } else if (e.shiftKey){
15054 newDate = this.moveMonth(this.date, dir);
15055 newViewDate = this.moveMonth(this.viewDate, dir);
15057 newDate = new Date(this.date);
15058 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15059 newViewDate = new Date(this.viewDate);
15060 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15062 if (this.dateWithinRange(newDate)){
15063 this.date = newDate;
15064 this.viewDate = newViewDate;
15065 this.setValue(this.formatDate(this.date));
15067 e.preventDefault();
15068 dateChanged = true;
15072 this.setValue(this.formatDate(this.date));
15074 e.preventDefault();
15077 this.setValue(this.formatDate(this.date));
15091 onClick: function(e)
15093 e.stopPropagation();
15094 e.preventDefault();
15096 var target = e.getTarget();
15098 if(target.nodeName.toLowerCase() === 'i'){
15099 target = Roo.get(target).dom.parentNode;
15102 var nodeName = target.nodeName;
15103 var className = target.className;
15104 var html = target.innerHTML;
15105 //Roo.log(nodeName);
15107 switch(nodeName.toLowerCase()) {
15109 switch(className) {
15115 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15116 switch(this.viewMode){
15118 this.viewDate = this.moveMonth(this.viewDate, dir);
15122 this.viewDate = this.moveYear(this.viewDate, dir);
15128 var date = new Date();
15129 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15131 this.setValue(this.formatDate(this.date));
15138 if (className.indexOf('disabled') < 0) {
15139 this.viewDate.setUTCDate(1);
15140 if (className.indexOf('month') > -1) {
15141 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15143 var year = parseInt(html, 10) || 0;
15144 this.viewDate.setUTCFullYear(year);
15148 if(this.singleMode){
15149 this.setValue(this.formatDate(this.viewDate));
15160 //Roo.log(className);
15161 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15162 var day = parseInt(html, 10) || 1;
15163 var year = this.viewDate.getUTCFullYear(),
15164 month = this.viewDate.getUTCMonth();
15166 if (className.indexOf('old') > -1) {
15173 } else if (className.indexOf('new') > -1) {
15181 //Roo.log([year,month,day]);
15182 this.date = this.UTCDate(year, month, day,0,0,0,0);
15183 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15185 //Roo.log(this.formatDate(this.date));
15186 this.setValue(this.formatDate(this.date));
15193 setStartDate: function(startDate)
15195 this.startDate = startDate || -Infinity;
15196 if (this.startDate !== -Infinity) {
15197 this.startDate = this.parseDate(this.startDate);
15200 this.updateNavArrows();
15203 setEndDate: function(endDate)
15205 this.endDate = endDate || Infinity;
15206 if (this.endDate !== Infinity) {
15207 this.endDate = this.parseDate(this.endDate);
15210 this.updateNavArrows();
15213 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15215 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15216 if (typeof(this.daysOfWeekDisabled) !== 'object') {
15217 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15219 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15220 return parseInt(d, 10);
15223 this.updateNavArrows();
15226 updateNavArrows: function()
15228 if(this.singleMode){
15232 var d = new Date(this.viewDate),
15233 year = d.getUTCFullYear(),
15234 month = d.getUTCMonth();
15236 Roo.each(this.picker().select('.prev', true).elements, function(v){
15238 switch (this.viewMode) {
15241 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15247 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15254 Roo.each(this.picker().select('.next', true).elements, function(v){
15256 switch (this.viewMode) {
15259 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15265 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15273 moveMonth: function(date, dir)
15275 if (!dir) return date;
15276 var new_date = new Date(date.valueOf()),
15277 day = new_date.getUTCDate(),
15278 month = new_date.getUTCMonth(),
15279 mag = Math.abs(dir),
15281 dir = dir > 0 ? 1 : -1;
15284 // If going back one month, make sure month is not current month
15285 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15287 return new_date.getUTCMonth() == month;
15289 // If going forward one month, make sure month is as expected
15290 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15292 return new_date.getUTCMonth() != new_month;
15294 new_month = month + dir;
15295 new_date.setUTCMonth(new_month);
15296 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15297 if (new_month < 0 || new_month > 11)
15298 new_month = (new_month + 12) % 12;
15300 // For magnitudes >1, move one month at a time...
15301 for (var i=0; i<mag; i++)
15302 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15303 new_date = this.moveMonth(new_date, dir);
15304 // ...then reset the day, keeping it in the new month
15305 new_month = new_date.getUTCMonth();
15306 new_date.setUTCDate(day);
15308 return new_month != new_date.getUTCMonth();
15311 // Common date-resetting loop -- if date is beyond end of month, make it
15314 new_date.setUTCDate(--day);
15315 new_date.setUTCMonth(new_month);
15320 moveYear: function(date, dir)
15322 return this.moveMonth(date, dir*12);
15325 dateWithinRange: function(date)
15327 return date >= this.startDate && date <= this.endDate;
15333 this.picker().remove();
15338 Roo.apply(Roo.bootstrap.DateField, {
15349 html: '<i class="fa fa-arrow-left"/>'
15359 html: '<i class="fa fa-arrow-right"/>'
15401 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15402 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15403 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15404 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15405 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15418 navFnc: 'FullYear',
15423 navFnc: 'FullYear',
15428 Roo.apply(Roo.bootstrap.DateField, {
15432 cls: 'datepicker dropdown-menu roo-dynamic',
15436 cls: 'datepicker-days',
15440 cls: 'table-condensed',
15442 Roo.bootstrap.DateField.head,
15446 Roo.bootstrap.DateField.footer
15453 cls: 'datepicker-months',
15457 cls: 'table-condensed',
15459 Roo.bootstrap.DateField.head,
15460 Roo.bootstrap.DateField.content,
15461 Roo.bootstrap.DateField.footer
15468 cls: 'datepicker-years',
15472 cls: 'table-condensed',
15474 Roo.bootstrap.DateField.head,
15475 Roo.bootstrap.DateField.content,
15476 Roo.bootstrap.DateField.footer
15495 * @class Roo.bootstrap.TimeField
15496 * @extends Roo.bootstrap.Input
15497 * Bootstrap DateField class
15501 * Create a new TimeField
15502 * @param {Object} config The config object
15505 Roo.bootstrap.TimeField = function(config){
15506 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15510 * Fires when this field show.
15511 * @param {Roo.bootstrap.DateField} this
15512 * @param {Mixed} date The date value
15517 * Fires when this field hide.
15518 * @param {Roo.bootstrap.DateField} this
15519 * @param {Mixed} date The date value
15524 * Fires when select a date.
15525 * @param {Roo.bootstrap.DateField} this
15526 * @param {Mixed} date The date value
15532 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
15535 * @cfg {String} format
15536 * The default time format string which can be overriden for localization support. The format must be
15537 * valid according to {@link Date#parseDate} (defaults to 'H:i').
15541 onRender: function(ct, position)
15544 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15546 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15548 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15550 this.pop = this.picker().select('>.datepicker-time',true).first();
15551 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
15553 this.picker().on('mousedown', this.onMousedown, this);
15554 this.picker().on('click', this.onClick, this);
15556 this.picker().addClass('datepicker-dropdown');
15561 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15562 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15563 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15564 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15565 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15566 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15570 fireKey: function(e){
15571 if (!this.picker().isVisible()){
15572 if (e.keyCode == 27) // allow escape to hide and re-show picker
15577 e.preventDefault();
15585 this.onTogglePeriod();
15588 this.onIncrementMinutes();
15591 this.onDecrementMinutes();
15600 onClick: function(e) {
15601 e.stopPropagation();
15602 e.preventDefault();
15605 picker : function()
15607 return this.el.select('.datepicker', true).first();
15610 fillTime: function()
15612 var time = this.pop.select('tbody', true).first();
15614 time.dom.innerHTML = '';
15629 cls: 'hours-up glyphicon glyphicon-chevron-up'
15649 cls: 'minutes-up glyphicon glyphicon-chevron-up'
15670 cls: 'timepicker-hour',
15685 cls: 'timepicker-minute',
15700 cls: 'btn btn-primary period',
15722 cls: 'hours-down glyphicon glyphicon-chevron-down'
15742 cls: 'minutes-down glyphicon glyphicon-chevron-down'
15760 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15767 var hours = this.time.getHours();
15768 var minutes = this.time.getMinutes();
15781 hours = hours - 12;
15785 hours = '0' + hours;
15789 minutes = '0' + minutes;
15792 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15793 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15794 this.pop.select('button', true).first().dom.innerHTML = period;
15800 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15802 var cls = ['bottom'];
15804 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15811 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15816 this.picker().addClass(cls.join('-'));
15820 Roo.each(cls, function(c){
15822 _this.picker().setTop(_this.inputEl().getHeight());
15826 _this.picker().setTop(0 - _this.picker().getHeight());
15831 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15835 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15842 onFocus : function()
15844 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15848 onBlur : function()
15850 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15856 this.picker().show();
15861 this.fireEvent('show', this, this.date);
15866 this.picker().hide();
15869 this.fireEvent('hide', this, this.date);
15872 setTime : function()
15875 this.setValue(this.time.format(this.format));
15877 this.fireEvent('select', this, this.date);
15882 onMousedown: function(e){
15883 e.stopPropagation();
15884 e.preventDefault();
15887 onIncrementHours: function()
15889 Roo.log('onIncrementHours');
15890 this.time = this.time.add(Date.HOUR, 1);
15895 onDecrementHours: function()
15897 Roo.log('onDecrementHours');
15898 this.time = this.time.add(Date.HOUR, -1);
15902 onIncrementMinutes: function()
15904 Roo.log('onIncrementMinutes');
15905 this.time = this.time.add(Date.MINUTE, 1);
15909 onDecrementMinutes: function()
15911 Roo.log('onDecrementMinutes');
15912 this.time = this.time.add(Date.MINUTE, -1);
15916 onTogglePeriod: function()
15918 Roo.log('onTogglePeriod');
15919 this.time = this.time.add(Date.HOUR, 12);
15926 Roo.apply(Roo.bootstrap.TimeField, {
15956 cls: 'btn btn-info ok',
15968 Roo.apply(Roo.bootstrap.TimeField, {
15972 cls: 'datepicker dropdown-menu',
15976 cls: 'datepicker-time',
15980 cls: 'table-condensed',
15982 Roo.bootstrap.TimeField.content,
15983 Roo.bootstrap.TimeField.footer
16002 * @class Roo.bootstrap.CheckBox
16003 * @extends Roo.bootstrap.Input
16004 * Bootstrap CheckBox class
16006 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
16007 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
16008 * @cfg {String} boxLabel The text that appears beside the checkbox
16009 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
16010 * @cfg {Boolean} checked initnal the element
16014 * Create a new CheckBox
16015 * @param {Object} config The config object
16018 Roo.bootstrap.CheckBox = function(config){
16019 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
16024 * Fires when the element is checked or unchecked.
16025 * @param {Roo.bootstrap.CheckBox} this This input
16026 * @param {Boolean} checked The new checked value
16032 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
16034 inputType: 'checkbox',
16041 getAutoCreate : function()
16043 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16049 cfg.cls = 'form-group checkbox' //input-group
16057 type : this.inputType,
16058 value : (!this.checked) ? this.valueOff : this.inputValue,
16059 cls : 'roo-checkbox', //'form-box',
16060 placeholder : this.placeholder || ''
16064 if (this.weight) { // Validity check?
16065 cfg.cls += " checkbox-" + this.weight;
16068 if (this.disabled) {
16069 input.disabled=true;
16073 input.checked = this.checked;
16077 input.name = this.name;
16081 input.cls += ' input-' + this.size;
16085 ['xs','sm','md','lg'].map(function(size){
16086 if (settings[size]) {
16087 cfg.cls += ' col-' + size + '-' + settings[size];
16093 var inputblock = input;
16098 if (this.before || this.after) {
16101 cls : 'input-group',
16105 inputblock.cn.push({
16107 cls : 'input-group-addon',
16111 inputblock.cn.push(input);
16113 inputblock.cn.push({
16115 cls : 'input-group-addon',
16122 if (align ==='left' && this.fieldLabel.length) {
16123 Roo.log("left and has label");
16129 cls : 'control-label col-md-' + this.labelWidth,
16130 html : this.fieldLabel
16134 cls : "col-md-" + (12 - this.labelWidth),
16141 } else if ( this.fieldLabel.length) {
16146 tag: this.boxLabel ? 'span' : 'label',
16148 cls: 'control-label box-input-label',
16149 //cls : 'input-group-addon',
16150 html : this.fieldLabel
16160 Roo.log(" no label && no align");
16161 cfg.cn = [ inputblock ] ;
16170 html: this.boxLabel
16182 * return the real input element.
16184 inputEl: function ()
16186 return this.el.select('input.roo-checkbox',true).first();
16191 return this.el.select('label.control-label',true).first();
16194 initEvents : function()
16196 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16198 this.inputEl().on('click', this.onClick, this);
16202 onClick : function()
16204 this.setChecked(!this.checked);
16207 setChecked : function(state,suppressEvent)
16209 this.checked = state;
16211 this.inputEl().dom.checked = state;
16213 if(suppressEvent !== true){
16214 this.fireEvent('check', this, state);
16217 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16221 setValue : function(v,suppressEvent)
16223 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16237 * @class Roo.bootstrap.Radio
16238 * @extends Roo.bootstrap.CheckBox
16239 * Bootstrap Radio class
16242 * Create a new Radio
16243 * @param {Object} config The config object
16246 Roo.bootstrap.Radio = function(config){
16247 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16251 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
16253 inputType: 'radio',
16257 getAutoCreate : function()
16259 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16265 cfg.cls = 'form-group radio' //input-group
16270 type : this.inputType,
16271 value : (!this.checked) ? this.valueOff : this.inputValue,
16273 placeholder : this.placeholder || ''
16276 if (this.weight) { // Validity check?
16277 cfg.cls += " radio-" + this.weight;
16279 if (this.disabled) {
16280 input.disabled=true;
16284 input.checked = this.checked;
16288 input.name = this.name;
16292 input.cls += ' input-' + this.size;
16296 ['xs','sm','md','lg'].map(function(size){
16297 if (settings[size]) {
16298 cfg.cls += ' col-' + size + '-' + settings[size];
16302 var inputblock = input;
16304 if (this.before || this.after) {
16307 cls : 'input-group',
16311 inputblock.cn.push({
16313 cls : 'input-group-addon',
16317 inputblock.cn.push(input);
16319 inputblock.cn.push({
16321 cls : 'input-group-addon',
16328 if (align ==='left' && this.fieldLabel.length) {
16329 Roo.log("left and has label");
16335 cls : 'control-label col-md-' + this.labelWidth,
16336 html : this.fieldLabel
16340 cls : "col-md-" + (12 - this.labelWidth),
16347 } else if ( this.fieldLabel.length) {
16354 cls: 'control-label box-input-label',
16355 //cls : 'input-group-addon',
16356 html : this.fieldLabel
16366 Roo.log(" no label && no align");
16381 html: this.boxLabel
16388 inputEl: function ()
16390 return this.el.select('input.roo-radio',true).first();
16392 onClick : function()
16394 this.setChecked(true);
16397 setChecked : function(state,suppressEvent)
16400 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16401 v.dom.checked = false;
16405 this.checked = state;
16406 this.inputEl().dom.checked = state;
16408 if(suppressEvent !== true){
16409 this.fireEvent('check', this, state);
16412 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16416 getGroupValue : function()
16419 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16420 if(v.dom.checked == true){
16421 value = v.dom.value;
16429 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
16430 * @return {Mixed} value The field value
16432 getValue : function(){
16433 return this.getGroupValue();
16439 //<script type="text/javascript">
16442 * Based Ext JS Library 1.1.1
16443 * Copyright(c) 2006-2007, Ext JS, LLC.
16449 * @class Roo.HtmlEditorCore
16450 * @extends Roo.Component
16451 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16453 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16456 Roo.HtmlEditorCore = function(config){
16459 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16464 * @event initialize
16465 * Fires when the editor is fully initialized (including the iframe)
16466 * @param {Roo.HtmlEditorCore} this
16471 * Fires when the editor is first receives the focus. Any insertion must wait
16472 * until after this event.
16473 * @param {Roo.HtmlEditorCore} this
16477 * @event beforesync
16478 * Fires before the textarea is updated with content from the editor iframe. Return false
16479 * to cancel the sync.
16480 * @param {Roo.HtmlEditorCore} this
16481 * @param {String} html
16485 * @event beforepush
16486 * Fires before the iframe editor is updated with content from the textarea. Return false
16487 * to cancel the push.
16488 * @param {Roo.HtmlEditorCore} this
16489 * @param {String} html
16494 * Fires when the textarea is updated with content from the editor iframe.
16495 * @param {Roo.HtmlEditorCore} this
16496 * @param {String} html
16501 * Fires when the iframe editor is updated with content from the textarea.
16502 * @param {Roo.HtmlEditorCore} this
16503 * @param {String} html
16508 * @event editorevent
16509 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16510 * @param {Roo.HtmlEditorCore} this
16515 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16517 // defaults : white / black...
16518 this.applyBlacklists();
16525 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
16529 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
16535 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
16540 * @cfg {Number} height (in pixels)
16544 * @cfg {Number} width (in pixels)
16549 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16552 stylesheets: false,
16557 // private properties
16558 validationEvent : false,
16560 initialized : false,
16562 sourceEditMode : false,
16563 onFocus : Roo.emptyFn,
16565 hideMode:'offsets',
16569 // blacklist + whitelisted elements..
16576 * Protected method that will not generally be called directly. It
16577 * is called when the editor initializes the iframe with HTML contents. Override this method if you
16578 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16580 getDocMarkup : function(){
16583 Roo.log(this.stylesheets);
16585 // inherit styels from page...??
16586 if (this.stylesheets === false) {
16588 Roo.get(document.head).select('style').each(function(node) {
16589 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16592 Roo.get(document.head).select('link').each(function(node) {
16593 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16596 } else if (!this.stylesheets.length) {
16598 st = '<style type="text/css">' +
16599 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16602 Roo.each(this.stylesheets, function(s) {
16603 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16608 st += '<style type="text/css">' +
16609 'IMG { cursor: pointer } ' +
16613 return '<html><head>' + st +
16614 //<style type="text/css">' +
16615 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16617 ' </head><body class="roo-htmleditor-body"></body></html>';
16621 onRender : function(ct, position)
16624 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16625 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16628 this.el.dom.style.border = '0 none';
16629 this.el.dom.setAttribute('tabIndex', -1);
16630 this.el.addClass('x-hidden hide');
16634 if(Roo.isIE){ // fix IE 1px bogus margin
16635 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16639 this.frameId = Roo.id();
16643 var iframe = this.owner.wrap.createChild({
16645 cls: 'form-control', // bootstrap..
16647 name: this.frameId,
16648 frameBorder : 'no',
16649 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
16654 this.iframe = iframe.dom;
16656 this.assignDocWin();
16658 this.doc.designMode = 'on';
16661 this.doc.write(this.getDocMarkup());
16665 var task = { // must defer to wait for browser to be ready
16667 //console.log("run task?" + this.doc.readyState);
16668 this.assignDocWin();
16669 if(this.doc.body || this.doc.readyState == 'complete'){
16671 this.doc.designMode="on";
16675 Roo.TaskMgr.stop(task);
16676 this.initEditor.defer(10, this);
16683 Roo.TaskMgr.start(task);
16690 onResize : function(w, h)
16692 Roo.log('resize: ' +w + ',' + h );
16693 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16697 if(typeof w == 'number'){
16699 this.iframe.style.width = w + 'px';
16701 if(typeof h == 'number'){
16703 this.iframe.style.height = h + 'px';
16705 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16712 * Toggles the editor between standard and source edit mode.
16713 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16715 toggleSourceEdit : function(sourceEditMode){
16717 this.sourceEditMode = sourceEditMode === true;
16719 if(this.sourceEditMode){
16721 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
16724 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16725 //this.iframe.className = '';
16728 //this.setSize(this.owner.wrap.getSize());
16729 //this.fireEvent('editmodechange', this, this.sourceEditMode);
16736 * Protected method that will not generally be called directly. If you need/want
16737 * custom HTML cleanup, this is the method you should override.
16738 * @param {String} html The HTML to be cleaned
16739 * return {String} The cleaned HTML
16741 cleanHtml : function(html){
16742 html = String(html);
16743 if(html.length > 5){
16744 if(Roo.isSafari){ // strip safari nonsense
16745 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16748 if(html == ' '){
16755 * HTML Editor -> Textarea
16756 * Protected method that will not generally be called directly. Syncs the contents
16757 * of the editor iframe with the textarea.
16759 syncValue : function(){
16760 if(this.initialized){
16761 var bd = (this.doc.body || this.doc.documentElement);
16762 //this.cleanUpPaste(); -- this is done else where and causes havoc..
16763 var html = bd.innerHTML;
16765 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16766 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16768 html = '<div style="'+m[0]+'">' + html + '</div>';
16771 html = this.cleanHtml(html);
16772 // fix up the special chars.. normaly like back quotes in word...
16773 // however we do not want to do this with chinese..
16774 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16775 var cc = b.charCodeAt();
16777 (cc >= 0x4E00 && cc < 0xA000 ) ||
16778 (cc >= 0x3400 && cc < 0x4E00 ) ||
16779 (cc >= 0xf900 && cc < 0xfb00 )
16785 if(this.owner.fireEvent('beforesync', this, html) !== false){
16786 this.el.dom.value = html;
16787 this.owner.fireEvent('sync', this, html);
16793 * Protected method that will not generally be called directly. Pushes the value of the textarea
16794 * into the iframe editor.
16796 pushValue : function(){
16797 if(this.initialized){
16798 var v = this.el.dom.value.trim();
16800 // if(v.length < 1){
16804 if(this.owner.fireEvent('beforepush', this, v) !== false){
16805 var d = (this.doc.body || this.doc.documentElement);
16807 this.cleanUpPaste();
16808 this.el.dom.value = d.innerHTML;
16809 this.owner.fireEvent('push', this, v);
16815 deferFocus : function(){
16816 this.focus.defer(10, this);
16820 focus : function(){
16821 if(this.win && !this.sourceEditMode){
16828 assignDocWin: function()
16830 var iframe = this.iframe;
16833 this.doc = iframe.contentWindow.document;
16834 this.win = iframe.contentWindow;
16836 // if (!Roo.get(this.frameId)) {
16839 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16840 // this.win = Roo.get(this.frameId).dom.contentWindow;
16842 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16846 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16847 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16852 initEditor : function(){
16853 //console.log("INIT EDITOR");
16854 this.assignDocWin();
16858 this.doc.designMode="on";
16860 this.doc.write(this.getDocMarkup());
16863 var dbody = (this.doc.body || this.doc.documentElement);
16864 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16865 // this copies styles from the containing element into thsi one..
16866 // not sure why we need all of this..
16867 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16869 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16870 //ss['background-attachment'] = 'fixed'; // w3c
16871 dbody.bgProperties = 'fixed'; // ie
16872 //Roo.DomHelper.applyStyles(dbody, ss);
16873 Roo.EventManager.on(this.doc, {
16874 //'mousedown': this.onEditorEvent,
16875 'mouseup': this.onEditorEvent,
16876 'dblclick': this.onEditorEvent,
16877 'click': this.onEditorEvent,
16878 'keyup': this.onEditorEvent,
16883 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16885 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16886 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16888 this.initialized = true;
16890 this.owner.fireEvent('initialize', this);
16895 onDestroy : function(){
16901 //for (var i =0; i < this.toolbars.length;i++) {
16902 // // fixme - ask toolbars for heights?
16903 // this.toolbars[i].onDestroy();
16906 //this.wrap.dom.innerHTML = '';
16907 //this.wrap.remove();
16912 onFirstFocus : function(){
16914 this.assignDocWin();
16917 this.activated = true;
16920 if(Roo.isGecko){ // prevent silly gecko errors
16922 var s = this.win.getSelection();
16923 if(!s.focusNode || s.focusNode.nodeType != 3){
16924 var r = s.getRangeAt(0);
16925 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16930 this.execCmd('useCSS', true);
16931 this.execCmd('styleWithCSS', false);
16934 this.owner.fireEvent('activate', this);
16938 adjustFont: function(btn){
16939 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16940 //if(Roo.isSafari){ // safari
16943 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16944 if(Roo.isSafari){ // safari
16945 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16946 v = (v < 10) ? 10 : v;
16947 v = (v > 48) ? 48 : v;
16948 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16953 v = Math.max(1, v+adjust);
16955 this.execCmd('FontSize', v );
16958 onEditorEvent : function(e){
16959 this.owner.fireEvent('editorevent', this, e);
16960 // this.updateToolbar();
16961 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16964 insertTag : function(tg)
16966 // could be a bit smarter... -> wrap the current selected tRoo..
16967 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16969 range = this.createRange(this.getSelection());
16970 var wrappingNode = this.doc.createElement(tg.toLowerCase());
16971 wrappingNode.appendChild(range.extractContents());
16972 range.insertNode(wrappingNode);
16979 this.execCmd("formatblock", tg);
16983 insertText : function(txt)
16987 var range = this.createRange();
16988 range.deleteContents();
16989 //alert(Sender.getAttribute('label'));
16991 range.insertNode(this.doc.createTextNode(txt));
16997 * Executes a Midas editor command on the editor document and performs necessary focus and
16998 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16999 * @param {String} cmd The Midas command
17000 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
17002 relayCmd : function(cmd, value){
17004 this.execCmd(cmd, value);
17005 this.owner.fireEvent('editorevent', this);
17006 //this.updateToolbar();
17007 this.owner.deferFocus();
17011 * Executes a Midas editor command directly on the editor document.
17012 * For visual commands, you should use {@link #relayCmd} instead.
17013 * <b>This should only be called after the editor is initialized.</b>
17014 * @param {String} cmd The Midas command
17015 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
17017 execCmd : function(cmd, value){
17018 this.doc.execCommand(cmd, false, value === undefined ? null : value);
17025 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
17027 * @param {String} text | dom node..
17029 insertAtCursor : function(text)
17034 if(!this.activated){
17040 var r = this.doc.selection.createRange();
17051 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
17055 // from jquery ui (MIT licenced)
17057 var win = this.win;
17059 if (win.getSelection && win.getSelection().getRangeAt) {
17060 range = win.getSelection().getRangeAt(0);
17061 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
17062 range.insertNode(node);
17063 } else if (win.document.selection && win.document.selection.createRange) {
17064 // no firefox support
17065 var txt = typeof(text) == 'string' ? text : text.outerHTML;
17066 win.document.selection.createRange().pasteHTML(txt);
17068 // no firefox support
17069 var txt = typeof(text) == 'string' ? text : text.outerHTML;
17070 this.execCmd('InsertHTML', txt);
17079 mozKeyPress : function(e){
17081 var c = e.getCharCode(), cmd;
17084 c = String.fromCharCode(c).toLowerCase();
17098 this.cleanUpPaste.defer(100, this);
17106 e.preventDefault();
17114 fixKeys : function(){ // load time branching for fastest keydown performance
17116 return function(e){
17117 var k = e.getKey(), r;
17120 r = this.doc.selection.createRange();
17123 r.pasteHTML('    ');
17130 r = this.doc.selection.createRange();
17132 var target = r.parentElement();
17133 if(!target || target.tagName.toLowerCase() != 'li'){
17135 r.pasteHTML('<br />');
17141 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17142 this.cleanUpPaste.defer(100, this);
17148 }else if(Roo.isOpera){
17149 return function(e){
17150 var k = e.getKey();
17154 this.execCmd('InsertHTML','    ');
17157 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17158 this.cleanUpPaste.defer(100, this);
17163 }else if(Roo.isSafari){
17164 return function(e){
17165 var k = e.getKey();
17169 this.execCmd('InsertText','\t');
17173 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17174 this.cleanUpPaste.defer(100, this);
17182 getAllAncestors: function()
17184 var p = this.getSelectedNode();
17187 a.push(p); // push blank onto stack..
17188 p = this.getParentElement();
17192 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17196 a.push(this.doc.body);
17200 lastSelNode : false,
17203 getSelection : function()
17205 this.assignDocWin();
17206 return Roo.isIE ? this.doc.selection : this.win.getSelection();
17209 getSelectedNode: function()
17211 // this may only work on Gecko!!!
17213 // should we cache this!!!!
17218 var range = this.createRange(this.getSelection()).cloneRange();
17221 var parent = range.parentElement();
17223 var testRange = range.duplicate();
17224 testRange.moveToElementText(parent);
17225 if (testRange.inRange(range)) {
17228 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17231 parent = parent.parentElement;
17236 // is ancestor a text element.
17237 var ac = range.commonAncestorContainer;
17238 if (ac.nodeType == 3) {
17239 ac = ac.parentNode;
17242 var ar = ac.childNodes;
17245 var other_nodes = [];
17246 var has_other_nodes = false;
17247 for (var i=0;i<ar.length;i++) {
17248 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
17251 // fullly contained node.
17253 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17258 // probably selected..
17259 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17260 other_nodes.push(ar[i]);
17264 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
17269 has_other_nodes = true;
17271 if (!nodes.length && other_nodes.length) {
17272 nodes= other_nodes;
17274 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17280 createRange: function(sel)
17282 // this has strange effects when using with
17283 // top toolbar - not sure if it's a great idea.
17284 //this.editor.contentWindow.focus();
17285 if (typeof sel != "undefined") {
17287 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17289 return this.doc.createRange();
17292 return this.doc.createRange();
17295 getParentElement: function()
17298 this.assignDocWin();
17299 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17301 var range = this.createRange(sel);
17304 var p = range.commonAncestorContainer;
17305 while (p.nodeType == 3) { // text node
17316 * Range intersection.. the hard stuff...
17320 * [ -- selected range --- ]
17324 * if end is before start or hits it. fail.
17325 * if start is after end or hits it fail.
17327 * if either hits (but other is outside. - then it's not
17333 // @see http://www.thismuchiknow.co.uk/?p=64.
17334 rangeIntersectsNode : function(range, node)
17336 var nodeRange = node.ownerDocument.createRange();
17338 nodeRange.selectNode(node);
17340 nodeRange.selectNodeContents(node);
17343 var rangeStartRange = range.cloneRange();
17344 rangeStartRange.collapse(true);
17346 var rangeEndRange = range.cloneRange();
17347 rangeEndRange.collapse(false);
17349 var nodeStartRange = nodeRange.cloneRange();
17350 nodeStartRange.collapse(true);
17352 var nodeEndRange = nodeRange.cloneRange();
17353 nodeEndRange.collapse(false);
17355 return rangeStartRange.compareBoundaryPoints(
17356 Range.START_TO_START, nodeEndRange) == -1 &&
17357 rangeEndRange.compareBoundaryPoints(
17358 Range.START_TO_START, nodeStartRange) == 1;
17362 rangeCompareNode : function(range, node)
17364 var nodeRange = node.ownerDocument.createRange();
17366 nodeRange.selectNode(node);
17368 nodeRange.selectNodeContents(node);
17372 range.collapse(true);
17374 nodeRange.collapse(true);
17376 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17377 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
17379 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17381 var nodeIsBefore = ss == 1;
17382 var nodeIsAfter = ee == -1;
17384 if (nodeIsBefore && nodeIsAfter)
17386 if (!nodeIsBefore && nodeIsAfter)
17387 return 1; //right trailed.
17389 if (nodeIsBefore && !nodeIsAfter)
17390 return 2; // left trailed.
17395 // private? - in a new class?
17396 cleanUpPaste : function()
17398 // cleans up the whole document..
17399 Roo.log('cleanuppaste');
17401 this.cleanUpChildren(this.doc.body);
17402 var clean = this.cleanWordChars(this.doc.body.innerHTML);
17403 if (clean != this.doc.body.innerHTML) {
17404 this.doc.body.innerHTML = clean;
17409 cleanWordChars : function(input) {// change the chars to hex code
17410 var he = Roo.HtmlEditorCore;
17412 var output = input;
17413 Roo.each(he.swapCodes, function(sw) {
17414 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17416 output = output.replace(swapper, sw[1]);
17423 cleanUpChildren : function (n)
17425 if (!n.childNodes.length) {
17428 for (var i = n.childNodes.length-1; i > -1 ; i--) {
17429 this.cleanUpChild(n.childNodes[i]);
17436 cleanUpChild : function (node)
17439 //console.log(node);
17440 if (node.nodeName == "#text") {
17441 // clean up silly Windows -- stuff?
17444 if (node.nodeName == "#comment") {
17445 node.parentNode.removeChild(node);
17446 // clean up silly Windows -- stuff?
17449 var lcname = node.tagName.toLowerCase();
17450 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17451 // whitelist of tags..
17453 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17455 node.parentNode.removeChild(node);
17460 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17462 // remove <a name=....> as rendering on yahoo mailer is borked with this.
17463 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17465 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17466 // remove_keep_children = true;
17469 if (remove_keep_children) {
17470 this.cleanUpChildren(node);
17471 // inserts everything just before this node...
17472 while (node.childNodes.length) {
17473 var cn = node.childNodes[0];
17474 node.removeChild(cn);
17475 node.parentNode.insertBefore(cn, node);
17477 node.parentNode.removeChild(node);
17481 if (!node.attributes || !node.attributes.length) {
17482 this.cleanUpChildren(node);
17486 function cleanAttr(n,v)
17489 if (v.match(/^\./) || v.match(/^\//)) {
17492 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17495 if (v.match(/^#/)) {
17498 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17499 node.removeAttribute(n);
17503 var cwhite = this.cwhite;
17504 var cblack = this.cblack;
17506 function cleanStyle(n,v)
17508 if (v.match(/expression/)) { //XSS?? should we even bother..
17509 node.removeAttribute(n);
17513 var parts = v.split(/;/);
17516 Roo.each(parts, function(p) {
17517 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17521 var l = p.split(':').shift().replace(/\s+/g,'');
17522 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17524 if ( cwhite.length && cblack.indexOf(l) > -1) {
17525 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17526 //node.removeAttribute(n);
17530 // only allow 'c whitelisted system attributes'
17531 if ( cwhite.length && cwhite.indexOf(l) < 0) {
17532 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17533 //node.removeAttribute(n);
17543 if (clean.length) {
17544 node.setAttribute(n, clean.join(';'));
17546 node.removeAttribute(n);
17552 for (var i = node.attributes.length-1; i > -1 ; i--) {
17553 var a = node.attributes[i];
17556 if (a.name.toLowerCase().substr(0,2)=='on') {
17557 node.removeAttribute(a.name);
17560 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17561 node.removeAttribute(a.name);
17564 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17565 cleanAttr(a.name,a.value); // fixme..
17568 if (a.name == 'style') {
17569 cleanStyle(a.name,a.value);
17572 /// clean up MS crap..
17573 // tecnically this should be a list of valid class'es..
17576 if (a.name == 'class') {
17577 if (a.value.match(/^Mso/)) {
17578 node.className = '';
17581 if (a.value.match(/body/)) {
17582 node.className = '';
17593 this.cleanUpChildren(node);
17598 * Clean up MS wordisms...
17600 cleanWord : function(node)
17603 var cleanWordChildren = function()
17605 if (!node.childNodes.length) {
17608 for (var i = node.childNodes.length-1; i > -1 ; i--) {
17609 _t.cleanWord(node.childNodes[i]);
17615 this.cleanWord(this.doc.body);
17618 if (node.nodeName == "#text") {
17619 // clean up silly Windows -- stuff?
17622 if (node.nodeName == "#comment") {
17623 node.parentNode.removeChild(node);
17624 // clean up silly Windows -- stuff?
17628 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17629 node.parentNode.removeChild(node);
17633 // remove - but keep children..
17634 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17635 while (node.childNodes.length) {
17636 var cn = node.childNodes[0];
17637 node.removeChild(cn);
17638 node.parentNode.insertBefore(cn, node);
17640 node.parentNode.removeChild(node);
17641 cleanWordChildren();
17645 if (node.className.length) {
17647 var cn = node.className.split(/\W+/);
17649 Roo.each(cn, function(cls) {
17650 if (cls.match(/Mso[a-zA-Z]+/)) {
17655 node.className = cna.length ? cna.join(' ') : '';
17657 node.removeAttribute("class");
17661 if (node.hasAttribute("lang")) {
17662 node.removeAttribute("lang");
17665 if (node.hasAttribute("style")) {
17667 var styles = node.getAttribute("style").split(";");
17669 Roo.each(styles, function(s) {
17670 if (!s.match(/:/)) {
17673 var kv = s.split(":");
17674 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17677 // what ever is left... we allow.
17680 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17681 if (!nstyle.length) {
17682 node.removeAttribute('style');
17686 cleanWordChildren();
17690 domToHTML : function(currentElement, depth, nopadtext) {
17692 depth = depth || 0;
17693 nopadtext = nopadtext || false;
17695 if (!currentElement) {
17696 return this.domToHTML(this.doc.body);
17699 //Roo.log(currentElement);
17701 var allText = false;
17702 var nodeName = currentElement.nodeName;
17703 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17705 if (nodeName == '#text') {
17706 return currentElement.nodeValue;
17711 if (nodeName != 'BODY') {
17714 // Prints the node tagName, such as <A>, <IMG>, etc
17717 for(i = 0; i < currentElement.attributes.length;i++) {
17719 var aname = currentElement.attributes.item(i).name;
17720 if (!currentElement.attributes.item(i).value.length) {
17723 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17726 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17735 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17738 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17743 // Traverse the tree
17745 var currentElementChild = currentElement.childNodes.item(i);
17746 var allText = true;
17747 var innerHTML = '';
17749 while (currentElementChild) {
17750 // Formatting code (indent the tree so it looks nice on the screen)
17751 var nopad = nopadtext;
17752 if (lastnode == 'SPAN') {
17756 if (currentElementChild.nodeName == '#text') {
17757 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17758 if (!nopad && toadd.length > 80) {
17759 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
17761 innerHTML += toadd;
17764 currentElementChild = currentElement.childNodes.item(i);
17770 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
17772 // Recursively traverse the tree structure of the child node
17773 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
17774 lastnode = currentElementChild.nodeName;
17776 currentElementChild=currentElement.childNodes.item(i);
17782 // The remaining code is mostly for formatting the tree
17783 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
17788 ret+= "</"+tagName+">";
17794 applyBlacklists : function()
17796 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
17797 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
17801 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17802 if (b.indexOf(tag) > -1) {
17805 this.white.push(tag);
17809 Roo.each(w, function(tag) {
17810 if (b.indexOf(tag) > -1) {
17813 if (this.white.indexOf(tag) > -1) {
17816 this.white.push(tag);
17821 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17822 if (w.indexOf(tag) > -1) {
17825 this.black.push(tag);
17829 Roo.each(b, function(tag) {
17830 if (w.indexOf(tag) > -1) {
17833 if (this.black.indexOf(tag) > -1) {
17836 this.black.push(tag);
17841 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
17842 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
17846 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17847 if (b.indexOf(tag) > -1) {
17850 this.cwhite.push(tag);
17854 Roo.each(w, function(tag) {
17855 if (b.indexOf(tag) > -1) {
17858 if (this.cwhite.indexOf(tag) > -1) {
17861 this.cwhite.push(tag);
17866 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17867 if (w.indexOf(tag) > -1) {
17870 this.cblack.push(tag);
17874 Roo.each(b, function(tag) {
17875 if (w.indexOf(tag) > -1) {
17878 if (this.cblack.indexOf(tag) > -1) {
17881 this.cblack.push(tag);
17886 // hide stuff that is not compatible
17900 * @event specialkey
17904 * @cfg {String} fieldClass @hide
17907 * @cfg {String} focusClass @hide
17910 * @cfg {String} autoCreate @hide
17913 * @cfg {String} inputType @hide
17916 * @cfg {String} invalidClass @hide
17919 * @cfg {String} invalidText @hide
17922 * @cfg {String} msgFx @hide
17925 * @cfg {String} validateOnBlur @hide
17929 Roo.HtmlEditorCore.white = [
17930 'area', 'br', 'img', 'input', 'hr', 'wbr',
17932 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
17933 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
17934 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
17935 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
17936 'table', 'ul', 'xmp',
17938 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
17941 'dir', 'menu', 'ol', 'ul', 'dl',
17947 Roo.HtmlEditorCore.black = [
17948 // 'embed', 'object', // enable - backend responsiblity to clean thiese
17950 'base', 'basefont', 'bgsound', 'blink', 'body',
17951 'frame', 'frameset', 'head', 'html', 'ilayer',
17952 'iframe', 'layer', 'link', 'meta', 'object',
17953 'script', 'style' ,'title', 'xml' // clean later..
17955 Roo.HtmlEditorCore.clean = [
17956 'script', 'style', 'title', 'xml'
17958 Roo.HtmlEditorCore.remove = [
17963 Roo.HtmlEditorCore.ablack = [
17967 Roo.HtmlEditorCore.aclean = [
17968 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
17972 Roo.HtmlEditorCore.pwhite= [
17973 'http', 'https', 'mailto'
17976 // white listed style attributes.
17977 Roo.HtmlEditorCore.cwhite= [
17978 // 'text-align', /// default is to allow most things..
17984 // black listed style attributes.
17985 Roo.HtmlEditorCore.cblack= [
17986 // 'font-size' -- this can be set by the project
17990 Roo.HtmlEditorCore.swapCodes =[
18009 * @class Roo.bootstrap.HtmlEditor
18010 * @extends Roo.bootstrap.TextArea
18011 * Bootstrap HtmlEditor class
18014 * Create a new HtmlEditor
18015 * @param {Object} config The config object
18018 Roo.bootstrap.HtmlEditor = function(config){
18019 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
18020 if (!this.toolbars) {
18021 this.toolbars = [];
18023 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
18026 * @event initialize
18027 * Fires when the editor is fully initialized (including the iframe)
18028 * @param {HtmlEditor} this
18033 * Fires when the editor is first receives the focus. Any insertion must wait
18034 * until after this event.
18035 * @param {HtmlEditor} this
18039 * @event beforesync
18040 * Fires before the textarea is updated with content from the editor iframe. Return false
18041 * to cancel the sync.
18042 * @param {HtmlEditor} this
18043 * @param {String} html
18047 * @event beforepush
18048 * Fires before the iframe editor is updated with content from the textarea. Return false
18049 * to cancel the push.
18050 * @param {HtmlEditor} this
18051 * @param {String} html
18056 * Fires when the textarea is updated with content from the editor iframe.
18057 * @param {HtmlEditor} this
18058 * @param {String} html
18063 * Fires when the iframe editor is updated with content from the textarea.
18064 * @param {HtmlEditor} this
18065 * @param {String} html
18069 * @event editmodechange
18070 * Fires when the editor switches edit modes
18071 * @param {HtmlEditor} this
18072 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
18074 editmodechange: true,
18076 * @event editorevent
18077 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18078 * @param {HtmlEditor} this
18082 * @event firstfocus
18083 * Fires when on first focus - needed by toolbars..
18084 * @param {HtmlEditor} this
18089 * Auto save the htmlEditor value as a file into Events
18090 * @param {HtmlEditor} this
18094 * @event savedpreview
18095 * preview the saved version of htmlEditor
18096 * @param {HtmlEditor} this
18103 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
18107 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
18112 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
18117 * @cfg {Number} height (in pixels)
18121 * @cfg {Number} width (in pixels)
18126 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18129 stylesheets: false,
18134 // private properties
18135 validationEvent : false,
18137 initialized : false,
18140 onFocus : Roo.emptyFn,
18142 hideMode:'offsets',
18145 tbContainer : false,
18147 toolbarContainer :function() {
18148 return this.wrap.select('.x-html-editor-tb',true).first();
18152 * Protected method that will not generally be called directly. It
18153 * is called when the editor creates its toolbar. Override this method if you need to
18154 * add custom toolbar buttons.
18155 * @param {HtmlEditor} editor
18157 createToolbar : function(){
18159 Roo.log("create toolbars");
18161 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18162 this.toolbars[0].render(this.toolbarContainer());
18166 // if (!editor.toolbars || !editor.toolbars.length) {
18167 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18170 // for (var i =0 ; i < editor.toolbars.length;i++) {
18171 // editor.toolbars[i] = Roo.factory(
18172 // typeof(editor.toolbars[i]) == 'string' ?
18173 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
18174 // Roo.bootstrap.HtmlEditor);
18175 // editor.toolbars[i].init(editor);
18181 onRender : function(ct, position)
18183 // Roo.log("Call onRender: " + this.xtype);
18185 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18187 this.wrap = this.inputEl().wrap({
18188 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18191 this.editorcore.onRender(ct, position);
18193 if (this.resizable) {
18194 this.resizeEl = new Roo.Resizable(this.wrap, {
18198 minHeight : this.height,
18199 height: this.height,
18200 handles : this.resizable,
18203 resize : function(r, w, h) {
18204 _t.onResize(w,h); // -something
18210 this.createToolbar(this);
18213 if(!this.width && this.resizable){
18214 this.setSize(this.wrap.getSize());
18216 if (this.resizeEl) {
18217 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18218 // should trigger onReize..
18224 onResize : function(w, h)
18226 Roo.log('resize: ' +w + ',' + h );
18227 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18231 if(this.inputEl() ){
18232 if(typeof w == 'number'){
18233 var aw = w - this.wrap.getFrameWidth('lr');
18234 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18237 if(typeof h == 'number'){
18238 var tbh = -11; // fixme it needs to tool bar size!
18239 for (var i =0; i < this.toolbars.length;i++) {
18240 // fixme - ask toolbars for heights?
18241 tbh += this.toolbars[i].el.getHeight();
18242 //if (this.toolbars[i].footer) {
18243 // tbh += this.toolbars[i].footer.el.getHeight();
18251 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18252 ah -= 5; // knock a few pixes off for look..
18253 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18257 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18258 this.editorcore.onResize(ew,eh);
18263 * Toggles the editor between standard and source edit mode.
18264 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18266 toggleSourceEdit : function(sourceEditMode)
18268 this.editorcore.toggleSourceEdit(sourceEditMode);
18270 if(this.editorcore.sourceEditMode){
18271 Roo.log('editor - showing textarea');
18274 // Roo.log(this.syncValue());
18276 this.inputEl().removeClass(['hide', 'x-hidden']);
18277 this.inputEl().dom.removeAttribute('tabIndex');
18278 this.inputEl().focus();
18280 Roo.log('editor - hiding textarea');
18282 // Roo.log(this.pushValue());
18285 this.inputEl().addClass(['hide', 'x-hidden']);
18286 this.inputEl().dom.setAttribute('tabIndex', -1);
18287 //this.deferFocus();
18290 if(this.resizable){
18291 this.setSize(this.wrap.getSize());
18294 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18297 // private (for BoxComponent)
18298 adjustSize : Roo.BoxComponent.prototype.adjustSize,
18300 // private (for BoxComponent)
18301 getResizeEl : function(){
18305 // private (for BoxComponent)
18306 getPositionEl : function(){
18311 initEvents : function(){
18312 this.originalValue = this.getValue();
18316 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18319 // markInvalid : Roo.emptyFn,
18321 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18324 // clearInvalid : Roo.emptyFn,
18326 setValue : function(v){
18327 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18328 this.editorcore.pushValue();
18333 deferFocus : function(){
18334 this.focus.defer(10, this);
18338 focus : function(){
18339 this.editorcore.focus();
18345 onDestroy : function(){
18351 for (var i =0; i < this.toolbars.length;i++) {
18352 // fixme - ask toolbars for heights?
18353 this.toolbars[i].onDestroy();
18356 this.wrap.dom.innerHTML = '';
18357 this.wrap.remove();
18362 onFirstFocus : function(){
18363 //Roo.log("onFirstFocus");
18364 this.editorcore.onFirstFocus();
18365 for (var i =0; i < this.toolbars.length;i++) {
18366 this.toolbars[i].onFirstFocus();
18372 syncValue : function()
18374 this.editorcore.syncValue();
18377 pushValue : function()
18379 this.editorcore.pushValue();
18383 // hide stuff that is not compatible
18397 * @event specialkey
18401 * @cfg {String} fieldClass @hide
18404 * @cfg {String} focusClass @hide
18407 * @cfg {String} autoCreate @hide
18410 * @cfg {String} inputType @hide
18413 * @cfg {String} invalidClass @hide
18416 * @cfg {String} invalidText @hide
18419 * @cfg {String} msgFx @hide
18422 * @cfg {String} validateOnBlur @hide
18431 Roo.namespace('Roo.bootstrap.htmleditor');
18433 * @class Roo.bootstrap.HtmlEditorToolbar1
18438 new Roo.bootstrap.HtmlEditor({
18441 new Roo.bootstrap.HtmlEditorToolbar1({
18442 disable : { fonts: 1 , format: 1, ..., ... , ...],
18448 * @cfg {Object} disable List of elements to disable..
18449 * @cfg {Array} btns List of additional buttons.
18453 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18456 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18459 Roo.apply(this, config);
18461 // default disabled, based on 'good practice'..
18462 this.disable = this.disable || {};
18463 Roo.applyIf(this.disable, {
18466 specialElements : true
18468 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18470 this.editor = config.editor;
18471 this.editorcore = config.editor.editorcore;
18473 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18475 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18476 // dont call parent... till later.
18478 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
18483 editorcore : false,
18488 "h1","h2","h3","h4","h5","h6",
18490 "abbr", "acronym", "address", "cite", "samp", "var",
18494 onRender : function(ct, position)
18496 // Roo.log("Call onRender: " + this.xtype);
18498 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18500 this.el.dom.style.marginBottom = '0';
18502 var editorcore = this.editorcore;
18503 var editor= this.editor;
18506 var btn = function(id,cmd , toggle, handler){
18508 var event = toggle ? 'toggle' : 'click';
18513 xns: Roo.bootstrap,
18516 enableToggle:toggle !== false,
18518 pressed : toggle ? false : null,
18521 a.listeners[toggle ? 'toggle' : 'click'] = function() {
18522 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
18531 xns: Roo.bootstrap,
18532 glyphicon : 'font',
18536 xns: Roo.bootstrap,
18540 Roo.each(this.formats, function(f) {
18541 style.menu.items.push({
18543 xns: Roo.bootstrap,
18544 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18549 editorcore.insertTag(this.tagname);
18556 children.push(style);
18559 btn('bold',false,true);
18560 btn('italic',false,true);
18561 btn('align-left', 'justifyleft',true);
18562 btn('align-center', 'justifycenter',true);
18563 btn('align-right' , 'justifyright',true);
18564 btn('link', false, false, function(btn) {
18565 //Roo.log("create link?");
18566 var url = prompt(this.createLinkText, this.defaultLinkValue);
18567 if(url && url != 'http:/'+'/'){
18568 this.editorcore.relayCmd('createlink', url);
18571 btn('list','insertunorderedlist',true);
18572 btn('pencil', false,true, function(btn){
18575 this.toggleSourceEdit(btn.pressed);
18581 xns: Roo.bootstrap,
18586 xns: Roo.bootstrap,
18591 cog.menu.items.push({
18593 xns: Roo.bootstrap,
18594 html : Clean styles,
18599 editorcore.insertTag(this.tagname);
18608 this.xtype = 'NavSimplebar';
18610 for(var i=0;i< children.length;i++) {
18612 this.buttons.add(this.addxtypeChild(children[i]));
18616 editor.on('editorevent', this.updateToolbar, this);
18618 onBtnClick : function(id)
18620 this.editorcore.relayCmd(id);
18621 this.editorcore.focus();
18625 * Protected method that will not generally be called directly. It triggers
18626 * a toolbar update by reading the markup state of the current selection in the editor.
18628 updateToolbar: function(){
18630 if(!this.editorcore.activated){
18631 this.editor.onFirstFocus(); // is this neeed?
18635 var btns = this.buttons;
18636 var doc = this.editorcore.doc;
18637 btns.get('bold').setActive(doc.queryCommandState('bold'));
18638 btns.get('italic').setActive(doc.queryCommandState('italic'));
18639 //btns.get('underline').setActive(doc.queryCommandState('underline'));
18641 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18642 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18643 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18645 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18646 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18649 var ans = this.editorcore.getAllAncestors();
18650 if (this.formatCombo) {
18653 var store = this.formatCombo.store;
18654 this.formatCombo.setValue("");
18655 for (var i =0; i < ans.length;i++) {
18656 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18658 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18666 // hides menus... - so this cant be on a menu...
18667 Roo.bootstrap.MenuMgr.hideAll();
18669 Roo.bootstrap.MenuMgr.hideAll();
18670 //this.editorsyncValue();
18672 onFirstFocus: function() {
18673 this.buttons.each(function(item){
18677 toggleSourceEdit : function(sourceEditMode){
18680 if(sourceEditMode){
18681 Roo.log("disabling buttons");
18682 this.buttons.each( function(item){
18683 if(item.cmd != 'pencil'){
18689 Roo.log("enabling buttons");
18690 if(this.editorcore.initialized){
18691 this.buttons.each( function(item){
18697 Roo.log("calling toggole on editor");
18698 // tell the editor that it's been pressed..
18699 this.editor.toggleSourceEdit(sourceEditMode);
18709 * @class Roo.bootstrap.Table.AbstractSelectionModel
18710 * @extends Roo.util.Observable
18711 * Abstract base class for grid SelectionModels. It provides the interface that should be
18712 * implemented by descendant classes. This class should not be directly instantiated.
18715 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18716 this.locked = false;
18717 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18721 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
18722 /** @ignore Called by the grid automatically. Do not call directly. */
18723 init : function(grid){
18729 * Locks the selections.
18732 this.locked = true;
18736 * Unlocks the selections.
18738 unlock : function(){
18739 this.locked = false;
18743 * Returns true if the selections are locked.
18744 * @return {Boolean}
18746 isLocked : function(){
18747 return this.locked;
18751 * @extends Roo.bootstrap.Table.AbstractSelectionModel
18752 * @class Roo.bootstrap.Table.RowSelectionModel
18753 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18754 * It supports multiple selections and keyboard selection/navigation.
18756 * @param {Object} config
18759 Roo.bootstrap.Table.RowSelectionModel = function(config){
18760 Roo.apply(this, config);
18761 this.selections = new Roo.util.MixedCollection(false, function(o){
18766 this.lastActive = false;
18770 * @event selectionchange
18771 * Fires when the selection changes
18772 * @param {SelectionModel} this
18774 "selectionchange" : true,
18776 * @event afterselectionchange
18777 * Fires after the selection changes (eg. by key press or clicking)
18778 * @param {SelectionModel} this
18780 "afterselectionchange" : true,
18782 * @event beforerowselect
18783 * Fires when a row is selected being selected, return false to cancel.
18784 * @param {SelectionModel} this
18785 * @param {Number} rowIndex The selected index
18786 * @param {Boolean} keepExisting False if other selections will be cleared
18788 "beforerowselect" : true,
18791 * Fires when a row is selected.
18792 * @param {SelectionModel} this
18793 * @param {Number} rowIndex The selected index
18794 * @param {Roo.data.Record} r The record
18796 "rowselect" : true,
18798 * @event rowdeselect
18799 * Fires when a row is deselected.
18800 * @param {SelectionModel} this
18801 * @param {Number} rowIndex The selected index
18803 "rowdeselect" : true
18805 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18806 this.locked = false;
18809 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
18811 * @cfg {Boolean} singleSelect
18812 * True to allow selection of only one row at a time (defaults to false)
18814 singleSelect : false,
18817 initEvents : function(){
18819 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18820 this.grid.on("mousedown", this.handleMouseDown, this);
18821 }else{ // allow click to work like normal
18822 this.grid.on("rowclick", this.handleDragableRowClick, this);
18825 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18826 "up" : function(e){
18828 this.selectPrevious(e.shiftKey);
18829 }else if(this.last !== false && this.lastActive !== false){
18830 var last = this.last;
18831 this.selectRange(this.last, this.lastActive-1);
18832 this.grid.getView().focusRow(this.lastActive);
18833 if(last !== false){
18837 this.selectFirstRow();
18839 this.fireEvent("afterselectionchange", this);
18841 "down" : function(e){
18843 this.selectNext(e.shiftKey);
18844 }else if(this.last !== false && this.lastActive !== false){
18845 var last = this.last;
18846 this.selectRange(this.last, this.lastActive+1);
18847 this.grid.getView().focusRow(this.lastActive);
18848 if(last !== false){
18852 this.selectFirstRow();
18854 this.fireEvent("afterselectionchange", this);
18859 var view = this.grid.view;
18860 view.on("refresh", this.onRefresh, this);
18861 view.on("rowupdated", this.onRowUpdated, this);
18862 view.on("rowremoved", this.onRemove, this);
18866 onRefresh : function(){
18867 var ds = this.grid.dataSource, i, v = this.grid.view;
18868 var s = this.selections;
18869 s.each(function(r){
18870 if((i = ds.indexOfId(r.id)) != -1){
18879 onRemove : function(v, index, r){
18880 this.selections.remove(r);
18884 onRowUpdated : function(v, index, r){
18885 if(this.isSelected(r)){
18886 v.onRowSelect(index);
18892 * @param {Array} records The records to select
18893 * @param {Boolean} keepExisting (optional) True to keep existing selections
18895 selectRecords : function(records, keepExisting){
18897 this.clearSelections();
18899 var ds = this.grid.dataSource;
18900 for(var i = 0, len = records.length; i < len; i++){
18901 this.selectRow(ds.indexOf(records[i]), true);
18906 * Gets the number of selected rows.
18909 getCount : function(){
18910 return this.selections.length;
18914 * Selects the first row in the grid.
18916 selectFirstRow : function(){
18921 * Select the last row.
18922 * @param {Boolean} keepExisting (optional) True to keep existing selections
18924 selectLastRow : function(keepExisting){
18925 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18929 * Selects the row immediately following the last selected row.
18930 * @param {Boolean} keepExisting (optional) True to keep existing selections
18932 selectNext : function(keepExisting){
18933 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18934 this.selectRow(this.last+1, keepExisting);
18935 this.grid.getView().focusRow(this.last);
18940 * Selects the row that precedes the last selected row.
18941 * @param {Boolean} keepExisting (optional) True to keep existing selections
18943 selectPrevious : function(keepExisting){
18945 this.selectRow(this.last-1, keepExisting);
18946 this.grid.getView().focusRow(this.last);
18951 * Returns the selected records
18952 * @return {Array} Array of selected records
18954 getSelections : function(){
18955 return [].concat(this.selections.items);
18959 * Returns the first selected record.
18962 getSelected : function(){
18963 return this.selections.itemAt(0);
18968 * Clears all selections.
18970 clearSelections : function(fast){
18971 if(this.locked) return;
18973 var ds = this.grid.dataSource;
18974 var s = this.selections;
18975 s.each(function(r){
18976 this.deselectRow(ds.indexOfId(r.id));
18980 this.selections.clear();
18987 * Selects all rows.
18989 selectAll : function(){
18990 if(this.locked) return;
18991 this.selections.clear();
18992 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18993 this.selectRow(i, true);
18998 * Returns True if there is a selection.
18999 * @return {Boolean}
19001 hasSelection : function(){
19002 return this.selections.length > 0;
19006 * Returns True if the specified row is selected.
19007 * @param {Number/Record} record The record or index of the record to check
19008 * @return {Boolean}
19010 isSelected : function(index){
19011 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
19012 return (r && this.selections.key(r.id) ? true : false);
19016 * Returns True if the specified record id is selected.
19017 * @param {String} id The id of record to check
19018 * @return {Boolean}
19020 isIdSelected : function(id){
19021 return (this.selections.key(id) ? true : false);
19025 handleMouseDown : function(e, t){
19026 var view = this.grid.getView(), rowIndex;
19027 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
19030 if(e.shiftKey && this.last !== false){
19031 var last = this.last;
19032 this.selectRange(last, rowIndex, e.ctrlKey);
19033 this.last = last; // reset the last
19034 view.focusRow(rowIndex);
19036 var isSelected = this.isSelected(rowIndex);
19037 if(e.button !== 0 && isSelected){
19038 view.focusRow(rowIndex);
19039 }else if(e.ctrlKey && isSelected){
19040 this.deselectRow(rowIndex);
19041 }else if(!isSelected){
19042 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
19043 view.focusRow(rowIndex);
19046 this.fireEvent("afterselectionchange", this);
19049 handleDragableRowClick : function(grid, rowIndex, e)
19051 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
19052 this.selectRow(rowIndex, false);
19053 grid.view.focusRow(rowIndex);
19054 this.fireEvent("afterselectionchange", this);
19059 * Selects multiple rows.
19060 * @param {Array} rows Array of the indexes of the row to select
19061 * @param {Boolean} keepExisting (optional) True to keep existing selections
19063 selectRows : function(rows, keepExisting){
19065 this.clearSelections();
19067 for(var i = 0, len = rows.length; i < len; i++){
19068 this.selectRow(rows[i], true);
19073 * Selects a range of rows. All rows in between startRow and endRow are also selected.
19074 * @param {Number} startRow The index of the first row in the range
19075 * @param {Number} endRow The index of the last row in the range
19076 * @param {Boolean} keepExisting (optional) True to retain existing selections
19078 selectRange : function(startRow, endRow, keepExisting){
19079 if(this.locked) return;
19081 this.clearSelections();
19083 if(startRow <= endRow){
19084 for(var i = startRow; i <= endRow; i++){
19085 this.selectRow(i, true);
19088 for(var i = startRow; i >= endRow; i--){
19089 this.selectRow(i, true);
19095 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
19096 * @param {Number} startRow The index of the first row in the range
19097 * @param {Number} endRow The index of the last row in the range
19099 deselectRange : function(startRow, endRow, preventViewNotify){
19100 if(this.locked) return;
19101 for(var i = startRow; i <= endRow; i++){
19102 this.deselectRow(i, preventViewNotify);
19108 * @param {Number} row The index of the row to select
19109 * @param {Boolean} keepExisting (optional) True to keep existing selections
19111 selectRow : function(index, keepExisting, preventViewNotify){
19112 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
19113 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
19114 if(!keepExisting || this.singleSelect){
19115 this.clearSelections();
19117 var r = this.grid.dataSource.getAt(index);
19118 this.selections.add(r);
19119 this.last = this.lastActive = index;
19120 if(!preventViewNotify){
19121 this.grid.getView().onRowSelect(index);
19123 this.fireEvent("rowselect", this, index, r);
19124 this.fireEvent("selectionchange", this);
19130 * @param {Number} row The index of the row to deselect
19132 deselectRow : function(index, preventViewNotify){
19133 if(this.locked) return;
19134 if(this.last == index){
19137 if(this.lastActive == index){
19138 this.lastActive = false;
19140 var r = this.grid.dataSource.getAt(index);
19141 this.selections.remove(r);
19142 if(!preventViewNotify){
19143 this.grid.getView().onRowDeselect(index);
19145 this.fireEvent("rowdeselect", this, index);
19146 this.fireEvent("selectionchange", this);
19150 restoreLast : function(){
19152 this.last = this._last;
19157 acceptsNav : function(row, col, cm){
19158 return !cm.isHidden(col) && cm.isCellEditable(col, row);
19162 onEditorKey : function(field, e){
19163 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19168 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19170 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19172 }else if(k == e.ENTER && !e.ctrlKey){
19176 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19178 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19180 }else if(k == e.ESC){
19184 g.startEditing(newCell[0], newCell[1]);
19189 * Ext JS Library 1.1.1
19190 * Copyright(c) 2006-2007, Ext JS, LLC.
19192 * Originally Released Under LGPL - original licence link has changed is not relivant.
19195 * <script type="text/javascript">
19199 * @class Roo.bootstrap.PagingToolbar
19201 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19203 * Create a new PagingToolbar
19204 * @param {Object} config The config object
19206 Roo.bootstrap.PagingToolbar = function(config)
19208 // old args format still supported... - xtype is prefered..
19209 // created from xtype...
19210 var ds = config.dataSource;
19211 this.toolbarItems = [];
19212 if (config.items) {
19213 this.toolbarItems = config.items;
19214 // config.items = [];
19217 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19224 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19228 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19230 * @cfg {Roo.data.Store} dataSource
19231 * The underlying data store providing the paged data
19234 * @cfg {String/HTMLElement/Element} container
19235 * container The id or element that will contain the toolbar
19238 * @cfg {Boolean} displayInfo
19239 * True to display the displayMsg (defaults to false)
19242 * @cfg {Number} pageSize
19243 * The number of records to display per page (defaults to 20)
19247 * @cfg {String} displayMsg
19248 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19250 displayMsg : 'Displaying {0} - {1} of {2}',
19252 * @cfg {String} emptyMsg
19253 * The message to display when no records are found (defaults to "No data to display")
19255 emptyMsg : 'No data to display',
19257 * Customizable piece of the default paging text (defaults to "Page")
19260 beforePageText : "Page",
19262 * Customizable piece of the default paging text (defaults to "of %0")
19265 afterPageText : "of {0}",
19267 * Customizable piece of the default paging text (defaults to "First Page")
19270 firstText : "First Page",
19272 * Customizable piece of the default paging text (defaults to "Previous Page")
19275 prevText : "Previous Page",
19277 * Customizable piece of the default paging text (defaults to "Next Page")
19280 nextText : "Next Page",
19282 * Customizable piece of the default paging text (defaults to "Last Page")
19285 lastText : "Last Page",
19287 * Customizable piece of the default paging text (defaults to "Refresh")
19290 refreshText : "Refresh",
19294 onRender : function(ct, position)
19296 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19297 this.navgroup.parentId = this.id;
19298 this.navgroup.onRender(this.el, null);
19299 // add the buttons to the navgroup
19301 if(this.displayInfo){
19302 Roo.log(this.el.select('ul.navbar-nav',true).first());
19303 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19304 this.displayEl = this.el.select('.x-paging-info', true).first();
19305 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19306 // this.displayEl = navel.el.select('span',true).first();
19312 Roo.each(_this.buttons, function(e){
19313 Roo.factory(e).onRender(_this.el, null);
19317 Roo.each(_this.toolbarItems, function(e) {
19318 _this.navgroup.addItem(e);
19321 this.first = this.navgroup.addItem({
19322 tooltip: this.firstText,
19324 icon : 'fa fa-backward',
19326 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19329 this.prev = this.navgroup.addItem({
19330 tooltip: this.prevText,
19332 icon : 'fa fa-step-backward',
19334 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
19336 //this.addSeparator();
19339 var field = this.navgroup.addItem( {
19341 cls : 'x-paging-position',
19343 html : this.beforePageText +
19344 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19345 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
19348 this.field = field.el.select('input', true).first();
19349 this.field.on("keydown", this.onPagingKeydown, this);
19350 this.field.on("focus", function(){this.dom.select();});
19353 this.afterTextEl = field.el.select('.x-paging-after',true).first();
19354 //this.field.setHeight(18);
19355 //this.addSeparator();
19356 this.next = this.navgroup.addItem({
19357 tooltip: this.nextText,
19359 html : ' <i class="fa fa-step-forward">',
19361 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
19363 this.last = this.navgroup.addItem({
19364 tooltip: this.lastText,
19365 icon : 'fa fa-forward',
19368 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
19370 //this.addSeparator();
19371 this.loading = this.navgroup.addItem({
19372 tooltip: this.refreshText,
19373 icon: 'fa fa-refresh',
19375 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19381 updateInfo : function(){
19382 if(this.displayEl){
19383 var count = this.ds.getCount();
19384 var msg = count == 0 ?
19388 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
19390 this.displayEl.update(msg);
19395 onLoad : function(ds, r, o){
19396 this.cursor = o.params ? o.params.start : 0;
19397 var d = this.getPageData(),
19401 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19402 this.field.dom.value = ap;
19403 this.first.setDisabled(ap == 1);
19404 this.prev.setDisabled(ap == 1);
19405 this.next.setDisabled(ap == ps);
19406 this.last.setDisabled(ap == ps);
19407 this.loading.enable();
19412 getPageData : function(){
19413 var total = this.ds.getTotalCount();
19416 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19417 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19422 onLoadError : function(){
19423 this.loading.enable();
19427 onPagingKeydown : function(e){
19428 var k = e.getKey();
19429 var d = this.getPageData();
19431 var v = this.field.dom.value, pageNum;
19432 if(!v || isNaN(pageNum = parseInt(v, 10))){
19433 this.field.dom.value = d.activePage;
19436 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19437 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19440 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))
19442 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19443 this.field.dom.value = pageNum;
19444 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19447 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19449 var v = this.field.dom.value, pageNum;
19450 var increment = (e.shiftKey) ? 10 : 1;
19451 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19453 if(!v || isNaN(pageNum = parseInt(v, 10))) {
19454 this.field.dom.value = d.activePage;
19457 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19459 this.field.dom.value = parseInt(v, 10) + increment;
19460 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19461 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19468 beforeLoad : function(){
19470 this.loading.disable();
19475 onClick : function(which){
19482 ds.load({params:{start: 0, limit: this.pageSize}});
19485 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19488 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19491 var total = ds.getTotalCount();
19492 var extra = total % this.pageSize;
19493 var lastStart = extra ? (total - extra) : total-this.pageSize;
19494 ds.load({params:{start: lastStart, limit: this.pageSize}});
19497 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19503 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19504 * @param {Roo.data.Store} store The data store to unbind
19506 unbind : function(ds){
19507 ds.un("beforeload", this.beforeLoad, this);
19508 ds.un("load", this.onLoad, this);
19509 ds.un("loadexception", this.onLoadError, this);
19510 ds.un("remove", this.updateInfo, this);
19511 ds.un("add", this.updateInfo, this);
19512 this.ds = undefined;
19516 * Binds the paging toolbar to the specified {@link Roo.data.Store}
19517 * @param {Roo.data.Store} store The data store to bind
19519 bind : function(ds){
19520 ds.on("beforeload", this.beforeLoad, this);
19521 ds.on("load", this.onLoad, this);
19522 ds.on("loadexception", this.onLoadError, this);
19523 ds.on("remove", this.updateInfo, this);
19524 ds.on("add", this.updateInfo, this);
19535 * @class Roo.bootstrap.MessageBar
19536 * @extends Roo.bootstrap.Component
19537 * Bootstrap MessageBar class
19538 * @cfg {String} html contents of the MessageBar
19539 * @cfg {String} weight (info | success | warning | danger) default info
19540 * @cfg {String} beforeClass insert the bar before the given class
19541 * @cfg {Boolean} closable (true | false) default false
19542 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19545 * Create a new Element
19546 * @param {Object} config The config object
19549 Roo.bootstrap.MessageBar = function(config){
19550 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19553 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
19559 beforeClass: 'bootstrap-sticky-wrap',
19561 getAutoCreate : function(){
19565 cls: 'alert alert-dismissable alert-' + this.weight,
19570 html: this.html || ''
19576 cfg.cls += ' alert-messages-fixed';
19590 onRender : function(ct, position)
19592 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19595 var cfg = Roo.apply({}, this.getAutoCreate());
19599 cfg.cls += ' ' + this.cls;
19602 cfg.style = this.style;
19604 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19606 this.el.setVisibilityMode(Roo.Element.DISPLAY);
19609 this.el.select('>button.close').on('click', this.hide, this);
19615 if (!this.rendered) {
19621 this.fireEvent('show', this);
19627 if (!this.rendered) {
19633 this.fireEvent('hide', this);
19636 update : function()
19638 // var e = this.el.dom.firstChild;
19640 // if(this.closable){
19641 // e = e.nextSibling;
19644 // e.data = this.html || '';
19646 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19662 * @class Roo.bootstrap.Graph
19663 * @extends Roo.bootstrap.Component
19664 * Bootstrap Graph class
19668 @cfg {String} graphtype bar | vbar | pie
19669 @cfg {number} g_x coodinator | centre x (pie)
19670 @cfg {number} g_y coodinator | centre y (pie)
19671 @cfg {number} g_r radius (pie)
19672 @cfg {number} g_height height of the chart (respected by all elements in the set)
19673 @cfg {number} g_width width of the chart (respected by all elements in the set)
19674 @cfg {Object} title The title of the chart
19677 -opts (object) options for the chart
19679 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19680 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19682 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.
19683 o stacked (boolean) whether or not to tread values as in a stacked bar chart
19685 o stretch (boolean)
19687 -opts (object) options for the pie
19690 o startAngle (number)
19691 o endAngle (number)
19695 * Create a new Input
19696 * @param {Object} config The config object
19699 Roo.bootstrap.Graph = function(config){
19700 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19706 * The img click event for the img.
19707 * @param {Roo.EventObject} e
19713 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
19724 //g_colors: this.colors,
19731 getAutoCreate : function(){
19742 onRender : function(ct,position){
19743 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19744 this.raphael = Raphael(this.el.dom);
19746 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19747 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19748 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19749 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19751 r.text(160, 10, "Single Series Chart").attr(txtattr);
19752 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19753 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19754 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19756 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19757 r.barchart(330, 10, 300, 220, data1);
19758 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19759 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19762 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19763 // r.barchart(30, 30, 560, 250, xdata, {
19764 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19765 // axis : "0 0 1 1",
19766 // axisxlabels : xdata
19767 // //yvalues : cols,
19770 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19772 // this.load(null,xdata,{
19773 // axis : "0 0 1 1",
19774 // axisxlabels : xdata
19779 load : function(graphtype,xdata,opts){
19780 this.raphael.clear();
19782 graphtype = this.graphtype;
19787 var r = this.raphael,
19788 fin = function () {
19789 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19791 fout = function () {
19792 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19794 pfin = function() {
19795 this.sector.stop();
19796 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19799 this.label[0].stop();
19800 this.label[0].attr({ r: 7.5 });
19801 this.label[1].attr({ "font-weight": 800 });
19804 pfout = function() {
19805 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19808 this.label[0].animate({ r: 5 }, 500, "bounce");
19809 this.label[1].attr({ "font-weight": 400 });
19815 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19818 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19821 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
19822 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19824 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19831 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19836 setTitle: function(o)
19841 initEvents: function() {
19844 this.el.on('click', this.onClick, this);
19848 onClick : function(e)
19850 Roo.log('img onclick');
19851 this.fireEvent('click', this, e);
19863 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19866 * @class Roo.bootstrap.dash.NumberBox
19867 * @extends Roo.bootstrap.Component
19868 * Bootstrap NumberBox class
19869 * @cfg {String} headline Box headline
19870 * @cfg {String} content Box content
19871 * @cfg {String} icon Box icon
19872 * @cfg {String} footer Footer text
19873 * @cfg {String} fhref Footer href
19876 * Create a new NumberBox
19877 * @param {Object} config The config object
19881 Roo.bootstrap.dash.NumberBox = function(config){
19882 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19886 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
19895 getAutoCreate : function(){
19899 cls : 'small-box ',
19907 cls : 'roo-headline',
19908 html : this.headline
19912 cls : 'roo-content',
19913 html : this.content
19927 cls : 'ion ' + this.icon
19936 cls : 'small-box-footer',
19937 href : this.fhref || '#',
19941 cfg.cn.push(footer);
19948 onRender : function(ct,position){
19949 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19956 setHeadline: function (value)
19958 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19961 setFooter: function (value, href)
19963 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19966 this.el.select('a.small-box-footer',true).first().attr('href', href);
19971 setContent: function (value)
19973 this.el.select('.roo-content',true).first().dom.innerHTML = value;
19976 initEvents: function()
19990 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19993 * @class Roo.bootstrap.dash.TabBox
19994 * @extends Roo.bootstrap.Component
19995 * Bootstrap TabBox class
19996 * @cfg {String} title Title of the TabBox
19997 * @cfg {String} icon Icon of the TabBox
19998 * @cfg {Boolean} showtabs (true|false) show the tabs default true
19999 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
20002 * Create a new TabBox
20003 * @param {Object} config The config object
20007 Roo.bootstrap.dash.TabBox = function(config){
20008 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
20013 * When a pane is added
20014 * @param {Roo.bootstrap.dash.TabPane} pane
20018 * @event activatepane
20019 * When a pane is activated
20020 * @param {Roo.bootstrap.dash.TabPane} pane
20022 "activatepane" : true
20030 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
20035 tabScrollable : false,
20037 getChildContainer : function()
20039 return this.el.select('.tab-content', true).first();
20042 getAutoCreate : function(){
20046 cls: 'pull-left header',
20054 cls: 'fa ' + this.icon
20060 cls: 'nav nav-tabs pull-right',
20066 if(this.tabScrollable){
20073 cls: 'nav nav-tabs pull-right',
20084 cls: 'nav-tabs-custom',
20089 cls: 'tab-content no-padding',
20097 initEvents : function()
20099 //Roo.log('add add pane handler');
20100 this.on('addpane', this.onAddPane, this);
20103 * Updates the box title
20104 * @param {String} html to set the title to.
20106 setTitle : function(value)
20108 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
20110 onAddPane : function(pane)
20112 this.panes.push(pane);
20113 //Roo.log('addpane');
20115 // tabs are rendere left to right..
20116 if(!this.showtabs){
20120 var ctr = this.el.select('.nav-tabs', true).first();
20123 var existing = ctr.select('.nav-tab',true);
20124 var qty = existing.getCount();;
20127 var tab = ctr.createChild({
20129 cls : 'nav-tab' + (qty ? '' : ' active'),
20137 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
20140 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
20142 pane.el.addClass('active');
20147 onTabClick : function(ev,un,ob,pane)
20149 //Roo.log('tab - prev default');
20150 ev.preventDefault();
20153 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20154 pane.tab.addClass('active');
20155 //Roo.log(pane.title);
20156 this.getChildContainer().select('.tab-pane',true).removeClass('active');
20157 // technically we should have a deactivate event.. but maybe add later.
20158 // and it should not de-activate the selected tab...
20159 this.fireEvent('activatepane', pane);
20160 pane.el.addClass('active');
20161 pane.fireEvent('activate');
20166 getActivePane : function()
20169 Roo.each(this.panes, function(p) {
20170 if(p.el.hasClass('active')){
20191 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20193 * @class Roo.bootstrap.TabPane
20194 * @extends Roo.bootstrap.Component
20195 * Bootstrap TabPane class
20196 * @cfg {Boolean} active (false | true) Default false
20197 * @cfg {String} title title of panel
20201 * Create a new TabPane
20202 * @param {Object} config The config object
20205 Roo.bootstrap.dash.TabPane = function(config){
20206 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20212 * When a pane is activated
20213 * @param {Roo.bootstrap.dash.TabPane} pane
20220 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
20225 // the tabBox that this is attached to.
20228 getAutoCreate : function()
20236 cfg.cls += ' active';
20241 initEvents : function()
20243 //Roo.log('trigger add pane handler');
20244 this.parent().fireEvent('addpane', this)
20248 * Updates the tab title
20249 * @param {String} html to set the title to.
20251 setTitle: function(str)
20257 this.tab.select('a', true).first().dom.innerHTML = str;
20274 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20277 * @class Roo.bootstrap.menu.Menu
20278 * @extends Roo.bootstrap.Component
20279 * Bootstrap Menu class - container for Menu
20280 * @cfg {String} html Text of the menu
20281 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20282 * @cfg {String} icon Font awesome icon
20283 * @cfg {String} pos Menu align to (top | bottom) default bottom
20287 * Create a new Menu
20288 * @param {Object} config The config object
20292 Roo.bootstrap.menu.Menu = function(config){
20293 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20297 * @event beforeshow
20298 * Fires before this menu is displayed
20299 * @param {Roo.bootstrap.menu.Menu} this
20303 * @event beforehide
20304 * Fires before this menu is hidden
20305 * @param {Roo.bootstrap.menu.Menu} this
20310 * Fires after this menu is displayed
20311 * @param {Roo.bootstrap.menu.Menu} this
20316 * Fires after this menu is hidden
20317 * @param {Roo.bootstrap.menu.Menu} this
20322 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20323 * @param {Roo.bootstrap.menu.Menu} this
20324 * @param {Roo.EventObject} e
20331 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
20335 weight : 'default',
20340 getChildContainer : function() {
20341 if(this.isSubMenu){
20345 return this.el.select('ul.dropdown-menu', true).first();
20348 getAutoCreate : function()
20353 cls : 'roo-menu-text',
20361 cls : 'fa ' + this.icon
20372 cls : 'dropdown-button btn btn-' + this.weight,
20377 cls : 'dropdown-toggle btn btn-' + this.weight,
20387 cls : 'dropdown-menu'
20393 if(this.pos == 'top'){
20394 cfg.cls += ' dropup';
20397 if(this.isSubMenu){
20400 cls : 'dropdown-menu'
20407 onRender : function(ct, position)
20409 this.isSubMenu = ct.hasClass('dropdown-submenu');
20411 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20414 initEvents : function()
20416 if(this.isSubMenu){
20420 this.hidden = true;
20422 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20423 this.triggerEl.on('click', this.onTriggerPress, this);
20425 this.buttonEl = this.el.select('button.dropdown-button', true).first();
20426 this.buttonEl.on('click', this.onClick, this);
20432 if(this.isSubMenu){
20436 return this.el.select('ul.dropdown-menu', true).first();
20439 onClick : function(e)
20441 this.fireEvent("click", this, e);
20444 onTriggerPress : function(e)
20446 if (this.isVisible()) {
20453 isVisible : function(){
20454 return !this.hidden;
20459 this.fireEvent("beforeshow", this);
20461 this.hidden = false;
20462 this.el.addClass('open');
20464 Roo.get(document).on("mouseup", this.onMouseUp, this);
20466 this.fireEvent("show", this);
20473 this.fireEvent("beforehide", this);
20475 this.hidden = true;
20476 this.el.removeClass('open');
20478 Roo.get(document).un("mouseup", this.onMouseUp);
20480 this.fireEvent("hide", this);
20483 onMouseUp : function()
20497 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20500 * @class Roo.bootstrap.menu.Item
20501 * @extends Roo.bootstrap.Component
20502 * Bootstrap MenuItem class
20503 * @cfg {Boolean} submenu (true | false) default false
20504 * @cfg {String} html text of the item
20505 * @cfg {String} href the link
20506 * @cfg {Boolean} disable (true | false) default false
20507 * @cfg {Boolean} preventDefault (true | false) default true
20508 * @cfg {String} icon Font awesome icon
20509 * @cfg {String} pos Submenu align to (left | right) default right
20513 * Create a new Item
20514 * @param {Object} config The config object
20518 Roo.bootstrap.menu.Item = function(config){
20519 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20523 * Fires when the mouse is hovering over this menu
20524 * @param {Roo.bootstrap.menu.Item} this
20525 * @param {Roo.EventObject} e
20530 * Fires when the mouse exits this menu
20531 * @param {Roo.bootstrap.menu.Item} this
20532 * @param {Roo.EventObject} e
20538 * The raw click event for the entire grid.
20539 * @param {Roo.EventObject} e
20545 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
20550 preventDefault: true,
20555 getAutoCreate : function()
20560 cls : 'roo-menu-item-text',
20568 cls : 'fa ' + this.icon
20577 href : this.href || '#',
20584 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20588 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20590 if(this.pos == 'left'){
20591 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20598 initEvents : function()
20600 this.el.on('mouseover', this.onMouseOver, this);
20601 this.el.on('mouseout', this.onMouseOut, this);
20603 this.el.select('a', true).first().on('click', this.onClick, this);
20607 onClick : function(e)
20609 if(this.preventDefault){
20610 e.preventDefault();
20613 this.fireEvent("click", this, e);
20616 onMouseOver : function(e)
20618 if(this.submenu && this.pos == 'left'){
20619 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20622 this.fireEvent("mouseover", this, e);
20625 onMouseOut : function(e)
20627 this.fireEvent("mouseout", this, e);
20639 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20642 * @class Roo.bootstrap.menu.Separator
20643 * @extends Roo.bootstrap.Component
20644 * Bootstrap Separator class
20647 * Create a new Separator
20648 * @param {Object} config The config object
20652 Roo.bootstrap.menu.Separator = function(config){
20653 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20656 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
20658 getAutoCreate : function(){
20679 * @class Roo.bootstrap.Tooltip
20680 * Bootstrap Tooltip class
20681 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
20682 * to determine which dom element triggers the tooltip.
20684 * It needs to add support for additional attributes like tooltip-position
20687 * Create a new Toolti
20688 * @param {Object} config The config object
20691 Roo.bootstrap.Tooltip = function(config){
20692 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
20695 Roo.apply(Roo.bootstrap.Tooltip, {
20697 * @function init initialize tooltip monitoring.
20701 currentTip : false,
20702 currentRegion : false,
20708 Roo.get(document).on('mouseover', this.enter ,this);
20709 Roo.get(document).on('mouseout', this.leave, this);
20712 this.currentTip = new Roo.bootstrap.Tooltip();
20715 enter : function(ev)
20717 var dom = ev.getTarget();
20718 //Roo.log(['enter',dom]);
20719 var el = Roo.fly(dom);
20720 if (this.currentEl) {
20722 //Roo.log(this.currentEl);
20723 //Roo.log(this.currentEl.contains(dom));
20724 if (this.currentEl == el) {
20727 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
20735 if (this.currentTip.el) {
20736 this.currentTip.el.hide(); // force hiding...
20739 if (!el.attr('tooltip')) { // parents who have tip?
20742 this.currentEl = el;
20743 this.currentTip.bind(el);
20744 this.currentRegion = Roo.lib.Region.getRegion(dom);
20745 this.currentTip.enter();
20748 leave : function(ev)
20750 var dom = ev.getTarget();
20751 //Roo.log(['leave',dom]);
20752 if (!this.currentEl) {
20757 if (dom != this.currentEl.dom) {
20760 var xy = ev.getXY();
20761 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
20764 // only activate leave if mouse cursor is outside... bounding box..
20769 if (this.currentTip) {
20770 this.currentTip.leave();
20772 //Roo.log('clear currentEl');
20773 this.currentEl = false;
20778 'left' : ['r-l', [-2,0], 'right'],
20779 'right' : ['l-r', [2,0], 'left'],
20780 'bottom' : ['t-b', [0,2], 'top'],
20781 'top' : [ 'b-t', [0,-2], 'bottom']
20787 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
20792 delay : null, // can be { show : 300 , hide: 500}
20796 hoverState : null, //???
20798 placement : 'bottom',
20800 getAutoCreate : function(){
20807 cls : 'tooltip-arrow'
20810 cls : 'tooltip-inner'
20817 bind : function(el)
20823 enter : function () {
20825 if (this.timeout != null) {
20826 clearTimeout(this.timeout);
20829 this.hoverState = 'in'
20830 //Roo.log("enter - show");
20831 if (!this.delay || !this.delay.show) {
20836 this.timeout = setTimeout(function () {
20837 if (_t.hoverState == 'in') {
20840 }, this.delay.show);
20844 clearTimeout(this.timeout);
20846 this.hoverState = 'out'
20847 if (!this.delay || !this.delay.hide) {
20853 this.timeout = setTimeout(function () {
20854 //Roo.log("leave - timeout");
20856 if (_t.hoverState == 'out') {
20858 Roo.bootstrap.Tooltip.currentEl = false;
20866 this.render(document.body);
20869 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
20870 this.el.select('.tooltip-inner',true).first().dom.innerHTML = this.bindEl.attr('tooltip');
20872 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
20874 var placement = typeof this.placement == 'function' ?
20875 this.placement.call(this, this.el, on_el) :
20878 var autoToken = /\s?auto?\s?/i;
20879 var autoPlace = autoToken.test(placement);
20881 placement = placement.replace(autoToken, '') || 'top';
20885 //this.el.setXY([0,0]);
20887 //this.el.dom.style.display='block';
20888 this.el.addClass(placement);
20890 //this.el.appendTo(on_el);
20892 var p = this.getPosition();
20893 var box = this.el.getBox();
20898 var align = Roo.bootstrap.Tooltip.alignment[placement]
20899 this.el.alignTo(this.bindEl, align[0],align[1]);
20900 //var arrow = this.el.select('.arrow',true).first();
20901 //arrow.set(align[2],
20903 this.el.addClass('in fade');
20904 this.hoverState = null;
20906 if (this.el.hasClass('fade')) {
20917 //this.el.setXY([0,0]);
20918 this.el.removeClass('in');
20934 * @class Roo.bootstrap.LocationPicker
20935 * @extends Roo.bootstrap.Component
20936 * Bootstrap LocationPicker class
20937 * @cfg {Number} latitude Position when init default 0
20938 * @cfg {Number} longitude Position when init default 0
20939 * @cfg {Number} zoom default 15
20940 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
20941 * @cfg {Boolean} mapTypeControl default false
20942 * @cfg {Boolean} disableDoubleClickZoom default false
20943 * @cfg {Boolean} scrollwheel default true
20944 * @cfg {Boolean} streetViewControl default false
20945 * @cfg {Number} radius default 0
20946 * @cfg {String} locationName
20947 * @cfg {Boolean} draggable default true
20948 * @cfg {Boolean} enableAutocomplete default false
20949 * @cfg {Boolean} enableReverseGeocode default true
20950 * @cfg {String} markerTitle
20953 * Create a new LocationPicker
20954 * @param {Object} config The config object
20958 Roo.bootstrap.LocationPicker = function(config){
20960 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
20965 * Fires when the picker initialized.
20966 * @param {Roo.bootstrap.LocationPicker} this
20967 * @param {Google Location} location
20971 * @event positionchanged
20972 * Fires when the picker position changed.
20973 * @param {Roo.bootstrap.LocationPicker} this
20974 * @param {Google Location} location
20976 positionchanged : true,
20979 * Fires when the map resize.
20980 * @param {Roo.bootstrap.LocationPicker} this
20985 * Fires when the map show.
20986 * @param {Roo.bootstrap.LocationPicker} this
20991 * Fires when the map hide.
20992 * @param {Roo.bootstrap.LocationPicker} this
20999 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
21001 gMapContext: false,
21007 mapTypeControl: false,
21008 disableDoubleClickZoom: false,
21010 streetViewControl: false,
21014 enableAutocomplete: false,
21015 enableReverseGeocode: true,
21018 getAutoCreate: function()
21023 cls: 'roo-location-picker'
21029 initEvents: function(ct, position)
21031 if(!this.mapTypeId){
21032 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
21035 if(!this.el.getWidth() || this.isApplied()){
21039 this.el.setVisibilityMode(Roo.Element.DISPLAY);
21044 initial: function()
21046 this.gMapContext = this.GMapContext();
21050 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
21051 _this.setPosition(_this.gMapContext.marker.position);
21054 this.setPosition(this.gMapContext.location);
21056 this.fireEvent('initial', this, this.gMapContext.location);
21059 isApplied: function()
21061 return this.getGmapContext() == false ? false : true;
21064 getGmapContext: function()
21066 return this.gMapContext
21069 GMapContext: function()
21071 var _map = new google.maps.Map(this.el.dom, this);
21072 var _marker = new google.maps.Marker({
21073 position: new google.maps.LatLng(this.latitude, this.longitude),
21075 title: this.markerTitle,
21076 draggable: this.draggable
21083 location: _marker.position,
21084 radius: this.radius,
21085 locationName: this.locationName,
21086 addressComponents: {
21087 formatted_address: null,
21088 addressLine1: null,
21089 addressLine2: null,
21091 streetNumber: null,
21095 stateOrProvince: null
21098 domContainer: this.el.dom,
21099 geodecoder: new google.maps.Geocoder()
21103 drawCircle: function(center, radius, options)
21105 if (this.gMapContext.circle != null) {
21106 this.gMapContext.circle.setMap(null);
21110 options = Roo.apply({}, options, {
21111 strokeColor: "#0000FF",
21112 strokeOpacity: .35,
21114 fillColor: "#0000FF",
21118 options.map = this.gMapContext.map;
21119 options.radius = radius;
21120 options.center = center;
21121 this.gMapContext.circle = new google.maps.Circle(options);
21122 return this.gMapContext.circle;
21128 setPosition: function(location)
21130 this.gMapContext.location = location;
21131 this.gMapContext.marker.setPosition(location);
21132 this.gMapContext.map.panTo(location);
21133 this.drawCircle(location, this.gMapContext.radius, {});
21137 if (this.gMapContext.settings.enableReverseGeocode) {
21138 this.gMapContext.geodecoder.geocode({
21139 latLng: this.gMapContext.location
21140 }, function(results, status) {
21142 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
21143 _this.gMapContext.locationName = results[0].formatted_address;
21144 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
21146 _this.fireEvent('positionchanged', this, location);
21153 this.fireEvent('positionchanged', this, location);
21158 google.maps.event.trigger(this.gMapContext.map, "resize");
21160 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
21162 this.fireEvent('resize', this);
21165 setPositionByLatLng: function(latitude, longitude)
21167 this.setPosition(new google.maps.LatLng(latitude, longitude));
21170 getCurrentPosition: function()
21173 latitude: this.gMapContext.location.lat(),
21174 longitude: this.gMapContext.location.lng()
21178 getAddressName: function()
21180 return this.gMapContext.locationName;
21183 getAddressComponents: function()
21185 return this.gMapContext.addressComponents;
21188 address_component_from_google_geocode: function(address_components)
21192 for (var i = 0; i < address_components.length; i++) {
21193 var component = address_components[i];
21194 if (component.types.indexOf("postal_code") >= 0) {
21195 result.postalCode = component.short_name;
21196 } else if (component.types.indexOf("street_number") >= 0) {
21197 result.streetNumber = component.short_name;
21198 } else if (component.types.indexOf("route") >= 0) {
21199 result.streetName = component.short_name;
21200 } else if (component.types.indexOf("neighborhood") >= 0) {
21201 result.city = component.short_name;
21202 } else if (component.types.indexOf("locality") >= 0) {
21203 result.city = component.short_name;
21204 } else if (component.types.indexOf("sublocality") >= 0) {
21205 result.district = component.short_name;
21206 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
21207 result.stateOrProvince = component.short_name;
21208 } else if (component.types.indexOf("country") >= 0) {
21209 result.country = component.short_name;
21213 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
21214 result.addressLine2 = "";
21218 setZoomLevel: function(zoom)
21220 this.gMapContext.map.setZoom(zoom);
21233 this.fireEvent('show', this);
21244 this.fireEvent('hide', this);