4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
21 * Do not use directly - it does not do anything..
22 * @param {Object} config The config object
27 Roo.bootstrap.Component = function(config){
28 Roo.bootstrap.Component.superclass.constructor.call(this, config);
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
34 allowDomMove : false, // to stop relocations in parent onRender...
42 initEvents : function() { },
48 can_build_overlaid : true,
55 // returns the parent component..
56 return Roo.ComponentMgr.get(this.parentId)
62 onRender : function(ct, position)
64 // Roo.log("Call onRender: " + this.xtype);
66 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
69 if (this.el.attr('xtype')) {
70 this.el.attr('xtypex', this.el.attr('xtype'));
71 this.el.dom.removeAttribute('xtype');
81 var cfg = Roo.apply({}, this.getAutoCreate());
84 // fill in the extra attributes
85 if (this.xattr && typeof(this.xattr) =='object') {
86 for (var i in this.xattr) {
87 cfg[i] = this.xattr[i];
92 cfg.dataId = this.dataId;
96 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
99 if (this.style) { // fixme needs to support more complex style data.
100 cfg.style = this.style;
104 cfg.name = this.name;
107 this.el = ct.createChild(cfg, position);
109 if(this.tabIndex !== undefined){
110 this.el.dom.setAttribute('tabIndex', this.tabIndex);
117 getChildContainer : function()
123 addxtype : function(tree,cntr)
127 cn = Roo.factory(tree);
129 cn.parentType = this.xtype; //??
130 cn.parentId = this.id;
132 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
134 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
136 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
138 var build_from_html = Roo.XComponent.build_from_html;
140 var is_body = (tree.xtype == 'Body') ;
142 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
144 var self_cntr_el = Roo.get(this[cntr](false));
146 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148 return this.addxtypeChild(tree,cntr);
151 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
154 return this.addxtypeChild(Roo.apply({}, tree),cntr);
157 Roo.log('skipping render');
165 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
171 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
175 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
180 addxtypeChild : function (tree, cntr)
182 Roo.log('addxtypeChild:' + cntr);
184 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
187 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188 (typeof(tree['flexy:foreach']) != 'undefined');
193 // render the element if it's not BODY.
194 if (tree.xtype != 'Body') {
196 cn = Roo.factory(tree);
198 cn.parentType = this.xtype; //??
199 cn.parentId = this.id;
201 var build_from_html = Roo.XComponent.build_from_html;
204 // does the container contain child eleemnts with 'xtype' attributes.
205 // that match this xtype..
206 // note - when we render we create these as well..
207 // so we should check to see if body has xtype set.
208 if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
210 var self_cntr_el = Roo.get(this[cntr](false));
211 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
213 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
220 //echild.dom.removeAttribute('xtype');
222 Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
229 // if object has flexy:if - then it may or may not be rendered.
230 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
231 // skip a flexy if element.
232 Roo.log('skipping render');
235 // actually if flexy:foreach is found, we really want to create
236 // multiple copies here...
238 //Roo.log(this[cntr]());
239 cn.render(this[cntr](true));
241 // then add the element..
249 if (typeof (tree.menu) != 'undefined') {
250 tree.menu.parentType = cn.xtype;
251 tree.menu.triggerEl = cn.el;
252 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
256 if (!tree.items || !tree.items.length) {
260 var items = tree.items;
263 //Roo.log(items.length);
265 for(var i =0;i < items.length;i++) {
266 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
287 * @class Roo.bootstrap.Body
288 * @extends Roo.bootstrap.Component
289 * Bootstrap Body class
293 * @param {Object} config The config object
296 Roo.bootstrap.Body = function(config){
297 Roo.bootstrap.Body.superclass.constructor.call(this, config);
298 this.el = Roo.get(document.body);
299 if (this.cls && this.cls.length) {
300 Roo.get(document.body).addClass(this.cls);
304 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
309 onRender : function(ct, position)
311 /* Roo.log("Roo.bootstrap.Body - onRender");
312 if (this.cls && this.cls.length) {
313 Roo.get(document.body).addClass(this.cls);
333 * @class Roo.bootstrap.ButtonGroup
334 * @extends Roo.bootstrap.Component
335 * Bootstrap ButtonGroup class
336 * @cfg {String} size lg | sm | xs (default empty normal)
337 * @cfg {String} align vertical | justified (default none)
338 * @cfg {String} direction up | down (default down)
339 * @cfg {Boolean} toolbar false | true
340 * @cfg {Boolean} btn true | false
345 * @param {Object} config The config object
348 Roo.bootstrap.ButtonGroup = function(config){
349 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
352 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
360 getAutoCreate : function(){
366 cfg.html = this.html || cfg.html;
377 if (['vertical','justified'].indexOf(this.align)!==-1) {
378 cfg.cls = 'btn-group-' + this.align;
380 if (this.align == 'justified') {
381 console.log(this.items);
385 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
386 cfg.cls += ' btn-group-' + this.size;
389 if (this.direction == 'up') {
390 cfg.cls += ' dropup' ;
406 * @class Roo.bootstrap.Button
407 * @extends Roo.bootstrap.Component
408 * Bootstrap Button class
409 * @cfg {String} html The button content
410 * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
411 * @cfg {String} size empty | lg | sm | xs
412 * @cfg {String} tag empty | a | input | submit
413 * @cfg {String} href empty or href
414 * @cfg {Boolean} disabled false | true
415 * @cfg {Boolean} isClose false | true
416 * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
417 * @cfg {String} badge text for badge
418 * @cfg {String} theme default (or empty) | glow
419 * @cfg {Boolean} inverse false | true
420 * @cfg {Boolean} toggle false | true
421 * @cfg {String} ontext text for on toggle state
422 * @cfg {String} offtext text for off toggle state
423 * @cfg {Boolean} defaulton true | false
424 * @cfg {Boolean} preventDefault (true | false) default true
425 * @cfg {Boolean} removeClass true | false remove the standard class..
426 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
429 * Create a new button
430 * @param {Object} config The config object
434 Roo.bootstrap.Button = function(config){
435 Roo.bootstrap.Button.superclass.constructor.call(this, config);
440 * When a butotn is pressed
441 * @param {Roo.EventObject} e
446 * After the button has been toggles
447 * @param {Roo.EventObject} e
448 * @param {boolean} pressed (also available as button.pressed)
454 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
472 preventDefault: true,
481 getAutoCreate : function(){
489 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
490 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
495 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
497 if (this.toggle == true) {
500 cls: 'slider-frame roo-button',
505 'data-off-text':'OFF',
506 cls: 'slider-button',
512 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
513 cfg.cls += ' '+this.weight;
522 cfg["aria-hidden"] = true;
524 cfg.html = "×";
530 if (this.theme==='default') {
531 cfg.cls = 'btn roo-button';
533 //if (this.parentType != 'Navbar') {
534 this.weight = this.weight.length ? this.weight : 'default';
536 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
538 cfg.cls += ' btn-' + this.weight;
540 } else if (this.theme==='glow') {
543 cfg.cls = 'btn-glow roo-button';
545 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
547 cfg.cls += ' ' + this.weight;
553 this.cls += ' inverse';
558 cfg.cls += ' active';
562 cfg.disabled = 'disabled';
566 Roo.log('changing to ul' );
568 this.glyphicon = 'caret';
571 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
573 //gsRoo.log(this.parentType);
574 if (this.parentType === 'Navbar' && !this.parent().bar) {
575 Roo.log('changing to li?');
584 href : this.href || '#'
587 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
588 cfg.cls += ' dropdown';
595 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
597 if (this.glyphicon) {
598 cfg.html = ' ' + cfg.html;
603 cls: 'glyphicon glyphicon-' + this.glyphicon
613 // cfg.cls='btn roo-button';
617 var value = cfg.html;
622 cls: 'glyphicon glyphicon-' + this.glyphicon,
641 cfg.cls += ' dropdown';
642 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
645 if (cfg.tag !== 'a' && this.href !== '') {
646 throw "Tag must be a to set href.";
647 } else if (this.href.length > 0) {
648 cfg.href = this.href;
651 if(this.removeClass){
656 cfg.target = this.target;
661 initEvents: function() {
662 // Roo.log('init events?');
663 // Roo.log(this.el.dom);
666 if (typeof (this.menu) != 'undefined') {
667 this.menu.parentType = this.xtype;
668 this.menu.triggerEl = this.el;
669 this.addxtype(Roo.apply({}, this.menu));
673 if (this.el.hasClass('roo-button')) {
674 this.el.on('click', this.onClick, this);
676 this.el.select('.roo-button').on('click', this.onClick, this);
679 if(this.removeClass){
680 this.el.on('click', this.onClick, this);
683 this.el.enableDisplayMode();
686 onClick : function(e)
692 Roo.log('button on click ');
693 if(this.preventDefault){
696 if (this.pressed === true || this.pressed === false) {
697 this.pressed = !this.pressed;
698 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
699 this.fireEvent('toggle', this, e, this.pressed);
703 this.fireEvent('click', this, e);
707 * Enables this button
711 this.disabled = false;
712 this.el.removeClass('disabled');
716 * Disable this button
720 this.disabled = true;
721 this.el.addClass('disabled');
724 * sets the active state on/off,
725 * @param {Boolean} state (optional) Force a particular state
727 setActive : function(v) {
729 this.el[v ? 'addClass' : 'removeClass']('active');
732 * toggles the current active state
734 toggleActive : function()
736 var active = this.el.hasClass('active');
737 this.setActive(!active);
741 setText : function(str)
743 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
747 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
770 * @class Roo.bootstrap.Column
771 * @extends Roo.bootstrap.Component
772 * Bootstrap Column class
773 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
774 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
775 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
776 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
777 * @cfg {Boolean} hidden (true|false) hide the element
778 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
779 * @cfg {String} fa (ban|check|...) font awesome icon
780 * @cfg {String} icon (info-sign|check|...) glyphicon name
782 * @cfg {String} html content of column.
785 * Create a new Column
786 * @param {Object} config The config object
789 Roo.bootstrap.Column = function(config){
790 Roo.bootstrap.Column.superclass.constructor.call(this, config);
793 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
806 getAutoCreate : function(){
807 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
815 ['xs','sm','md','lg'].map(function(size){
816 Roo.log( size + ':' + settings[size]);
817 if (settings[size] === false) {
820 Roo.log(settings[size]);
821 if (!settings[size]) { // 0 = hidden
822 cfg.cls += ' hidden-' + size;
825 cfg.cls += ' col-' + size + '-' + settings[size];
830 cfg.cls += ' hidden';
833 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
834 cfg.cls +=' alert alert-' + this.alert;
838 if (this.html.length) {
839 cfg.html = this.html;
842 cfg.html = '<i class="fa fa-'+this.fa + '"></i>' + (cfg.html || '');
845 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
864 * @class Roo.bootstrap.Container
865 * @extends Roo.bootstrap.Component
866 * Bootstrap Container class
867 * @cfg {Boolean} jumbotron is it a jumbotron element
868 * @cfg {String} html content of element
869 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
870 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
871 * @cfg {String} header content of header (for panel)
872 * @cfg {String} footer content of footer (for panel)
873 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
874 * @cfg {String} tag (header|aside|section) type of HTML tag.
875 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
876 * @cfg {String} fa (ban|check|...) font awesome icon
877 * @cfg {String} icon (info-sign|check|...) glyphicon name
878 * @cfg {Boolean} hidden (true|false) hide the element
882 * Create a new Container
883 * @param {Object} config The config object
886 Roo.bootstrap.Container = function(config){
887 Roo.bootstrap.Container.superclass.constructor.call(this, config);
890 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
904 getChildContainer : function() {
910 if (this.panel.length) {
911 return this.el.select('.panel-body',true).first();
918 getAutoCreate : function(){
921 tag : this.tag || 'div',
925 if (this.jumbotron) {
926 cfg.cls = 'jumbotron';
931 // - this is applied by the parent..
933 // cfg.cls = this.cls + '';
936 if (this.sticky.length) {
938 var bd = Roo.get(document.body);
939 if (!bd.hasClass('bootstrap-sticky')) {
940 bd.addClass('bootstrap-sticky');
941 Roo.select('html',true).setStyle('height', '100%');
944 cfg.cls += 'bootstrap-sticky-' + this.sticky;
948 if (this.well.length) {
952 cfg.cls +=' well well-' +this.well;
961 cfg.cls += ' hidden';
965 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
966 cfg.cls +=' alert alert-' + this.alert;
971 if (this.panel.length) {
972 cfg.cls += ' panel panel-' + this.panel;
974 if (this.header.length) {
977 cls : 'panel-heading',
993 if (this.footer.length) {
995 cls : 'panel-footer',
1004 body.html = this.html || cfg.html;
1005 // prefix with the icons..
1007 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1010 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1015 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1016 cfg.cls = 'container';
1022 titleEl : function()
1024 if(!this.el || !this.panel.length || !this.header.length){
1028 return this.el.select('.panel-title',true).first();
1031 setTitle : function(v)
1033 var titleEl = this.titleEl();
1039 titleEl.dom.innerHTML = v;
1042 getTitle : function()
1045 var titleEl = this.titleEl();
1051 return titleEl.dom.innerHTML;
1065 * @class Roo.bootstrap.Img
1066 * @extends Roo.bootstrap.Component
1067 * Bootstrap Img class
1068 * @cfg {Boolean} imgResponsive false | true
1069 * @cfg {String} border rounded | circle | thumbnail
1070 * @cfg {String} src image source
1071 * @cfg {String} alt image alternative text
1072 * @cfg {String} href a tag href
1073 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1076 * Create a new Input
1077 * @param {Object} config The config object
1080 Roo.bootstrap.Img = function(config){
1081 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1087 * The img click event for the img.
1088 * @param {Roo.EventObject} e
1094 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1096 imgResponsive: true,
1102 getAutoCreate : function(){
1106 cls: (this.imgResponsive) ? 'img-responsive' : '',
1110 cfg.html = this.html || cfg.html;
1112 cfg.src = this.src || cfg.src;
1114 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1115 cfg.cls += ' img-' + this.border;
1132 a.target = this.target;
1138 return (this.href) ? a : cfg;
1141 initEvents: function() {
1144 this.el.on('click', this.onClick, this);
1148 onClick : function(e)
1150 Roo.log('img onclick');
1151 this.fireEvent('click', this, e);
1165 * @class Roo.bootstrap.Link
1166 * @extends Roo.bootstrap.Component
1167 * Bootstrap Link Class
1168 * @cfg {String} alt image alternative text
1169 * @cfg {String} href a tag href
1170 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1171 * @cfg {String} html the content of the link.
1172 * @cfg {Boolean} preventDefault (true | false) default false
1176 * Create a new Input
1177 * @param {Object} config The config object
1180 Roo.bootstrap.Link = function(config){
1181 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1187 * The img click event for the img.
1188 * @param {Roo.EventObject} e
1194 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1198 preventDefault: false,
1200 getAutoCreate : function(){
1204 html : this.html || 'html-missing'
1211 cfg.href = this.href || '#';
1213 cfg.target = this.target;
1219 initEvents: function() {
1221 if(!this.href || this.preventDefault){
1222 this.el.on('click', this.onClick, this);
1226 onClick : function(e)
1228 if(this.preventDefault){
1231 //Roo.log('img onclick');
1232 this.fireEvent('click', this, e);
1245 * @class Roo.bootstrap.Header
1246 * @extends Roo.bootstrap.Component
1247 * Bootstrap Header class
1248 * @cfg {String} html content of header
1249 * @cfg {Number} level (1|2|3|4|5|6) default 1
1252 * Create a new Header
1253 * @param {Object} config The config object
1257 Roo.bootstrap.Header = function(config){
1258 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1261 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1269 getAutoCreate : function(){
1272 tag: 'h' + (1 *this.level),
1273 html: this.html || 'fill in html'
1285 * Ext JS Library 1.1.1
1286 * Copyright(c) 2006-2007, Ext JS, LLC.
1288 * Originally Released Under LGPL - original licence link has changed is not relivant.
1291 * <script type="text/javascript">
1295 * @class Roo.bootstrap.MenuMgr
1296 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1299 Roo.bootstrap.MenuMgr = function(){
1300 var menus, active, groups = {}, attached = false, lastShow = new Date();
1302 // private - called when first menu is created
1305 active = new Roo.util.MixedCollection();
1306 Roo.get(document).addKeyListener(27, function(){
1307 if(active.length > 0){
1315 if(active && active.length > 0){
1316 var c = active.clone();
1326 if(active.length < 1){
1327 Roo.get(document).un("mouseup", onMouseDown);
1335 var last = active.last();
1336 lastShow = new Date();
1339 Roo.get(document).on("mouseup", onMouseDown);
1344 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1345 m.parentMenu.activeChild = m;
1346 }else if(last && last.isVisible()){
1347 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1352 function onBeforeHide(m){
1354 m.activeChild.hide();
1356 if(m.autoHideTimer){
1357 clearTimeout(m.autoHideTimer);
1358 delete m.autoHideTimer;
1363 function onBeforeShow(m){
1364 var pm = m.parentMenu;
1365 if(!pm && !m.allowOtherMenus){
1367 }else if(pm && pm.activeChild && active != m){
1368 pm.activeChild.hide();
1373 function onMouseDown(e){
1374 Roo.log("on MouseDown");
1375 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1383 function onBeforeCheck(mi, state){
1385 var g = groups[mi.group];
1386 for(var i = 0, l = g.length; i < l; i++){
1388 g[i].setChecked(false);
1397 * Hides all menus that are currently visible
1399 hideAll : function(){
1404 register : function(menu){
1408 menus[menu.id] = menu;
1409 menu.on("beforehide", onBeforeHide);
1410 menu.on("hide", onHide);
1411 menu.on("beforeshow", onBeforeShow);
1412 menu.on("show", onShow);
1414 if(g && menu.events["checkchange"]){
1418 groups[g].push(menu);
1419 menu.on("checkchange", onCheck);
1424 * Returns a {@link Roo.menu.Menu} object
1425 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1426 * be used to generate and return a new Menu instance.
1428 get : function(menu){
1429 if(typeof menu == "string"){ // menu id
1431 }else if(menu.events){ // menu instance
1434 /*else if(typeof menu.length == 'number'){ // array of menu items?
1435 return new Roo.bootstrap.Menu({items:menu});
1436 }else{ // otherwise, must be a config
1437 return new Roo.bootstrap.Menu(menu);
1444 unregister : function(menu){
1445 delete menus[menu.id];
1446 menu.un("beforehide", onBeforeHide);
1447 menu.un("hide", onHide);
1448 menu.un("beforeshow", onBeforeShow);
1449 menu.un("show", onShow);
1451 if(g && menu.events["checkchange"]){
1452 groups[g].remove(menu);
1453 menu.un("checkchange", onCheck);
1458 registerCheckable : function(menuItem){
1459 var g = menuItem.group;
1464 groups[g].push(menuItem);
1465 menuItem.on("beforecheckchange", onBeforeCheck);
1470 unregisterCheckable : function(menuItem){
1471 var g = menuItem.group;
1473 groups[g].remove(menuItem);
1474 menuItem.un("beforecheckchange", onBeforeCheck);
1486 * @class Roo.bootstrap.Menu
1487 * @extends Roo.bootstrap.Component
1488 * Bootstrap Menu class - container for MenuItems
1489 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1493 * @param {Object} config The config object
1497 Roo.bootstrap.Menu = function(config){
1498 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1499 if (this.registerMenu) {
1500 Roo.bootstrap.MenuMgr.register(this);
1505 * Fires before this menu is displayed
1506 * @param {Roo.menu.Menu} this
1511 * Fires before this menu is hidden
1512 * @param {Roo.menu.Menu} this
1517 * Fires after this menu is displayed
1518 * @param {Roo.menu.Menu} this
1523 * Fires after this menu is hidden
1524 * @param {Roo.menu.Menu} this
1529 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1530 * @param {Roo.menu.Menu} this
1531 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1532 * @param {Roo.EventObject} e
1537 * Fires when the mouse is hovering over this menu
1538 * @param {Roo.menu.Menu} this
1539 * @param {Roo.EventObject} e
1540 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1545 * Fires when the mouse exits this menu
1546 * @param {Roo.menu.Menu} this
1547 * @param {Roo.EventObject} e
1548 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1553 * Fires when a menu item contained in this menu is clicked
1554 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1555 * @param {Roo.EventObject} e
1559 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1562 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1566 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1569 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1571 registerMenu : true,
1573 menuItems :false, // stores the menu items..
1579 getChildContainer : function() {
1583 getAutoCreate : function(){
1585 //if (['right'].indexOf(this.align)!==-1) {
1586 // cfg.cn[1].cls += ' pull-right'
1592 cls : 'dropdown-menu' ,
1593 style : 'z-index:1000'
1597 if (this.type === 'submenu') {
1598 cfg.cls = 'submenu active';
1600 if (this.type === 'treeview') {
1601 cfg.cls = 'treeview-menu';
1606 initEvents : function() {
1608 // Roo.log("ADD event");
1609 // Roo.log(this.triggerEl.dom);
1610 this.triggerEl.on('click', this.onTriggerPress, this);
1611 this.triggerEl.addClass('dropdown-toggle');
1612 this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1614 this.el.on("mouseover", this.onMouseOver, this);
1615 this.el.on("mouseout", this.onMouseOut, this);
1619 findTargetItem : function(e){
1620 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1624 //Roo.log(t); Roo.log(t.id);
1626 //Roo.log(this.menuitems);
1627 return this.menuitems.get(t.id);
1629 //return this.items.get(t.menuItemId);
1634 onClick : function(e){
1635 Roo.log("menu.onClick");
1636 var t = this.findTargetItem(e);
1642 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
1643 if(t == this.activeItem && t.shouldDeactivate(e)){
1644 this.activeItem.deactivate();
1645 delete this.activeItem;
1649 this.setActiveItem(t, true);
1656 Roo.log('pass click event');
1660 this.fireEvent("click", this, t, e);
1664 onMouseOver : function(e){
1665 var t = this.findTargetItem(e);
1668 // if(t.canActivate && !t.disabled){
1669 // this.setActiveItem(t, true);
1673 this.fireEvent("mouseover", this, e, t);
1675 isVisible : function(){
1676 return !this.hidden;
1678 onMouseOut : function(e){
1679 var t = this.findTargetItem(e);
1682 // if(t == this.activeItem && t.shouldDeactivate(e)){
1683 // this.activeItem.deactivate();
1684 // delete this.activeItem;
1687 this.fireEvent("mouseout", this, e, t);
1692 * Displays this menu relative to another element
1693 * @param {String/HTMLElement/Roo.Element} element The element to align to
1694 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1695 * the element (defaults to this.defaultAlign)
1696 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1698 show : function(el, pos, parentMenu){
1699 this.parentMenu = parentMenu;
1703 this.fireEvent("beforeshow", this);
1704 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1707 * Displays this menu at a specific xy position
1708 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1709 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1711 showAt : function(xy, parentMenu, /* private: */_e){
1712 this.parentMenu = parentMenu;
1717 this.fireEvent("beforeshow", this);
1719 //xy = this.el.adjustForConstraints(xy);
1721 //this.el.setXY(xy);
1723 this.hideMenuItems();
1724 this.hidden = false;
1725 this.triggerEl.addClass('open');
1727 this.fireEvent("show", this);
1733 this.doFocus.defer(50, this);
1737 doFocus : function(){
1739 this.focusEl.focus();
1744 * Hides this menu and optionally all parent menus
1745 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1747 hide : function(deep){
1749 this.hideMenuItems();
1750 if(this.el && this.isVisible()){
1751 this.fireEvent("beforehide", this);
1752 if(this.activeItem){
1753 this.activeItem.deactivate();
1754 this.activeItem = null;
1756 this.triggerEl.removeClass('open');;
1758 this.fireEvent("hide", this);
1760 if(deep === true && this.parentMenu){
1761 this.parentMenu.hide(true);
1765 onTriggerPress : function(e)
1768 Roo.log('trigger press');
1769 //Roo.log(e.getTarget());
1770 // Roo.log(this.triggerEl.dom);
1771 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1774 if (this.isVisible()) {
1778 this.show(this.triggerEl, false, false);
1787 hideMenuItems : function()
1789 //$(backdrop).remove()
1790 Roo.select('.open',true).each(function(aa) {
1792 aa.removeClass('open');
1793 //var parent = getParent($(this))
1794 //var relatedTarget = { relatedTarget: this }
1796 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1797 //if (e.isDefaultPrevented()) return
1798 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1801 addxtypeChild : function (tree, cntr) {
1802 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1804 this.menuitems.add(comp);
1825 * @class Roo.bootstrap.MenuItem
1826 * @extends Roo.bootstrap.Component
1827 * Bootstrap MenuItem class
1828 * @cfg {String} html the menu label
1829 * @cfg {String} href the link
1830 * @cfg {Boolean} preventDefault (true | false) default true
1834 * Create a new MenuItem
1835 * @param {Object} config The config object
1839 Roo.bootstrap.MenuItem = function(config){
1840 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1845 * The raw click event for the entire grid.
1846 * @param {Roo.EventObject} e
1852 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
1856 preventDefault: true,
1858 getAutoCreate : function(){
1861 cls: 'dropdown-menu-item',
1870 if (this.parent().type == 'treeview') {
1871 cfg.cls = 'treeview-menu';
1874 cfg.cn[0].href = this.href || cfg.cn[0].href ;
1875 cfg.cn[0].html = this.html || cfg.cn[0].html ;
1879 initEvents: function() {
1881 //this.el.select('a').on('click', this.onClick, this);
1884 onClick : function(e)
1886 Roo.log('item on click ');
1887 //if(this.preventDefault){
1888 // e.preventDefault();
1890 //this.parent().hideMenuItems();
1892 this.fireEvent('click', this, e);
1911 * @class Roo.bootstrap.MenuSeparator
1912 * @extends Roo.bootstrap.Component
1913 * Bootstrap MenuSeparator class
1916 * Create a new MenuItem
1917 * @param {Object} config The config object
1921 Roo.bootstrap.MenuSeparator = function(config){
1922 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1925 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
1927 getAutoCreate : function(){
1942 <div class="modal fade">
1943 <div class="modal-dialog">
1944 <div class="modal-content">
1945 <div class="modal-header">
1946 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
1947 <h4 class="modal-title">Modal title</h4>
1949 <div class="modal-body">
1950 <p>One fine body…</p>
1952 <div class="modal-footer">
1953 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1954 <button type="button" class="btn btn-primary">Save changes</button>
1956 </div><!-- /.modal-content -->
1957 </div><!-- /.modal-dialog -->
1958 </div><!-- /.modal -->
1968 * @class Roo.bootstrap.Modal
1969 * @extends Roo.bootstrap.Component
1970 * Bootstrap Modal class
1971 * @cfg {String} title Title of dialog
1972 * @cfg {Boolean} specificTitle (true|false) default false
1973 * @cfg {Array} buttons Array of buttons or standard button set..
1974 * @cfg {String} buttonPosition (left|right|center) default right
1977 * Create a new Modal Dialog
1978 * @param {Object} config The config object
1981 Roo.bootstrap.Modal = function(config){
1982 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1987 * The raw btnclick event for the button
1988 * @param {Roo.EventObject} e
1992 this.buttons = this.buttons || [];
1995 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
1997 title : 'test dialog',
2004 specificTitle: false,
2006 buttonPosition: 'right',
2008 onRender : function(ct, position)
2010 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2013 var cfg = Roo.apply({}, this.getAutoCreate());
2016 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2018 //if (!cfg.name.length) {
2022 cfg.cls += ' ' + this.cls;
2025 cfg.style = this.style;
2027 this.el = Roo.get(document.body).createChild(cfg, position);
2029 //var type = this.el.dom.type;
2031 if(this.tabIndex !== undefined){
2032 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2037 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2038 this.maskEl.enableDisplayMode("block");
2040 //this.el.addClass("x-dlg-modal");
2042 if (this.buttons.length) {
2043 Roo.each(this.buttons, function(bb) {
2044 b = Roo.apply({}, bb);
2045 b.xns = b.xns || Roo.bootstrap;
2046 b.xtype = b.xtype || 'Button';
2047 if (typeof(b.listeners) == 'undefined') {
2048 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2051 var btn = Roo.factory(b);
2053 btn.onRender(this.el.select('.modal-footer div').first());
2057 // render the children.
2060 if(typeof(this.items) != 'undefined'){
2061 var items = this.items;
2064 for(var i =0;i < items.length;i++) {
2065 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2069 this.items = nitems;
2071 this.body = this.el.select('.modal-body',true).first();
2072 this.close = this.el.select('.modal-header .close', true).first();
2073 this.footer = this.el.select('.modal-footer',true).first();
2075 //this.el.addClass([this.fieldClass, this.cls]);
2078 getAutoCreate : function(){
2083 html : this.html || ''
2088 cls : 'modal-title',
2092 if(this.specificTitle){
2098 style : 'display: none',
2101 cls: "modal-dialog",
2104 cls : "modal-content",
2107 cls : 'modal-header',
2119 cls : 'modal-footer',
2123 cls: 'btn-' + this.buttonPosition
2142 getChildContainer : function() {
2144 return this.el.select('.modal-body',true).first();
2147 getButtonContainer : function() {
2148 return this.el.select('.modal-footer div',true).first();
2151 initEvents : function()
2153 this.el.select('.modal-header .close').on('click', this.hide, this);
2155 // this.addxtype(this);
2159 if (!this.rendered) {
2163 this.el.addClass('on');
2164 this.el.removeClass('fade');
2165 this.el.setStyle('display', 'block');
2166 Roo.get(document.body).addClass("x-body-masked");
2167 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2169 this.el.setStyle('zIndex', '10001');
2170 this.fireEvent('show', this);
2176 Roo.log('Modal hide?!');
2178 Roo.get(document.body).removeClass("x-body-masked");
2179 this.el.removeClass('on');
2180 this.el.addClass('fade');
2181 this.el.setStyle('display', 'none');
2182 this.fireEvent('hide', this);
2185 addButton : function(str, cb)
2189 var b = Roo.apply({}, { html : str } );
2190 b.xns = b.xns || Roo.bootstrap;
2191 b.xtype = b.xtype || 'Button';
2192 if (typeof(b.listeners) == 'undefined') {
2193 b.listeners = { click : cb.createDelegate(this) };
2196 var btn = Roo.factory(b);
2198 btn.onRender(this.el.select('.modal-footer div').first());
2204 setDefaultButton : function(btn)
2206 //this.el.select('.modal-footer').()
2208 resizeTo: function(w,h)
2212 setContentSize : function(w, h)
2216 onButtonClick: function(btn,e)
2219 this.fireEvent('btnclick', btn.name, e);
2221 setTitle: function(str) {
2222 this.el.select('.modal-title',true).first().dom.innerHTML = str;
2228 Roo.apply(Roo.bootstrap.Modal, {
2230 * Button config that displays a single OK button
2239 * Button config that displays Yes and No buttons
2255 * Button config that displays OK and Cancel buttons
2270 * Button config that displays Yes, No and Cancel buttons
2292 * messagebox - can be used as a replace
2296 * @class Roo.MessageBox
2297 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2301 Roo.Msg.alert('Status', 'Changes saved successfully.');
2303 // Prompt for user data:
2304 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2306 // process text value...
2310 // Show a dialog using config options:
2312 title:'Save Changes?',
2313 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2314 buttons: Roo.Msg.YESNOCANCEL,
2321 Roo.bootstrap.MessageBox = function(){
2322 var dlg, opt, mask, waitTimer;
2323 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2324 var buttons, activeTextEl, bwidth;
2328 var handleButton = function(button){
2330 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2334 var handleHide = function(){
2336 dlg.el.removeClass(opt.cls);
2339 // Roo.TaskMgr.stop(waitTimer);
2340 // waitTimer = null;
2345 var updateButtons = function(b){
2348 buttons["ok"].hide();
2349 buttons["cancel"].hide();
2350 buttons["yes"].hide();
2351 buttons["no"].hide();
2352 //dlg.footer.dom.style.display = 'none';
2355 dlg.footer.dom.style.display = '';
2356 for(var k in buttons){
2357 if(typeof buttons[k] != "function"){
2360 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2361 width += buttons[k].el.getWidth()+15;
2371 var handleEsc = function(d, k, e){
2372 if(opt && opt.closable !== false){
2382 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2383 * @return {Roo.BasicDialog} The BasicDialog element
2385 getDialog : function(){
2387 dlg = new Roo.bootstrap.Modal( {
2390 //constraintoviewport:false,
2392 //collapsible : false,
2397 //buttonAlign:"center",
2398 closeClick : function(){
2399 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2402 handleButton("cancel");
2407 dlg.on("hide", handleHide);
2409 //dlg.addKeyListener(27, handleEsc);
2411 this.buttons = buttons;
2412 var bt = this.buttonText;
2413 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2414 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2415 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2416 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2418 bodyEl = dlg.body.createChild({
2420 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2421 '<textarea class="roo-mb-textarea"></textarea>' +
2422 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2424 msgEl = bodyEl.dom.firstChild;
2425 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2426 textboxEl.enableDisplayMode();
2427 textboxEl.addKeyListener([10,13], function(){
2428 if(dlg.isVisible() && opt && opt.buttons){
2431 }else if(opt.buttons.yes){
2432 handleButton("yes");
2436 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2437 textareaEl.enableDisplayMode();
2438 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2439 progressEl.enableDisplayMode();
2440 var pf = progressEl.dom.firstChild;
2442 pp = Roo.get(pf.firstChild);
2443 pp.setHeight(pf.offsetHeight);
2451 * Updates the message box body text
2452 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2453 * the XHTML-compliant non-breaking space character '&#160;')
2454 * @return {Roo.MessageBox} This message box
2456 updateText : function(text){
2457 if(!dlg.isVisible() && !opt.width){
2458 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2460 msgEl.innerHTML = text || ' ';
2462 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2463 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2465 Math.min(opt.width || cw , this.maxWidth),
2466 Math.max(opt.minWidth || this.minWidth, bwidth)
2469 activeTextEl.setWidth(w);
2471 if(dlg.isVisible()){
2472 dlg.fixedcenter = false;
2474 // to big, make it scroll. = But as usual stupid IE does not support
2477 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2478 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2479 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2481 bodyEl.dom.style.height = '';
2482 bodyEl.dom.style.overflowY = '';
2485 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2487 bodyEl.dom.style.overflowX = '';
2490 dlg.setContentSize(w, bodyEl.getHeight());
2491 if(dlg.isVisible()){
2492 dlg.fixedcenter = true;
2498 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2499 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2500 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2501 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2502 * @return {Roo.MessageBox} This message box
2504 updateProgress : function(value, text){
2506 this.updateText(text);
2508 if (pp) { // weird bug on my firefox - for some reason this is not defined
2509 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2515 * Returns true if the message box is currently displayed
2516 * @return {Boolean} True if the message box is visible, else false
2518 isVisible : function(){
2519 return dlg && dlg.isVisible();
2523 * Hides the message box if it is displayed
2526 if(this.isVisible()){
2532 * Displays a new message box, or reinitializes an existing message box, based on the config options
2533 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2534 * The following config object properties are supported:
2536 Property Type Description
2537 ---------- --------------- ------------------------------------------------------------------------------------
2538 animEl String/Element An id or Element from which the message box should animate as it opens and
2539 closes (defaults to undefined)
2540 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2541 cancel:'Bar'}), or false to not show any buttons (defaults to false)
2542 closable Boolean False to hide the top-right close button (defaults to true). Note that
2543 progress and wait dialogs will ignore this property and always hide the
2544 close button as they can only be closed programmatically.
2545 cls String A custom CSS class to apply to the message box element
2546 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
2547 displayed (defaults to 75)
2548 fn Function A callback function to execute after closing the dialog. The arguments to the
2549 function will be btn (the name of the button that was clicked, if applicable,
2550 e.g. "ok"), and text (the value of the active text field, if applicable).
2551 Progress and wait dialogs will ignore this option since they do not respond to
2552 user actions and can only be closed programmatically, so any required function
2553 should be called by the same code after it closes the dialog.
2554 icon String A CSS class that provides a background image to be used as an icon for
2555 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2556 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
2557 minWidth Number The minimum width in pixels of the message box (defaults to 100)
2558 modal Boolean False to allow user interaction with the page while the message box is
2559 displayed (defaults to true)
2560 msg String A string that will replace the existing message box body text (defaults
2561 to the XHTML-compliant non-breaking space character ' ')
2562 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
2563 progress Boolean True to display a progress bar (defaults to false)
2564 progressText String The text to display inside the progress bar if progress = true (defaults to '')
2565 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
2566 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
2567 title String The title text
2568 value String The string value to set into the active textbox element if displayed
2569 wait Boolean True to display a progress bar (defaults to false)
2570 width Number The width of the dialog in pixels
2577 msg: 'Please enter your address:',
2579 buttons: Roo.MessageBox.OKCANCEL,
2582 animEl: 'addAddressBtn'
2585 * @param {Object} config Configuration options
2586 * @return {Roo.MessageBox} This message box
2588 show : function(options)
2591 // this causes nightmares if you show one dialog after another
2592 // especially on callbacks..
2594 if(this.isVisible()){
2597 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2598 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
2599 Roo.log("New Dialog Message:" + options.msg )
2600 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2601 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2604 var d = this.getDialog();
2606 d.setTitle(opt.title || " ");
2607 d.close.setDisplayed(opt.closable !== false);
2608 activeTextEl = textboxEl;
2609 opt.prompt = opt.prompt || (opt.multiline ? true : false);
2614 textareaEl.setHeight(typeof opt.multiline == "number" ?
2615 opt.multiline : this.defaultTextHeight);
2616 activeTextEl = textareaEl;
2625 progressEl.setDisplayed(opt.progress === true);
2626 this.updateProgress(0);
2627 activeTextEl.dom.value = opt.value || "";
2629 dlg.setDefaultButton(activeTextEl);
2631 var bs = opt.buttons;
2635 }else if(bs && bs.yes){
2636 db = buttons["yes"];
2638 dlg.setDefaultButton(db);
2640 bwidth = updateButtons(opt.buttons);
2641 this.updateText(opt.msg);
2643 d.el.addClass(opt.cls);
2645 d.proxyDrag = opt.proxyDrag === true;
2646 d.modal = opt.modal !== false;
2647 d.mask = opt.modal !== false ? mask : false;
2649 // force it to the end of the z-index stack so it gets a cursor in FF
2650 document.body.appendChild(dlg.el.dom);
2651 d.animateTarget = null;
2652 d.show(options.animEl);
2658 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
2659 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2660 * and closing the message box when the process is complete.
2661 * @param {String} title The title bar text
2662 * @param {String} msg The message box body text
2663 * @return {Roo.MessageBox} This message box
2665 progress : function(title, msg){
2672 minWidth: this.minProgressWidth,
2679 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2680 * If a callback function is passed it will be called after the user clicks the button, and the
2681 * id of the button that was clicked will be passed as the only parameter to the callback
2682 * (could also be the top-right close button).
2683 * @param {String} title The title bar text
2684 * @param {String} msg The message box body text
2685 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2686 * @param {Object} scope (optional) The scope of the callback function
2687 * @return {Roo.MessageBox} This message box
2689 alert : function(title, msg, fn, scope){
2702 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
2703 * interaction while waiting for a long-running process to complete that does not have defined intervals.
2704 * You are responsible for closing the message box when the process is complete.
2705 * @param {String} msg The message box body text
2706 * @param {String} title (optional) The title bar text
2707 * @return {Roo.MessageBox} This message box
2709 wait : function(msg, title){
2720 waitTimer = Roo.TaskMgr.start({
2722 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2730 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2731 * If a callback function is passed it will be called after the user clicks either button, and the id of the
2732 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2733 * @param {String} title The title bar text
2734 * @param {String} msg The message box body text
2735 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2736 * @param {Object} scope (optional) The scope of the callback function
2737 * @return {Roo.MessageBox} This message box
2739 confirm : function(title, msg, fn, scope){
2743 buttons: this.YESNO,
2752 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2753 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
2754 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2755 * (could also be the top-right close button) and the text that was entered will be passed as the two
2756 * parameters to the callback.
2757 * @param {String} title The title bar text
2758 * @param {String} msg The message box body text
2759 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2760 * @param {Object} scope (optional) The scope of the callback function
2761 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2762 * property, or the height in pixels to create the textbox (defaults to false / single-line)
2763 * @return {Roo.MessageBox} This message box
2765 prompt : function(title, msg, fn, scope, multiline){
2769 buttons: this.OKCANCEL,
2774 multiline: multiline,
2781 * Button config that displays a single OK button
2786 * Button config that displays Yes and No buttons
2789 YESNO : {yes:true, no:true},
2791 * Button config that displays OK and Cancel buttons
2794 OKCANCEL : {ok:true, cancel:true},
2796 * Button config that displays Yes, No and Cancel buttons
2799 YESNOCANCEL : {yes:true, no:true, cancel:true},
2802 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2805 defaultTextHeight : 75,
2807 * The maximum width in pixels of the message box (defaults to 600)
2812 * The minimum width in pixels of the message box (defaults to 100)
2817 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
2818 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2821 minProgressWidth : 250,
2823 * An object containing the default button text strings that can be overriden for localized language support.
2824 * Supported properties are: ok, cancel, yes and no.
2825 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2838 * Shorthand for {@link Roo.MessageBox}
2840 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox
2841 Roo.Msg = Roo.Msg || Roo.MessageBox;
2850 * @class Roo.bootstrap.Navbar
2851 * @extends Roo.bootstrap.Component
2852 * Bootstrap Navbar class
2855 * Create a new Navbar
2856 * @param {Object} config The config object
2860 Roo.bootstrap.Navbar = function(config){
2861 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2865 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
2874 getAutoCreate : function(){
2877 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2881 initEvents :function ()
2883 //Roo.log(this.el.select('.navbar-toggle',true));
2884 this.el.select('.navbar-toggle',true).on('click', function() {
2885 // Roo.log('click');
2886 this.el.select('.navbar-collapse',true).toggleClass('in');
2894 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2896 var size = this.el.getSize();
2897 this.maskEl.setSize(size.width, size.height);
2898 this.maskEl.enableDisplayMode("block");
2907 getChildContainer : function()
2909 if (this.el.select('.collapse').getCount()) {
2910 return this.el.select('.collapse',true).first();
2943 * @class Roo.bootstrap.NavSimplebar
2944 * @extends Roo.bootstrap.Navbar
2945 * Bootstrap Sidebar class
2947 * @cfg {Boolean} inverse is inverted color
2949 * @cfg {String} type (nav | pills | tabs)
2950 * @cfg {Boolean} arrangement stacked | justified
2951 * @cfg {String} align (left | right) alignment
2953 * @cfg {Boolean} main (true|false) main nav bar? default false
2954 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2956 * @cfg {String} tag (header|footer|nav|div) default is nav
2962 * Create a new Sidebar
2963 * @param {Object} config The config object
2967 Roo.bootstrap.NavSimplebar = function(config){
2968 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2971 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
2987 getAutoCreate : function(){
2991 tag : this.tag || 'div',
3004 this.type = this.type || 'nav';
3005 if (['tabs','pills'].indexOf(this.type)!==-1) {
3006 cfg.cn[0].cls += ' nav-' + this.type
3010 if (this.type!=='nav') {
3011 Roo.log('nav type must be nav/tabs/pills')
3013 cfg.cn[0].cls += ' navbar-nav'
3019 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3020 cfg.cn[0].cls += ' nav-' + this.arrangement;
3024 if (this.align === 'right') {
3025 cfg.cn[0].cls += ' navbar-right';
3029 cfg.cls += ' navbar-inverse';
3056 * @class Roo.bootstrap.NavHeaderbar
3057 * @extends Roo.bootstrap.NavSimplebar
3058 * Bootstrap Sidebar class
3060 * @cfg {String} brand what is brand
3061 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3062 * @cfg {String} brand_href href of the brand
3063 * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3064 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3067 * Create a new Sidebar
3068 * @param {Object} config The config object
3072 Roo.bootstrap.NavHeaderbar = function(config){
3073 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3076 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3084 getAutoCreate : function(){
3087 tag: this.nav || 'nav',
3096 cls: 'navbar-header',
3101 cls: 'navbar-toggle',
3102 'data-toggle': 'collapse',
3107 html: 'Toggle navigation'
3129 cls: 'collapse navbar-collapse',
3133 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3135 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3136 cfg.cls += ' navbar-' + this.position;
3138 // tag can override this..
3140 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3143 if (this.brand !== '') {
3146 href: this.brand_href ? this.brand_href : '#',
3147 cls: 'navbar-brand',
3155 cfg.cls += ' main-nav';
3163 initEvents : function()
3165 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3167 if (this.autohide) {
3172 Roo.get(document).on('scroll',function(e) {
3173 var ns = Roo.get(document).getScroll().top;
3174 var os = prevScroll;
3178 ft.removeClass('slideDown');
3179 ft.addClass('slideUp');
3182 ft.removeClass('slideUp');
3183 ft.addClass('slideDown');
3207 * @class Roo.bootstrap.NavSidebar
3208 * @extends Roo.bootstrap.Navbar
3209 * Bootstrap Sidebar class
3212 * Create a new Sidebar
3213 * @param {Object} config The config object
3217 Roo.bootstrap.NavSidebar = function(config){
3218 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3221 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3223 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3225 getAutoCreate : function(){
3230 cls: 'sidebar sidebar-nav'
3252 * @class Roo.bootstrap.NavGroup
3253 * @extends Roo.bootstrap.Component
3254 * Bootstrap NavGroup class
3255 * @cfg {String} align left | right
3256 * @cfg {Boolean} inverse false | true
3257 * @cfg {String} type (nav|pills|tab) default nav
3258 * @cfg {String} navId - reference Id for navbar.
3262 * Create a new nav group
3263 * @param {Object} config The config object
3266 Roo.bootstrap.NavGroup = function(config){
3267 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3270 Roo.bootstrap.NavGroup.register(this);
3274 * Fires when the active item changes
3275 * @param {Roo.bootstrap.NavGroup} this
3276 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3277 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3284 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3295 getAutoCreate : function()
3297 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3304 if (['tabs','pills'].indexOf(this.type)!==-1) {
3305 cfg.cls += ' nav-' + this.type
3307 if (this.type!=='nav') {
3308 Roo.log('nav type must be nav/tabs/pills')
3310 cfg.cls += ' navbar-nav'
3313 if (this.parent().sidebar) {
3316 cls: 'dashboard-menu sidebar-menu'
3322 if (this.form === true) {
3328 if (this.align === 'right') {
3329 cfg.cls += ' navbar-right';
3331 cfg.cls += ' navbar-left';
3335 if (this.align === 'right') {
3336 cfg.cls += ' navbar-right';
3340 cfg.cls += ' navbar-inverse';
3348 * sets the active Navigation item
3349 * @param {Roo.bootstrap.NavItem} the new current navitem
3351 setActiveItem : function(item)
3354 Roo.each(this.navItems, function(v){
3359 v.setActive(false, true);
3366 item.setActive(true, true);
3367 this.fireEvent('changed', this, item, prev);
3372 * gets the active Navigation item
3373 * @return {Roo.bootstrap.NavItem} the current navitem
3375 getActive : function()
3379 Roo.each(this.navItems, function(v){
3390 indexOfNav : function()
3394 Roo.each(this.navItems, function(v,i){
3405 * adds a Navigation item
3406 * @param {Roo.bootstrap.NavItem} the navitem to add
3408 addItem : function(cfg)
3410 var cn = new Roo.bootstrap.NavItem(cfg);
3412 cn.parentId = this.id;
3413 cn.onRender(this.el, null);
3417 * register a Navigation item
3418 * @param {Roo.bootstrap.NavItem} the navitem to add
3420 register : function(item)
3422 this.navItems.push( item);
3423 item.navId = this.navId;
3428 getNavItem: function(tabId)
3431 Roo.each(this.navItems, function(e) {
3432 if (e.tabId == tabId) {
3442 setActiveNext : function()
3444 var i = this.indexOfNav(this.getActive());
3445 if (i > this.navItems.length) {
3448 this.setActiveItem(this.navItems[i+1]);
3450 setActivePrev : function()
3452 var i = this.indexOfNav(this.getActive());
3456 this.setActiveItem(this.navItems[i-1]);
3458 clearWasActive : function(except) {
3459 Roo.each(this.navItems, function(e) {
3460 if (e.tabId != except.tabId && e.was_active) {
3461 e.was_active = false;
3468 getWasActive : function ()
3471 Roo.each(this.navItems, function(e) {
3486 Roo.apply(Roo.bootstrap.NavGroup, {
3490 * register a Navigation Group
3491 * @param {Roo.bootstrap.NavGroup} the navgroup to add
3493 register : function(navgrp)
3495 this.groups[navgrp.navId] = navgrp;
3499 * fetch a Navigation Group based on the navigation ID
3500 * @param {string} the navgroup to add
3501 * @returns {Roo.bootstrap.NavGroup} the navgroup
3503 get: function(navId) {
3504 if (typeof(this.groups[navId]) == 'undefined') {
3506 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3508 return this.groups[navId] ;
3523 * @class Roo.bootstrap.NavItem
3524 * @extends Roo.bootstrap.Component
3525 * Bootstrap Navbar.NavItem class
3526 * @cfg {String} href link to
3527 * @cfg {String} html content of button
3528 * @cfg {String} badge text inside badge
3529 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3530 * @cfg {String} glyphicon name of glyphicon
3531 * @cfg {String} icon name of font awesome icon
3532 * @cfg {Boolean} active Is item active
3533 * @cfg {Boolean} disabled Is item disabled
3535 * @cfg {Boolean} preventDefault (true | false) default false
3536 * @cfg {String} tabId the tab that this item activates.
3537 * @cfg {String} tagtype (a|span) render as a href or span?
3540 * Create a new Navbar Item
3541 * @param {Object} config The config object
3543 Roo.bootstrap.NavItem = function(config){
3544 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3549 * The raw click event for the entire grid.
3550 * @param {Roo.EventObject} e
3555 * Fires when the active item active state changes
3556 * @param {Roo.bootstrap.NavItem} this
3557 * @param {boolean} state the new state
3565 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
3573 preventDefault : false,
3580 getAutoCreate : function(){
3588 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3590 if (this.disabled) {
3591 cfg.cls += ' disabled';
3594 if (this.href || this.html || this.glyphicon || this.icon) {
3598 href : this.href || "#",
3599 html: this.html || ''
3604 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3607 if(this.glyphicon) {
3608 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
3613 cfg.cn[0].html += " <span class='caret'></span>";
3617 if (this.badge !== '') {
3619 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3627 initEvents: function() {
3628 // Roo.log('init events?');
3629 // Roo.log(this.el.dom);
3630 if (typeof (this.menu) != 'undefined') {
3631 this.menu.parentType = this.xtype;
3632 this.menu.triggerEl = this.el;
3633 this.addxtype(Roo.apply({}, this.menu));
3637 this.el.select('a',true).on('click', this.onClick, this);
3638 // at this point parent should be available..
3639 this.parent().register(this);
3642 onClick : function(e)
3645 if(this.preventDefault){
3648 if (this.disabled) {
3652 var tg = Roo.bootstrap.TabGroup.get(this.navId);
3653 if (tg && tg.transition) {
3654 Roo.log("waiting for the transitionend");
3658 Roo.log("fire event clicked");
3659 if(this.fireEvent('click', this, e) === false){
3663 if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3664 if (typeof(this.parent().setActiveItem) !== 'undefined') {
3665 this.parent().setActiveItem(this);
3670 isActive: function () {
3673 setActive : function(state, fire, is_was_active)
3675 if (this.active && !state & this.navId) {
3676 this.was_active = true;
3677 var nv = Roo.bootstrap.NavGroup.get(this.navId);
3679 nv.clearWasActive(this);
3683 this.active = state;
3686 this.el.removeClass('active');
3687 } else if (!this.el.hasClass('active')) {
3688 this.el.addClass('active');
3691 this.fireEvent('changed', this, state);
3694 // show a panel if it's registered and related..
3696 if (!this.navId || !this.tabId || !state || is_was_active) {
3700 var tg = Roo.bootstrap.TabGroup.get(this.navId);
3704 var pan = tg.getPanelByName(this.tabId);
3708 // if we can not flip to new panel - go back to old nav highlight..
3709 if (false == tg.showPanel(pan)) {
3710 var nv = Roo.bootstrap.NavGroup.get(this.navId);
3712 var onav = nv.getWasActive();
3714 onav.setActive(true, false, true);
3723 // this should not be here...
3724 setDisabled : function(state)
3726 this.disabled = state;
3728 this.el.removeClass('disabled');
3729 } else if (!this.el.hasClass('disabled')) {
3730 this.el.addClass('disabled');
3743 * <span> icon </span>
3744 * <span> text </span>
3745 * <span>badge </span>
3749 * @class Roo.bootstrap.NavSidebarItem
3750 * @extends Roo.bootstrap.NavItem
3751 * Bootstrap Navbar.NavSidebarItem class
3753 * Create a new Navbar Button
3754 * @param {Object} config The config object
3756 Roo.bootstrap.NavSidebarItem = function(config){
3757 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3762 * The raw click event for the entire grid.
3763 * @param {Roo.EventObject} e
3768 * Fires when the active item active state changes
3769 * @param {Roo.bootstrap.NavSidebarItem} this
3770 * @param {boolean} state the new state
3778 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
3781 getAutoCreate : function(){
3786 href : this.href || '#',
3798 html : this.html || ''
3803 cfg.cls += ' active';
3807 if (this.glyphicon || this.icon) {
3808 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
3809 a.cn.push({ tag : 'i', cls : c }) ;
3814 if (this.badge !== '') {
3815 a.cn.push({ tag: 'span', cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge });
3819 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3820 a.cls += 'dropdown-toggle treeview' ;
3844 * @class Roo.bootstrap.Row
3845 * @extends Roo.bootstrap.Component
3846 * Bootstrap Row class (contains columns...)
3850 * @param {Object} config The config object
3853 Roo.bootstrap.Row = function(config){
3854 Roo.bootstrap.Row.superclass.constructor.call(this, config);
3857 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
3859 getAutoCreate : function(){
3878 * @class Roo.bootstrap.Element
3879 * @extends Roo.bootstrap.Component
3880 * Bootstrap Element class
3881 * @cfg {String} html contents of the element
3882 * @cfg {String} tag tag of the element
3883 * @cfg {String} cls class of the element
3886 * Create a new Element
3887 * @param {Object} config The config object
3890 Roo.bootstrap.Element = function(config){
3891 Roo.bootstrap.Element.superclass.constructor.call(this, config);
3894 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
3901 getAutoCreate : function(){
3926 * @class Roo.bootstrap.Pagination
3927 * @extends Roo.bootstrap.Component
3928 * Bootstrap Pagination class
3929 * @cfg {String} size xs | sm | md | lg
3930 * @cfg {Boolean} inverse false | true
3933 * Create a new Pagination
3934 * @param {Object} config The config object
3937 Roo.bootstrap.Pagination = function(config){
3938 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3941 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
3947 getAutoCreate : function(){
3953 cfg.cls += ' inverse';
3959 cfg.cls += " " + this.cls;
3977 * @class Roo.bootstrap.PaginationItem
3978 * @extends Roo.bootstrap.Component
3979 * Bootstrap PaginationItem class
3980 * @cfg {String} html text
3981 * @cfg {String} href the link
3982 * @cfg {Boolean} preventDefault (true | false) default true
3983 * @cfg {Boolean} active (true | false) default false
3987 * Create a new PaginationItem
3988 * @param {Object} config The config object
3992 Roo.bootstrap.PaginationItem = function(config){
3993 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3998 * The raw click event for the entire grid.
3999 * @param {Roo.EventObject} e
4005 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4009 preventDefault: true,
4013 getAutoCreate : function(){
4019 href : this.href ? this.href : '#',
4020 html : this.html ? this.html : ''
4030 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4036 initEvents: function() {
4038 this.el.on('click', this.onClick, this);
4041 onClick : function(e)
4043 Roo.log('PaginationItem on click ');
4044 if(this.preventDefault){
4048 this.fireEvent('click', this, e);
4064 * @class Roo.bootstrap.Slider
4065 * @extends Roo.bootstrap.Component
4066 * Bootstrap Slider class
4069 * Create a new Slider
4070 * @param {Object} config The config object
4073 Roo.bootstrap.Slider = function(config){
4074 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4077 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4079 getAutoCreate : function(){
4083 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4087 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4099 * Ext JS Library 1.1.1
4100 * Copyright(c) 2006-2007, Ext JS, LLC.
4102 * Originally Released Under LGPL - original licence link has changed is not relivant.
4105 * <script type="text/javascript">
4110 * @class Roo.grid.ColumnModel
4111 * @extends Roo.util.Observable
4112 * This is the default implementation of a ColumnModel used by the Grid. It defines
4113 * the columns in the grid.
4116 var colModel = new Roo.grid.ColumnModel([
4117 {header: "Ticker", width: 60, sortable: true, locked: true},
4118 {header: "Company Name", width: 150, sortable: true},
4119 {header: "Market Cap.", width: 100, sortable: true},
4120 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4121 {header: "Employees", width: 100, sortable: true, resizable: false}
4126 * The config options listed for this class are options which may appear in each
4127 * individual column definition.
4128 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4130 * @param {Object} config An Array of column config objects. See this class's
4131 * config objects for details.
4133 Roo.grid.ColumnModel = function(config){
4135 * The config passed into the constructor
4137 this.config = config;
4140 // if no id, create one
4141 // if the column does not have a dataIndex mapping,
4142 // map it to the order it is in the config
4143 for(var i = 0, len = config.length; i < len; i++){
4145 if(typeof c.dataIndex == "undefined"){
4148 if(typeof c.renderer == "string"){
4149 c.renderer = Roo.util.Format[c.renderer];
4151 if(typeof c.id == "undefined"){
4154 if(c.editor && c.editor.xtype){
4155 c.editor = Roo.factory(c.editor, Roo.grid);
4157 if(c.editor && c.editor.isFormField){
4158 c.editor = new Roo.grid.GridEditor(c.editor);
4160 this.lookup[c.id] = c;
4164 * The width of columns which have no width specified (defaults to 100)
4167 this.defaultWidth = 100;
4170 * Default sortable of columns which have no sortable specified (defaults to false)
4173 this.defaultSortable = false;
4177 * @event widthchange
4178 * Fires when the width of a column changes.
4179 * @param {ColumnModel} this
4180 * @param {Number} columnIndex The column index
4181 * @param {Number} newWidth The new width
4183 "widthchange": true,
4185 * @event headerchange
4186 * Fires when the text of a header changes.
4187 * @param {ColumnModel} this
4188 * @param {Number} columnIndex The column index
4189 * @param {Number} newText The new header text
4191 "headerchange": true,
4193 * @event hiddenchange
4194 * Fires when a column is hidden or "unhidden".
4195 * @param {ColumnModel} this
4196 * @param {Number} columnIndex The column index
4197 * @param {Boolean} hidden true if hidden, false otherwise
4199 "hiddenchange": true,
4201 * @event columnmoved
4202 * Fires when a column is moved.
4203 * @param {ColumnModel} this
4204 * @param {Number} oldIndex
4205 * @param {Number} newIndex
4207 "columnmoved" : true,
4209 * @event columlockchange
4210 * Fires when a column's locked state is changed
4211 * @param {ColumnModel} this
4212 * @param {Number} colIndex
4213 * @param {Boolean} locked true if locked
4215 "columnlockchange" : true
4217 Roo.grid.ColumnModel.superclass.constructor.call(this);
4219 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4221 * @cfg {String} header The header text to display in the Grid view.
4224 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4225 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4226 * specified, the column's index is used as an index into the Record's data Array.
4229 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4230 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4233 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4234 * Defaults to the value of the {@link #defaultSortable} property.
4235 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4238 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4241 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4244 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4247 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4250 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4251 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4252 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4253 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4256 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4259 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4263 * Returns the id of the column at the specified index.
4264 * @param {Number} index The column index
4265 * @return {String} the id
4267 getColumnId : function(index){
4268 return this.config[index].id;
4272 * Returns the column for a specified id.
4273 * @param {String} id The column id
4274 * @return {Object} the column
4276 getColumnById : function(id){
4277 return this.lookup[id];
4282 * Returns the column for a specified dataIndex.
4283 * @param {String} dataIndex The column dataIndex
4284 * @return {Object|Boolean} the column or false if not found
4286 getColumnByDataIndex: function(dataIndex){
4287 var index = this.findColumnIndex(dataIndex);
4288 return index > -1 ? this.config[index] : false;
4292 * Returns the index for a specified column id.
4293 * @param {String} id The column id
4294 * @return {Number} the index, or -1 if not found
4296 getIndexById : function(id){
4297 for(var i = 0, len = this.config.length; i < len; i++){
4298 if(this.config[i].id == id){
4306 * Returns the index for a specified column dataIndex.
4307 * @param {String} dataIndex The column dataIndex
4308 * @return {Number} the index, or -1 if not found
4311 findColumnIndex : function(dataIndex){
4312 for(var i = 0, len = this.config.length; i < len; i++){
4313 if(this.config[i].dataIndex == dataIndex){
4321 moveColumn : function(oldIndex, newIndex){
4322 var c = this.config[oldIndex];
4323 this.config.splice(oldIndex, 1);
4324 this.config.splice(newIndex, 0, c);
4325 this.dataMap = null;
4326 this.fireEvent("columnmoved", this, oldIndex, newIndex);
4329 isLocked : function(colIndex){
4330 return this.config[colIndex].locked === true;
4333 setLocked : function(colIndex, value, suppressEvent){
4334 if(this.isLocked(colIndex) == value){
4337 this.config[colIndex].locked = value;
4339 this.fireEvent("columnlockchange", this, colIndex, value);
4343 getTotalLockedWidth : function(){
4345 for(var i = 0; i < this.config.length; i++){
4346 if(this.isLocked(i) && !this.isHidden(i)){
4347 this.totalWidth += this.getColumnWidth(i);
4353 getLockedCount : function(){
4354 for(var i = 0, len = this.config.length; i < len; i++){
4355 if(!this.isLocked(i)){
4362 * Returns the number of columns.
4365 getColumnCount : function(visibleOnly){
4366 if(visibleOnly === true){
4368 for(var i = 0, len = this.config.length; i < len; i++){
4369 if(!this.isHidden(i)){
4375 return this.config.length;
4379 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4380 * @param {Function} fn
4381 * @param {Object} scope (optional)
4382 * @return {Array} result
4384 getColumnsBy : function(fn, scope){
4386 for(var i = 0, len = this.config.length; i < len; i++){
4387 var c = this.config[i];
4388 if(fn.call(scope||this, c, i) === true){
4396 * Returns true if the specified column is sortable.
4397 * @param {Number} col The column index
4400 isSortable : function(col){
4401 if(typeof this.config[col].sortable == "undefined"){
4402 return this.defaultSortable;
4404 return this.config[col].sortable;
4408 * Returns the rendering (formatting) function defined for the column.
4409 * @param {Number} col The column index.
4410 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4412 getRenderer : function(col){
4413 if(!this.config[col].renderer){
4414 return Roo.grid.ColumnModel.defaultRenderer;
4416 return this.config[col].renderer;
4420 * Sets the rendering (formatting) function for a column.
4421 * @param {Number} col The column index
4422 * @param {Function} fn The function to use to process the cell's raw data
4423 * to return HTML markup for the grid view. The render function is called with
4424 * the following parameters:<ul>
4425 * <li>Data value.</li>
4426 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4427 * <li>css A CSS style string to apply to the table cell.</li>
4428 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4429 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4430 * <li>Row index</li>
4431 * <li>Column index</li>
4432 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4434 setRenderer : function(col, fn){
4435 this.config[col].renderer = fn;
4439 * Returns the width for the specified column.
4440 * @param {Number} col The column index
4443 getColumnWidth : function(col){
4444 return this.config[col].width * 1 || this.defaultWidth;
4448 * Sets the width for a column.
4449 * @param {Number} col The column index
4450 * @param {Number} width The new width
4452 setColumnWidth : function(col, width, suppressEvent){
4453 this.config[col].width = width;
4454 this.totalWidth = null;
4456 this.fireEvent("widthchange", this, col, width);
4461 * Returns the total width of all columns.
4462 * @param {Boolean} includeHidden True to include hidden column widths
4465 getTotalWidth : function(includeHidden){
4466 if(!this.totalWidth){
4467 this.totalWidth = 0;
4468 for(var i = 0, len = this.config.length; i < len; i++){
4469 if(includeHidden || !this.isHidden(i)){
4470 this.totalWidth += this.getColumnWidth(i);
4474 return this.totalWidth;
4478 * Returns the header for the specified column.
4479 * @param {Number} col The column index
4482 getColumnHeader : function(col){
4483 return this.config[col].header;
4487 * Sets the header for a column.
4488 * @param {Number} col The column index
4489 * @param {String} header The new header
4491 setColumnHeader : function(col, header){
4492 this.config[col].header = header;
4493 this.fireEvent("headerchange", this, col, header);
4497 * Returns the tooltip for the specified column.
4498 * @param {Number} col The column index
4501 getColumnTooltip : function(col){
4502 return this.config[col].tooltip;
4505 * Sets the tooltip for a column.
4506 * @param {Number} col The column index
4507 * @param {String} tooltip The new tooltip
4509 setColumnTooltip : function(col, tooltip){
4510 this.config[col].tooltip = tooltip;
4514 * Returns the dataIndex for the specified column.
4515 * @param {Number} col The column index
4518 getDataIndex : function(col){
4519 return this.config[col].dataIndex;
4523 * Sets the dataIndex for a column.
4524 * @param {Number} col The column index
4525 * @param {Number} dataIndex The new dataIndex
4527 setDataIndex : function(col, dataIndex){
4528 this.config[col].dataIndex = dataIndex;
4534 * Returns true if the cell is editable.
4535 * @param {Number} colIndex The column index
4536 * @param {Number} rowIndex The row index
4539 isCellEditable : function(colIndex, rowIndex){
4540 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4544 * Returns the editor defined for the cell/column.
4545 * return false or null to disable editing.
4546 * @param {Number} colIndex The column index
4547 * @param {Number} rowIndex The row index
4550 getCellEditor : function(colIndex, rowIndex){
4551 return this.config[colIndex].editor;
4555 * Sets if a column is editable.
4556 * @param {Number} col The column index
4557 * @param {Boolean} editable True if the column is editable
4559 setEditable : function(col, editable){
4560 this.config[col].editable = editable;
4565 * Returns true if the column is hidden.
4566 * @param {Number} colIndex The column index
4569 isHidden : function(colIndex){
4570 return this.config[colIndex].hidden;
4575 * Returns true if the column width cannot be changed
4577 isFixed : function(colIndex){
4578 return this.config[colIndex].fixed;
4582 * Returns true if the column can be resized
4585 isResizable : function(colIndex){
4586 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4589 * Sets if a column is hidden.
4590 * @param {Number} colIndex The column index
4591 * @param {Boolean} hidden True if the column is hidden
4593 setHidden : function(colIndex, hidden){
4594 this.config[colIndex].hidden = hidden;
4595 this.totalWidth = null;
4596 this.fireEvent("hiddenchange", this, colIndex, hidden);
4600 * Sets the editor for a column.
4601 * @param {Number} col The column index
4602 * @param {Object} editor The editor object
4604 setEditor : function(col, editor){
4605 this.config[col].editor = editor;
4609 Roo.grid.ColumnModel.defaultRenderer = function(value){
4610 if(typeof value == "string" && value.length < 1){
4616 // Alias for backwards compatibility
4617 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4620 * Ext JS Library 1.1.1
4621 * Copyright(c) 2006-2007, Ext JS, LLC.
4623 * Originally Released Under LGPL - original licence link has changed is not relivant.
4626 * <script type="text/javascript">
4630 * @class Roo.LoadMask
4631 * A simple utility class for generically masking elements while loading data. If the element being masked has
4632 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4633 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
4634 * element's UpdateManager load indicator and will be destroyed after the initial load.
4636 * Create a new LoadMask
4637 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4638 * @param {Object} config The config object
4640 Roo.LoadMask = function(el, config){
4641 this.el = Roo.get(el);
4642 Roo.apply(this, config);
4644 this.store.on('beforeload', this.onBeforeLoad, this);
4645 this.store.on('load', this.onLoad, this);
4646 this.store.on('loadexception', this.onLoadException, this);
4647 this.removeMask = false;
4649 var um = this.el.getUpdateManager();
4650 um.showLoadIndicator = false; // disable the default indicator
4651 um.on('beforeupdate', this.onBeforeLoad, this);
4652 um.on('update', this.onLoad, this);
4653 um.on('failure', this.onLoad, this);
4654 this.removeMask = true;
4658 Roo.LoadMask.prototype = {
4660 * @cfg {Boolean} removeMask
4661 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4662 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
4666 * The text to display in a centered loading message box (defaults to 'Loading...')
4670 * @cfg {String} msgCls
4671 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4673 msgCls : 'x-mask-loading',
4676 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4682 * Disables the mask to prevent it from being displayed
4684 disable : function(){
4685 this.disabled = true;
4689 * Enables the mask so that it can be displayed
4691 enable : function(){
4692 this.disabled = false;
4695 onLoadException : function()
4699 if (typeof(arguments[3]) != 'undefined') {
4700 Roo.MessageBox.alert("Error loading",arguments[3]);
4704 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4705 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4714 this.el.unmask(this.removeMask);
4719 this.el.unmask(this.removeMask);
4723 onBeforeLoad : function(){
4725 this.el.mask(this.msg, this.msgCls);
4730 destroy : function(){
4732 this.store.un('beforeload', this.onBeforeLoad, this);
4733 this.store.un('load', this.onLoad, this);
4734 this.store.un('loadexception', this.onLoadException, this);
4736 var um = this.el.getUpdateManager();
4737 um.un('beforeupdate', this.onBeforeLoad, this);
4738 um.un('update', this.onLoad, this);
4739 um.un('failure', this.onLoad, this);
4750 * @class Roo.bootstrap.Table
4751 * @extends Roo.bootstrap.Component
4752 * Bootstrap Table class
4753 * @cfg {String} cls table class
4754 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4755 * @cfg {String} bgcolor Specifies the background color for a table
4756 * @cfg {Number} border Specifies whether the table cells should have borders or not
4757 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4758 * @cfg {Number} cellspacing Specifies the space between cells
4759 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4760 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4761 * @cfg {String} sortable Specifies that the table should be sortable
4762 * @cfg {String} summary Specifies a summary of the content of a table
4763 * @cfg {Number} width Specifies the width of a table
4764 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4766 * @cfg {boolean} striped Should the rows be alternative striped
4767 * @cfg {boolean} bordered Add borders to the table
4768 * @cfg {boolean} hover Add hover highlighting
4769 * @cfg {boolean} condensed Format condensed
4770 * @cfg {boolean} responsive Format condensed
4771 * @cfg {Boolean} loadMask (true|false) default false
4772 * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4773 * @cfg {Boolean} thead (true|false) generate thead, default true
4774 * @cfg {Boolean} RowSelection (true|false) default false
4775 * @cfg {Boolean} CellSelection (true|false) default false
4777 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
4781 * Create a new Table
4782 * @param {Object} config The config object
4785 Roo.bootstrap.Table = function(config){
4786 Roo.bootstrap.Table.superclass.constructor.call(this, config);
4789 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4790 this.sm = this.selModel;
4791 this.sm.xmodule = this.xmodule || false;
4793 if (this.cm && typeof(this.cm.config) == 'undefined') {
4794 this.colModel = new Roo.grid.ColumnModel(this.cm);
4795 this.cm = this.colModel;
4796 this.cm.xmodule = this.xmodule || false;
4799 this.store= Roo.factory(this.store, Roo.data);
4800 this.ds = this.store;
4801 this.ds.xmodule = this.xmodule || false;
4804 if (this.footer && this.store) {
4805 this.footer.dataSource = this.ds;
4806 this.footer = Roo.factory(this.footer);
4813 * Fires when a cell is clicked
4814 * @param {Roo.bootstrap.Table} this
4815 * @param {Roo.Element} el
4816 * @param {Number} rowIndex
4817 * @param {Number} columnIndex
4818 * @param {Roo.EventObject} e
4822 * @event celldblclick
4823 * Fires when a cell is double clicked
4824 * @param {Roo.bootstrap.Table} this
4825 * @param {Roo.Element} el
4826 * @param {Number} rowIndex
4827 * @param {Number} columnIndex
4828 * @param {Roo.EventObject} e
4830 "celldblclick" : true,
4833 * Fires when a row is clicked
4834 * @param {Roo.bootstrap.Table} this
4835 * @param {Roo.Element} el
4836 * @param {Number} rowIndex
4837 * @param {Roo.EventObject} e
4841 * @event rowdblclick
4842 * Fires when a row is double clicked
4843 * @param {Roo.bootstrap.Table} this
4844 * @param {Roo.Element} el
4845 * @param {Number} rowIndex
4846 * @param {Roo.EventObject} e
4848 "rowdblclick" : true,
4851 * Fires when a mouseover occur
4852 * @param {Roo.bootstrap.Table} this
4853 * @param {Roo.Element} el
4854 * @param {Number} rowIndex
4855 * @param {Number} columnIndex
4856 * @param {Roo.EventObject} e
4861 * Fires when a mouseout occur
4862 * @param {Roo.bootstrap.Table} this
4863 * @param {Roo.Element} el
4864 * @param {Number} rowIndex
4865 * @param {Number} columnIndex
4866 * @param {Roo.EventObject} e
4871 * Fires when a row is rendered, so you can change add a style to it.
4872 * @param {Roo.bootstrap.Table} this
4873 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
4880 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
4904 RowSelection : false,
4905 CellSelection : false,
4908 // Roo.Element - the tbody
4911 getAutoCreate : function(){
4912 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4921 cfg.cls += ' table-striped';
4925 cfg.cls += ' table-hover';
4927 if (this.bordered) {
4928 cfg.cls += ' table-bordered';
4930 if (this.condensed) {
4931 cfg.cls += ' table-condensed';
4933 if (this.responsive) {
4934 cfg.cls += ' table-responsive';
4938 cfg.cls+= ' ' +this.cls;
4941 // this lot should be simplifed...
4944 cfg.align=this.align;
4947 cfg.bgcolor=this.bgcolor;
4950 cfg.border=this.border;
4952 if (this.cellpadding) {
4953 cfg.cellpadding=this.cellpadding;
4955 if (this.cellspacing) {
4956 cfg.cellspacing=this.cellspacing;
4959 cfg.frame=this.frame;
4962 cfg.rules=this.rules;
4964 if (this.sortable) {
4965 cfg.sortable=this.sortable;
4968 cfg.summary=this.summary;
4971 cfg.width=this.width;
4974 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
4977 if(this.store || this.cm){
4979 cfg.cn.push(this.renderHeader());
4982 cfg.cn.push(this.renderBody());
4985 cfg.cn.push(this.renderFooter());
4988 cfg.cls+= ' TableGrid';
4991 return { cn : [ cfg ] };
4994 initEvents : function()
4996 if(!this.store || !this.cm){
5000 //Roo.log('initEvents with ds!!!!');
5002 this.mainBody = this.el.select('tbody', true).first();
5007 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5008 e.on('click', _this.sort, _this);
5011 this.el.on("click", this.onClick, this);
5012 this.el.on("dblclick", this.onDblClick, this);
5014 this.parent().el.setStyle('position', 'relative');
5016 this.footer.parentId = this.id;
5017 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5020 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5022 this.store.on('load', this.onLoad, this);
5023 this.store.on('beforeload', this.onBeforeLoad, this);
5024 this.store.on('update', this.onUpdate, this);
5028 onMouseover : function(e, el)
5030 var cell = Roo.get(el);
5036 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5037 cell = cell.findParent('td', false, true);
5040 var row = cell.findParent('tr', false, true);
5041 var cellIndex = cell.dom.cellIndex;
5042 var rowIndex = row.dom.rowIndex - 1; // start from 0
5044 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5048 onMouseout : function(e, el)
5050 var cell = Roo.get(el);
5056 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5057 cell = cell.findParent('td', false, true);
5060 var row = cell.findParent('tr', false, true);
5061 var cellIndex = cell.dom.cellIndex;
5062 var rowIndex = row.dom.rowIndex - 1; // start from 0
5064 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5068 onClick : function(e, el)
5070 var cell = Roo.get(el);
5072 if(!cell || (!this.CellSelection && !this.RowSelection)){
5077 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5078 cell = cell.findParent('td', false, true);
5081 var row = cell.findParent('tr', false, true);
5082 var cellIndex = cell.dom.cellIndex;
5083 var rowIndex = row.dom.rowIndex - 1;
5085 if(this.CellSelection){
5086 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5089 if(this.RowSelection){
5090 this.fireEvent('rowclick', this, row, rowIndex, e);
5096 onDblClick : function(e,el)
5098 var cell = Roo.get(el);
5100 if(!cell || (!this.CellSelection && !this.RowSelection)){
5104 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5105 cell = cell.findParent('td', false, true);
5108 var row = cell.findParent('tr', false, true);
5109 var cellIndex = cell.dom.cellIndex;
5110 var rowIndex = row.dom.rowIndex - 1;
5112 if(this.CellSelection){
5113 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5116 if(this.RowSelection){
5117 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5121 sort : function(e,el)
5123 var col = Roo.get(el)
5125 if(!col.hasClass('sortable')){
5129 var sort = col.attr('sort');
5132 if(col.hasClass('glyphicon-arrow-up')){
5136 this.store.sortInfo = {field : sort, direction : dir};
5139 Roo.log("calling footer first");
5140 this.footer.onClick('first');
5143 this.store.load({ params : { start : 0 } });
5147 renderHeader : function()
5156 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5158 var config = cm.config[i];
5163 html: cm.getColumnHeader(i)
5166 if(typeof(config.hidden) != 'undefined' && config.hidden){
5167 c.style += ' display:none;';
5170 if(typeof(config.dataIndex) != 'undefined'){
5171 c.sort = config.dataIndex;
5174 if(typeof(config.sortable) != 'undefined' && config.sortable){
5178 if(typeof(config.align) != 'undefined' && config.align.length){
5179 c.style += ' text-align:' + config.align + ';';
5182 if(typeof(config.width) != 'undefined'){
5183 c.style += ' width:' + config.width + 'px;';
5192 renderBody : function()
5202 colspan : this.cm.getColumnCount()
5212 renderFooter : function()
5222 colspan : this.cm.getColumnCount()
5236 Roo.log('ds onload');
5241 var ds = this.store;
5243 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5244 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5246 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5247 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5250 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5251 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5255 var tbody = this.mainBody;
5257 if(ds.getCount() > 0){
5258 ds.data.each(function(d,rowIndex){
5259 var row = this.renderRow(cm, ds, rowIndex);
5261 tbody.createChild(row);
5265 if(row.cellObjects.length){
5266 Roo.each(row.cellObjects, function(r){
5267 _this.renderCellObject(r);
5274 Roo.each(this.el.select('tbody td', true).elements, function(e){
5275 e.on('mouseover', _this.onMouseover, _this);
5278 Roo.each(this.el.select('tbody td', true).elements, function(e){
5279 e.on('mouseout', _this.onMouseout, _this);
5282 //if(this.loadMask){
5283 // this.maskEl.hide();
5288 onUpdate : function(ds,record)
5290 this.refreshRow(record);
5292 onRemove : function(ds, record, index, isUpdate){
5293 if(isUpdate !== true){
5294 this.fireEvent("beforerowremoved", this, index, record);
5296 var bt = this.mainBody.dom;
5298 bt.removeChild(bt.rows[index]);
5301 if(isUpdate !== true){
5302 //this.stripeRows(index);
5303 //this.syncRowHeights(index, index);
5305 this.fireEvent("rowremoved", this, index, record);
5310 refreshRow : function(record){
5311 var ds = this.store, index;
5312 if(typeof record == 'number'){
5314 record = ds.getAt(index);
5316 index = ds.indexOf(record);
5318 this.insertRow(ds, index, true);
5319 this.onRemove(ds, record, index+1, true);
5320 //this.syncRowHeights(index, index);
5322 this.fireEvent("rowupdated", this, index, record);
5325 insertRow : function(dm, rowIndex, isUpdate){
5328 this.fireEvent("beforerowsinserted", this, rowIndex);
5330 //var s = this.getScrollState();
5331 var row = this.renderRow(this.cm, this.store, rowIndex);
5332 // insert before rowIndex..
5333 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5337 if(row.cellObjects.length){
5338 Roo.each(row.cellObjects, function(r){
5339 _this.renderCellObject(r);
5344 this.fireEvent("rowsinserted", this, rowIndex);
5345 //this.syncRowHeights(firstRow, lastRow);
5346 //this.stripeRows(firstRow);
5353 getRowDom : function(rowIndex)
5355 // not sure if I need to check this.. but let's do it anyway..
5356 return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5357 this.mainBody.dom.rows[rowIndex] : false
5359 // returns the object tree for a tr..
5362 renderRow : function(cm, ds, rowIndex) {
5364 var d = ds.getAt(rowIndex);
5371 var cellObjects = [];
5373 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5374 var config = cm.config[i];
5376 var renderer = cm.getRenderer(i);
5380 if(typeof(renderer) !== 'undefined'){
5381 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5383 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5384 // and are rendered into the cells after the row is rendered - using the id for the element.
5386 if(typeof(value) === 'object'){
5396 rowIndex : rowIndex,
5401 this.fireEvent('rowclass', this, rowcfg);
5405 cls : rowcfg.rowClass,
5407 html: (typeof(value) === 'object') ? '' : value
5414 if(typeof(config.hidden) != 'undefined' && config.hidden){
5415 td.style += ' display:none;';
5418 if(typeof(config.align) != 'undefined' && config.align.length){
5419 td.style += ' text-align:' + config.align + ';';
5422 if(typeof(config.width) != 'undefined'){
5423 td.style += ' width:' + config.width + 'px;';
5430 row.cellObjects = cellObjects;
5438 onBeforeLoad : function()
5440 //Roo.log('ds onBeforeLoad');
5444 //if(this.loadMask){
5445 // this.maskEl.show();
5451 this.el.select('tbody', true).first().dom.innerHTML = '';
5454 getSelectionModel : function(){
5456 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5458 return this.selModel;
5461 * Render the Roo.bootstrap object from renderder
5463 renderCellObject : function(r)
5467 var t = r.cfg.render(r.container);
5470 Roo.each(r.cfg.cn, function(c){
5472 container: t.getChildContainer(),
5475 _this.renderCellObject(child);
5492 * @class Roo.bootstrap.TableCell
5493 * @extends Roo.bootstrap.Component
5494 * Bootstrap TableCell class
5495 * @cfg {String} html cell contain text
5496 * @cfg {String} cls cell class
5497 * @cfg {String} tag cell tag (td|th) default td
5498 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5499 * @cfg {String} align Aligns the content in a cell
5500 * @cfg {String} axis Categorizes cells
5501 * @cfg {String} bgcolor Specifies the background color of a cell
5502 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5503 * @cfg {Number} colspan Specifies the number of columns a cell should span
5504 * @cfg {String} headers Specifies one or more header cells a cell is related to
5505 * @cfg {Number} height Sets the height of a cell
5506 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5507 * @cfg {Number} rowspan Sets the number of rows a cell should span
5508 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5509 * @cfg {String} valign Vertical aligns the content in a cell
5510 * @cfg {Number} width Specifies the width of a cell
5513 * Create a new TableCell
5514 * @param {Object} config The config object
5517 Roo.bootstrap.TableCell = function(config){
5518 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5521 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
5541 getAutoCreate : function(){
5542 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5562 cfg.align=this.align
5568 cfg.bgcolor=this.bgcolor
5571 cfg.charoff=this.charoff
5574 cfg.colspan=this.colspan
5577 cfg.headers=this.headers
5580 cfg.height=this.height
5583 cfg.nowrap=this.nowrap
5586 cfg.rowspan=this.rowspan
5589 cfg.scope=this.scope
5592 cfg.valign=this.valign
5595 cfg.width=this.width
5614 * @class Roo.bootstrap.TableRow
5615 * @extends Roo.bootstrap.Component
5616 * Bootstrap TableRow class
5617 * @cfg {String} cls row class
5618 * @cfg {String} align Aligns the content in a table row
5619 * @cfg {String} bgcolor Specifies a background color for a table row
5620 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5621 * @cfg {String} valign Vertical aligns the content in a table row
5624 * Create a new TableRow
5625 * @param {Object} config The config object
5628 Roo.bootstrap.TableRow = function(config){
5629 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5632 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
5640 getAutoCreate : function(){
5641 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5651 cfg.align = this.align;
5654 cfg.bgcolor = this.bgcolor;
5657 cfg.charoff = this.charoff;
5660 cfg.valign = this.valign;
5678 * @class Roo.bootstrap.TableBody
5679 * @extends Roo.bootstrap.Component
5680 * Bootstrap TableBody class
5681 * @cfg {String} cls element class
5682 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5683 * @cfg {String} align Aligns the content inside the element
5684 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5685 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5688 * Create a new TableBody
5689 * @param {Object} config The config object
5692 Roo.bootstrap.TableBody = function(config){
5693 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5696 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
5704 getAutoCreate : function(){
5705 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5719 cfg.align = this.align;
5722 cfg.charoff = this.charoff;
5725 cfg.valign = this.valign;
5732 // initEvents : function()
5739 // this.store = Roo.factory(this.store, Roo.data);
5740 // this.store.on('load', this.onLoad, this);
5742 // this.store.load();
5746 // onLoad: function ()
5748 // this.fireEvent('load', this);
5758 * Ext JS Library 1.1.1
5759 * Copyright(c) 2006-2007, Ext JS, LLC.
5761 * Originally Released Under LGPL - original licence link has changed is not relivant.
5764 * <script type="text/javascript">
5767 // as we use this in bootstrap.
5768 Roo.namespace('Roo.form');
5770 * @class Roo.form.Action
5771 * Internal Class used to handle form actions
5773 * @param {Roo.form.BasicForm} el The form element or its id
5774 * @param {Object} config Configuration options
5779 // define the action interface
5780 Roo.form.Action = function(form, options){
5782 this.options = options || {};
5785 * Client Validation Failed
5788 Roo.form.Action.CLIENT_INVALID = 'client';
5790 * Server Validation Failed
5793 Roo.form.Action.SERVER_INVALID = 'server';
5795 * Connect to Server Failed
5798 Roo.form.Action.CONNECT_FAILURE = 'connect';
5800 * Reading Data from Server Failed
5803 Roo.form.Action.LOAD_FAILURE = 'load';
5805 Roo.form.Action.prototype = {
5807 failureType : undefined,
5808 response : undefined,
5812 run : function(options){
5817 success : function(response){
5822 handleResponse : function(response){
5826 // default connection failure
5827 failure : function(response){
5829 this.response = response;
5830 this.failureType = Roo.form.Action.CONNECT_FAILURE;
5831 this.form.afterAction(this, false);
5834 processResponse : function(response){
5835 this.response = response;
5836 if(!response.responseText){
5839 this.result = this.handleResponse(response);
5843 // utility functions used internally
5844 getUrl : function(appendParams){
5845 var url = this.options.url || this.form.url || this.form.el.dom.action;
5847 var p = this.getParams();
5849 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5855 getMethod : function(){
5856 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5859 getParams : function(){
5860 var bp = this.form.baseParams;
5861 var p = this.options.params;
5863 if(typeof p == "object"){
5864 p = Roo.urlEncode(Roo.applyIf(p, bp));
5865 }else if(typeof p == 'string' && bp){
5866 p += '&' + Roo.urlEncode(bp);
5869 p = Roo.urlEncode(bp);
5874 createCallback : function(){
5876 success: this.success,
5877 failure: this.failure,
5879 timeout: (this.form.timeout*1000),
5880 upload: this.form.fileUpload ? this.success : undefined
5885 Roo.form.Action.Submit = function(form, options){
5886 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5889 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5892 haveProgress : false,
5893 uploadComplete : false,
5895 // uploadProgress indicator.
5896 uploadProgress : function()
5898 if (!this.form.progressUrl) {
5902 if (!this.haveProgress) {
5903 Roo.MessageBox.progress("Uploading", "Uploading");
5905 if (this.uploadComplete) {
5906 Roo.MessageBox.hide();
5910 this.haveProgress = true;
5912 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5914 var c = new Roo.data.Connection();
5916 url : this.form.progressUrl,
5921 success : function(req){
5922 //console.log(data);
5926 rdata = Roo.decode(req.responseText)
5928 Roo.log("Invalid data from server..");
5932 if (!rdata || !rdata.success) {
5934 Roo.MessageBox.alert(Roo.encode(rdata));
5937 var data = rdata.data;
5939 if (this.uploadComplete) {
5940 Roo.MessageBox.hide();
5945 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5946 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
5949 this.uploadProgress.defer(2000,this);
5952 failure: function(data) {
5953 Roo.log('progress url failed ');
5964 // run get Values on the form, so it syncs any secondary forms.
5965 this.form.getValues();
5967 var o = this.options;
5968 var method = this.getMethod();
5969 var isPost = method == 'POST';
5970 if(o.clientValidation === false || this.form.isValid()){
5972 if (this.form.progressUrl) {
5973 this.form.findField('UPLOAD_IDENTIFIER').setValue(
5974 (new Date() * 1) + '' + Math.random());
5979 Roo.Ajax.request(Roo.apply(this.createCallback(), {
5980 form:this.form.el.dom,
5981 url:this.getUrl(!isPost),
5983 params:isPost ? this.getParams() : null,
5984 isUpload: this.form.fileUpload
5987 this.uploadProgress();
5989 }else if (o.clientValidation !== false){ // client validation failed
5990 this.failureType = Roo.form.Action.CLIENT_INVALID;
5991 this.form.afterAction(this, false);
5995 success : function(response)
5997 this.uploadComplete= true;
5998 if (this.haveProgress) {
5999 Roo.MessageBox.hide();
6003 var result = this.processResponse(response);
6004 if(result === true || result.success){
6005 this.form.afterAction(this, true);
6009 this.form.markInvalid(result.errors);
6010 this.failureType = Roo.form.Action.SERVER_INVALID;
6012 this.form.afterAction(this, false);
6014 failure : function(response)
6016 this.uploadComplete= true;
6017 if (this.haveProgress) {
6018 Roo.MessageBox.hide();
6021 this.response = response;
6022 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6023 this.form.afterAction(this, false);
6026 handleResponse : function(response){
6027 if(this.form.errorReader){
6028 var rs = this.form.errorReader.read(response);
6031 for(var i = 0, len = rs.records.length; i < len; i++) {
6032 var r = rs.records[i];
6036 if(errors.length < 1){
6040 success : rs.success,
6046 ret = Roo.decode(response.responseText);
6050 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6060 Roo.form.Action.Load = function(form, options){
6061 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6062 this.reader = this.form.reader;
6065 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6070 Roo.Ajax.request(Roo.apply(
6071 this.createCallback(), {
6072 method:this.getMethod(),
6073 url:this.getUrl(false),
6074 params:this.getParams()
6078 success : function(response){
6080 var result = this.processResponse(response);
6081 if(result === true || !result.success || !result.data){
6082 this.failureType = Roo.form.Action.LOAD_FAILURE;
6083 this.form.afterAction(this, false);
6086 this.form.clearInvalid();
6087 this.form.setValues(result.data);
6088 this.form.afterAction(this, true);
6091 handleResponse : function(response){
6092 if(this.form.reader){
6093 var rs = this.form.reader.read(response);
6094 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6096 success : rs.success,
6100 return Roo.decode(response.responseText);
6104 Roo.form.Action.ACTION_TYPES = {
6105 'load' : Roo.form.Action.Load,
6106 'submit' : Roo.form.Action.Submit
6115 * @class Roo.bootstrap.Form
6116 * @extends Roo.bootstrap.Component
6117 * Bootstrap Form class
6118 * @cfg {String} method GET | POST (default POST)
6119 * @cfg {String} labelAlign top | left (default top)
6120 * @cfg {String} align left | right - for navbars
6125 * @param {Object} config The config object
6129 Roo.bootstrap.Form = function(config){
6130 Roo.bootstrap.Form.superclass.constructor.call(this, config);
6133 * @event clientvalidation
6134 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6135 * @param {Form} this
6136 * @param {Boolean} valid true if the form has passed client-side validation
6138 clientvalidation: true,
6140 * @event beforeaction
6141 * Fires before any action is performed. Return false to cancel the action.
6142 * @param {Form} this
6143 * @param {Action} action The action to be performed
6147 * @event actionfailed
6148 * Fires when an action fails.
6149 * @param {Form} this
6150 * @param {Action} action The action that failed
6152 actionfailed : true,
6154 * @event actioncomplete
6155 * Fires when an action is completed.
6156 * @param {Form} this
6157 * @param {Action} action The action that completed
6159 actioncomplete : true
6164 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
6167 * @cfg {String} method
6168 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6173 * The URL to use for form actions if one isn't supplied in the action options.
6176 * @cfg {Boolean} fileUpload
6177 * Set to true if this form is a file upload.
6181 * @cfg {Object} baseParams
6182 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6186 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6190 * @cfg {Sting} align (left|right) for navbar forms
6195 activeAction : null,
6198 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6199 * element by passing it or its id or mask the form itself by passing in true.
6202 waitMsgTarget : false,
6207 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6208 * element by passing it or its id or mask the form itself by passing in true.
6212 getAutoCreate : function(){
6216 method : this.method || 'POST',
6217 id : this.id || Roo.id(),
6220 if (this.parent().xtype.match(/^Nav/)) {
6221 cfg.cls = 'navbar-form navbar-' + this.align;
6225 if (this.labelAlign == 'left' ) {
6226 cfg.cls += ' form-horizontal';
6232 initEvents : function()
6234 this.el.on('submit', this.onSubmit, this);
6235 // this was added as random key presses on the form where triggering form submit.
6236 this.el.on('keypress', function(e) {
6237 if (e.getCharCode() != 13) {
6240 // we might need to allow it for textareas.. and some other items.
6241 // check e.getTarget().
6243 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6247 Roo.log("keypress blocked");
6255 onSubmit : function(e){
6260 * Returns true if client-side validation on the form is successful.
6263 isValid : function(){
6264 var items = this.getItems();
6266 items.each(function(f){
6275 * Returns true if any fields in this form have changed since their original load.
6278 isDirty : function(){
6280 var items = this.getItems();
6281 items.each(function(f){
6291 * Performs a predefined action (submit or load) or custom actions you define on this form.
6292 * @param {String} actionName The name of the action type
6293 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
6294 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6295 * accept other config options):
6297 Property Type Description
6298 ---------------- --------------- ----------------------------------------------------------------------------------
6299 url String The url for the action (defaults to the form's url)
6300 method String The form method to use (defaults to the form's method, or POST if not defined)
6301 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
6302 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
6303 validate the form on the client (defaults to false)
6305 * @return {BasicForm} this
6307 doAction : function(action, options){
6308 if(typeof action == 'string'){
6309 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6311 if(this.fireEvent('beforeaction', this, action) !== false){
6312 this.beforeAction(action);
6313 action.run.defer(100, action);
6319 beforeAction : function(action){
6320 var o = action.options;
6322 // not really supported yet.. ??
6324 //if(this.waitMsgTarget === true){
6325 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6326 //}else if(this.waitMsgTarget){
6327 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6328 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6330 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6336 afterAction : function(action, success){
6337 this.activeAction = null;
6338 var o = action.options;
6340 //if(this.waitMsgTarget === true){
6342 //}else if(this.waitMsgTarget){
6343 // this.waitMsgTarget.unmask();
6345 // Roo.MessageBox.updateProgress(1);
6346 // Roo.MessageBox.hide();
6353 Roo.callback(o.success, o.scope, [this, action]);
6354 this.fireEvent('actioncomplete', this, action);
6358 // failure condition..
6359 // we have a scenario where updates need confirming.
6360 // eg. if a locking scenario exists..
6361 // we look for { errors : { needs_confirm : true }} in the response.
6363 (typeof(action.result) != 'undefined') &&
6364 (typeof(action.result.errors) != 'undefined') &&
6365 (typeof(action.result.errors.needs_confirm) != 'undefined')
6368 Roo.log("not supported yet");
6371 Roo.MessageBox.confirm(
6372 "Change requires confirmation",
6373 action.result.errorMsg,
6378 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
6388 Roo.callback(o.failure, o.scope, [this, action]);
6389 // show an error message if no failed handler is set..
6390 if (!this.hasListener('actionfailed')) {
6391 Roo.log("need to add dialog support");
6393 Roo.MessageBox.alert("Error",
6394 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6395 action.result.errorMsg :
6396 "Saving Failed, please check your entries or try again"
6401 this.fireEvent('actionfailed', this, action);
6406 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6407 * @param {String} id The value to search for
6410 findField : function(id){
6411 var items = this.getItems();
6412 var field = items.get(id);
6414 items.each(function(f){
6415 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6422 return field || null;
6425 * Mark fields in this form invalid in bulk.
6426 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6427 * @return {BasicForm} this
6429 markInvalid : function(errors){
6430 if(errors instanceof Array){
6431 for(var i = 0, len = errors.length; i < len; i++){
6432 var fieldError = errors[i];
6433 var f = this.findField(fieldError.id);
6435 f.markInvalid(fieldError.msg);
6441 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6442 field.markInvalid(errors[id]);
6446 //Roo.each(this.childForms || [], function (f) {
6447 // f.markInvalid(errors);
6454 * Set values for fields in this form in bulk.
6455 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6456 * @return {BasicForm} this
6458 setValues : function(values){
6459 if(values instanceof Array){ // array of objects
6460 for(var i = 0, len = values.length; i < len; i++){
6462 var f = this.findField(v.id);
6464 f.setValue(v.value);
6465 if(this.trackResetOnLoad){
6466 f.originalValue = f.getValue();
6470 }else{ // object hash
6473 if(typeof values[id] != 'function' && (field = this.findField(id))){
6475 if (field.setFromData &&
6477 field.displayField &&
6478 // combos' with local stores can
6479 // be queried via setValue()
6480 // to set their value..
6481 (field.store && !field.store.isLocal)
6485 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6486 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6487 field.setFromData(sd);
6490 field.setValue(values[id]);
6494 if(this.trackResetOnLoad){
6495 field.originalValue = field.getValue();
6501 //Roo.each(this.childForms || [], function (f) {
6502 // f.setValues(values);
6509 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6510 * they are returned as an array.
6511 * @param {Boolean} asString
6514 getValues : function(asString){
6515 //if (this.childForms) {
6516 // copy values from the child forms
6517 // Roo.each(this.childForms, function (f) {
6518 // this.setValues(f.getValues());
6524 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6525 if(asString === true){
6528 return Roo.urlDecode(fs);
6532 * Returns the fields in this form as an object with key/value pairs.
6533 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6536 getFieldValues : function(with_hidden)
6538 var items = this.getItems();
6540 items.each(function(f){
6544 var v = f.getValue();
6545 if (f.inputType =='radio') {
6546 if (typeof(ret[f.getName()]) == 'undefined') {
6547 ret[f.getName()] = ''; // empty..
6550 if (!f.el.dom.checked) {
6558 // not sure if this supported any more..
6559 if ((typeof(v) == 'object') && f.getRawValue) {
6560 v = f.getRawValue() ; // dates..
6562 // combo boxes where name != hiddenName...
6563 if (f.name != f.getName()) {
6564 ret[f.name] = f.getRawValue();
6566 ret[f.getName()] = v;
6573 * Clears all invalid messages in this form.
6574 * @return {BasicForm} this
6576 clearInvalid : function(){
6577 var items = this.getItems();
6579 items.each(function(f){
6590 * @return {BasicForm} this
6593 var items = this.getItems();
6594 items.each(function(f){
6598 Roo.each(this.childForms || [], function (f) {
6605 getItems : function()
6607 var r=new Roo.util.MixedCollection(false, function(o){
6608 return o.id || (o.id = Roo.id());
6610 var iter = function(el) {
6617 Roo.each(el.items,function(e) {
6636 * Ext JS Library 1.1.1
6637 * Copyright(c) 2006-2007, Ext JS, LLC.
6639 * Originally Released Under LGPL - original licence link has changed is not relivant.
6642 * <script type="text/javascript">
6645 * @class Roo.form.VTypes
6646 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6649 Roo.form.VTypes = function(){
6650 // closure these in so they are only created once.
6651 var alpha = /^[a-zA-Z_]+$/;
6652 var alphanum = /^[a-zA-Z0-9_]+$/;
6653 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6654 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6656 // All these messages and functions are configurable
6659 * The function used to validate email addresses
6660 * @param {String} value The email address
6662 'email' : function(v){
6663 return email.test(v);
6666 * The error text to display when the email validation function returns false
6669 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6671 * The keystroke filter mask to be applied on email input
6674 'emailMask' : /[a-z0-9_\.\-@]/i,
6677 * The function used to validate URLs
6678 * @param {String} value The URL
6680 'url' : function(v){
6684 * The error text to display when the url validation function returns false
6687 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6690 * The function used to validate alpha values
6691 * @param {String} value The value
6693 'alpha' : function(v){
6694 return alpha.test(v);
6697 * The error text to display when the alpha validation function returns false
6700 'alphaText' : 'This field should only contain letters and _',
6702 * The keystroke filter mask to be applied on alpha input
6705 'alphaMask' : /[a-z_]/i,
6708 * The function used to validate alphanumeric values
6709 * @param {String} value The value
6711 'alphanum' : function(v){
6712 return alphanum.test(v);
6715 * The error text to display when the alphanumeric validation function returns false
6718 'alphanumText' : 'This field should only contain letters, numbers and _',
6720 * The keystroke filter mask to be applied on alphanumeric input
6723 'alphanumMask' : /[a-z0-9_]/i
6733 * @class Roo.bootstrap.Input
6734 * @extends Roo.bootstrap.Component
6735 * Bootstrap Input class
6736 * @cfg {Boolean} disabled is it disabled
6737 * @cfg {String} fieldLabel - the label associated
6738 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6739 * @cfg {String} name name of the input
6740 * @cfg {string} fieldLabel - the label associated
6741 * @cfg {string} inputType - input / file submit ...
6742 * @cfg {string} placeholder - placeholder to put in text.
6743 * @cfg {string} before - input group add on before
6744 * @cfg {string} after - input group add on after
6745 * @cfg {string} size - (lg|sm) or leave empty..
6746 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6747 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6748 * @cfg {Number} md colspan out of 12 for computer-sized screens
6749 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6750 * @cfg {string} value default value of the input
6751 * @cfg {Number} labelWidth set the width of label (0-12)
6752 * @cfg {String} labelAlign (top|left)
6753 * @cfg {Boolean} readOnly Specifies that the field should be read-only
6754 * @cfg {String} align (left|center|right) Default left
6758 * Create a new Input
6759 * @param {Object} config The config object
6762 Roo.bootstrap.Input = function(config){
6763 Roo.bootstrap.Input.superclass.constructor.call(this, config);
6768 * Fires when this field receives input focus.
6769 * @param {Roo.form.Field} this
6774 * Fires when this field loses input focus.
6775 * @param {Roo.form.Field} this
6780 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
6781 * {@link Roo.EventObject#getKey} to determine which key was pressed.
6782 * @param {Roo.form.Field} this
6783 * @param {Roo.EventObject} e The event object
6788 * Fires just before the field blurs if the field value has changed.
6789 * @param {Roo.form.Field} this
6790 * @param {Mixed} newValue The new value
6791 * @param {Mixed} oldValue The original value
6796 * Fires after the field has been marked as invalid.
6797 * @param {Roo.form.Field} this
6798 * @param {String} msg The validation message
6803 * Fires after the field has been validated with no errors.
6804 * @param {Roo.form.Field} this
6809 * Fires after the key up
6810 * @param {Roo.form.Field} this
6811 * @param {Roo.EventObject} e The event Object
6817 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
6819 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6820 automatic validation (defaults to "keyup").
6822 validationEvent : "keyup",
6824 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6826 validateOnBlur : true,
6828 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6830 validationDelay : 250,
6832 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6834 focusClass : "x-form-focus", // not needed???
6838 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6840 invalidClass : "has-error",
6843 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6845 selectOnFocus : false,
6848 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6852 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6857 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6859 disableKeyFilter : false,
6862 * @cfg {Boolean} disabled True to disable the field (defaults to false).
6866 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6870 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6872 blankText : "This field is required",
6875 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6879 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6881 maxLength : Number.MAX_VALUE,
6883 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6885 minLengthText : "The minimum length for this field is {0}",
6887 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6889 maxLengthText : "The maximum length for this field is {0}",
6893 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6894 * If available, this function will be called only after the basic validators all return true, and will be passed the
6895 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6899 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6900 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6901 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
6905 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6928 formatedValue : false,
6930 parentLabelAlign : function()
6933 while (parent.parent()) {
6934 parent = parent.parent();
6935 if (typeof(parent.labelAlign) !='undefined') {
6936 return parent.labelAlign;
6943 getAutoCreate : function(){
6945 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6951 if(this.inputType != 'hidden'){
6952 cfg.cls = 'form-group' //input-group
6958 type : this.inputType,
6960 cls : 'form-control',
6961 placeholder : this.placeholder || ''
6966 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
6969 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6970 input.maxLength = this.maxLength;
6973 if (this.disabled) {
6974 input.disabled=true;
6977 if (this.readOnly) {
6978 input.readonly=true;
6982 input.name = this.name;
6985 input.cls += ' input-' + this.size;
6988 ['xs','sm','md','lg'].map(function(size){
6989 if (settings[size]) {
6990 cfg.cls += ' col-' + size + '-' + settings[size];
6994 var inputblock = input;
6996 if (this.before || this.after) {
6999 cls : 'input-group',
7002 if (this.before && typeof(this.before) == 'string') {
7004 inputblock.cn.push({
7006 cls : 'roo-input-before input-group-addon',
7010 if (this.before && typeof(this.before) == 'object') {
7011 this.before = Roo.factory(this.before);
7012 Roo.log(this.before);
7013 inputblock.cn.push({
7015 cls : 'roo-input-before input-group-' +
7016 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7020 inputblock.cn.push(input);
7022 if (this.after && typeof(this.after) == 'string') {
7023 inputblock.cn.push({
7025 cls : 'roo-input-after input-group-addon',
7029 if (this.after && typeof(this.after) == 'object') {
7030 this.after = Roo.factory(this.after);
7031 Roo.log(this.after);
7032 inputblock.cn.push({
7034 cls : 'roo-input-after input-group-' +
7035 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7040 if (align ==='left' && this.fieldLabel.length) {
7041 Roo.log("left and has label");
7047 cls : 'control-label col-sm-' + this.labelWidth,
7048 html : this.fieldLabel
7052 cls : "col-sm-" + (12 - this.labelWidth),
7059 } else if ( this.fieldLabel.length) {
7065 //cls : 'input-group-addon',
7066 html : this.fieldLabel
7076 Roo.log(" no label && no align");
7085 Roo.log('input-parentType: ' + this.parentType);
7087 if (this.parentType === 'Navbar' && this.parent().bar) {
7088 cfg.cls += ' navbar-form';
7096 * return the real input element.
7098 inputEl: function ()
7100 return this.el.select('input.form-control',true).first();
7102 setDisabled : function(v)
7104 var i = this.inputEl().dom;
7106 i.removeAttribute('disabled');
7110 i.setAttribute('disabled','true');
7112 initEvents : function()
7115 this.inputEl().on("keydown" , this.fireKey, this);
7116 this.inputEl().on("focus", this.onFocus, this);
7117 this.inputEl().on("blur", this.onBlur, this);
7119 this.inputEl().relayEvent('keyup', this);
7121 // reference to original value for reset
7122 this.originalValue = this.getValue();
7123 //Roo.form.TextField.superclass.initEvents.call(this);
7124 if(this.validationEvent == 'keyup'){
7125 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7126 this.inputEl().on('keyup', this.filterValidation, this);
7128 else if(this.validationEvent !== false){
7129 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7132 if(this.selectOnFocus){
7133 this.on("focus", this.preFocus, this);
7136 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7137 this.inputEl().on("keypress", this.filterKeys, this);
7140 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
7141 this.el.on("click", this.autoSize, this);
7144 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7145 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7148 if (typeof(this.before) == 'object') {
7149 this.before.render(this.el.select('.roo-input-before',true).first());
7151 if (typeof(this.after) == 'object') {
7152 this.after.render(this.el.select('.roo-input-after',true).first());
7157 filterValidation : function(e){
7158 if(!e.isNavKeyPress()){
7159 this.validationTask.delay(this.validationDelay);
7163 * Validates the field value
7164 * @return {Boolean} True if the value is valid, else false
7166 validate : function(){
7167 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7168 if(this.disabled || this.validateValue(this.getRawValue())){
7169 this.clearInvalid();
7177 * Validates a value according to the field's validation rules and marks the field as invalid
7178 * if the validation fails
7179 * @param {Mixed} value The value to validate
7180 * @return {Boolean} True if the value is valid, else false
7182 validateValue : function(value){
7183 if(value.length < 1) { // if it's blank
7184 if(this.allowBlank){
7185 this.clearInvalid();
7188 this.markInvalid(this.blankText);
7192 if(value.length < this.minLength){
7193 this.markInvalid(String.format(this.minLengthText, this.minLength));
7196 if(value.length > this.maxLength){
7197 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7201 var vt = Roo.form.VTypes;
7202 if(!vt[this.vtype](value, this)){
7203 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7207 if(typeof this.validator == "function"){
7208 var msg = this.validator(value);
7210 this.markInvalid(msg);
7214 if(this.regex && !this.regex.test(value)){
7215 this.markInvalid(this.regexText);
7224 fireKey : function(e){
7225 //Roo.log('field ' + e.getKey());
7226 if(e.isNavKeyPress()){
7227 this.fireEvent("specialkey", this, e);
7230 focus : function (selectText){
7232 this.inputEl().focus();
7233 if(selectText === true){
7234 this.inputEl().dom.select();
7240 onFocus : function(){
7241 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7242 // this.el.addClass(this.focusClass);
7245 this.hasFocus = true;
7246 this.startValue = this.getValue();
7247 this.fireEvent("focus", this);
7251 beforeBlur : Roo.emptyFn,
7255 onBlur : function(){
7257 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7258 //this.el.removeClass(this.focusClass);
7260 this.hasFocus = false;
7261 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7264 var v = this.getValue();
7265 if(String(v) !== String(this.startValue)){
7266 this.fireEvent('change', this, v, this.startValue);
7268 this.fireEvent("blur", this);
7272 * Resets the current field value to the originally loaded value and clears any validation messages
7275 this.setValue(this.originalValue);
7276 this.clearInvalid();
7279 * Returns the name of the field
7280 * @return {Mixed} name The name field
7282 getName: function(){
7286 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
7287 * @return {Mixed} value The field value
7289 getValue : function(){
7291 var v = this.inputEl().getValue();
7296 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
7297 * @return {Mixed} value The field value
7299 getRawValue : function(){
7300 var v = this.inputEl().getValue();
7306 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
7307 * @param {Mixed} value The value to set
7309 setRawValue : function(v){
7310 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7313 selectText : function(start, end){
7314 var v = this.getRawValue();
7316 start = start === undefined ? 0 : start;
7317 end = end === undefined ? v.length : end;
7318 var d = this.inputEl().dom;
7319 if(d.setSelectionRange){
7320 d.setSelectionRange(start, end);
7321 }else if(d.createTextRange){
7322 var range = d.createTextRange();
7323 range.moveStart("character", start);
7324 range.moveEnd("character", v.length-end);
7331 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
7332 * @param {Mixed} value The value to set
7334 setValue : function(v){
7337 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7343 processValue : function(value){
7344 if(this.stripCharsRe){
7345 var newValue = value.replace(this.stripCharsRe, '');
7346 if(newValue !== value){
7347 this.setRawValue(newValue);
7354 preFocus : function(){
7356 if(this.selectOnFocus){
7357 this.inputEl().dom.select();
7360 filterKeys : function(e){
7362 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7365 var c = e.getCharCode(), cc = String.fromCharCode(c);
7366 if(Roo.isIE && (e.isSpecialKey() || !cc)){
7369 if(!this.maskRe.test(cc)){
7374 * Clear any invalid styles/messages for this field
7376 clearInvalid : function(){
7378 if(!this.el || this.preventMark){ // not rendered
7381 this.el.removeClass(this.invalidClass);
7383 switch(this.msgTarget){
7385 this.el.dom.qtip = '';
7388 this.el.dom.title = '';
7392 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7397 this.errorIcon.dom.qtip = '';
7398 this.errorIcon.hide();
7399 this.un('resize', this.alignErrorIcon, this);
7403 var t = Roo.getDom(this.msgTarget);
7405 t.style.display = 'none';
7409 this.fireEvent('valid', this);
7412 * Mark this field as invalid
7413 * @param {String} msg The validation message
7415 markInvalid : function(msg){
7416 if(!this.el || this.preventMark){ // not rendered
7419 this.el.addClass(this.invalidClass);
7421 msg = msg || this.invalidText;
7422 switch(this.msgTarget){
7424 this.el.dom.qtip = msg;
7425 this.el.dom.qclass = 'x-form-invalid-tip';
7426 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7427 Roo.QuickTips.enable();
7431 this.el.dom.title = msg;
7435 var elp = this.el.findParent('.x-form-element', 5, true);
7436 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7437 this.errorEl.setWidth(elp.getWidth(true)-20);
7439 this.errorEl.update(msg);
7440 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7443 if(!this.errorIcon){
7444 var elp = this.el.findParent('.x-form-element', 5, true);
7445 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7447 this.alignErrorIcon();
7448 this.errorIcon.dom.qtip = msg;
7449 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7450 this.errorIcon.show();
7451 this.on('resize', this.alignErrorIcon, this);
7454 var t = Roo.getDom(this.msgTarget);
7456 t.style.display = this.msgDisplay;
7460 this.fireEvent('invalid', this, msg);
7463 SafariOnKeyDown : function(event)
7465 // this is a workaround for a password hang bug on chrome/ webkit.
7467 var isSelectAll = false;
7469 if(this.inputEl().dom.selectionEnd > 0){
7470 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7472 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7473 event.preventDefault();
7478 if(isSelectAll){ // backspace and delete key
7480 event.preventDefault();
7481 // this is very hacky as keydown always get's upper case.
7483 var cc = String.fromCharCode(event.getCharCode());
7484 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
7488 adjustWidth : function(tag, w){
7489 tag = tag.toLowerCase();
7490 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7491 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7495 if(tag == 'textarea'){
7498 }else if(Roo.isOpera){
7502 if(tag == 'textarea'){
7521 * @class Roo.bootstrap.TextArea
7522 * @extends Roo.bootstrap.Input
7523 * Bootstrap TextArea class
7524 * @cfg {Number} cols Specifies the visible width of a text area
7525 * @cfg {Number} rows Specifies the visible number of lines in a text area
7526 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7527 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7528 * @cfg {string} html text
7531 * Create a new TextArea
7532 * @param {Object} config The config object
7535 Roo.bootstrap.TextArea = function(config){
7536 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7540 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
7550 getAutoCreate : function(){
7552 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7563 value : this.value || '',
7564 html: this.html || '',
7565 cls : 'form-control',
7566 placeholder : this.placeholder || ''
7570 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7571 input.maxLength = this.maxLength;
7575 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7579 input.cols = this.cols;
7582 if (this.readOnly) {
7583 input.readonly = true;
7587 input.name = this.name;
7591 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7595 ['xs','sm','md','lg'].map(function(size){
7596 if (settings[size]) {
7597 cfg.cls += ' col-' + size + '-' + settings[size];
7601 var inputblock = input;
7603 if (this.before || this.after) {
7606 cls : 'input-group',
7610 inputblock.cn.push({
7612 cls : 'input-group-addon',
7616 inputblock.cn.push(input);
7618 inputblock.cn.push({
7620 cls : 'input-group-addon',
7627 if (align ==='left' && this.fieldLabel.length) {
7628 Roo.log("left and has label");
7634 cls : 'control-label col-sm-' + this.labelWidth,
7635 html : this.fieldLabel
7639 cls : "col-sm-" + (12 - this.labelWidth),
7646 } else if ( this.fieldLabel.length) {
7652 //cls : 'input-group-addon',
7653 html : this.fieldLabel
7663 Roo.log(" no label && no align");
7673 if (this.disabled) {
7674 input.disabled=true;
7681 * return the real textarea element.
7683 inputEl: function ()
7685 return this.el.select('textarea.form-control',true).first();
7693 * trigger field - base class for combo..
7698 * @class Roo.bootstrap.TriggerField
7699 * @extends Roo.bootstrap.Input
7700 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7701 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7702 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7703 * for which you can provide a custom implementation. For example:
7705 var trigger = new Roo.bootstrap.TriggerField();
7706 trigger.onTriggerClick = myTriggerFn;
7707 trigger.applyTo('my-field');
7710 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7711 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7712 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
7713 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7715 * Create a new TriggerField.
7716 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7717 * to the base TextField)
7719 Roo.bootstrap.TriggerField = function(config){
7720 this.mimicing = false;
7721 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7724 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
7726 * @cfg {String} triggerClass A CSS class to apply to the trigger
7729 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7733 /** @cfg {Boolean} grow @hide */
7734 /** @cfg {Number} growMin @hide */
7735 /** @cfg {Number} growMax @hide */
7741 autoSize: Roo.emptyFn,
7748 actionMode : 'wrap',
7752 getAutoCreate : function(){
7754 var align = this.labelAlign || this.parentLabelAlign();
7759 cls: 'form-group' //input-group
7766 type : this.inputType,
7767 cls : 'form-control',
7768 autocomplete: 'off',
7769 placeholder : this.placeholder || ''
7773 input.name = this.name;
7776 input.cls += ' input-' + this.size;
7779 if (this.disabled) {
7780 input.disabled=true;
7783 var inputblock = input;
7785 if (this.before || this.after) {
7788 cls : 'input-group',
7792 inputblock.cn.push({
7794 cls : 'input-group-addon',
7798 inputblock.cn.push(input);
7800 inputblock.cn.push({
7802 cls : 'input-group-addon',
7815 cls: 'form-hidden-field'
7823 Roo.log('multiple');
7831 cls: 'form-hidden-field'
7835 cls: 'select2-choices',
7839 cls: 'select2-search-field',
7852 cls: 'select2-container input-group',
7857 // cls: 'typeahead typeahead-long dropdown-menu',
7858 // style: 'display:none'
7863 if(!this.multiple && this.showToggleBtn){
7866 cls : 'input-group-addon btn dropdown-toggle',
7874 cls: 'combobox-clear',
7888 combobox.cls += ' select2-container-multi';
7891 if (align ==='left' && this.fieldLabel.length) {
7893 Roo.log("left and has label");
7899 cls : 'control-label col-sm-' + this.labelWidth,
7900 html : this.fieldLabel
7904 cls : "col-sm-" + (12 - this.labelWidth),
7911 } else if ( this.fieldLabel.length) {
7917 //cls : 'input-group-addon',
7918 html : this.fieldLabel
7928 Roo.log(" no label && no align");
7935 ['xs','sm','md','lg'].map(function(size){
7936 if (settings[size]) {
7937 cfg.cls += ' col-' + size + '-' + settings[size];
7948 onResize : function(w, h){
7949 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
7950 // if(typeof w == 'number'){
7951 // var x = w - this.trigger.getWidth();
7952 // this.inputEl().setWidth(this.adjustWidth('input', x));
7953 // this.trigger.setStyle('left', x+'px');
7958 adjustSize : Roo.BoxComponent.prototype.adjustSize,
7961 getResizeEl : function(){
7962 return this.inputEl();
7966 getPositionEl : function(){
7967 return this.inputEl();
7971 alignErrorIcon : function(){
7972 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
7976 initEvents : function(){
7980 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
7981 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
7982 if(!this.multiple && this.showToggleBtn){
7983 this.trigger = this.el.select('span.dropdown-toggle',true).first();
7984 if(this.hideTrigger){
7985 this.trigger.setDisplayed(false);
7987 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
7991 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
7994 //this.trigger.addClassOnOver('x-form-trigger-over');
7995 //this.trigger.addClassOnClick('x-form-trigger-click');
7998 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8002 createList : function()
8004 this.list = Roo.get(document.body).createChild({
8006 cls: 'typeahead typeahead-long dropdown-menu',
8007 style: 'display:none'
8010 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8015 initTrigger : function(){
8020 onDestroy : function(){
8022 this.trigger.removeAllListeners();
8023 // this.trigger.remove();
8026 // this.wrap.remove();
8028 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8032 onFocus : function(){
8033 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8036 this.wrap.addClass('x-trigger-wrap-focus');
8037 this.mimicing = true;
8038 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8039 if(this.monitorTab){
8040 this.el.on("keydown", this.checkTab, this);
8047 checkTab : function(e){
8048 if(e.getKey() == e.TAB){
8054 onBlur : function(){
8059 mimicBlur : function(e, t){
8061 if(!this.wrap.contains(t) && this.validateBlur()){
8068 triggerBlur : function(){
8069 this.mimicing = false;
8070 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8071 if(this.monitorTab){
8072 this.el.un("keydown", this.checkTab, this);
8074 //this.wrap.removeClass('x-trigger-wrap-focus');
8075 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8079 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8080 validateBlur : function(e, t){
8085 onDisable : function(){
8086 this.inputEl().dom.disabled = true;
8087 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8089 // this.wrap.addClass('x-item-disabled');
8094 onEnable : function(){
8095 this.inputEl().dom.disabled = false;
8096 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8098 // this.el.removeClass('x-item-disabled');
8103 onShow : function(){
8104 var ae = this.getActionEl();
8107 ae.dom.style.display = '';
8108 ae.dom.style.visibility = 'visible';
8114 onHide : function(){
8115 var ae = this.getActionEl();
8116 ae.dom.style.display = 'none';
8120 * The function that should handle the trigger's click event. This method does nothing by default until overridden
8121 * by an implementing function.
8123 * @param {EventObject} e
8125 onTriggerClick : Roo.emptyFn
8129 * Ext JS Library 1.1.1
8130 * Copyright(c) 2006-2007, Ext JS, LLC.
8132 * Originally Released Under LGPL - original licence link has changed is not relivant.
8135 * <script type="text/javascript">
8140 * @class Roo.data.SortTypes
8142 * Defines the default sorting (casting?) comparison functions used when sorting data.
8144 Roo.data.SortTypes = {
8146 * Default sort that does nothing
8147 * @param {Mixed} s The value being converted
8148 * @return {Mixed} The comparison value
8155 * The regular expression used to strip tags
8159 stripTagsRE : /<\/?[^>]+>/gi,
8162 * Strips all HTML tags to sort on text only
8163 * @param {Mixed} s The value being converted
8164 * @return {String} The comparison value
8166 asText : function(s){
8167 return String(s).replace(this.stripTagsRE, "");
8171 * Strips all HTML tags to sort on text only - Case insensitive
8172 * @param {Mixed} s The value being converted
8173 * @return {String} The comparison value
8175 asUCText : function(s){
8176 return String(s).toUpperCase().replace(this.stripTagsRE, "");
8180 * Case insensitive string
8181 * @param {Mixed} s The value being converted
8182 * @return {String} The comparison value
8184 asUCString : function(s) {
8185 return String(s).toUpperCase();
8190 * @param {Mixed} s The value being converted
8191 * @return {Number} The comparison value
8193 asDate : function(s) {
8197 if(s instanceof Date){
8200 return Date.parse(String(s));
8205 * @param {Mixed} s The value being converted
8206 * @return {Float} The comparison value
8208 asFloat : function(s) {
8209 var val = parseFloat(String(s).replace(/,/g, ""));
8210 if(isNaN(val)) val = 0;
8216 * @param {Mixed} s The value being converted
8217 * @return {Number} The comparison value
8219 asInt : function(s) {
8220 var val = parseInt(String(s).replace(/,/g, ""));
8221 if(isNaN(val)) val = 0;
8226 * Ext JS Library 1.1.1
8227 * Copyright(c) 2006-2007, Ext JS, LLC.
8229 * Originally Released Under LGPL - original licence link has changed is not relivant.
8232 * <script type="text/javascript">
8236 * @class Roo.data.Record
8237 * Instances of this class encapsulate both record <em>definition</em> information, and record
8238 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8239 * to access Records cached in an {@link Roo.data.Store} object.<br>
8241 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8242 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8245 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8247 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8248 * {@link #create}. The parameters are the same.
8249 * @param {Array} data An associative Array of data values keyed by the field name.
8250 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8251 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8252 * not specified an integer id is generated.
8254 Roo.data.Record = function(data, id){
8255 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8260 * Generate a constructor for a specific record layout.
8261 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8262 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8263 * Each field definition object may contain the following properties: <ul>
8264 * <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,
8265 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8266 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8267 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8268 * is being used, then this is a string containing the javascript expression to reference the data relative to
8269 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8270 * to the data item relative to the record element. If the mapping expression is the same as the field name,
8271 * this may be omitted.</p></li>
8272 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8273 * <ul><li>auto (Default, implies no conversion)</li>
8278 * <li>date</li></ul></p></li>
8279 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8280 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8281 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8282 * by the Reader into an object that will be stored in the Record. It is passed the
8283 * following parameters:<ul>
8284 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8286 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8288 * <br>usage:<br><pre><code>
8289 var TopicRecord = Roo.data.Record.create(
8290 {name: 'title', mapping: 'topic_title'},
8291 {name: 'author', mapping: 'username'},
8292 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8293 {name: 'lastPost', mapping: 'post_time', type: 'date'},
8294 {name: 'lastPoster', mapping: 'user2'},
8295 {name: 'excerpt', mapping: 'post_text'}
8298 var myNewRecord = new TopicRecord({
8299 title: 'Do my job please',
8302 lastPost: new Date(),
8303 lastPoster: 'Animal',
8304 excerpt: 'No way dude!'
8306 myStore.add(myNewRecord);
8311 Roo.data.Record.create = function(o){
8313 f.superclass.constructor.apply(this, arguments);
8315 Roo.extend(f, Roo.data.Record);
8316 var p = f.prototype;
8317 p.fields = new Roo.util.MixedCollection(false, function(field){
8320 for(var i = 0, len = o.length; i < len; i++){
8321 p.fields.add(new Roo.data.Field(o[i]));
8323 f.getField = function(name){
8324 return p.fields.get(name);
8329 Roo.data.Record.AUTO_ID = 1000;
8330 Roo.data.Record.EDIT = 'edit';
8331 Roo.data.Record.REJECT = 'reject';
8332 Roo.data.Record.COMMIT = 'commit';
8334 Roo.data.Record.prototype = {
8336 * Readonly flag - true if this record has been modified.
8345 join : function(store){
8350 * Set the named field to the specified value.
8351 * @param {String} name The name of the field to set.
8352 * @param {Object} value The value to set the field to.
8354 set : function(name, value){
8355 if(this.data[name] == value){
8362 if(typeof this.modified[name] == 'undefined'){
8363 this.modified[name] = this.data[name];
8365 this.data[name] = value;
8366 if(!this.editing && this.store){
8367 this.store.afterEdit(this);
8372 * Get the value of the named field.
8373 * @param {String} name The name of the field to get the value of.
8374 * @return {Object} The value of the field.
8376 get : function(name){
8377 return this.data[name];
8381 beginEdit : function(){
8382 this.editing = true;
8387 cancelEdit : function(){
8388 this.editing = false;
8389 delete this.modified;
8393 endEdit : function(){
8394 this.editing = false;
8395 if(this.dirty && this.store){
8396 this.store.afterEdit(this);
8401 * Usually called by the {@link Roo.data.Store} which owns the Record.
8402 * Rejects all changes made to the Record since either creation, or the last commit operation.
8403 * Modified fields are reverted to their original values.
8405 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8406 * of reject operations.
8408 reject : function(){
8409 var m = this.modified;
8411 if(typeof m[n] != "function"){
8412 this.data[n] = m[n];
8416 delete this.modified;
8417 this.editing = false;
8419 this.store.afterReject(this);
8424 * Usually called by the {@link Roo.data.Store} which owns the Record.
8425 * Commits all changes made to the Record since either creation, or the last commit operation.
8427 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8428 * of commit operations.
8430 commit : function(){
8432 delete this.modified;
8433 this.editing = false;
8435 this.store.afterCommit(this);
8440 hasError : function(){
8441 return this.error != null;
8445 clearError : function(){
8450 * Creates a copy of this record.
8451 * @param {String} id (optional) A new record id if you don't want to use this record's id
8454 copy : function(newId) {
8455 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8459 * Ext JS Library 1.1.1
8460 * Copyright(c) 2006-2007, Ext JS, LLC.
8462 * Originally Released Under LGPL - original licence link has changed is not relivant.
8465 * <script type="text/javascript">
8471 * @class Roo.data.Store
8472 * @extends Roo.util.Observable
8473 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8474 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8476 * 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
8477 * has no knowledge of the format of the data returned by the Proxy.<br>
8479 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8480 * instances from the data object. These records are cached and made available through accessor functions.
8482 * Creates a new Store.
8483 * @param {Object} config A config object containing the objects needed for the Store to access data,
8484 * and read the data into Records.
8486 Roo.data.Store = function(config){
8487 this.data = new Roo.util.MixedCollection(false);
8488 this.data.getKey = function(o){
8491 this.baseParams = {};
8498 "multisort" : "_multisort"
8501 if(config && config.data){
8502 this.inlineData = config.data;
8506 Roo.apply(this, config);
8508 if(this.reader){ // reader passed
8509 this.reader = Roo.factory(this.reader, Roo.data);
8510 this.reader.xmodule = this.xmodule || false;
8511 if(!this.recordType){
8512 this.recordType = this.reader.recordType;
8514 if(this.reader.onMetaChange){
8515 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8519 if(this.recordType){
8520 this.fields = this.recordType.prototype.fields;
8526 * @event datachanged
8527 * Fires when the data cache has changed, and a widget which is using this Store
8528 * as a Record cache should refresh its view.
8529 * @param {Store} this
8534 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8535 * @param {Store} this
8536 * @param {Object} meta The JSON metadata
8541 * Fires when Records have been added to the Store
8542 * @param {Store} this
8543 * @param {Roo.data.Record[]} records The array of Records added
8544 * @param {Number} index The index at which the record(s) were added
8549 * Fires when a Record has been removed from the Store
8550 * @param {Store} this
8551 * @param {Roo.data.Record} record The Record that was removed
8552 * @param {Number} index The index at which the record was removed
8557 * Fires when a Record has been updated
8558 * @param {Store} this
8559 * @param {Roo.data.Record} record The Record that was updated
8560 * @param {String} operation The update operation being performed. Value may be one of:
8562 Roo.data.Record.EDIT
8563 Roo.data.Record.REJECT
8564 Roo.data.Record.COMMIT
8570 * Fires when the data cache has been cleared.
8571 * @param {Store} this
8576 * Fires before a request is made for a new data object. If the beforeload handler returns false
8577 * the load action will be canceled.
8578 * @param {Store} this
8579 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8583 * @event beforeloadadd
8584 * Fires after a new set of Records has been loaded.
8585 * @param {Store} this
8586 * @param {Roo.data.Record[]} records The Records that were loaded
8587 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8589 beforeloadadd : true,
8592 * Fires after a new set of Records has been loaded, before they are added to the store.
8593 * @param {Store} this
8594 * @param {Roo.data.Record[]} records The Records that were loaded
8595 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8596 * @params {Object} return from reader
8600 * @event loadexception
8601 * Fires if an exception occurs in the Proxy during loading.
8602 * Called with the signature of the Proxy's "loadexception" event.
8603 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8606 * @param {Object} return from JsonData.reader() - success, totalRecords, records
8607 * @param {Object} load options
8608 * @param {Object} jsonData from your request (normally this contains the Exception)
8610 loadexception : true
8614 this.proxy = Roo.factory(this.proxy, Roo.data);
8615 this.proxy.xmodule = this.xmodule || false;
8616 this.relayEvents(this.proxy, ["loadexception"]);
8618 this.sortToggle = {};
8619 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8621 Roo.data.Store.superclass.constructor.call(this);
8623 if(this.inlineData){
8624 this.loadData(this.inlineData);
8625 delete this.inlineData;
8629 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8631 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
8632 * without a remote query - used by combo/forms at present.
8636 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8639 * @cfg {Array} data Inline data to be loaded when the store is initialized.
8642 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8643 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8646 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8647 * on any HTTP request
8650 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8653 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8657 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8658 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8663 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8664 * loaded or when a record is removed. (defaults to false).
8666 pruneModifiedRecords : false,
8672 * Add Records to the Store and fires the add event.
8673 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8675 add : function(records){
8676 records = [].concat(records);
8677 for(var i = 0, len = records.length; i < len; i++){
8678 records[i].join(this);
8680 var index = this.data.length;
8681 this.data.addAll(records);
8682 this.fireEvent("add", this, records, index);
8686 * Remove a Record from the Store and fires the remove event.
8687 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8689 remove : function(record){
8690 var index = this.data.indexOf(record);
8691 this.data.removeAt(index);
8692 if(this.pruneModifiedRecords){
8693 this.modified.remove(record);
8695 this.fireEvent("remove", this, record, index);
8699 * Remove all Records from the Store and fires the clear event.
8701 removeAll : function(){
8703 if(this.pruneModifiedRecords){
8706 this.fireEvent("clear", this);
8710 * Inserts Records to the Store at the given index and fires the add event.
8711 * @param {Number} index The start index at which to insert the passed Records.
8712 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8714 insert : function(index, records){
8715 records = [].concat(records);
8716 for(var i = 0, len = records.length; i < len; i++){
8717 this.data.insert(index, records[i]);
8718 records[i].join(this);
8720 this.fireEvent("add", this, records, index);
8724 * Get the index within the cache of the passed Record.
8725 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8726 * @return {Number} The index of the passed Record. Returns -1 if not found.
8728 indexOf : function(record){
8729 return this.data.indexOf(record);
8733 * Get the index within the cache of the Record with the passed id.
8734 * @param {String} id The id of the Record to find.
8735 * @return {Number} The index of the Record. Returns -1 if not found.
8737 indexOfId : function(id){
8738 return this.data.indexOfKey(id);
8742 * Get the Record with the specified id.
8743 * @param {String} id The id of the Record to find.
8744 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8746 getById : function(id){
8747 return this.data.key(id);
8751 * Get the Record at the specified index.
8752 * @param {Number} index The index of the Record to find.
8753 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8755 getAt : function(index){
8756 return this.data.itemAt(index);
8760 * Returns a range of Records between specified indices.
8761 * @param {Number} startIndex (optional) The starting index (defaults to 0)
8762 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8763 * @return {Roo.data.Record[]} An array of Records
8765 getRange : function(start, end){
8766 return this.data.getRange(start, end);
8770 storeOptions : function(o){
8771 o = Roo.apply({}, o);
8774 this.lastOptions = o;
8778 * Loads the Record cache from the configured Proxy using the configured Reader.
8780 * If using remote paging, then the first load call must specify the <em>start</em>
8781 * and <em>limit</em> properties in the options.params property to establish the initial
8782 * position within the dataset, and the number of Records to cache on each read from the Proxy.
8784 * <strong>It is important to note that for remote data sources, loading is asynchronous,
8785 * and this call will return before the new data has been loaded. Perform any post-processing
8786 * in a callback function, or in a "load" event handler.</strong>
8788 * @param {Object} options An object containing properties which control loading options:<ul>
8789 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8790 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8791 * passed the following arguments:<ul>
8792 * <li>r : Roo.data.Record[]</li>
8793 * <li>options: Options object from the load call</li>
8794 * <li>success: Boolean success indicator</li></ul></li>
8795 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8796 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8799 load : function(options){
8800 options = options || {};
8801 if(this.fireEvent("beforeload", this, options) !== false){
8802 this.storeOptions(options);
8803 var p = Roo.apply(options.params || {}, this.baseParams);
8804 // if meta was not loaded from remote source.. try requesting it.
8805 if (!this.reader.metaFromRemote) {
8808 if(this.sortInfo && this.remoteSort){
8809 var pn = this.paramNames;
8810 p[pn["sort"]] = this.sortInfo.field;
8811 p[pn["dir"]] = this.sortInfo.direction;
8813 if (this.multiSort) {
8814 var pn = this.paramNames;
8815 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8818 this.proxy.load(p, this.reader, this.loadRecords, this, options);
8823 * Reloads the Record cache from the configured Proxy using the configured Reader and
8824 * the options from the last load operation performed.
8825 * @param {Object} options (optional) An object containing properties which may override the options
8826 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8827 * the most recently used options are reused).
8829 reload : function(options){
8830 this.load(Roo.applyIf(options||{}, this.lastOptions));
8834 // Called as a callback by the Reader during a load operation.
8835 loadRecords : function(o, options, success){
8836 if(!o || success === false){
8837 if(success !== false){
8838 this.fireEvent("load", this, [], options, o);
8840 if(options.callback){
8841 options.callback.call(options.scope || this, [], options, false);
8845 // if data returned failure - throw an exception.
8846 if (o.success === false) {
8847 // show a message if no listener is registered.
8848 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8849 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8851 // loadmask wil be hooked into this..
8852 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8855 var r = o.records, t = o.totalRecords || r.length;
8857 this.fireEvent("beforeloadadd", this, r, options, o);
8859 if(!options || options.add !== true){
8860 if(this.pruneModifiedRecords){
8863 for(var i = 0, len = r.length; i < len; i++){
8867 this.data = this.snapshot;
8868 delete this.snapshot;
8871 this.data.addAll(r);
8872 this.totalLength = t;
8874 this.fireEvent("datachanged", this);
8876 this.totalLength = Math.max(t, this.data.length+r.length);
8879 this.fireEvent("load", this, r, options, o);
8880 if(options.callback){
8881 options.callback.call(options.scope || this, r, options, true);
8887 * Loads data from a passed data block. A Reader which understands the format of the data
8888 * must have been configured in the constructor.
8889 * @param {Object} data The data block from which to read the Records. The format of the data expected
8890 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8891 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8893 loadData : function(o, append){
8894 var r = this.reader.readRecords(o);
8895 this.loadRecords(r, {add: append}, true);
8899 * Gets the number of cached records.
8901 * <em>If using paging, this may not be the total size of the dataset. If the data object
8902 * used by the Reader contains the dataset size, then the getTotalCount() function returns
8903 * the data set size</em>
8905 getCount : function(){
8906 return this.data.length || 0;
8910 * Gets the total number of records in the dataset as returned by the server.
8912 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8913 * the dataset size</em>
8915 getTotalCount : function(){
8916 return this.totalLength || 0;
8920 * Returns the sort state of the Store as an object with two properties:
8922 field {String} The name of the field by which the Records are sorted
8923 direction {String} The sort order, "ASC" or "DESC"
8926 getSortState : function(){
8927 return this.sortInfo;
8931 applySort : function(){
8932 if(this.sortInfo && !this.remoteSort){
8933 var s = this.sortInfo, f = s.field;
8934 var st = this.fields.get(f).sortType;
8935 var fn = function(r1, r2){
8936 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8937 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8939 this.data.sort(s.direction, fn);
8940 if(this.snapshot && this.snapshot != this.data){
8941 this.snapshot.sort(s.direction, fn);
8947 * Sets the default sort column and order to be used by the next load operation.
8948 * @param {String} fieldName The name of the field to sort by.
8949 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8951 setDefaultSort : function(field, dir){
8952 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
8957 * If remote sorting is used, the sort is performed on the server, and the cache is
8958 * reloaded. If local sorting is used, the cache is sorted internally.
8959 * @param {String} fieldName The name of the field to sort by.
8960 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8962 sort : function(fieldName, dir){
8963 var f = this.fields.get(fieldName);
8965 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
8967 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
8968 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
8973 this.sortToggle[f.name] = dir;
8974 this.sortInfo = {field: f.name, direction: dir};
8975 if(!this.remoteSort){
8977 this.fireEvent("datachanged", this);
8979 this.load(this.lastOptions);
8984 * Calls the specified function for each of the Records in the cache.
8985 * @param {Function} fn The function to call. The Record is passed as the first parameter.
8986 * Returning <em>false</em> aborts and exits the iteration.
8987 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
8989 each : function(fn, scope){
8990 this.data.each(fn, scope);
8994 * Gets all records modified since the last commit. Modified records are persisted across load operations
8995 * (e.g., during paging).
8996 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
8998 getModifiedRecords : function(){
8999 return this.modified;
9003 createFilterFn : function(property, value, anyMatch){
9004 if(!value.exec){ // not a regex
9005 value = String(value);
9006 if(value.length == 0){
9009 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9012 return value.test(r.data[property]);
9017 * Sums the value of <i>property</i> for each record between start and end and returns the result.
9018 * @param {String} property A field on your records
9019 * @param {Number} start The record index to start at (defaults to 0)
9020 * @param {Number} end The last record index to include (defaults to length - 1)
9021 * @return {Number} The sum
9023 sum : function(property, start, end){
9024 var rs = this.data.items, v = 0;
9026 end = (end || end === 0) ? end : rs.length-1;
9028 for(var i = start; i <= end; i++){
9029 v += (rs[i].data[property] || 0);
9035 * Filter the records by a specified property.
9036 * @param {String} field A field on your records
9037 * @param {String/RegExp} value Either a string that the field
9038 * should start with or a RegExp to test against the field
9039 * @param {Boolean} anyMatch True to match any part not just the beginning
9041 filter : function(property, value, anyMatch){
9042 var fn = this.createFilterFn(property, value, anyMatch);
9043 return fn ? this.filterBy(fn) : this.clearFilter();
9047 * Filter by a function. The specified function will be called with each
9048 * record in this data source. If the function returns true the record is included,
9049 * otherwise it is filtered.
9050 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9051 * @param {Object} scope (optional) The scope of the function (defaults to this)
9053 filterBy : function(fn, scope){
9054 this.snapshot = this.snapshot || this.data;
9055 this.data = this.queryBy(fn, scope||this);
9056 this.fireEvent("datachanged", this);
9060 * Query the records by a specified property.
9061 * @param {String} field A field on your records
9062 * @param {String/RegExp} value Either a string that the field
9063 * should start with or a RegExp to test against the field
9064 * @param {Boolean} anyMatch True to match any part not just the beginning
9065 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9067 query : function(property, value, anyMatch){
9068 var fn = this.createFilterFn(property, value, anyMatch);
9069 return fn ? this.queryBy(fn) : this.data.clone();
9073 * Query by a function. The specified function will be called with each
9074 * record in this data source. If the function returns true the record is included
9076 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9077 * @param {Object} scope (optional) The scope of the function (defaults to this)
9078 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9080 queryBy : function(fn, scope){
9081 var data = this.snapshot || this.data;
9082 return data.filterBy(fn, scope||this);
9086 * Collects unique values for a particular dataIndex from this store.
9087 * @param {String} dataIndex The property to collect
9088 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9089 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9090 * @return {Array} An array of the unique values
9092 collect : function(dataIndex, allowNull, bypassFilter){
9093 var d = (bypassFilter === true && this.snapshot) ?
9094 this.snapshot.items : this.data.items;
9095 var v, sv, r = [], l = {};
9096 for(var i = 0, len = d.length; i < len; i++){
9097 v = d[i].data[dataIndex];
9099 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9108 * Revert to a view of the Record cache with no filtering applied.
9109 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9111 clearFilter : function(suppressEvent){
9112 if(this.snapshot && this.snapshot != this.data){
9113 this.data = this.snapshot;
9114 delete this.snapshot;
9115 if(suppressEvent !== true){
9116 this.fireEvent("datachanged", this);
9122 afterEdit : function(record){
9123 if(this.modified.indexOf(record) == -1){
9124 this.modified.push(record);
9126 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9130 afterReject : function(record){
9131 this.modified.remove(record);
9132 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9136 afterCommit : function(record){
9137 this.modified.remove(record);
9138 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9142 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9143 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9145 commitChanges : function(){
9146 var m = this.modified.slice(0);
9148 for(var i = 0, len = m.length; i < len; i++){
9154 * Cancel outstanding changes on all changed records.
9156 rejectChanges : function(){
9157 var m = this.modified.slice(0);
9159 for(var i = 0, len = m.length; i < len; i++){
9164 onMetaChange : function(meta, rtype, o){
9165 this.recordType = rtype;
9166 this.fields = rtype.prototype.fields;
9167 delete this.snapshot;
9168 this.sortInfo = meta.sortInfo || this.sortInfo;
9170 this.fireEvent('metachange', this, this.reader.meta);
9173 moveIndex : function(data, type)
9175 var index = this.indexOf(data);
9177 var newIndex = index + type;
9181 this.insert(newIndex, data);
9186 * Ext JS Library 1.1.1
9187 * Copyright(c) 2006-2007, Ext JS, LLC.
9189 * Originally Released Under LGPL - original licence link has changed is not relivant.
9192 * <script type="text/javascript">
9196 * @class Roo.data.SimpleStore
9197 * @extends Roo.data.Store
9198 * Small helper class to make creating Stores from Array data easier.
9199 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9200 * @cfg {Array} fields An array of field definition objects, or field name strings.
9201 * @cfg {Array} data The multi-dimensional array of data
9203 * @param {Object} config
9205 Roo.data.SimpleStore = function(config){
9206 Roo.data.SimpleStore.superclass.constructor.call(this, {
9208 reader: new Roo.data.ArrayReader({
9211 Roo.data.Record.create(config.fields)
9213 proxy : new Roo.data.MemoryProxy(config.data)
9217 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9219 * Ext JS Library 1.1.1
9220 * Copyright(c) 2006-2007, Ext JS, LLC.
9222 * Originally Released Under LGPL - original licence link has changed is not relivant.
9225 * <script type="text/javascript">
9230 * @extends Roo.data.Store
9231 * @class Roo.data.JsonStore
9232 * Small helper class to make creating Stores for JSON data easier. <br/>
9234 var store = new Roo.data.JsonStore({
9235 url: 'get-images.php',
9237 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9240 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9241 * JsonReader and HttpProxy (unless inline data is provided).</b>
9242 * @cfg {Array} fields An array of field definition objects, or field name strings.
9244 * @param {Object} config
9246 Roo.data.JsonStore = function(c){
9247 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9248 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9249 reader: new Roo.data.JsonReader(c, c.fields)
9252 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9254 * Ext JS Library 1.1.1
9255 * Copyright(c) 2006-2007, Ext JS, LLC.
9257 * Originally Released Under LGPL - original licence link has changed is not relivant.
9260 * <script type="text/javascript">
9264 Roo.data.Field = function(config){
9265 if(typeof config == "string"){
9266 config = {name: config};
9268 Roo.apply(this, config);
9274 var st = Roo.data.SortTypes;
9275 // named sortTypes are supported, here we look them up
9276 if(typeof this.sortType == "string"){
9277 this.sortType = st[this.sortType];
9280 // set default sortType for strings and dates
9284 this.sortType = st.asUCString;
9287 this.sortType = st.asDate;
9290 this.sortType = st.none;
9295 var stripRe = /[\$,%]/g;
9297 // prebuilt conversion function for this field, instead of
9298 // switching every time we're reading a value
9300 var cv, dateFormat = this.dateFormat;
9305 cv = function(v){ return v; };
9308 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9312 return v !== undefined && v !== null && v !== '' ?
9313 parseInt(String(v).replace(stripRe, ""), 10) : '';
9318 return v !== undefined && v !== null && v !== '' ?
9319 parseFloat(String(v).replace(stripRe, ""), 10) : '';
9324 cv = function(v){ return v === true || v === "true" || v == 1; };
9331 if(v instanceof Date){
9335 if(dateFormat == "timestamp"){
9336 return new Date(v*1000);
9338 return Date.parseDate(v, dateFormat);
9340 var parsed = Date.parse(v);
9341 return parsed ? new Date(parsed) : null;
9350 Roo.data.Field.prototype = {
9358 * Ext JS Library 1.1.1
9359 * Copyright(c) 2006-2007, Ext JS, LLC.
9361 * Originally Released Under LGPL - original licence link has changed is not relivant.
9364 * <script type="text/javascript">
9367 // Base class for reading structured data from a data source. This class is intended to be
9368 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9371 * @class Roo.data.DataReader
9372 * Base class for reading structured data from a data source. This class is intended to be
9373 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9376 Roo.data.DataReader = function(meta, recordType){
9380 this.recordType = recordType instanceof Array ?
9381 Roo.data.Record.create(recordType) : recordType;
9384 Roo.data.DataReader.prototype = {
9386 * Create an empty record
9387 * @param {Object} data (optional) - overlay some values
9388 * @return {Roo.data.Record} record created.
9390 newRow : function(d) {
9392 this.recordType.prototype.fields.each(function(c) {
9394 case 'int' : da[c.name] = 0; break;
9395 case 'date' : da[c.name] = new Date(); break;
9396 case 'float' : da[c.name] = 0.0; break;
9397 case 'boolean' : da[c.name] = false; break;
9398 default : da[c.name] = ""; break;
9402 return new this.recordType(Roo.apply(da, d));
9407 * Ext JS Library 1.1.1
9408 * Copyright(c) 2006-2007, Ext JS, LLC.
9410 * Originally Released Under LGPL - original licence link has changed is not relivant.
9413 * <script type="text/javascript">
9417 * @class Roo.data.DataProxy
9418 * @extends Roo.data.Observable
9419 * This class is an abstract base class for implementations which provide retrieval of
9420 * unformatted data objects.<br>
9422 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9423 * (of the appropriate type which knows how to parse the data object) to provide a block of
9424 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9426 * Custom implementations must implement the load method as described in
9427 * {@link Roo.data.HttpProxy#load}.
9429 Roo.data.DataProxy = function(){
9433 * Fires before a network request is made to retrieve a data object.
9434 * @param {Object} This DataProxy object.
9435 * @param {Object} params The params parameter to the load function.
9440 * Fires before the load method's callback is called.
9441 * @param {Object} This DataProxy object.
9442 * @param {Object} o The data object.
9443 * @param {Object} arg The callback argument object passed to the load function.
9447 * @event loadexception
9448 * Fires if an Exception occurs during data retrieval.
9449 * @param {Object} This DataProxy object.
9450 * @param {Object} o The data object.
9451 * @param {Object} arg The callback argument object passed to the load function.
9452 * @param {Object} e The Exception.
9454 loadexception : true
9456 Roo.data.DataProxy.superclass.constructor.call(this);
9459 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9462 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9466 * Ext JS Library 1.1.1
9467 * Copyright(c) 2006-2007, Ext JS, LLC.
9469 * Originally Released Under LGPL - original licence link has changed is not relivant.
9472 * <script type="text/javascript">
9475 * @class Roo.data.MemoryProxy
9476 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9477 * to the Reader when its load method is called.
9479 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9481 Roo.data.MemoryProxy = function(data){
9485 Roo.data.MemoryProxy.superclass.constructor.call(this);
9489 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9491 * Load data from the requested source (in this case an in-memory
9492 * data object passed to the constructor), read the data object into
9493 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9494 * process that block using the passed callback.
9495 * @param {Object} params This parameter is not used by the MemoryProxy class.
9496 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9497 * object into a block of Roo.data.Records.
9498 * @param {Function} callback The function into which to pass the block of Roo.data.records.
9499 * The function must be passed <ul>
9500 * <li>The Record block object</li>
9501 * <li>The "arg" argument from the load function</li>
9502 * <li>A boolean success indicator</li>
9504 * @param {Object} scope The scope in which to call the callback
9505 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9507 load : function(params, reader, callback, scope, arg){
9508 params = params || {};
9511 result = reader.readRecords(this.data);
9513 this.fireEvent("loadexception", this, arg, null, e);
9514 callback.call(scope, null, arg, false);
9517 callback.call(scope, result, arg, true);
9521 update : function(params, records){
9526 * Ext JS Library 1.1.1
9527 * Copyright(c) 2006-2007, Ext JS, LLC.
9529 * Originally Released Under LGPL - original licence link has changed is not relivant.
9532 * <script type="text/javascript">
9535 * @class Roo.data.HttpProxy
9536 * @extends Roo.data.DataProxy
9537 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9538 * configured to reference a certain URL.<br><br>
9540 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9541 * from which the running page was served.<br><br>
9543 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9545 * Be aware that to enable the browser to parse an XML document, the server must set
9546 * the Content-Type header in the HTTP response to "text/xml".
9548 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9549 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
9550 * will be used to make the request.
9552 Roo.data.HttpProxy = function(conn){
9553 Roo.data.HttpProxy.superclass.constructor.call(this);
9554 // is conn a conn config or a real conn?
9556 this.useAjax = !conn || !conn.events;
9560 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9561 // thse are take from connection...
9564 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9567 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9568 * extra parameters to each request made by this object. (defaults to undefined)
9571 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9572 * to each request made by this object. (defaults to undefined)
9575 * @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)
9578 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9581 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9587 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9591 * Return the {@link Roo.data.Connection} object being used by this Proxy.
9592 * @return {Connection} The Connection object. This object may be used to subscribe to events on
9593 * a finer-grained basis than the DataProxy events.
9595 getConnection : function(){
9596 return this.useAjax ? Roo.Ajax : this.conn;
9600 * Load data from the configured {@link Roo.data.Connection}, read the data object into
9601 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9602 * process that block using the passed callback.
9603 * @param {Object} params An object containing properties which are to be used as HTTP parameters
9604 * for the request to the remote server.
9605 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9606 * object into a block of Roo.data.Records.
9607 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9608 * The function must be passed <ul>
9609 * <li>The Record block object</li>
9610 * <li>The "arg" argument from the load function</li>
9611 * <li>A boolean success indicator</li>
9613 * @param {Object} scope The scope in which to call the callback
9614 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9616 load : function(params, reader, callback, scope, arg){
9617 if(this.fireEvent("beforeload", this, params) !== false){
9619 params : params || {},
9621 callback : callback,
9626 callback : this.loadResponse,
9630 Roo.applyIf(o, this.conn);
9631 if(this.activeRequest){
9632 Roo.Ajax.abort(this.activeRequest);
9634 this.activeRequest = Roo.Ajax.request(o);
9636 this.conn.request(o);
9639 callback.call(scope||this, null, arg, false);
9644 loadResponse : function(o, success, response){
9645 delete this.activeRequest;
9647 this.fireEvent("loadexception", this, o, response);
9648 o.request.callback.call(o.request.scope, null, o.request.arg, false);
9653 result = o.reader.read(response);
9655 this.fireEvent("loadexception", this, o, response, e);
9656 o.request.callback.call(o.request.scope, null, o.request.arg, false);
9660 this.fireEvent("load", this, o, o.request.arg);
9661 o.request.callback.call(o.request.scope, result, o.request.arg, true);
9665 update : function(dataSet){
9670 updateResponse : function(dataSet){
9675 * Ext JS Library 1.1.1
9676 * Copyright(c) 2006-2007, Ext JS, LLC.
9678 * Originally Released Under LGPL - original licence link has changed is not relivant.
9681 * <script type="text/javascript">
9685 * @class Roo.data.ScriptTagProxy
9686 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9687 * other than the originating domain of the running page.<br><br>
9689 * <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
9690 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9692 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9693 * source code that is used as the source inside a <script> tag.<br><br>
9695 * In order for the browser to process the returned data, the server must wrap the data object
9696 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9697 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9698 * depending on whether the callback name was passed:
9701 boolean scriptTag = false;
9702 String cb = request.getParameter("callback");
9705 response.setContentType("text/javascript");
9707 response.setContentType("application/x-json");
9709 Writer out = response.getWriter();
9711 out.write(cb + "(");
9713 out.print(dataBlock.toJsonString());
9720 * @param {Object} config A configuration object.
9722 Roo.data.ScriptTagProxy = function(config){
9723 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9724 Roo.apply(this, config);
9725 this.head = document.getElementsByTagName("head")[0];
9728 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9730 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9732 * @cfg {String} url The URL from which to request the data object.
9735 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9739 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9740 * the server the name of the callback function set up by the load call to process the returned data object.
9741 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9742 * javascript output which calls this named function passing the data object as its only parameter.
9744 callbackParam : "callback",
9746 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9747 * name to the request.
9752 * Load data from the configured URL, read the data object into
9753 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9754 * process that block using the passed callback.
9755 * @param {Object} params An object containing properties which are to be used as HTTP parameters
9756 * for the request to the remote server.
9757 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9758 * object into a block of Roo.data.Records.
9759 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9760 * The function must be passed <ul>
9761 * <li>The Record block object</li>
9762 * <li>The "arg" argument from the load function</li>
9763 * <li>A boolean success indicator</li>
9765 * @param {Object} scope The scope in which to call the callback
9766 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9768 load : function(params, reader, callback, scope, arg){
9769 if(this.fireEvent("beforeload", this, params) !== false){
9771 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9774 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9776 url += "&_dc=" + (new Date().getTime());
9778 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9781 cb : "stcCallback"+transId,
9782 scriptId : "stcScript"+transId,
9786 callback : callback,
9792 window[trans.cb] = function(o){
9793 conn.handleResponse(o, trans);
9796 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9798 if(this.autoAbort !== false){
9802 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9804 var script = document.createElement("script");
9805 script.setAttribute("src", url);
9806 script.setAttribute("type", "text/javascript");
9807 script.setAttribute("id", trans.scriptId);
9808 this.head.appendChild(script);
9812 callback.call(scope||this, null, arg, false);
9817 isLoading : function(){
9818 return this.trans ? true : false;
9822 * Abort the current server request.
9825 if(this.isLoading()){
9826 this.destroyTrans(this.trans);
9831 destroyTrans : function(trans, isLoaded){
9832 this.head.removeChild(document.getElementById(trans.scriptId));
9833 clearTimeout(trans.timeoutId);
9835 window[trans.cb] = undefined;
9837 delete window[trans.cb];
9840 // if hasn't been loaded, wait for load to remove it to prevent script error
9841 window[trans.cb] = function(){
9842 window[trans.cb] = undefined;
9844 delete window[trans.cb];
9851 handleResponse : function(o, trans){
9853 this.destroyTrans(trans, true);
9856 result = trans.reader.readRecords(o);
9858 this.fireEvent("loadexception", this, o, trans.arg, e);
9859 trans.callback.call(trans.scope||window, null, trans.arg, false);
9862 this.fireEvent("load", this, o, trans.arg);
9863 trans.callback.call(trans.scope||window, result, trans.arg, true);
9867 handleFailure : function(trans){
9869 this.destroyTrans(trans, false);
9870 this.fireEvent("loadexception", this, null, trans.arg);
9871 trans.callback.call(trans.scope||window, null, trans.arg, false);
9875 * Ext JS Library 1.1.1
9876 * Copyright(c) 2006-2007, Ext JS, LLC.
9878 * Originally Released Under LGPL - original licence link has changed is not relivant.
9881 * <script type="text/javascript">
9885 * @class Roo.data.JsonReader
9886 * @extends Roo.data.DataReader
9887 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9888 * based on mappings in a provided Roo.data.Record constructor.
9890 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9891 * in the reply previously.
9896 var RecordDef = Roo.data.Record.create([
9897 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
9898 {name: 'occupation'} // This field will use "occupation" as the mapping.
9900 var myReader = new Roo.data.JsonReader({
9901 totalProperty: "results", // The property which contains the total dataset size (optional)
9902 root: "rows", // The property which contains an Array of row objects
9903 id: "id" // The property within each row object that provides an ID for the record (optional)
9907 * This would consume a JSON file like this:
9909 { 'results': 2, 'rows': [
9910 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9911 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9914 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9915 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9916 * paged from the remote server.
9917 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9918 * @cfg {String} root name of the property which contains the Array of row objects.
9919 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9921 * Create a new JsonReader
9922 * @param {Object} meta Metadata configuration options
9923 * @param {Object} recordType Either an Array of field definition objects,
9924 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9926 Roo.data.JsonReader = function(meta, recordType){
9929 // set some defaults:
9931 totalProperty: 'total',
9932 successProperty : 'success',
9937 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9939 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9942 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
9943 * Used by Store query builder to append _requestMeta to params.
9946 metaFromRemote : false,
9948 * This method is only used by a DataProxy which has retrieved data from a remote server.
9949 * @param {Object} response The XHR object which contains the JSON data in its responseText.
9950 * @return {Object} data A data block which is used by an Roo.data.Store object as
9951 * a cache of Roo.data.Records.
9953 read : function(response){
9954 var json = response.responseText;
9956 var o = /* eval:var:o */ eval("("+json+")");
9958 throw {message: "JsonReader.read: Json object not found"};
9964 this.metaFromRemote = true;
9965 this.meta = o.metaData;
9966 this.recordType = Roo.data.Record.create(o.metaData.fields);
9967 this.onMetaChange(this.meta, this.recordType, o);
9969 return this.readRecords(o);
9972 // private function a store will implement
9973 onMetaChange : function(meta, recordType, o){
9980 simpleAccess: function(obj, subsc) {
9987 getJsonAccessor: function(){
9989 return function(expr) {
9991 return(re.test(expr))
9992 ? new Function("obj", "return obj." + expr)
10002 * Create a data block containing Roo.data.Records from an XML document.
10003 * @param {Object} o An object which contains an Array of row objects in the property specified
10004 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10005 * which contains the total size of the dataset.
10006 * @return {Object} data A data block which is used by an Roo.data.Store object as
10007 * a cache of Roo.data.Records.
10009 readRecords : function(o){
10011 * After any data loads, the raw JSON data is available for further custom processing.
10015 var s = this.meta, Record = this.recordType,
10016 f = Record.prototype.fields, fi = f.items, fl = f.length;
10018 // Generate extraction functions for the totalProperty, the root, the id, and for each field
10020 if(s.totalProperty) {
10021 this.getTotal = this.getJsonAccessor(s.totalProperty);
10023 if(s.successProperty) {
10024 this.getSuccess = this.getJsonAccessor(s.successProperty);
10026 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10028 var g = this.getJsonAccessor(s.id);
10029 this.getId = function(rec) {
10031 return (r === undefined || r === "") ? null : r;
10034 this.getId = function(){return null;};
10037 for(var jj = 0; jj < fl; jj++){
10039 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10040 this.ef[jj] = this.getJsonAccessor(map);
10044 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10045 if(s.totalProperty){
10046 var vt = parseInt(this.getTotal(o), 10);
10051 if(s.successProperty){
10052 var vs = this.getSuccess(o);
10053 if(vs === false || vs === 'false'){
10058 for(var i = 0; i < c; i++){
10061 var id = this.getId(n);
10062 for(var j = 0; j < fl; j++){
10064 var v = this.ef[j](n);
10066 Roo.log('missing convert for ' + f.name);
10070 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10072 var record = new Record(values, id);
10074 records[i] = record;
10080 totalRecords : totalRecords
10085 * Ext JS Library 1.1.1
10086 * Copyright(c) 2006-2007, Ext JS, LLC.
10088 * Originally Released Under LGPL - original licence link has changed is not relivant.
10091 * <script type="text/javascript">
10095 * @class Roo.data.ArrayReader
10096 * @extends Roo.data.DataReader
10097 * Data reader class to create an Array of Roo.data.Record objects from an Array.
10098 * Each element of that Array represents a row of data fields. The
10099 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10100 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10104 var RecordDef = Roo.data.Record.create([
10105 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
10106 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
10108 var myReader = new Roo.data.ArrayReader({
10109 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
10113 * This would consume an Array like this:
10115 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10117 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10119 * Create a new JsonReader
10120 * @param {Object} meta Metadata configuration options.
10121 * @param {Object} recordType Either an Array of field definition objects
10122 * as specified to {@link Roo.data.Record#create},
10123 * or an {@link Roo.data.Record} object
10124 * created using {@link Roo.data.Record#create}.
10126 Roo.data.ArrayReader = function(meta, recordType){
10127 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10130 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10132 * Create a data block containing Roo.data.Records from an XML document.
10133 * @param {Object} o An Array of row objects which represents the dataset.
10134 * @return {Object} data A data block which is used by an Roo.data.Store object as
10135 * a cache of Roo.data.Records.
10137 readRecords : function(o){
10138 var sid = this.meta ? this.meta.id : null;
10139 var recordType = this.recordType, fields = recordType.prototype.fields;
10142 for(var i = 0; i < root.length; i++){
10145 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10146 for(var j = 0, jlen = fields.length; j < jlen; j++){
10147 var f = fields.items[j];
10148 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10149 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10151 values[f.name] = v;
10153 var record = new recordType(values, id);
10155 records[records.length] = record;
10159 totalRecords : records.length
10168 * @class Roo.bootstrap.ComboBox
10169 * @extends Roo.bootstrap.TriggerField
10170 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10171 * @cfg {Boolean} append (true|false) default false
10172 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10173 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10174 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10175 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10176 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10178 * Create a new ComboBox.
10179 * @param {Object} config Configuration options
10181 Roo.bootstrap.ComboBox = function(config){
10182 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10186 * Fires when the dropdown list is expanded
10187 * @param {Roo.bootstrap.ComboBox} combo This combo box
10192 * Fires when the dropdown list is collapsed
10193 * @param {Roo.bootstrap.ComboBox} combo This combo box
10197 * @event beforeselect
10198 * Fires before a list item is selected. Return false to cancel the selection.
10199 * @param {Roo.bootstrap.ComboBox} combo This combo box
10200 * @param {Roo.data.Record} record The data record returned from the underlying store
10201 * @param {Number} index The index of the selected item in the dropdown list
10203 'beforeselect' : true,
10206 * Fires when a list item is selected
10207 * @param {Roo.bootstrap.ComboBox} combo This combo box
10208 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10209 * @param {Number} index The index of the selected item in the dropdown list
10213 * @event beforequery
10214 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10215 * The event object passed has these properties:
10216 * @param {Roo.bootstrap.ComboBox} combo This combo box
10217 * @param {String} query The query
10218 * @param {Boolean} forceAll true to force "all" query
10219 * @param {Boolean} cancel true to cancel the query
10220 * @param {Object} e The query event object
10222 'beforequery': true,
10225 * Fires when the 'add' icon is pressed (add a listener to enable add button)
10226 * @param {Roo.bootstrap.ComboBox} combo This combo box
10231 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10232 * @param {Roo.bootstrap.ComboBox} combo This combo box
10233 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10238 * Fires when the remove value from the combobox array
10239 * @param {Roo.bootstrap.ComboBox} combo This combo box
10246 this.tickItems = [];
10248 this.selectedIndex = -1;
10249 if(this.mode == 'local'){
10250 if(config.queryDelay === undefined){
10251 this.queryDelay = 10;
10253 if(config.minChars === undefined){
10259 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10262 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10263 * rendering into an Roo.Editor, defaults to false)
10266 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10267 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10270 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10273 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10274 * the dropdown list (defaults to undefined, with no header element)
10278 * @cfg {String/Roo.Template} tpl The template to use to render the output
10282 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10284 listWidth: undefined,
10286 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10287 * mode = 'remote' or 'text' if mode = 'local')
10289 displayField: undefined,
10291 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10292 * mode = 'remote' or 'value' if mode = 'local').
10293 * Note: use of a valueField requires the user make a selection
10294 * in order for a value to be mapped.
10296 valueField: undefined,
10300 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10301 * field's data value (defaults to the underlying DOM element's name)
10303 hiddenName: undefined,
10305 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10309 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10311 selectedClass: 'active',
10314 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10318 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10319 * anchor positions (defaults to 'tl-bl')
10321 listAlign: 'tl-bl?',
10323 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10327 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
10328 * query specified by the allQuery config option (defaults to 'query')
10330 triggerAction: 'query',
10332 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10333 * (defaults to 4, does not apply if editable = false)
10337 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10338 * delay (typeAheadDelay) if it matches a known value (defaults to false)
10342 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10343 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10347 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10348 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
10352 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
10353 * when editable = true (defaults to false)
10355 selectOnFocus:false,
10357 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10359 queryParam: 'query',
10361 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
10362 * when mode = 'remote' (defaults to 'Loading...')
10364 loadingText: 'Loading...',
10366 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10370 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10374 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10375 * traditional select (defaults to true)
10379 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10383 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10387 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10388 * listWidth has a higher value)
10392 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10393 * allow the user to set arbitrary text into the field (defaults to false)
10395 forceSelection:false,
10397 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10398 * if typeAhead = true (defaults to 250)
10400 typeAheadDelay : 250,
10402 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10403 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10405 valueNotFoundText : undefined,
10407 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10409 blockFocus : false,
10412 * @cfg {Boolean} disableClear Disable showing of clear button.
10414 disableClear : false,
10416 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
10418 alwaysQuery : false,
10421 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
10435 btnPosition : 'right',
10436 triggerList : true,
10437 showToggleBtn : true,
10438 // element that contains real text value.. (when hidden is used..)
10440 getAutoCreate : function()
10447 if(!this.tickable){
10448 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10453 * ComboBox with tickable selections
10456 var align = this.labelAlign || this.parentLabelAlign();
10459 cls : 'form-group roo-combobox-tickable' //input-group
10465 cls : 'tickable-buttons',
10470 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10477 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10484 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10491 Roo.each(buttons.cn, function(c){
10493 c.cls += ' btn-' + _this.size;
10496 if (_this.disabled) {
10507 cls: 'form-hidden-field'
10511 cls: 'select2-choices',
10515 cls: 'select2-search-field',
10527 cls: 'select2-container input-group select2-container-multi',
10532 // cls: 'typeahead typeahead-long dropdown-menu',
10533 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
10538 if (align ==='left' && this.fieldLabel.length) {
10540 Roo.log("left and has label");
10546 cls : 'control-label col-sm-' + this.labelWidth,
10547 html : this.fieldLabel
10551 cls : "col-sm-" + (12 - this.labelWidth),
10558 } else if ( this.fieldLabel.length) {
10564 //cls : 'input-group-addon',
10565 html : this.fieldLabel
10575 Roo.log(" no label && no align");
10582 ['xs','sm','md','lg'].map(function(size){
10583 if (settings[size]) {
10584 cfg.cls += ' col-' + size + '-' + settings[size];
10593 initEvents: function()
10597 throw "can not find store for combo";
10599 this.store = Roo.factory(this.store, Roo.data);
10602 this.initTickableEvents();
10606 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10608 if(this.hiddenName){
10610 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10612 this.hiddenField.dom.value =
10613 this.hiddenValue !== undefined ? this.hiddenValue :
10614 this.value !== undefined ? this.value : '';
10616 // prevent input submission
10617 this.el.dom.removeAttribute('name');
10618 this.hiddenField.dom.setAttribute('name', this.hiddenName);
10623 // this.el.dom.setAttribute('autocomplete', 'off');
10626 var cls = 'x-combo-list';
10628 //this.list = new Roo.Layer({
10629 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10635 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10636 _this.list.setWidth(lw);
10639 this.list.on('mouseover', this.onViewOver, this);
10640 this.list.on('mousemove', this.onViewMove, this);
10642 this.list.on('scroll', this.onViewScroll, this);
10645 this.list.swallowEvent('mousewheel');
10646 this.assetHeight = 0;
10649 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10650 this.assetHeight += this.header.getHeight();
10653 this.innerList = this.list.createChild({cls:cls+'-inner'});
10654 this.innerList.on('mouseover', this.onViewOver, this);
10655 this.innerList.on('mousemove', this.onViewMove, this);
10656 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10658 if(this.allowBlank && !this.pageSize && !this.disableClear){
10659 this.footer = this.list.createChild({cls:cls+'-ft'});
10660 this.pageTb = new Roo.Toolbar(this.footer);
10664 this.footer = this.list.createChild({cls:cls+'-ft'});
10665 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10666 {pageSize: this.pageSize});
10670 if (this.pageTb && this.allowBlank && !this.disableClear) {
10672 this.pageTb.add(new Roo.Toolbar.Fill(), {
10673 cls: 'x-btn-icon x-btn-clear',
10675 handler: function()
10678 _this.clearValue();
10679 _this.onSelect(false, -1);
10684 this.assetHeight += this.footer.getHeight();
10689 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10692 this.view = new Roo.View(this.list, this.tpl, {
10693 singleSelect:true, store: this.store, selectedClass: this.selectedClass
10695 //this.view.wrapEl.setDisplayed(false);
10696 this.view.on('click', this.onViewClick, this);
10700 this.store.on('beforeload', this.onBeforeLoad, this);
10701 this.store.on('load', this.onLoad, this);
10702 this.store.on('loadexception', this.onLoadException, this);
10704 if(this.resizable){
10705 this.resizer = new Roo.Resizable(this.list, {
10706 pinned:true, handles:'se'
10708 this.resizer.on('resize', function(r, w, h){
10709 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10710 this.listWidth = w;
10711 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10712 this.restrictHeight();
10714 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10717 if(!this.editable){
10718 this.editable = true;
10719 this.setEditable(false);
10724 if (typeof(this.events.add.listeners) != 'undefined') {
10726 this.addicon = this.wrap.createChild(
10727 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
10729 this.addicon.on('click', function(e) {
10730 this.fireEvent('add', this);
10733 if (typeof(this.events.edit.listeners) != 'undefined') {
10735 this.editicon = this.wrap.createChild(
10736 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
10737 if (this.addicon) {
10738 this.editicon.setStyle('margin-left', '40px');
10740 this.editicon.on('click', function(e) {
10742 // we fire even if inothing is selected..
10743 this.fireEvent('edit', this, this.lastData );
10749 this.keyNav = new Roo.KeyNav(this.inputEl(), {
10750 "up" : function(e){
10751 this.inKeyMode = true;
10755 "down" : function(e){
10756 if(!this.isExpanded()){
10757 this.onTriggerClick();
10759 this.inKeyMode = true;
10764 "enter" : function(e){
10765 // this.onViewClick();
10769 if(this.fireEvent("specialkey", this, e)){
10770 this.onViewClick(false);
10776 "esc" : function(e){
10780 "tab" : function(e){
10783 if(this.fireEvent("specialkey", this, e)){
10784 this.onViewClick(false);
10792 doRelay : function(foo, bar, hname){
10793 if(hname == 'down' || this.scope.isExpanded()){
10794 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10803 this.queryDelay = Math.max(this.queryDelay || 10,
10804 this.mode == 'local' ? 10 : 250);
10807 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10809 if(this.typeAhead){
10810 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10812 if(this.editable !== false){
10813 this.inputEl().on("keyup", this.onKeyUp, this);
10815 if(this.forceSelection){
10816 this.inputEl().on('blur', this.doForce, this);
10820 this.choices = this.el.select('ul.select2-choices', true).first();
10821 this.searchField = this.el.select('ul li.select2-search-field', true).first();
10825 initTickableEvents: function()
10829 if(this.hiddenName){
10831 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10833 this.hiddenField.dom.value =
10834 this.hiddenValue !== undefined ? this.hiddenValue :
10835 this.value !== undefined ? this.value : '';
10837 // prevent input submission
10838 this.el.dom.removeAttribute('name');
10839 this.hiddenField.dom.setAttribute('name', this.hiddenName);
10844 // this.list = this.el.select('ul.dropdown-menu',true).first();
10846 this.choices = this.el.select('ul.select2-choices', true).first();
10847 this.searchField = this.el.select('ul li.select2-search-field', true).first();
10848 if(this.triggerList){
10849 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10852 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10853 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10855 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10856 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10858 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10859 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10861 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10862 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10863 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10866 this.cancelBtn.hide();
10871 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10872 _this.list.setWidth(lw);
10875 this.list.on('mouseover', this.onViewOver, this);
10876 this.list.on('mousemove', this.onViewMove, this);
10878 this.list.on('scroll', this.onViewScroll, this);
10881 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>';
10884 this.view = new Roo.View(this.list, this.tpl, {
10885 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10888 //this.view.wrapEl.setDisplayed(false);
10889 this.view.on('click', this.onViewClick, this);
10893 this.store.on('beforeload', this.onBeforeLoad, this);
10894 this.store.on('load', this.onLoad, this);
10895 this.store.on('loadexception', this.onLoadException, this);
10897 // this.keyNav = new Roo.KeyNav(this.inputEl(), {
10898 // "up" : function(e){
10899 // this.inKeyMode = true;
10900 // this.selectPrev();
10903 // "down" : function(e){
10904 // if(!this.isExpanded()){
10905 // this.onTriggerClick();
10907 // this.inKeyMode = true;
10908 // this.selectNext();
10912 // "enter" : function(e){
10913 //// this.onViewClick();
10915 // this.collapse();
10917 // if(this.fireEvent("specialkey", this, e)){
10918 // this.onViewClick(false);
10924 // "esc" : function(e){
10925 // this.collapse();
10928 // "tab" : function(e){
10929 // this.collapse();
10931 // if(this.fireEvent("specialkey", this, e)){
10932 // this.onViewClick(false);
10940 // doRelay : function(foo, bar, hname){
10941 // if(hname == 'down' || this.scope.isExpanded()){
10942 // return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10947 // forceKeyDown: true
10951 this.queryDelay = Math.max(this.queryDelay || 10,
10952 this.mode == 'local' ? 10 : 250);
10955 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10957 if(this.typeAhead){
10958 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10962 onDestroy : function(){
10964 this.view.setStore(null);
10965 this.view.el.removeAllListeners();
10966 this.view.el.remove();
10967 this.view.purgeListeners();
10970 this.list.dom.innerHTML = '';
10974 this.store.un('beforeload', this.onBeforeLoad, this);
10975 this.store.un('load', this.onLoad, this);
10976 this.store.un('loadexception', this.onLoadException, this);
10978 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
10982 fireKey : function(e){
10983 if(e.isNavKeyPress() && !this.list.isVisible()){
10984 this.fireEvent("specialkey", this, e);
10989 onResize: function(w, h){
10990 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
10992 // if(typeof w != 'number'){
10993 // // we do not handle it!?!?
10996 // var tw = this.trigger.getWidth();
10997 // // tw += this.addicon ? this.addicon.getWidth() : 0;
10998 // // tw += this.editicon ? this.editicon.getWidth() : 0;
11000 // this.inputEl().setWidth( this.adjustWidth('input', x));
11002 // //this.trigger.setStyle('left', x+'px');
11004 // if(this.list && this.listWidth === undefined){
11005 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11006 // this.list.setWidth(lw);
11007 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11015 * Allow or prevent the user from directly editing the field text. If false is passed,
11016 * the user will only be able to select from the items defined in the dropdown list. This method
11017 * is the runtime equivalent of setting the 'editable' config option at config time.
11018 * @param {Boolean} value True to allow the user to directly edit the field text
11020 setEditable : function(value){
11021 if(value == this.editable){
11024 this.editable = value;
11026 this.inputEl().dom.setAttribute('readOnly', true);
11027 this.inputEl().on('mousedown', this.onTriggerClick, this);
11028 this.inputEl().addClass('x-combo-noedit');
11030 this.inputEl().dom.setAttribute('readOnly', false);
11031 this.inputEl().un('mousedown', this.onTriggerClick, this);
11032 this.inputEl().removeClass('x-combo-noedit');
11038 onBeforeLoad : function(combo,opts){
11039 if(!this.hasFocus){
11043 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11045 // this.restrictHeight();
11046 this.selectedIndex = -1;
11050 onLoad : function(){
11052 this.hasQuery = false;
11054 if(!this.hasFocus){
11058 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11059 this.loading.hide();
11062 if(this.store.getCount() > 0){
11064 // this.restrictHeight();
11065 if(this.lastQuery == this.allQuery){
11066 if(this.editable && !this.tickable){
11067 this.inputEl().dom.select();
11071 !this.selectByValue(this.value, true) &&
11072 this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' ||
11073 this.store.lastOptions.add != true)
11075 this.select(0, true);
11078 if(this.autoFocus){
11081 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11082 this.taTask.delay(this.typeAheadDelay);
11086 this.onEmptyResults();
11092 onLoadException : function()
11094 this.hasQuery = false;
11096 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11097 this.loading.hide();
11101 Roo.log(this.store.reader.jsonData);
11102 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11104 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11110 onTypeAhead : function(){
11111 if(this.store.getCount() > 0){
11112 var r = this.store.getAt(0);
11113 var newValue = r.data[this.displayField];
11114 var len = newValue.length;
11115 var selStart = this.getRawValue().length;
11117 if(selStart != len){
11118 this.setRawValue(newValue);
11119 this.selectText(selStart, newValue.length);
11125 onSelect : function(record, index){
11127 if(this.fireEvent('beforeselect', this, record, index) !== false){
11129 this.setFromData(index > -1 ? record.data : false);
11132 this.fireEvent('select', this, record, index);
11137 * Returns the currently selected field value or empty string if no value is set.
11138 * @return {String} value The selected value
11140 getValue : function(){
11143 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11146 if(this.valueField){
11147 return typeof this.value != 'undefined' ? this.value : '';
11149 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11154 * Clears any text/value currently set in the field
11156 clearValue : function(){
11157 if(this.hiddenField){
11158 this.hiddenField.dom.value = '';
11161 this.setRawValue('');
11162 this.lastSelectionText = '';
11167 * Sets the specified value into the field. If the value finds a match, the corresponding record text
11168 * will be displayed in the field. If the value does not match the data value of an existing item,
11169 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11170 * Otherwise the field will be blank (although the value will still be set).
11171 * @param {String} value The value to match
11173 setValue : function(v){
11180 if(this.valueField){
11181 var r = this.findRecord(this.valueField, v);
11183 text = r.data[this.displayField];
11184 }else if(this.valueNotFoundText !== undefined){
11185 text = this.valueNotFoundText;
11188 this.lastSelectionText = text;
11189 if(this.hiddenField){
11190 this.hiddenField.dom.value = v;
11192 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11196 * @property {Object} the last set data for the element
11201 * Sets the value of the field based on a object which is related to the record format for the store.
11202 * @param {Object} value the value to set as. or false on reset?
11204 setFromData : function(o){
11207 if(typeof o.display_name !== 'string'){
11208 for(var i=0;i<o.display_name.length;i++){
11209 this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
11217 var dv = ''; // display value
11218 var vv = ''; // value value..
11220 if (this.displayField) {
11221 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11223 // this is an error condition!!!
11224 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
11227 if(this.valueField){
11228 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11231 if(this.hiddenField){
11232 this.hiddenField.dom.value = vv;
11234 this.lastSelectionText = dv;
11235 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11239 // no hidden field.. - we store the value in 'value', but still display
11240 // display field!!!!
11241 this.lastSelectionText = dv;
11242 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11248 reset : function(){
11249 // overridden so that last data is reset..
11250 this.setValue(this.originalValue);
11251 this.clearInvalid();
11252 this.lastData = false;
11254 this.view.clearSelections();
11258 findRecord : function(prop, value){
11260 if(this.store.getCount() > 0){
11261 this.store.each(function(r){
11262 if(r.data[prop] == value){
11272 getName: function()
11274 // returns hidden if it's set..
11275 if (!this.rendered) {return ''};
11276 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
11280 onViewMove : function(e, t){
11281 this.inKeyMode = false;
11285 onViewOver : function(e, t){
11286 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11289 var item = this.view.findItemFromChild(t);
11292 var index = this.view.indexOf(item);
11293 this.select(index, false);
11298 onViewClick : function(view, doFocus, el, e)
11300 var index = this.view.getSelectedIndexes()[0];
11302 var r = this.store.getAt(index);
11306 if(e.getTarget().nodeName.toLowerCase() != 'input'){
11313 Roo.each(this.tickItems, function(v,k){
11315 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11316 _this.tickItems.splice(k, 1);
11326 this.tickItems.push(r.data);
11331 this.onSelect(r, index);
11333 if(doFocus !== false && !this.blockFocus){
11334 this.inputEl().focus();
11339 restrictHeight : function(){
11340 //this.innerList.dom.style.height = '';
11341 //var inner = this.innerList.dom;
11342 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11343 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11344 //this.list.beginUpdate();
11345 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11346 this.list.alignTo(this.inputEl(), this.listAlign);
11347 this.list.alignTo(this.inputEl(), this.listAlign);
11348 //this.list.endUpdate();
11352 onEmptyResults : function(){
11357 * Returns true if the dropdown list is expanded, else false.
11359 isExpanded : function(){
11360 return this.list.isVisible();
11364 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11365 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11366 * @param {String} value The data value of the item to select
11367 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11368 * selected item if it is not currently in view (defaults to true)
11369 * @return {Boolean} True if the value matched an item in the list, else false
11371 selectByValue : function(v, scrollIntoView){
11372 if(v !== undefined && v !== null){
11373 var r = this.findRecord(this.valueField || this.displayField, v);
11375 this.select(this.store.indexOf(r), scrollIntoView);
11383 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11384 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11385 * @param {Number} index The zero-based index of the list item to select
11386 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11387 * selected item if it is not currently in view (defaults to true)
11389 select : function(index, scrollIntoView){
11390 this.selectedIndex = index;
11391 this.view.select(index);
11392 if(scrollIntoView !== false){
11393 var el = this.view.getNode(index);
11394 if(el && !this.multiple && !this.tickable){
11395 this.list.scrollChildIntoView(el, false);
11401 selectNext : function(){
11402 var ct = this.store.getCount();
11404 if(this.selectedIndex == -1){
11406 }else if(this.selectedIndex < ct-1){
11407 this.select(this.selectedIndex+1);
11413 selectPrev : function(){
11414 var ct = this.store.getCount();
11416 if(this.selectedIndex == -1){
11418 }else if(this.selectedIndex != 0){
11419 this.select(this.selectedIndex-1);
11425 onKeyUp : function(e){
11426 if(this.editable !== false && !e.isSpecialKey()){
11427 this.lastKey = e.getKey();
11428 this.dqTask.delay(this.queryDelay);
11433 validateBlur : function(){
11434 return !this.list || !this.list.isVisible();
11438 initQuery : function(){
11439 this.doQuery(this.getRawValue());
11443 doForce : function(){
11444 if(this.inputEl().dom.value.length > 0){
11445 this.inputEl().dom.value =
11446 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11452 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
11453 * query allowing the query action to be canceled if needed.
11454 * @param {String} query The SQL query to execute
11455 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11456 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
11457 * saved in the current store (defaults to false)
11459 doQuery : function(q, forceAll){
11461 if(q === undefined || q === null){
11466 forceAll: forceAll,
11470 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11475 forceAll = qe.forceAll;
11476 if(forceAll === true || (q.length >= this.minChars)){
11478 this.hasQuery = true;
11480 if(this.lastQuery != q || this.alwaysQuery){
11481 this.lastQuery = q;
11482 if(this.mode == 'local'){
11483 this.selectedIndex = -1;
11485 this.store.clearFilter();
11487 this.store.filter(this.displayField, q);
11491 this.store.baseParams[this.queryParam] = q;
11493 var options = {params : this.getParams(q)};
11496 options.add = true;
11497 options.params.start = this.page * this.pageSize;
11500 this.store.load(options);
11502 * this code will make the page width larger, at the beginning, the list not align correctly,
11503 * we should expand the list on onLoad
11504 * so command out it
11509 this.selectedIndex = -1;
11514 this.loadNext = false;
11518 getParams : function(q){
11520 //p[this.queryParam] = q;
11524 p.limit = this.pageSize;
11530 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11532 collapse : function(){
11533 if(!this.isExpanded()){
11541 this.cancelBtn.hide();
11542 this.trigger.show();
11545 Roo.get(document).un('mousedown', this.collapseIf, this);
11546 Roo.get(document).un('mousewheel', this.collapseIf, this);
11547 if (!this.editable) {
11548 Roo.get(document).un('keydown', this.listKeyPress, this);
11550 this.fireEvent('collapse', this);
11554 collapseIf : function(e){
11555 var in_combo = e.within(this.el);
11556 var in_list = e.within(this.list);
11557 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11559 if (in_combo || in_list || is_list) {
11560 //e.stopPropagation();
11565 this.onTickableFooterButtonClick(e, false, false);
11573 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11575 expand : function(){
11577 if(this.isExpanded() || !this.hasFocus){
11581 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11582 this.list.setWidth(lw);
11587 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11588 this.list.setWidth(lw);
11592 this.restrictHeight();
11596 this.tickItems = Roo.apply([], this.item);
11599 this.cancelBtn.show();
11600 this.trigger.hide();
11604 Roo.get(document).on('mousedown', this.collapseIf, this);
11605 Roo.get(document).on('mousewheel', this.collapseIf, this);
11606 if (!this.editable) {
11607 Roo.get(document).on('keydown', this.listKeyPress, this);
11610 this.fireEvent('expand', this);
11614 // Implements the default empty TriggerField.onTriggerClick function
11615 onTriggerClick : function(e)
11617 Roo.log('trigger click');
11619 if(this.disabled || !this.triggerList){
11624 this.loadNext = false;
11626 if(this.isExpanded()){
11628 if (!this.blockFocus) {
11629 this.inputEl().focus();
11633 this.hasFocus = true;
11634 if(this.triggerAction == 'all') {
11635 this.doQuery(this.allQuery, true);
11637 this.doQuery(this.getRawValue());
11639 if (!this.blockFocus) {
11640 this.inputEl().focus();
11645 onTickableTriggerClick : function(e)
11652 this.loadNext = false;
11653 this.hasFocus = true;
11655 if(this.triggerAction == 'all') {
11656 this.doQuery(this.allQuery, true);
11658 this.doQuery(this.getRawValue());
11662 onSearchFieldClick : function(e)
11664 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11669 this.loadNext = false;
11670 this.hasFocus = true;
11672 if(this.triggerAction == 'all') {
11673 this.doQuery(this.allQuery, true);
11675 this.doQuery(this.getRawValue());
11679 listKeyPress : function(e)
11681 //Roo.log('listkeypress');
11682 // scroll to first matching element based on key pres..
11683 if (e.isSpecialKey()) {
11686 var k = String.fromCharCode(e.getKey()).toUpperCase();
11689 var csel = this.view.getSelectedNodes();
11690 var cselitem = false;
11692 var ix = this.view.indexOf(csel[0]);
11693 cselitem = this.store.getAt(ix);
11694 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11700 this.store.each(function(v) {
11702 // start at existing selection.
11703 if (cselitem.id == v.id) {
11709 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11710 match = this.store.indexOf(v);
11716 if (match === false) {
11717 return true; // no more action?
11720 this.view.select(match);
11721 var sn = Roo.get(this.view.getSelectedNodes()[0])
11722 //sn.scrollIntoView(sn.dom.parentNode, false);
11725 onViewScroll : function(e, t){
11727 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){
11731 this.hasQuery = true;
11733 this.loading = this.list.select('.loading', true).first();
11735 if(this.loading === null){
11736 this.list.createChild({
11738 cls: 'loading select2-more-results select2-active',
11739 html: 'Loading more results...'
11742 this.loading = this.list.select('.loading', true).first();
11744 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11746 this.loading.hide();
11749 this.loading.show();
11754 this.loadNext = true;
11756 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11761 addItem : function(o)
11763 var dv = ''; // display value
11765 if (this.displayField) {
11766 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11768 // this is an error condition!!!
11769 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
11776 var choice = this.choices.createChild({
11778 cls: 'select2-search-choice',
11787 cls: 'select2-search-choice-close',
11792 }, this.searchField);
11794 var close = choice.select('a.select2-search-choice-close', true).first()
11796 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11804 this.inputEl().dom.value = '';
11808 onRemoveItem : function(e, _self, o)
11810 e.preventDefault();
11811 var index = this.item.indexOf(o.data) * 1;
11814 Roo.log('not this item?!');
11818 this.item.splice(index, 1);
11823 this.fireEvent('remove', this, e);
11827 syncValue : function()
11829 if(!this.item.length){
11836 Roo.each(this.item, function(i){
11837 if(_this.valueField){
11838 value.push(i[_this.valueField]);
11845 this.value = value.join(',');
11847 if(this.hiddenField){
11848 this.hiddenField.dom.value = this.value;
11852 clearItem : function()
11854 if(!this.multiple){
11860 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11867 inputEl: function ()
11870 return this.searchField;
11872 return this.el.select('input.form-control',true).first();
11876 onTickableFooterButtonClick : function(e, btn, el)
11878 e.preventDefault();
11880 if(btn && btn.name == 'cancel'){
11881 this.tickItems = Roo.apply([], this.item);
11890 Roo.each(this.tickItems, function(o){
11901 * @cfg {Boolean} grow
11905 * @cfg {Number} growMin
11909 * @cfg {Number} growMax
11919 * Ext JS Library 1.1.1
11920 * Copyright(c) 2006-2007, Ext JS, LLC.
11922 * Originally Released Under LGPL - original licence link has changed is not relivant.
11925 * <script type="text/javascript">
11930 * @extends Roo.util.Observable
11931 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
11932 * This class also supports single and multi selection modes. <br>
11933 * Create a data model bound view:
11935 var store = new Roo.data.Store(...);
11937 var view = new Roo.View({
11939 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
11941 singleSelect: true,
11942 selectedClass: "ydataview-selected",
11946 // listen for node click?
11947 view.on("click", function(vw, index, node, e){
11948 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
11952 dataModel.load("foobar.xml");
11954 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
11956 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
11957 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
11959 * Note: old style constructor is still suported (container, template, config)
11962 * Create a new View
11963 * @param {Object} config The config object
11966 Roo.View = function(config, depreciated_tpl, depreciated_config){
11968 this.parent = false;
11970 if (typeof(depreciated_tpl) == 'undefined') {
11971 // new way.. - universal constructor.
11972 Roo.apply(this, config);
11973 this.el = Roo.get(this.el);
11976 this.el = Roo.get(config);
11977 this.tpl = depreciated_tpl;
11978 Roo.apply(this, depreciated_config);
11980 this.wrapEl = this.el.wrap().wrap();
11981 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
11984 if(typeof(this.tpl) == "string"){
11985 this.tpl = new Roo.Template(this.tpl);
11987 // support xtype ctors..
11988 this.tpl = new Roo.factory(this.tpl, Roo);
11992 this.tpl.compile();
11997 * @event beforeclick
11998 * Fires before a click is processed. Returns false to cancel the default action.
11999 * @param {Roo.View} this
12000 * @param {Number} index The index of the target node
12001 * @param {HTMLElement} node The target node
12002 * @param {Roo.EventObject} e The raw event object
12004 "beforeclick" : true,
12007 * Fires when a template node is clicked.
12008 * @param {Roo.View} this
12009 * @param {Number} index The index of the target node
12010 * @param {HTMLElement} node The target node
12011 * @param {Roo.EventObject} e The raw event object
12016 * Fires when a template node is double clicked.
12017 * @param {Roo.View} this
12018 * @param {Number} index The index of the target node
12019 * @param {HTMLElement} node The target node
12020 * @param {Roo.EventObject} e The raw event object
12024 * @event contextmenu
12025 * Fires when a template node is right clicked.
12026 * @param {Roo.View} this
12027 * @param {Number} index The index of the target node
12028 * @param {HTMLElement} node The target node
12029 * @param {Roo.EventObject} e The raw event object
12031 "contextmenu" : true,
12033 * @event selectionchange
12034 * Fires when the selected nodes change.
12035 * @param {Roo.View} this
12036 * @param {Array} selections Array of the selected nodes
12038 "selectionchange" : true,
12041 * @event beforeselect
12042 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12043 * @param {Roo.View} this
12044 * @param {HTMLElement} node The node to be selected
12045 * @param {Array} selections Array of currently selected nodes
12047 "beforeselect" : true,
12049 * @event preparedata
12050 * Fires on every row to render, to allow you to change the data.
12051 * @param {Roo.View} this
12052 * @param {Object} data to be rendered (change this)
12054 "preparedata" : true
12062 "click": this.onClick,
12063 "dblclick": this.onDblClick,
12064 "contextmenu": this.onContextMenu,
12068 this.selections = [];
12070 this.cmp = new Roo.CompositeElementLite([]);
12072 this.store = Roo.factory(this.store, Roo.data);
12073 this.setStore(this.store, true);
12076 if ( this.footer && this.footer.xtype) {
12078 var fctr = this.wrapEl.appendChild(document.createElement("div"));
12080 this.footer.dataSource = this.store
12081 this.footer.container = fctr;
12082 this.footer = Roo.factory(this.footer, Roo);
12083 fctr.insertFirst(this.el);
12085 // this is a bit insane - as the paging toolbar seems to detach the el..
12086 // dom.parentNode.parentNode.parentNode
12087 // they get detached?
12091 Roo.View.superclass.constructor.call(this);
12096 Roo.extend(Roo.View, Roo.util.Observable, {
12099 * @cfg {Roo.data.Store} store Data store to load data from.
12104 * @cfg {String|Roo.Element} el The container element.
12109 * @cfg {String|Roo.Template} tpl The template used by this View
12113 * @cfg {String} dataName the named area of the template to use as the data area
12114 * Works with domtemplates roo-name="name"
12118 * @cfg {String} selectedClass The css class to add to selected nodes
12120 selectedClass : "x-view-selected",
12122 * @cfg {String} emptyText The empty text to show when nothing is loaded.
12127 * @cfg {String} text to display on mask (default Loading)
12131 * @cfg {Boolean} multiSelect Allow multiple selection
12133 multiSelect : false,
12135 * @cfg {Boolean} singleSelect Allow single selection
12137 singleSelect: false,
12140 * @cfg {Boolean} toggleSelect - selecting
12142 toggleSelect : false,
12145 * @cfg {Boolean} tickable - selecting
12150 * Returns the element this view is bound to.
12151 * @return {Roo.Element}
12153 getEl : function(){
12154 return this.wrapEl;
12160 * Refreshes the view. - called by datachanged on the store. - do not call directly.
12162 refresh : function(){
12163 Roo.log('refresh');
12166 // if we are using something like 'domtemplate', then
12167 // the what gets used is:
12168 // t.applySubtemplate(NAME, data, wrapping data..)
12169 // the outer template then get' applied with
12170 // the store 'extra data'
12171 // and the body get's added to the
12172 // roo-name="data" node?
12173 // <span class='roo-tpl-{name}'></span> ?????
12177 this.clearSelections();
12178 this.el.update("");
12180 var records = this.store.getRange();
12181 if(records.length < 1) {
12183 // is this valid?? = should it render a template??
12185 this.el.update(this.emptyText);
12189 if (this.dataName) {
12190 this.el.update(t.apply(this.store.meta)); //????
12191 el = this.el.child('.roo-tpl-' + this.dataName);
12194 for(var i = 0, len = records.length; i < len; i++){
12195 var data = this.prepareData(records[i].data, i, records[i]);
12196 this.fireEvent("preparedata", this, data, i, records[i]);
12198 var d = Roo.apply({}, data);
12201 Roo.apply(d, {'roo-id' : Roo.id()});
12205 Roo.each(this.parent.item, function(item){
12206 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12209 Roo.apply(d, {'roo-data-checked' : 'checked'});
12213 html[html.length] = Roo.util.Format.trim(
12215 t.applySubtemplate(this.dataName, d, this.store.meta) :
12222 el.update(html.join(""));
12223 this.nodes = el.dom.childNodes;
12224 this.updateIndexes(0);
12229 * Function to override to reformat the data that is sent to
12230 * the template for each node.
12231 * DEPRICATED - use the preparedata event handler.
12232 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12233 * a JSON object for an UpdateManager bound view).
12235 prepareData : function(data, index, record)
12237 this.fireEvent("preparedata", this, data, index, record);
12241 onUpdate : function(ds, record){
12242 Roo.log('on update');
12243 this.clearSelections();
12244 var index = this.store.indexOf(record);
12245 var n = this.nodes[index];
12246 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12247 n.parentNode.removeChild(n);
12248 this.updateIndexes(index, index);
12254 onAdd : function(ds, records, index)
12256 Roo.log(['on Add', ds, records, index] );
12257 this.clearSelections();
12258 if(this.nodes.length == 0){
12262 var n = this.nodes[index];
12263 for(var i = 0, len = records.length; i < len; i++){
12264 var d = this.prepareData(records[i].data, i, records[i]);
12266 this.tpl.insertBefore(n, d);
12269 this.tpl.append(this.el, d);
12272 this.updateIndexes(index);
12275 onRemove : function(ds, record, index){
12276 Roo.log('onRemove');
12277 this.clearSelections();
12278 var el = this.dataName ?
12279 this.el.child('.roo-tpl-' + this.dataName) :
12282 el.dom.removeChild(this.nodes[index]);
12283 this.updateIndexes(index);
12287 * Refresh an individual node.
12288 * @param {Number} index
12290 refreshNode : function(index){
12291 this.onUpdate(this.store, this.store.getAt(index));
12294 updateIndexes : function(startIndex, endIndex){
12295 var ns = this.nodes;
12296 startIndex = startIndex || 0;
12297 endIndex = endIndex || ns.length - 1;
12298 for(var i = startIndex; i <= endIndex; i++){
12299 ns[i].nodeIndex = i;
12304 * Changes the data store this view uses and refresh the view.
12305 * @param {Store} store
12307 setStore : function(store, initial){
12308 if(!initial && this.store){
12309 this.store.un("datachanged", this.refresh);
12310 this.store.un("add", this.onAdd);
12311 this.store.un("remove", this.onRemove);
12312 this.store.un("update", this.onUpdate);
12313 this.store.un("clear", this.refresh);
12314 this.store.un("beforeload", this.onBeforeLoad);
12315 this.store.un("load", this.onLoad);
12316 this.store.un("loadexception", this.onLoad);
12320 store.on("datachanged", this.refresh, this);
12321 store.on("add", this.onAdd, this);
12322 store.on("remove", this.onRemove, this);
12323 store.on("update", this.onUpdate, this);
12324 store.on("clear", this.refresh, this);
12325 store.on("beforeload", this.onBeforeLoad, this);
12326 store.on("load", this.onLoad, this);
12327 store.on("loadexception", this.onLoad, this);
12335 * onbeforeLoad - masks the loading area.
12338 onBeforeLoad : function(store,opts)
12340 Roo.log('onBeforeLoad');
12342 this.el.update("");
12344 this.el.mask(this.mask ? this.mask : "Loading" );
12346 onLoad : function ()
12353 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12354 * @param {HTMLElement} node
12355 * @return {HTMLElement} The template node
12357 findItemFromChild : function(node){
12358 var el = this.dataName ?
12359 this.el.child('.roo-tpl-' + this.dataName,true) :
12362 if(!node || node.parentNode == el){
12365 var p = node.parentNode;
12366 while(p && p != el){
12367 if(p.parentNode == el){
12376 onClick : function(e){
12377 var item = this.findItemFromChild(e.getTarget());
12379 var index = this.indexOf(item);
12380 if(this.onItemClick(item, index, e) !== false){
12381 this.fireEvent("click", this, index, item, e);
12384 this.clearSelections();
12389 onContextMenu : function(e){
12390 var item = this.findItemFromChild(e.getTarget());
12392 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12397 onDblClick : function(e){
12398 var item = this.findItemFromChild(e.getTarget());
12400 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12404 onItemClick : function(item, index, e)
12406 if(this.fireEvent("beforeclick", this, index, item, e) === false){
12409 if (this.toggleSelect) {
12410 var m = this.isSelected(item) ? 'unselect' : 'select';
12413 _t[m](item, true, false);
12416 if(this.multiSelect || this.singleSelect){
12417 if(this.multiSelect && e.shiftKey && this.lastSelection){
12418 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12420 this.select(item, this.multiSelect && e.ctrlKey);
12421 this.lastSelection = item;
12424 if(!this.tickable){
12425 e.preventDefault();
12433 * Get the number of selected nodes.
12436 getSelectionCount : function(){
12437 return this.selections.length;
12441 * Get the currently selected nodes.
12442 * @return {Array} An array of HTMLElements
12444 getSelectedNodes : function(){
12445 return this.selections;
12449 * Get the indexes of the selected nodes.
12452 getSelectedIndexes : function(){
12453 var indexes = [], s = this.selections;
12454 for(var i = 0, len = s.length; i < len; i++){
12455 indexes.push(s[i].nodeIndex);
12461 * Clear all selections
12462 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12464 clearSelections : function(suppressEvent){
12465 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12466 this.cmp.elements = this.selections;
12467 this.cmp.removeClass(this.selectedClass);
12468 this.selections = [];
12469 if(!suppressEvent){
12470 this.fireEvent("selectionchange", this, this.selections);
12476 * Returns true if the passed node is selected
12477 * @param {HTMLElement/Number} node The node or node index
12478 * @return {Boolean}
12480 isSelected : function(node){
12481 var s = this.selections;
12485 node = this.getNode(node);
12486 return s.indexOf(node) !== -1;
12491 * @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
12492 * @param {Boolean} keepExisting (optional) true to keep existing selections
12493 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12495 select : function(nodeInfo, keepExisting, suppressEvent){
12496 if(nodeInfo instanceof Array){
12498 this.clearSelections(true);
12500 for(var i = 0, len = nodeInfo.length; i < len; i++){
12501 this.select(nodeInfo[i], true, true);
12505 var node = this.getNode(nodeInfo);
12506 if(!node || this.isSelected(node)){
12507 return; // already selected.
12510 this.clearSelections(true);
12512 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12513 Roo.fly(node).addClass(this.selectedClass);
12514 this.selections.push(node);
12515 if(!suppressEvent){
12516 this.fireEvent("selectionchange", this, this.selections);
12524 * @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
12525 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12526 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12528 unselect : function(nodeInfo, keepExisting, suppressEvent)
12530 if(nodeInfo instanceof Array){
12531 Roo.each(this.selections, function(s) {
12532 this.unselect(s, nodeInfo);
12536 var node = this.getNode(nodeInfo);
12537 if(!node || !this.isSelected(node)){
12538 Roo.log("not selected");
12539 return; // not selected.
12543 Roo.each(this.selections, function(s) {
12545 Roo.fly(node).removeClass(this.selectedClass);
12552 this.selections= ns;
12553 this.fireEvent("selectionchange", this, this.selections);
12557 * Gets a template node.
12558 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12559 * @return {HTMLElement} The node or null if it wasn't found
12561 getNode : function(nodeInfo){
12562 if(typeof nodeInfo == "string"){
12563 return document.getElementById(nodeInfo);
12564 }else if(typeof nodeInfo == "number"){
12565 return this.nodes[nodeInfo];
12571 * Gets a range template nodes.
12572 * @param {Number} startIndex
12573 * @param {Number} endIndex
12574 * @return {Array} An array of nodes
12576 getNodes : function(start, end){
12577 var ns = this.nodes;
12578 start = start || 0;
12579 end = typeof end == "undefined" ? ns.length - 1 : end;
12582 for(var i = start; i <= end; i++){
12586 for(var i = start; i >= end; i--){
12594 * Finds the index of the passed node
12595 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12596 * @return {Number} The index of the node or -1
12598 indexOf : function(node){
12599 node = this.getNode(node);
12600 if(typeof node.nodeIndex == "number"){
12601 return node.nodeIndex;
12603 var ns = this.nodes;
12604 for(var i = 0, len = ns.length; i < len; i++){
12615 * based on jquery fullcalendar
12619 Roo.bootstrap = Roo.bootstrap || {};
12621 * @class Roo.bootstrap.Calendar
12622 * @extends Roo.bootstrap.Component
12623 * Bootstrap Calendar class
12624 * @cfg {Boolean} loadMask (true|false) default false
12625 * @cfg {Object} header generate the user specific header of the calendar, default false
12628 * Create a new Container
12629 * @param {Object} config The config object
12634 Roo.bootstrap.Calendar = function(config){
12635 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12639 * Fires when a date is selected
12640 * @param {DatePicker} this
12641 * @param {Date} date The selected date
12645 * @event monthchange
12646 * Fires when the displayed month changes
12647 * @param {DatePicker} this
12648 * @param {Date} date The selected month
12650 'monthchange': true,
12652 * @event evententer
12653 * Fires when mouse over an event
12654 * @param {Calendar} this
12655 * @param {event} Event
12657 'evententer': true,
12659 * @event eventleave
12660 * Fires when the mouse leaves an
12661 * @param {Calendar} this
12664 'eventleave': true,
12666 * @event eventclick
12667 * Fires when the mouse click an
12668 * @param {Calendar} this
12677 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
12680 * @cfg {Number} startDay
12681 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12689 getAutoCreate : function(){
12692 var fc_button = function(name, corner, style, content ) {
12693 return Roo.apply({},{
12695 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
12697 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12700 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12711 style : 'width:100%',
12718 cls : 'fc-header-left',
12720 fc_button('prev', 'left', 'arrow', '‹' ),
12721 fc_button('next', 'right', 'arrow', '›' ),
12722 { tag: 'span', cls: 'fc-header-space' },
12723 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
12731 cls : 'fc-header-center',
12735 cls: 'fc-header-title',
12738 html : 'month / year'
12746 cls : 'fc-header-right',
12748 /* fc_button('month', 'left', '', 'month' ),
12749 fc_button('week', '', '', 'week' ),
12750 fc_button('day', 'right', '', 'day' )
12762 header = this.header;
12765 var cal_heads = function() {
12767 // fixme - handle this.
12769 for (var i =0; i < Date.dayNames.length; i++) {
12770 var d = Date.dayNames[i];
12773 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12774 html : d.substring(0,3)
12778 ret[0].cls += ' fc-first';
12779 ret[6].cls += ' fc-last';
12782 var cal_cell = function(n) {
12785 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12790 cls: 'fc-day-number',
12794 cls: 'fc-day-content',
12798 style: 'position: relative;' // height: 17px;
12810 var cal_rows = function() {
12813 for (var r = 0; r < 6; r++) {
12820 for (var i =0; i < Date.dayNames.length; i++) {
12821 var d = Date.dayNames[i];
12822 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12825 row.cn[0].cls+=' fc-first';
12826 row.cn[0].cn[0].style = 'min-height:90px';
12827 row.cn[6].cls+=' fc-last';
12831 ret[0].cls += ' fc-first';
12832 ret[4].cls += ' fc-prev-last';
12833 ret[5].cls += ' fc-last';
12840 cls: 'fc-border-separate',
12841 style : 'width:100%',
12849 cls : 'fc-first fc-last',
12867 cls : 'fc-content',
12868 style : "position: relative;",
12871 cls : 'fc-view fc-view-month fc-grid',
12872 style : 'position: relative',
12873 unselectable : 'on',
12876 cls : 'fc-event-container',
12877 style : 'position:absolute;z-index:8;top:0;left:0;'
12895 initEvents : function()
12898 throw "can not find store for calendar";
12904 style: "text-align:center",
12908 style: "background-color:white;width:50%;margin:250 auto",
12912 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
12923 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12925 var size = this.el.select('.fc-content', true).first().getSize();
12926 this.maskEl.setSize(size.width, size.height);
12927 this.maskEl.enableDisplayMode("block");
12928 if(!this.loadMask){
12929 this.maskEl.hide();
12932 this.store = Roo.factory(this.store, Roo.data);
12933 this.store.on('load', this.onLoad, this);
12934 this.store.on('beforeload', this.onBeforeLoad, this);
12938 this.cells = this.el.select('.fc-day',true);
12939 //Roo.log(this.cells);
12940 this.textNodes = this.el.query('.fc-day-number');
12941 this.cells.addClassOnOver('fc-state-hover');
12943 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12944 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12945 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12946 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12948 this.on('monthchange', this.onMonthChange, this);
12950 this.update(new Date().clearTime());
12953 resize : function() {
12954 var sz = this.el.getSize();
12956 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
12957 this.el.select('.fc-day-content div',true).setHeight(34);
12962 showPrevMonth : function(e){
12963 this.update(this.activeDate.add("mo", -1));
12965 showToday : function(e){
12966 this.update(new Date().clearTime());
12969 showNextMonth : function(e){
12970 this.update(this.activeDate.add("mo", 1));
12974 showPrevYear : function(){
12975 this.update(this.activeDate.add("y", -1));
12979 showNextYear : function(){
12980 this.update(this.activeDate.add("y", 1));
12985 update : function(date)
12987 var vd = this.activeDate;
12988 this.activeDate = date;
12989 // if(vd && this.el){
12990 // var t = date.getTime();
12991 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
12992 // Roo.log('using add remove');
12994 // this.fireEvent('monthchange', this, date);
12996 // this.cells.removeClass("fc-state-highlight");
12997 // this.cells.each(function(c){
12998 // if(c.dateValue == t){
12999 // c.addClass("fc-state-highlight");
13000 // setTimeout(function(){
13001 // try{c.dom.firstChild.focus();}catch(e){}
13011 var days = date.getDaysInMonth();
13013 var firstOfMonth = date.getFirstDateOfMonth();
13014 var startingPos = firstOfMonth.getDay()-this.startDay;
13016 if(startingPos < this.startDay){
13020 var pm = date.add(Date.MONTH, -1);
13021 var prevStart = pm.getDaysInMonth()-startingPos;
13023 this.cells = this.el.select('.fc-day',true);
13024 this.textNodes = this.el.query('.fc-day-number');
13025 this.cells.addClassOnOver('fc-state-hover');
13027 var cells = this.cells.elements;
13028 var textEls = this.textNodes;
13030 Roo.each(cells, function(cell){
13031 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13034 days += startingPos;
13036 // convert everything to numbers so it's fast
13037 var day = 86400000;
13038 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13041 //Roo.log(prevStart);
13043 var today = new Date().clearTime().getTime();
13044 var sel = date.clearTime().getTime();
13045 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13046 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13047 var ddMatch = this.disabledDatesRE;
13048 var ddText = this.disabledDatesText;
13049 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13050 var ddaysText = this.disabledDaysText;
13051 var format = this.format;
13053 var setCellClass = function(cal, cell){
13057 //Roo.log('set Cell Class');
13059 var t = d.getTime();
13063 cell.dateValue = t;
13065 cell.className += " fc-today";
13066 cell.className += " fc-state-highlight";
13067 cell.title = cal.todayText;
13070 // disable highlight in other month..
13071 //cell.className += " fc-state-highlight";
13076 cell.className = " fc-state-disabled";
13077 cell.title = cal.minText;
13081 cell.className = " fc-state-disabled";
13082 cell.title = cal.maxText;
13086 if(ddays.indexOf(d.getDay()) != -1){
13087 cell.title = ddaysText;
13088 cell.className = " fc-state-disabled";
13091 if(ddMatch && format){
13092 var fvalue = d.dateFormat(format);
13093 if(ddMatch.test(fvalue)){
13094 cell.title = ddText.replace("%0", fvalue);
13095 cell.className = " fc-state-disabled";
13099 if (!cell.initialClassName) {
13100 cell.initialClassName = cell.dom.className;
13103 cell.dom.className = cell.initialClassName + ' ' + cell.className;
13108 for(; i < startingPos; i++) {
13109 textEls[i].innerHTML = (++prevStart);
13110 d.setDate(d.getDate()+1);
13112 cells[i].className = "fc-past fc-other-month";
13113 setCellClass(this, cells[i]);
13118 for(; i < days; i++){
13119 intDay = i - startingPos + 1;
13120 textEls[i].innerHTML = (intDay);
13121 d.setDate(d.getDate()+1);
13123 cells[i].className = ''; // "x-date-active";
13124 setCellClass(this, cells[i]);
13128 for(; i < 42; i++) {
13129 textEls[i].innerHTML = (++extraDays);
13130 d.setDate(d.getDate()+1);
13132 cells[i].className = "fc-future fc-other-month";
13133 setCellClass(this, cells[i]);
13136 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13138 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13140 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13141 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13143 if(totalRows != 6){
13144 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13145 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13148 this.fireEvent('monthchange', this, date);
13152 if(!this.internalRender){
13153 var main = this.el.dom.firstChild;
13154 var w = main.offsetWidth;
13155 this.el.setWidth(w + this.el.getBorderWidth("lr"));
13156 Roo.fly(main).setWidth(w);
13157 this.internalRender = true;
13158 // opera does not respect the auto grow header center column
13159 // then, after it gets a width opera refuses to recalculate
13160 // without a second pass
13161 if(Roo.isOpera && !this.secondPass){
13162 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13163 this.secondPass = true;
13164 this.update.defer(10, this, [date]);
13171 findCell : function(dt) {
13172 dt = dt.clearTime().getTime();
13174 this.cells.each(function(c){
13175 //Roo.log("check " +c.dateValue + '?=' + dt);
13176 if(c.dateValue == dt){
13186 findCells : function(ev) {
13187 var s = ev.start.clone().clearTime().getTime();
13189 var e= ev.end.clone().clearTime().getTime();
13192 this.cells.each(function(c){
13193 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13195 if(c.dateValue > e){
13198 if(c.dateValue < s){
13207 // findBestRow: function(cells)
13211 // for (var i =0 ; i < cells.length;i++) {
13212 // ret = Math.max(cells[i].rows || 0,ret);
13219 addItem : function(ev)
13221 // look for vertical location slot in
13222 var cells = this.findCells(ev);
13224 // ev.row = this.findBestRow(cells);
13226 // work out the location.
13230 for(var i =0; i < cells.length; i++) {
13232 cells[i].row = cells[0].row;
13235 cells[i].row = cells[i].row + 1;
13245 if (crow.start.getY() == cells[i].getY()) {
13247 crow.end = cells[i];
13264 cells[0].events.push(ev);
13266 this.calevents.push(ev);
13269 clearEvents: function() {
13271 if(!this.calevents){
13275 Roo.each(this.cells.elements, function(c){
13281 Roo.each(this.calevents, function(e) {
13282 Roo.each(e.els, function(el) {
13283 el.un('mouseenter' ,this.onEventEnter, this);
13284 el.un('mouseleave' ,this.onEventLeave, this);
13289 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13295 renderEvents: function()
13299 this.cells.each(function(c) {
13308 if(c.row != c.events.length){
13309 r = 4 - (4 - (c.row - c.events.length));
13312 c.events = ev.slice(0, r);
13313 c.more = ev.slice(r);
13315 if(c.more.length && c.more.length == 1){
13316 c.events.push(c.more.pop());
13319 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13323 this.cells.each(function(c) {
13325 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13328 for (var e = 0; e < c.events.length; e++){
13329 var ev = c.events[e];
13330 var rows = ev.rows;
13332 for(var i = 0; i < rows.length; i++) {
13334 // how many rows should it span..
13337 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13338 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13340 unselectable : "on",
13343 cls: 'fc-event-inner',
13347 // cls: 'fc-event-time',
13348 // html : cells.length > 1 ? '' : ev.time
13352 cls: 'fc-event-title',
13353 html : String.format('{0}', ev.title)
13360 cls: 'ui-resizable-handle ui-resizable-e',
13361 html : '  '
13368 cfg.cls += ' fc-event-start';
13370 if ((i+1) == rows.length) {
13371 cfg.cls += ' fc-event-end';
13374 var ctr = _this.el.select('.fc-event-container',true).first();
13375 var cg = ctr.createChild(cfg);
13377 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13378 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13380 var r = (c.more.length) ? 1 : 0;
13381 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
13382 cg.setWidth(ebox.right - sbox.x -2);
13384 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13385 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13386 cg.on('click', _this.onEventClick, _this, ev);
13397 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13398 style : 'position: absolute',
13399 unselectable : "on",
13402 cls: 'fc-event-inner',
13406 cls: 'fc-event-title',
13414 cls: 'ui-resizable-handle ui-resizable-e',
13415 html : '  '
13421 var ctr = _this.el.select('.fc-event-container',true).first();
13422 var cg = ctr.createChild(cfg);
13424 var sbox = c.select('.fc-day-content',true).first().getBox();
13425 var ebox = c.select('.fc-day-content',true).first().getBox();
13427 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
13428 cg.setWidth(ebox.right - sbox.x -2);
13430 cg.on('click', _this.onMoreEventClick, _this, c.more);
13440 onEventEnter: function (e, el,event,d) {
13441 this.fireEvent('evententer', this, el, event);
13444 onEventLeave: function (e, el,event,d) {
13445 this.fireEvent('eventleave', this, el, event);
13448 onEventClick: function (e, el,event,d) {
13449 this.fireEvent('eventclick', this, el, event);
13452 onMonthChange: function () {
13456 onMoreEventClick: function(e, el, more)
13460 this.calpopover.placement = 'right';
13461 this.calpopover.setTitle('More');
13463 this.calpopover.setContent('');
13465 var ctr = this.calpopover.el.select('.popover-content', true).first();
13467 Roo.each(more, function(m){
13469 cls : 'fc-event-hori fc-event-draggable',
13472 var cg = ctr.createChild(cfg);
13474 cg.on('click', _this.onEventClick, _this, m);
13477 this.calpopover.show(el);
13482 onLoad: function ()
13484 this.calevents = [];
13487 if(this.store.getCount() > 0){
13488 this.store.data.each(function(d){
13491 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13492 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13493 time : d.data.start_time,
13494 title : d.data.title,
13495 description : d.data.description,
13496 venue : d.data.venue
13501 this.renderEvents();
13503 if(this.calevents.length && this.loadMask){
13504 this.maskEl.hide();
13508 onBeforeLoad: function()
13510 this.clearEvents();
13512 this.maskEl.show();
13526 * @class Roo.bootstrap.Popover
13527 * @extends Roo.bootstrap.Component
13528 * Bootstrap Popover class
13529 * @cfg {String} html contents of the popover (or false to use children..)
13530 * @cfg {String} title of popover (or false to hide)
13531 * @cfg {String} placement how it is placed
13532 * @cfg {String} trigger click || hover (or false to trigger manually)
13533 * @cfg {String} over what (parent or false to trigger manually.)
13536 * Create a new Popover
13537 * @param {Object} config The config object
13540 Roo.bootstrap.Popover = function(config){
13541 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13544 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
13546 title: 'Fill in a title',
13549 placement : 'right',
13550 trigger : 'hover', // hover
13554 can_build_overlaid : false,
13556 getChildContainer : function()
13558 return this.el.select('.popover-content',true).first();
13561 getAutoCreate : function(){
13562 Roo.log('make popover?');
13564 cls : 'popover roo-dynamic',
13565 style: 'display:block',
13571 cls : 'popover-inner',
13575 cls: 'popover-title',
13579 cls : 'popover-content',
13590 setTitle: function(str)
13592 this.el.select('.popover-title',true).first().dom.innerHTML = str;
13594 setContent: function(str)
13596 this.el.select('.popover-content',true).first().dom.innerHTML = str;
13598 // as it get's added to the bottom of the page.
13599 onRender : function(ct, position)
13601 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13603 var cfg = Roo.apply({}, this.getAutoCreate());
13607 cfg.cls += ' ' + this.cls;
13610 cfg.style = this.style;
13612 Roo.log("adding to ")
13613 this.el = Roo.get(document.body).createChild(cfg, position);
13619 initEvents : function()
13621 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13622 this.el.enableDisplayMode('block');
13624 if (this.over === false) {
13627 if (this.triggers === false) {
13630 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13631 var triggers = this.trigger ? this.trigger.split(' ') : [];
13632 Roo.each(triggers, function(trigger) {
13634 if (trigger == 'click') {
13635 on_el.on('click', this.toggle, this);
13636 } else if (trigger != 'manual') {
13637 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
13638 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13640 on_el.on(eventIn ,this.enter, this);
13641 on_el.on(eventOut, this.leave, this);
13652 toggle : function () {
13653 this.hoverState == 'in' ? this.leave() : this.enter();
13656 enter : function () {
13659 clearTimeout(this.timeout);
13661 this.hoverState = 'in'
13663 if (!this.delay || !this.delay.show) {
13668 this.timeout = setTimeout(function () {
13669 if (_t.hoverState == 'in') {
13672 }, this.delay.show)
13674 leave : function() {
13675 clearTimeout(this.timeout);
13677 this.hoverState = 'out'
13679 if (!this.delay || !this.delay.hide) {
13684 this.timeout = setTimeout(function () {
13685 if (_t.hoverState == 'out') {
13688 }, this.delay.hide)
13691 show : function (on_el)
13694 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13697 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13698 if (this.html !== false) {
13699 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13701 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13702 if (!this.title.length) {
13703 this.el.select('.popover-title',true).hide();
13706 var placement = typeof this.placement == 'function' ?
13707 this.placement.call(this, this.el, on_el) :
13710 var autoToken = /\s?auto?\s?/i;
13711 var autoPlace = autoToken.test(placement);
13713 placement = placement.replace(autoToken, '') || 'top';
13717 //this.el.setXY([0,0]);
13719 this.el.dom.style.display='block';
13720 this.el.addClass(placement);
13722 //this.el.appendTo(on_el);
13724 var p = this.getPosition();
13725 var box = this.el.getBox();
13730 var align = Roo.bootstrap.Popover.alignment[placement]
13731 this.el.alignTo(on_el, align[0],align[1]);
13732 //var arrow = this.el.select('.arrow',true).first();
13733 //arrow.set(align[2],
13735 this.el.addClass('in');
13736 this.hoverState = null;
13738 if (this.el.hasClass('fade')) {
13745 this.el.setXY([0,0]);
13746 this.el.removeClass('in');
13753 Roo.bootstrap.Popover.alignment = {
13754 'left' : ['r-l', [-10,0], 'right'],
13755 'right' : ['l-r', [10,0], 'left'],
13756 'bottom' : ['t-b', [0,10], 'top'],
13757 'top' : [ 'b-t', [0,-10], 'bottom']
13768 * @class Roo.bootstrap.Progress
13769 * @extends Roo.bootstrap.Component
13770 * Bootstrap Progress class
13771 * @cfg {Boolean} striped striped of the progress bar
13772 * @cfg {Boolean} active animated of the progress bar
13776 * Create a new Progress
13777 * @param {Object} config The config object
13780 Roo.bootstrap.Progress = function(config){
13781 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13784 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
13789 getAutoCreate : function(){
13797 cfg.cls += ' progress-striped';
13801 cfg.cls += ' active';
13820 * @class Roo.bootstrap.ProgressBar
13821 * @extends Roo.bootstrap.Component
13822 * Bootstrap ProgressBar class
13823 * @cfg {Number} aria_valuenow aria-value now
13824 * @cfg {Number} aria_valuemin aria-value min
13825 * @cfg {Number} aria_valuemax aria-value max
13826 * @cfg {String} label label for the progress bar
13827 * @cfg {String} panel (success | info | warning | danger )
13828 * @cfg {String} role role of the progress bar
13829 * @cfg {String} sr_only text
13833 * Create a new ProgressBar
13834 * @param {Object} config The config object
13837 Roo.bootstrap.ProgressBar = function(config){
13838 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13841 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
13845 aria_valuemax : 100,
13851 getAutoCreate : function()
13856 cls: 'progress-bar',
13857 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13869 cfg.role = this.role;
13872 if(this.aria_valuenow){
13873 cfg['aria-valuenow'] = this.aria_valuenow;
13876 if(this.aria_valuemin){
13877 cfg['aria-valuemin'] = this.aria_valuemin;
13880 if(this.aria_valuemax){
13881 cfg['aria-valuemax'] = this.aria_valuemax;
13884 if(this.label && !this.sr_only){
13885 cfg.html = this.label;
13889 cfg.cls += ' progress-bar-' + this.panel;
13895 update : function(aria_valuenow)
13897 this.aria_valuenow = aria_valuenow;
13899 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13914 * @class Roo.bootstrap.TabGroup
13915 * @extends Roo.bootstrap.Column
13916 * Bootstrap Column class
13917 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
13918 * @cfg {Boolean} carousel true to make the group behave like a carousel
13921 * Create a new TabGroup
13922 * @param {Object} config The config object
13925 Roo.bootstrap.TabGroup = function(config){
13926 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
13928 this.navId = Roo.id();
13931 Roo.bootstrap.TabGroup.register(this);
13935 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
13938 transition : false,
13940 getAutoCreate : function()
13942 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
13944 cfg.cls += ' tab-content';
13946 if (this.carousel) {
13947 cfg.cls += ' carousel slide';
13949 cls : 'carousel-inner'
13956 getChildContainer : function()
13958 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
13962 * register a Navigation item
13963 * @param {Roo.bootstrap.NavItem} the navitem to add
13965 register : function(item)
13967 this.tabs.push( item);
13968 item.navId = this.navId; // not really needed..
13972 getActivePanel : function()
13975 Roo.each(this.tabs, function(t) {
13985 getPanelByName : function(n)
13988 Roo.each(this.tabs, function(t) {
13989 if (t.tabId == n) {
13997 indexOfPanel : function(p)
14000 Roo.each(this.tabs, function(t,i) {
14001 if (t.tabId == p.tabId) {
14010 * show a specific panel
14011 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14012 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14014 showPanel : function (pan)
14017 if (typeof(pan) == 'number') {
14018 pan = this.tabs[pan];
14020 if (typeof(pan) == 'string') {
14021 pan = this.getPanelByName(pan);
14023 if (pan.tabId == this.getActivePanel().tabId) {
14026 var cur = this.getActivePanel();
14028 if (false === cur.fireEvent('beforedeactivate')) {
14032 if (this.carousel) {
14033 this.transition = true;
14034 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
14035 var lr = dir == 'next' ? 'left' : 'right';
14036 pan.el.addClass(dir); // or prev
14037 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14038 cur.el.addClass(lr); // or right
14039 pan.el.addClass(lr);
14042 cur.el.on('transitionend', function() {
14043 Roo.log("trans end?");
14045 pan.el.removeClass([lr,dir]);
14046 pan.setActive(true);
14048 cur.el.removeClass([lr]);
14049 cur.setActive(false);
14051 _this.transition = false;
14053 }, this, { single: true } );
14057 cur.setActive(false);
14058 pan.setActive(true);
14062 showPanelNext : function()
14064 var i = this.indexOfPanel(this.getActivePanel());
14065 if (i > this.tabs.length) {
14068 this.showPanel(this.tabs[i+1]);
14070 showPanelPrev : function()
14072 var i = this.indexOfPanel(this.getActivePanel());
14076 this.showPanel(this.tabs[i-1]);
14087 Roo.apply(Roo.bootstrap.TabGroup, {
14091 * register a Navigation Group
14092 * @param {Roo.bootstrap.NavGroup} the navgroup to add
14094 register : function(navgrp)
14096 this.groups[navgrp.navId] = navgrp;
14100 * fetch a Navigation Group based on the navigation ID
14101 * if one does not exist , it will get created.
14102 * @param {string} the navgroup to add
14103 * @returns {Roo.bootstrap.NavGroup} the navgroup
14105 get: function(navId) {
14106 if (typeof(this.groups[navId]) == 'undefined') {
14107 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14109 return this.groups[navId] ;
14124 * @class Roo.bootstrap.TabPanel
14125 * @extends Roo.bootstrap.Component
14126 * Bootstrap TabPanel class
14127 * @cfg {Boolean} active panel active
14128 * @cfg {String} html panel content
14129 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14130 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14134 * Create a new TabPanel
14135 * @param {Object} config The config object
14138 Roo.bootstrap.TabPanel = function(config){
14139 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14143 * Fires when the active status changes
14144 * @param {Roo.bootstrap.TabPanel} this
14145 * @param {Boolean} state the new state
14150 * @event beforedeactivate
14151 * Fires before a tab is de-activated - can be used to do validation on a form.
14152 * @param {Roo.bootstrap.TabPanel} this
14153 * @return {Boolean} false if there is an error
14156 'beforedeactivate': true
14159 this.tabId = this.tabId || Roo.id();
14163 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
14170 getAutoCreate : function(){
14173 // item is needed for carousel - not sure if it has any effect otherwise
14174 cls: 'tab-pane item',
14175 html: this.html || ''
14179 cfg.cls += ' active';
14183 cfg.tabId = this.tabId;
14190 initEvents: function()
14192 Roo.log('-------- init events on tab panel ---------');
14194 var p = this.parent();
14195 this.navId = this.navId || p.navId;
14197 if (typeof(this.navId) != 'undefined') {
14198 // not really needed.. but just in case.. parent should be a NavGroup.
14199 var tg = Roo.bootstrap.TabGroup.get(this.navId);
14200 Roo.log(['register', tg, this]);
14206 onRender : function(ct, position)
14208 // Roo.log("Call onRender: " + this.xtype);
14210 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14218 setActive: function(state)
14220 Roo.log("panel - set active " + this.tabId + "=" + state);
14222 this.active = state;
14224 this.el.removeClass('active');
14226 } else if (!this.el.hasClass('active')) {
14227 this.el.addClass('active');
14229 this.fireEvent('changed', this, state);
14246 * @class Roo.bootstrap.DateField
14247 * @extends Roo.bootstrap.Input
14248 * Bootstrap DateField class
14249 * @cfg {Number} weekStart default 0
14250 * @cfg {Number} weekStart default 0
14251 * @cfg {Number} viewMode default empty, (months|years)
14252 * @cfg {Number} minViewMode default empty, (months|years)
14253 * @cfg {Number} startDate default -Infinity
14254 * @cfg {Number} endDate default Infinity
14255 * @cfg {Boolean} todayHighlight default false
14256 * @cfg {Boolean} todayBtn default false
14257 * @cfg {Boolean} calendarWeeks default false
14258 * @cfg {Object} daysOfWeekDisabled default empty
14260 * @cfg {Boolean} keyboardNavigation default true
14261 * @cfg {String} language default en
14264 * Create a new DateField
14265 * @param {Object} config The config object
14268 Roo.bootstrap.DateField = function(config){
14269 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14273 * Fires when this field show.
14274 * @param {Roo.bootstrap.DateField} this
14275 * @param {Mixed} date The date value
14280 * Fires when this field hide.
14281 * @param {Roo.bootstrap.DateField} this
14282 * @param {Mixed} date The date value
14287 * Fires when select a date.
14288 * @param {Roo.bootstrap.DateField} this
14289 * @param {Mixed} date The date value
14295 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
14298 * @cfg {String} format
14299 * The default date format string which can be overriden for localization support. The format must be
14300 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14304 * @cfg {String} altFormats
14305 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14306 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14308 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14316 todayHighlight : false,
14322 keyboardNavigation: true,
14324 calendarWeeks: false,
14326 startDate: -Infinity,
14330 daysOfWeekDisabled: [],
14334 UTCDate: function()
14336 return new Date(Date.UTC.apply(Date, arguments));
14339 UTCToday: function()
14341 var today = new Date();
14342 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14345 getDate: function() {
14346 var d = this.getUTCDate();
14347 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14350 getUTCDate: function() {
14354 setDate: function(d) {
14355 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14358 setUTCDate: function(d) {
14360 this.setValue(this.formatDate(this.date));
14363 onRender: function(ct, position)
14366 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14368 this.language = this.language || 'en';
14369 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14370 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14372 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14373 this.format = this.format || 'm/d/y';
14374 this.isInline = false;
14375 this.isInput = true;
14376 this.component = this.el.select('.add-on', true).first() || false;
14377 this.component = (this.component && this.component.length === 0) ? false : this.component;
14378 this.hasInput = this.component && this.inputEL().length;
14380 if (typeof(this.minViewMode === 'string')) {
14381 switch (this.minViewMode) {
14383 this.minViewMode = 1;
14386 this.minViewMode = 2;
14389 this.minViewMode = 0;
14394 if (typeof(this.viewMode === 'string')) {
14395 switch (this.viewMode) {
14408 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14410 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14412 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14414 this.picker().on('mousedown', this.onMousedown, this);
14415 this.picker().on('click', this.onClick, this);
14417 this.picker().addClass('datepicker-dropdown');
14419 this.startViewMode = this.viewMode;
14422 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14423 if(!this.calendarWeeks){
14428 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14429 v.attr('colspan', function(i, val){
14430 return parseInt(val) + 1;
14435 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14437 this.setStartDate(this.startDate);
14438 this.setEndDate(this.endDate);
14440 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14447 if(this.isInline) {
14452 picker : function()
14454 return this.pickerEl;
14455 // return this.el.select('.datepicker', true).first();
14458 fillDow: function()
14460 var dowCnt = this.weekStart;
14469 if(this.calendarWeeks){
14477 while (dowCnt < this.weekStart + 7) {
14481 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14485 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14488 fillMonths: function()
14491 var months = this.picker().select('>.datepicker-months td', true).first();
14493 months.dom.innerHTML = '';
14499 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14502 months.createChild(month);
14510 this.date = (typeof(this.date) === 'undefined' || !this.date.length) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
14512 if (this.date < this.startDate) {
14513 this.viewDate = new Date(this.startDate);
14514 } else if (this.date > this.endDate) {
14515 this.viewDate = new Date(this.endDate);
14517 this.viewDate = new Date(this.date);
14525 var d = new Date(this.viewDate),
14526 year = d.getUTCFullYear(),
14527 month = d.getUTCMonth(),
14528 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14529 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14530 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14531 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14532 currentDate = this.date && this.date.valueOf(),
14533 today = this.UTCToday();
14535 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14537 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14539 // this.picker.select('>tfoot th.today').
14540 // .text(dates[this.language].today)
14541 // .toggle(this.todayBtn !== false);
14543 this.updateNavArrows();
14546 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14548 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14550 prevMonth.setUTCDate(day);
14552 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14554 var nextMonth = new Date(prevMonth);
14556 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14558 nextMonth = nextMonth.valueOf();
14560 var fillMonths = false;
14562 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14564 while(prevMonth.valueOf() < nextMonth) {
14567 if (prevMonth.getUTCDay() === this.weekStart) {
14569 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14577 if(this.calendarWeeks){
14578 // ISO 8601: First week contains first thursday.
14579 // ISO also states week starts on Monday, but we can be more abstract here.
14581 // Start of current week: based on weekstart/current date
14582 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14583 // Thursday of this week
14584 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14585 // First Thursday of year, year from thursday
14586 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14587 // Calendar week: ms between thursdays, div ms per day, div 7 days
14588 calWeek = (th - yth) / 864e5 / 7 + 1;
14590 fillMonths.cn.push({
14598 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14600 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14603 if (this.todayHighlight &&
14604 prevMonth.getUTCFullYear() == today.getFullYear() &&
14605 prevMonth.getUTCMonth() == today.getMonth() &&
14606 prevMonth.getUTCDate() == today.getDate()) {
14607 clsName += ' today';
14610 if (currentDate && prevMonth.valueOf() === currentDate) {
14611 clsName += ' active';
14614 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14615 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14616 clsName += ' disabled';
14619 fillMonths.cn.push({
14621 cls: 'day ' + clsName,
14622 html: prevMonth.getDate()
14625 prevMonth.setDate(prevMonth.getDate()+1);
14628 var currentYear = this.date && this.date.getUTCFullYear();
14629 var currentMonth = this.date && this.date.getUTCMonth();
14631 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14633 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14634 v.removeClass('active');
14636 if(currentYear === year && k === currentMonth){
14637 v.addClass('active');
14640 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14641 v.addClass('disabled');
14647 year = parseInt(year/10, 10) * 10;
14649 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14651 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14654 for (var i = -1; i < 11; i++) {
14655 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14657 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14665 showMode: function(dir)
14668 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14670 Roo.each(this.picker().select('>div',true).elements, function(v){
14671 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14674 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14679 if(this.isInline) return;
14681 this.picker().removeClass(['bottom', 'top']);
14683 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14685 * place to the top of element!
14689 this.picker().addClass('top');
14690 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14695 this.picker().addClass('bottom');
14697 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14700 parseDate : function(value)
14702 if(!value || value instanceof Date){
14705 var v = Date.parseDate(value, this.format);
14706 if (!v && this.useIso) {
14707 v = Date.parseDate(value, 'Y-m-d');
14709 if(!v && this.altFormats){
14710 if(!this.altFormatsArray){
14711 this.altFormatsArray = this.altFormats.split("|");
14713 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14714 v = Date.parseDate(value, this.altFormatsArray[i]);
14720 formatDate : function(date, fmt)
14722 return (!date || !(date instanceof Date)) ?
14723 date : date.dateFormat(fmt || this.format);
14726 onFocus : function()
14728 Roo.bootstrap.DateField.superclass.onFocus.call(this);
14732 onBlur : function()
14734 Roo.bootstrap.DateField.superclass.onBlur.call(this);
14736 var d = this.inputEl().getValue();
14745 this.picker().show();
14749 this.fireEvent('show', this, this.date);
14754 if(this.isInline) return;
14755 this.picker().hide();
14756 this.viewMode = this.startViewMode;
14759 this.fireEvent('hide', this, this.date);
14763 onMousedown: function(e)
14765 e.stopPropagation();
14766 e.preventDefault();
14771 Roo.bootstrap.DateField.superclass.keyup.call(this);
14775 setValue: function(v)
14777 var d = new Date(v);
14779 if(isNaN(d.getTime())){
14784 v = this.formatDate(d);
14786 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14788 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14792 this.fireEvent('select', this, this.date);
14796 getValue: function()
14798 return this.formatDate(this.date);
14801 fireKey: function(e)
14803 if (!this.picker().isVisible()){
14804 if (e.keyCode == 27) // allow escape to hide and re-show picker
14809 var dateChanged = false,
14811 newDate, newViewDate;
14816 e.preventDefault();
14820 if (!this.keyboardNavigation) break;
14821 dir = e.keyCode == 37 ? -1 : 1;
14824 newDate = this.moveYear(this.date, dir);
14825 newViewDate = this.moveYear(this.viewDate, dir);
14826 } else if (e.shiftKey){
14827 newDate = this.moveMonth(this.date, dir);
14828 newViewDate = this.moveMonth(this.viewDate, dir);
14830 newDate = new Date(this.date);
14831 newDate.setUTCDate(this.date.getUTCDate() + dir);
14832 newViewDate = new Date(this.viewDate);
14833 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14835 if (this.dateWithinRange(newDate)){
14836 this.date = newDate;
14837 this.viewDate = newViewDate;
14838 this.setValue(this.formatDate(this.date));
14840 e.preventDefault();
14841 dateChanged = true;
14846 if (!this.keyboardNavigation) break;
14847 dir = e.keyCode == 38 ? -1 : 1;
14849 newDate = this.moveYear(this.date, dir);
14850 newViewDate = this.moveYear(this.viewDate, dir);
14851 } else if (e.shiftKey){
14852 newDate = this.moveMonth(this.date, dir);
14853 newViewDate = this.moveMonth(this.viewDate, dir);
14855 newDate = new Date(this.date);
14856 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14857 newViewDate = new Date(this.viewDate);
14858 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14860 if (this.dateWithinRange(newDate)){
14861 this.date = newDate;
14862 this.viewDate = newViewDate;
14863 this.setValue(this.formatDate(this.date));
14865 e.preventDefault();
14866 dateChanged = true;
14870 this.setValue(this.formatDate(this.date));
14872 e.preventDefault();
14875 this.setValue(this.formatDate(this.date));
14889 onClick: function(e)
14891 e.stopPropagation();
14892 e.preventDefault();
14894 var target = e.getTarget();
14896 if(target.nodeName.toLowerCase() === 'i'){
14897 target = Roo.get(target).dom.parentNode;
14900 var nodeName = target.nodeName;
14901 var className = target.className;
14902 var html = target.innerHTML;
14904 switch(nodeName.toLowerCase()) {
14906 switch(className) {
14912 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14913 switch(this.viewMode){
14915 this.viewDate = this.moveMonth(this.viewDate, dir);
14919 this.viewDate = this.moveYear(this.viewDate, dir);
14925 var date = new Date();
14926 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14928 this.setValue(this.formatDate(this.date));
14935 if (className.indexOf('disabled') === -1) {
14936 this.viewDate.setUTCDate(1);
14937 if (className.indexOf('month') !== -1) {
14938 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14940 var year = parseInt(html, 10) || 0;
14941 this.viewDate.setUTCFullYear(year);
14950 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
14951 var day = parseInt(html, 10) || 1;
14952 var year = this.viewDate.getUTCFullYear(),
14953 month = this.viewDate.getUTCMonth();
14955 if (className.indexOf('old') !== -1) {
14962 } else if (className.indexOf('new') !== -1) {
14970 this.date = this.UTCDate(year, month, day,0,0,0,0);
14971 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
14973 this.setValue(this.formatDate(this.date));
14980 setStartDate: function(startDate)
14982 this.startDate = startDate || -Infinity;
14983 if (this.startDate !== -Infinity) {
14984 this.startDate = this.parseDate(this.startDate);
14987 this.updateNavArrows();
14990 setEndDate: function(endDate)
14992 this.endDate = endDate || Infinity;
14993 if (this.endDate !== Infinity) {
14994 this.endDate = this.parseDate(this.endDate);
14997 this.updateNavArrows();
15000 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15002 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15003 if (typeof(this.daysOfWeekDisabled) !== 'object') {
15004 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15006 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15007 return parseInt(d, 10);
15010 this.updateNavArrows();
15013 updateNavArrows: function()
15015 var d = new Date(this.viewDate),
15016 year = d.getUTCFullYear(),
15017 month = d.getUTCMonth();
15019 Roo.each(this.picker().select('.prev', true).elements, function(v){
15021 switch (this.viewMode) {
15024 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15030 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15037 Roo.each(this.picker().select('.next', true).elements, function(v){
15039 switch (this.viewMode) {
15042 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15048 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15056 moveMonth: function(date, dir)
15058 if (!dir) return date;
15059 var new_date = new Date(date.valueOf()),
15060 day = new_date.getUTCDate(),
15061 month = new_date.getUTCMonth(),
15062 mag = Math.abs(dir),
15064 dir = dir > 0 ? 1 : -1;
15067 // If going back one month, make sure month is not current month
15068 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15070 return new_date.getUTCMonth() == month;
15072 // If going forward one month, make sure month is as expected
15073 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15075 return new_date.getUTCMonth() != new_month;
15077 new_month = month + dir;
15078 new_date.setUTCMonth(new_month);
15079 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15080 if (new_month < 0 || new_month > 11)
15081 new_month = (new_month + 12) % 12;
15083 // For magnitudes >1, move one month at a time...
15084 for (var i=0; i<mag; i++)
15085 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15086 new_date = this.moveMonth(new_date, dir);
15087 // ...then reset the day, keeping it in the new month
15088 new_month = new_date.getUTCMonth();
15089 new_date.setUTCDate(day);
15091 return new_month != new_date.getUTCMonth();
15094 // Common date-resetting loop -- if date is beyond end of month, make it
15097 new_date.setUTCDate(--day);
15098 new_date.setUTCMonth(new_month);
15103 moveYear: function(date, dir)
15105 return this.moveMonth(date, dir*12);
15108 dateWithinRange: function(date)
15110 return date >= this.startDate && date <= this.endDate;
15116 this.picker().remove();
15121 Roo.apply(Roo.bootstrap.DateField, {
15132 html: '<i class="fa fa-arrow-left"/>'
15142 html: '<i class="fa fa-arrow-right"/>'
15184 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15185 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15186 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15187 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15188 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15201 navFnc: 'FullYear',
15206 navFnc: 'FullYear',
15211 Roo.apply(Roo.bootstrap.DateField, {
15215 cls: 'datepicker dropdown-menu',
15219 cls: 'datepicker-days',
15223 cls: 'table-condensed',
15225 Roo.bootstrap.DateField.head,
15229 Roo.bootstrap.DateField.footer
15236 cls: 'datepicker-months',
15240 cls: 'table-condensed',
15242 Roo.bootstrap.DateField.head,
15243 Roo.bootstrap.DateField.content,
15244 Roo.bootstrap.DateField.footer
15251 cls: 'datepicker-years',
15255 cls: 'table-condensed',
15257 Roo.bootstrap.DateField.head,
15258 Roo.bootstrap.DateField.content,
15259 Roo.bootstrap.DateField.footer
15278 * @class Roo.bootstrap.TimeField
15279 * @extends Roo.bootstrap.Input
15280 * Bootstrap DateField class
15284 * Create a new TimeField
15285 * @param {Object} config The config object
15288 Roo.bootstrap.TimeField = function(config){
15289 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15293 * Fires when this field show.
15294 * @param {Roo.bootstrap.DateField} this
15295 * @param {Mixed} date The date value
15300 * Fires when this field hide.
15301 * @param {Roo.bootstrap.DateField} this
15302 * @param {Mixed} date The date value
15307 * Fires when select a date.
15308 * @param {Roo.bootstrap.DateField} this
15309 * @param {Mixed} date The date value
15315 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
15318 * @cfg {String} format
15319 * The default time format string which can be overriden for localization support. The format must be
15320 * valid according to {@link Date#parseDate} (defaults to 'H:i').
15324 onRender: function(ct, position)
15327 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15329 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15331 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15333 this.pop = this.picker().select('>.datepicker-time',true).first();
15334 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
15336 this.picker().on('mousedown', this.onMousedown, this);
15337 this.picker().on('click', this.onClick, this);
15339 this.picker().addClass('datepicker-dropdown');
15344 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15345 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15346 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15347 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15348 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15349 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15353 fireKey: function(e){
15354 if (!this.picker().isVisible()){
15355 if (e.keyCode == 27) // allow escape to hide and re-show picker
15360 e.preventDefault();
15368 this.onTogglePeriod();
15371 this.onIncrementMinutes();
15374 this.onDecrementMinutes();
15383 onClick: function(e) {
15384 e.stopPropagation();
15385 e.preventDefault();
15388 picker : function()
15390 return this.el.select('.datepicker', true).first();
15393 fillTime: function()
15395 var time = this.pop.select('tbody', true).first();
15397 time.dom.innerHTML = '';
15412 cls: 'hours-up glyphicon glyphicon-chevron-up'
15432 cls: 'minutes-up glyphicon glyphicon-chevron-up'
15453 cls: 'timepicker-hour',
15468 cls: 'timepicker-minute',
15483 cls: 'btn btn-primary period',
15505 cls: 'hours-down glyphicon glyphicon-chevron-down'
15525 cls: 'minutes-down glyphicon glyphicon-chevron-down'
15543 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15550 var hours = this.time.getHours();
15551 var minutes = this.time.getMinutes();
15564 hours = hours - 12;
15568 hours = '0' + hours;
15572 minutes = '0' + minutes;
15575 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15576 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15577 this.pop.select('button', true).first().dom.innerHTML = period;
15583 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15585 var cls = ['bottom'];
15587 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15594 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15599 this.picker().addClass(cls.join('-'));
15603 Roo.each(cls, function(c){
15605 _this.picker().setTop(_this.inputEl().getHeight());
15609 _this.picker().setTop(0 - _this.picker().getHeight());
15614 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15618 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15625 onFocus : function()
15627 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15631 onBlur : function()
15633 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15639 this.picker().show();
15644 this.fireEvent('show', this, this.date);
15649 this.picker().hide();
15652 this.fireEvent('hide', this, this.date);
15655 setTime : function()
15658 this.setValue(this.time.format(this.format));
15660 this.fireEvent('select', this, this.date);
15665 onMousedown: function(e){
15666 e.stopPropagation();
15667 e.preventDefault();
15670 onIncrementHours: function()
15672 Roo.log('onIncrementHours');
15673 this.time = this.time.add(Date.HOUR, 1);
15678 onDecrementHours: function()
15680 Roo.log('onDecrementHours');
15681 this.time = this.time.add(Date.HOUR, -1);
15685 onIncrementMinutes: function()
15687 Roo.log('onIncrementMinutes');
15688 this.time = this.time.add(Date.MINUTE, 1);
15692 onDecrementMinutes: function()
15694 Roo.log('onDecrementMinutes');
15695 this.time = this.time.add(Date.MINUTE, -1);
15699 onTogglePeriod: function()
15701 Roo.log('onTogglePeriod');
15702 this.time = this.time.add(Date.HOUR, 12);
15709 Roo.apply(Roo.bootstrap.TimeField, {
15739 cls: 'btn btn-info ok',
15751 Roo.apply(Roo.bootstrap.TimeField, {
15755 cls: 'datepicker dropdown-menu',
15759 cls: 'datepicker-time',
15763 cls: 'table-condensed',
15765 Roo.bootstrap.TimeField.content,
15766 Roo.bootstrap.TimeField.footer
15785 * @class Roo.bootstrap.CheckBox
15786 * @extends Roo.bootstrap.Input
15787 * Bootstrap CheckBox class
15789 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15790 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15791 * @cfg {String} boxLabel The text that appears beside the checkbox
15792 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15793 * @cfg {Boolean} checked initnal the element
15797 * Create a new CheckBox
15798 * @param {Object} config The config object
15801 Roo.bootstrap.CheckBox = function(config){
15802 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15807 * Fires when the element is checked or unchecked.
15808 * @param {Roo.bootstrap.CheckBox} this This input
15809 * @param {Boolean} checked The new checked value
15815 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
15817 inputType: 'checkbox',
15824 getAutoCreate : function()
15826 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15832 cfg.cls = 'form-group checkbox' //input-group
15840 type : this.inputType,
15841 value : (!this.checked) ? this.valueOff : this.inputValue,
15842 cls : 'roo-checkbox', //'form-box',
15843 placeholder : this.placeholder || ''
15847 if (this.weight) { // Validity check?
15848 cfg.cls += " checkbox-" + this.weight;
15851 if (this.disabled) {
15852 input.disabled=true;
15856 input.checked = this.checked;
15860 input.name = this.name;
15864 input.cls += ' input-' + this.size;
15868 ['xs','sm','md','lg'].map(function(size){
15869 if (settings[size]) {
15870 cfg.cls += ' col-' + size + '-' + settings[size];
15876 var inputblock = input;
15881 if (this.before || this.after) {
15884 cls : 'input-group',
15888 inputblock.cn.push({
15890 cls : 'input-group-addon',
15894 inputblock.cn.push(input);
15896 inputblock.cn.push({
15898 cls : 'input-group-addon',
15905 if (align ==='left' && this.fieldLabel.length) {
15906 Roo.log("left and has label");
15912 cls : 'control-label col-md-' + this.labelWidth,
15913 html : this.fieldLabel
15917 cls : "col-md-" + (12 - this.labelWidth),
15924 } else if ( this.fieldLabel.length) {
15929 tag: this.boxLabel ? 'span' : 'label',
15931 cls: 'control-label box-input-label',
15932 //cls : 'input-group-addon',
15933 html : this.fieldLabel
15943 Roo.log(" no label && no align");
15944 cfg.cn = [ inputblock ] ;
15953 html: this.boxLabel
15965 * return the real input element.
15967 inputEl: function ()
15969 return this.el.select('input.roo-checkbox',true).first();
15974 return this.el.select('label.control-label',true).first();
15977 initEvents : function()
15979 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
15981 this.inputEl().on('click', this.onClick, this);
15985 onClick : function()
15987 this.setChecked(!this.checked);
15990 setChecked : function(state,suppressEvent)
15992 this.checked = state;
15994 this.inputEl().dom.checked = state;
15996 if(suppressEvent !== true){
15997 this.fireEvent('check', this, state);
16000 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16004 setValue : function(v,suppressEvent)
16006 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16020 * @class Roo.bootstrap.Radio
16021 * @extends Roo.bootstrap.CheckBox
16022 * Bootstrap Radio class
16025 * Create a new Radio
16026 * @param {Object} config The config object
16029 Roo.bootstrap.Radio = function(config){
16030 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16034 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
16036 inputType: 'radio',
16040 getAutoCreate : function()
16042 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16048 cfg.cls = 'form-group radio' //input-group
16053 type : this.inputType,
16054 value : (!this.checked) ? this.valueOff : this.inputValue,
16056 placeholder : this.placeholder || ''
16059 if (this.weight) { // Validity check?
16060 cfg.cls += " radio-" + this.weight;
16062 if (this.disabled) {
16063 input.disabled=true;
16067 input.checked = this.checked;
16071 input.name = this.name;
16075 input.cls += ' input-' + this.size;
16079 ['xs','sm','md','lg'].map(function(size){
16080 if (settings[size]) {
16081 cfg.cls += ' col-' + size + '-' + settings[size];
16085 var inputblock = input;
16087 if (this.before || this.after) {
16090 cls : 'input-group',
16094 inputblock.cn.push({
16096 cls : 'input-group-addon',
16100 inputblock.cn.push(input);
16102 inputblock.cn.push({
16104 cls : 'input-group-addon',
16111 if (align ==='left' && this.fieldLabel.length) {
16112 Roo.log("left and has label");
16118 cls : 'control-label col-md-' + this.labelWidth,
16119 html : this.fieldLabel
16123 cls : "col-md-" + (12 - this.labelWidth),
16130 } else if ( this.fieldLabel.length) {
16137 cls: 'control-label box-input-label',
16138 //cls : 'input-group-addon',
16139 html : this.fieldLabel
16149 Roo.log(" no label && no align");
16164 html: this.boxLabel
16171 inputEl: function ()
16173 return this.el.select('input.roo-radio',true).first();
16175 onClick : function()
16177 this.setChecked(true);
16180 setChecked : function(state,suppressEvent)
16183 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16184 v.dom.checked = false;
16188 this.checked = state;
16189 this.inputEl().dom.checked = state;
16191 if(suppressEvent !== true){
16192 this.fireEvent('check', this, state);
16195 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16199 getGroupValue : function()
16202 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16203 if(v.dom.checked == true){
16204 value = v.dom.value;
16212 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
16213 * @return {Mixed} value The field value
16215 getValue : function(){
16216 return this.getGroupValue();
16222 //<script type="text/javascript">
16225 * Based Ext JS Library 1.1.1
16226 * Copyright(c) 2006-2007, Ext JS, LLC.
16232 * @class Roo.HtmlEditorCore
16233 * @extends Roo.Component
16234 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16236 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16239 Roo.HtmlEditorCore = function(config){
16242 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16245 * @event initialize
16246 * Fires when the editor is fully initialized (including the iframe)
16247 * @param {Roo.HtmlEditorCore} this
16252 * Fires when the editor is first receives the focus. Any insertion must wait
16253 * until after this event.
16254 * @param {Roo.HtmlEditorCore} this
16258 * @event beforesync
16259 * Fires before the textarea is updated with content from the editor iframe. Return false
16260 * to cancel the sync.
16261 * @param {Roo.HtmlEditorCore} this
16262 * @param {String} html
16266 * @event beforepush
16267 * Fires before the iframe editor is updated with content from the textarea. Return false
16268 * to cancel the push.
16269 * @param {Roo.HtmlEditorCore} this
16270 * @param {String} html
16275 * Fires when the textarea is updated with content from the editor iframe.
16276 * @param {Roo.HtmlEditorCore} this
16277 * @param {String} html
16282 * Fires when the iframe editor is updated with content from the textarea.
16283 * @param {Roo.HtmlEditorCore} this
16284 * @param {String} html
16289 * @event editorevent
16290 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16291 * @param {Roo.HtmlEditorCore} this
16299 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
16303 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
16309 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
16314 * @cfg {Number} height (in pixels)
16318 * @cfg {Number} width (in pixels)
16323 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16326 stylesheets: false,
16331 // private properties
16332 validationEvent : false,
16334 initialized : false,
16336 sourceEditMode : false,
16337 onFocus : Roo.emptyFn,
16339 hideMode:'offsets',
16347 * Protected method that will not generally be called directly. It
16348 * is called when the editor initializes the iframe with HTML contents. Override this method if you
16349 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16351 getDocMarkup : function(){
16354 Roo.log(this.stylesheets);
16356 // inherit styels from page...??
16357 if (this.stylesheets === false) {
16359 Roo.get(document.head).select('style').each(function(node) {
16360 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16363 Roo.get(document.head).select('link').each(function(node) {
16364 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16367 } else if (!this.stylesheets.length) {
16369 st = '<style type="text/css">' +
16370 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16373 Roo.each(this.stylesheets, function(s) {
16374 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16379 st += '<style type="text/css">' +
16380 'IMG { cursor: pointer } ' +
16384 return '<html><head>' + st +
16385 //<style type="text/css">' +
16386 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16388 ' </head><body class="roo-htmleditor-body"></body></html>';
16392 onRender : function(ct, position)
16395 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16396 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16399 this.el.dom.style.border = '0 none';
16400 this.el.dom.setAttribute('tabIndex', -1);
16401 this.el.addClass('x-hidden hide');
16405 if(Roo.isIE){ // fix IE 1px bogus margin
16406 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16410 this.frameId = Roo.id();
16414 var iframe = this.owner.wrap.createChild({
16416 cls: 'form-control', // bootstrap..
16418 name: this.frameId,
16419 frameBorder : 'no',
16420 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
16425 this.iframe = iframe.dom;
16427 this.assignDocWin();
16429 this.doc.designMode = 'on';
16432 this.doc.write(this.getDocMarkup());
16436 var task = { // must defer to wait for browser to be ready
16438 //console.log("run task?" + this.doc.readyState);
16439 this.assignDocWin();
16440 if(this.doc.body || this.doc.readyState == 'complete'){
16442 this.doc.designMode="on";
16446 Roo.TaskMgr.stop(task);
16447 this.initEditor.defer(10, this);
16454 Roo.TaskMgr.start(task);
16461 onResize : function(w, h)
16463 Roo.log('resize: ' +w + ',' + h );
16464 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16468 if(typeof w == 'number'){
16470 this.iframe.style.width = w + 'px';
16472 if(typeof h == 'number'){
16474 this.iframe.style.height = h + 'px';
16476 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16483 * Toggles the editor between standard and source edit mode.
16484 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16486 toggleSourceEdit : function(sourceEditMode){
16488 this.sourceEditMode = sourceEditMode === true;
16490 if(this.sourceEditMode){
16492 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
16495 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16496 //this.iframe.className = '';
16499 //this.setSize(this.owner.wrap.getSize());
16500 //this.fireEvent('editmodechange', this, this.sourceEditMode);
16507 * Protected method that will not generally be called directly. If you need/want
16508 * custom HTML cleanup, this is the method you should override.
16509 * @param {String} html The HTML to be cleaned
16510 * return {String} The cleaned HTML
16512 cleanHtml : function(html){
16513 html = String(html);
16514 if(html.length > 5){
16515 if(Roo.isSafari){ // strip safari nonsense
16516 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16519 if(html == ' '){
16526 * HTML Editor -> Textarea
16527 * Protected method that will not generally be called directly. Syncs the contents
16528 * of the editor iframe with the textarea.
16530 syncValue : function(){
16531 if(this.initialized){
16532 var bd = (this.doc.body || this.doc.documentElement);
16533 //this.cleanUpPaste(); -- this is done else where and causes havoc..
16534 var html = bd.innerHTML;
16536 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16537 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16539 html = '<div style="'+m[0]+'">' + html + '</div>';
16542 html = this.cleanHtml(html);
16543 // fix up the special chars.. normaly like back quotes in word...
16544 // however we do not want to do this with chinese..
16545 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16546 var cc = b.charCodeAt();
16548 (cc >= 0x4E00 && cc < 0xA000 ) ||
16549 (cc >= 0x3400 && cc < 0x4E00 ) ||
16550 (cc >= 0xf900 && cc < 0xfb00 )
16556 if(this.owner.fireEvent('beforesync', this, html) !== false){
16557 this.el.dom.value = html;
16558 this.owner.fireEvent('sync', this, html);
16564 * Protected method that will not generally be called directly. Pushes the value of the textarea
16565 * into the iframe editor.
16567 pushValue : function(){
16568 if(this.initialized){
16569 var v = this.el.dom.value.trim();
16571 // if(v.length < 1){
16575 if(this.owner.fireEvent('beforepush', this, v) !== false){
16576 var d = (this.doc.body || this.doc.documentElement);
16578 this.cleanUpPaste();
16579 this.el.dom.value = d.innerHTML;
16580 this.owner.fireEvent('push', this, v);
16586 deferFocus : function(){
16587 this.focus.defer(10, this);
16591 focus : function(){
16592 if(this.win && !this.sourceEditMode){
16599 assignDocWin: function()
16601 var iframe = this.iframe;
16604 this.doc = iframe.contentWindow.document;
16605 this.win = iframe.contentWindow;
16607 // if (!Roo.get(this.frameId)) {
16610 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16611 // this.win = Roo.get(this.frameId).dom.contentWindow;
16613 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16617 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16618 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16623 initEditor : function(){
16624 //console.log("INIT EDITOR");
16625 this.assignDocWin();
16629 this.doc.designMode="on";
16631 this.doc.write(this.getDocMarkup());
16634 var dbody = (this.doc.body || this.doc.documentElement);
16635 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16636 // this copies styles from the containing element into thsi one..
16637 // not sure why we need all of this..
16638 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16640 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16641 //ss['background-attachment'] = 'fixed'; // w3c
16642 dbody.bgProperties = 'fixed'; // ie
16643 //Roo.DomHelper.applyStyles(dbody, ss);
16644 Roo.EventManager.on(this.doc, {
16645 //'mousedown': this.onEditorEvent,
16646 'mouseup': this.onEditorEvent,
16647 'dblclick': this.onEditorEvent,
16648 'click': this.onEditorEvent,
16649 'keyup': this.onEditorEvent,
16654 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16656 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16657 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16659 this.initialized = true;
16661 this.owner.fireEvent('initialize', this);
16666 onDestroy : function(){
16672 //for (var i =0; i < this.toolbars.length;i++) {
16673 // // fixme - ask toolbars for heights?
16674 // this.toolbars[i].onDestroy();
16677 //this.wrap.dom.innerHTML = '';
16678 //this.wrap.remove();
16683 onFirstFocus : function(){
16685 this.assignDocWin();
16688 this.activated = true;
16691 if(Roo.isGecko){ // prevent silly gecko errors
16693 var s = this.win.getSelection();
16694 if(!s.focusNode || s.focusNode.nodeType != 3){
16695 var r = s.getRangeAt(0);
16696 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16701 this.execCmd('useCSS', true);
16702 this.execCmd('styleWithCSS', false);
16705 this.owner.fireEvent('activate', this);
16709 adjustFont: function(btn){
16710 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16711 //if(Roo.isSafari){ // safari
16714 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16715 if(Roo.isSafari){ // safari
16716 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16717 v = (v < 10) ? 10 : v;
16718 v = (v > 48) ? 48 : v;
16719 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16724 v = Math.max(1, v+adjust);
16726 this.execCmd('FontSize', v );
16729 onEditorEvent : function(e){
16730 this.owner.fireEvent('editorevent', this, e);
16731 // this.updateToolbar();
16732 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16735 insertTag : function(tg)
16737 // could be a bit smarter... -> wrap the current selected tRoo..
16738 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16740 range = this.createRange(this.getSelection());
16741 var wrappingNode = this.doc.createElement(tg.toLowerCase());
16742 wrappingNode.appendChild(range.extractContents());
16743 range.insertNode(wrappingNode);
16750 this.execCmd("formatblock", tg);
16754 insertText : function(txt)
16758 var range = this.createRange();
16759 range.deleteContents();
16760 //alert(Sender.getAttribute('label'));
16762 range.insertNode(this.doc.createTextNode(txt));
16768 * Executes a Midas editor command on the editor document and performs necessary focus and
16769 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16770 * @param {String} cmd The Midas command
16771 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16773 relayCmd : function(cmd, value){
16775 this.execCmd(cmd, value);
16776 this.owner.fireEvent('editorevent', this);
16777 //this.updateToolbar();
16778 this.owner.deferFocus();
16782 * Executes a Midas editor command directly on the editor document.
16783 * For visual commands, you should use {@link #relayCmd} instead.
16784 * <b>This should only be called after the editor is initialized.</b>
16785 * @param {String} cmd The Midas command
16786 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16788 execCmd : function(cmd, value){
16789 this.doc.execCommand(cmd, false, value === undefined ? null : value);
16796 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16798 * @param {String} text | dom node..
16800 insertAtCursor : function(text)
16805 if(!this.activated){
16811 var r = this.doc.selection.createRange();
16822 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16826 // from jquery ui (MIT licenced)
16828 var win = this.win;
16830 if (win.getSelection && win.getSelection().getRangeAt) {
16831 range = win.getSelection().getRangeAt(0);
16832 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16833 range.insertNode(node);
16834 } else if (win.document.selection && win.document.selection.createRange) {
16835 // no firefox support
16836 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16837 win.document.selection.createRange().pasteHTML(txt);
16839 // no firefox support
16840 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16841 this.execCmd('InsertHTML', txt);
16850 mozKeyPress : function(e){
16852 var c = e.getCharCode(), cmd;
16855 c = String.fromCharCode(c).toLowerCase();
16869 this.cleanUpPaste.defer(100, this);
16877 e.preventDefault();
16885 fixKeys : function(){ // load time branching for fastest keydown performance
16887 return function(e){
16888 var k = e.getKey(), r;
16891 r = this.doc.selection.createRange();
16894 r.pasteHTML('    ');
16901 r = this.doc.selection.createRange();
16903 var target = r.parentElement();
16904 if(!target || target.tagName.toLowerCase() != 'li'){
16906 r.pasteHTML('<br />');
16912 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16913 this.cleanUpPaste.defer(100, this);
16919 }else if(Roo.isOpera){
16920 return function(e){
16921 var k = e.getKey();
16925 this.execCmd('InsertHTML','    ');
16928 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16929 this.cleanUpPaste.defer(100, this);
16934 }else if(Roo.isSafari){
16935 return function(e){
16936 var k = e.getKey();
16940 this.execCmd('InsertText','\t');
16944 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16945 this.cleanUpPaste.defer(100, this);
16953 getAllAncestors: function()
16955 var p = this.getSelectedNode();
16958 a.push(p); // push blank onto stack..
16959 p = this.getParentElement();
16963 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
16967 a.push(this.doc.body);
16971 lastSelNode : false,
16974 getSelection : function()
16976 this.assignDocWin();
16977 return Roo.isIE ? this.doc.selection : this.win.getSelection();
16980 getSelectedNode: function()
16982 // this may only work on Gecko!!!
16984 // should we cache this!!!!
16989 var range = this.createRange(this.getSelection()).cloneRange();
16992 var parent = range.parentElement();
16994 var testRange = range.duplicate();
16995 testRange.moveToElementText(parent);
16996 if (testRange.inRange(range)) {
16999 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17002 parent = parent.parentElement;
17007 // is ancestor a text element.
17008 var ac = range.commonAncestorContainer;
17009 if (ac.nodeType == 3) {
17010 ac = ac.parentNode;
17013 var ar = ac.childNodes;
17016 var other_nodes = [];
17017 var has_other_nodes = false;
17018 for (var i=0;i<ar.length;i++) {
17019 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
17022 // fullly contained node.
17024 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17029 // probably selected..
17030 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17031 other_nodes.push(ar[i]);
17035 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
17040 has_other_nodes = true;
17042 if (!nodes.length && other_nodes.length) {
17043 nodes= other_nodes;
17045 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17051 createRange: function(sel)
17053 // this has strange effects when using with
17054 // top toolbar - not sure if it's a great idea.
17055 //this.editor.contentWindow.focus();
17056 if (typeof sel != "undefined") {
17058 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17060 return this.doc.createRange();
17063 return this.doc.createRange();
17066 getParentElement: function()
17069 this.assignDocWin();
17070 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17072 var range = this.createRange(sel);
17075 var p = range.commonAncestorContainer;
17076 while (p.nodeType == 3) { // text node
17087 * Range intersection.. the hard stuff...
17091 * [ -- selected range --- ]
17095 * if end is before start or hits it. fail.
17096 * if start is after end or hits it fail.
17098 * if either hits (but other is outside. - then it's not
17104 // @see http://www.thismuchiknow.co.uk/?p=64.
17105 rangeIntersectsNode : function(range, node)
17107 var nodeRange = node.ownerDocument.createRange();
17109 nodeRange.selectNode(node);
17111 nodeRange.selectNodeContents(node);
17114 var rangeStartRange = range.cloneRange();
17115 rangeStartRange.collapse(true);
17117 var rangeEndRange = range.cloneRange();
17118 rangeEndRange.collapse(false);
17120 var nodeStartRange = nodeRange.cloneRange();
17121 nodeStartRange.collapse(true);
17123 var nodeEndRange = nodeRange.cloneRange();
17124 nodeEndRange.collapse(false);
17126 return rangeStartRange.compareBoundaryPoints(
17127 Range.START_TO_START, nodeEndRange) == -1 &&
17128 rangeEndRange.compareBoundaryPoints(
17129 Range.START_TO_START, nodeStartRange) == 1;
17133 rangeCompareNode : function(range, node)
17135 var nodeRange = node.ownerDocument.createRange();
17137 nodeRange.selectNode(node);
17139 nodeRange.selectNodeContents(node);
17143 range.collapse(true);
17145 nodeRange.collapse(true);
17147 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17148 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
17150 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17152 var nodeIsBefore = ss == 1;
17153 var nodeIsAfter = ee == -1;
17155 if (nodeIsBefore && nodeIsAfter)
17157 if (!nodeIsBefore && nodeIsAfter)
17158 return 1; //right trailed.
17160 if (nodeIsBefore && !nodeIsAfter)
17161 return 2; // left trailed.
17166 // private? - in a new class?
17167 cleanUpPaste : function()
17169 // cleans up the whole document..
17170 Roo.log('cleanuppaste');
17172 this.cleanUpChildren(this.doc.body);
17173 var clean = this.cleanWordChars(this.doc.body.innerHTML);
17174 if (clean != this.doc.body.innerHTML) {
17175 this.doc.body.innerHTML = clean;
17180 cleanWordChars : function(input) {// change the chars to hex code
17181 var he = Roo.HtmlEditorCore;
17183 var output = input;
17184 Roo.each(he.swapCodes, function(sw) {
17185 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17187 output = output.replace(swapper, sw[1]);
17194 cleanUpChildren : function (n)
17196 if (!n.childNodes.length) {
17199 for (var i = n.childNodes.length-1; i > -1 ; i--) {
17200 this.cleanUpChild(n.childNodes[i]);
17207 cleanUpChild : function (node)
17210 //console.log(node);
17211 if (node.nodeName == "#text") {
17212 // clean up silly Windows -- stuff?
17215 if (node.nodeName == "#comment") {
17216 node.parentNode.removeChild(node);
17217 // clean up silly Windows -- stuff?
17221 if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
17223 node.parentNode.removeChild(node);
17228 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17230 // remove <a name=....> as rendering on yahoo mailer is borked with this.
17231 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17233 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17234 // remove_keep_children = true;
17237 if (remove_keep_children) {
17238 this.cleanUpChildren(node);
17239 // inserts everything just before this node...
17240 while (node.childNodes.length) {
17241 var cn = node.childNodes[0];
17242 node.removeChild(cn);
17243 node.parentNode.insertBefore(cn, node);
17245 node.parentNode.removeChild(node);
17249 if (!node.attributes || !node.attributes.length) {
17250 this.cleanUpChildren(node);
17254 function cleanAttr(n,v)
17257 if (v.match(/^\./) || v.match(/^\//)) {
17260 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17263 if (v.match(/^#/)) {
17266 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17267 node.removeAttribute(n);
17271 function cleanStyle(n,v)
17273 if (v.match(/expression/)) { //XSS?? should we even bother..
17274 node.removeAttribute(n);
17277 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
17278 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
17281 var parts = v.split(/;/);
17284 Roo.each(parts, function(p) {
17285 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17289 var l = p.split(':').shift().replace(/\s+/g,'');
17290 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17292 if ( cblack.indexOf(l) > -1) {
17293 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17294 //node.removeAttribute(n);
17298 // only allow 'c whitelisted system attributes'
17299 if ( cwhite.length && cwhite.indexOf(l) < 0) {
17300 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17301 //node.removeAttribute(n);
17311 if (clean.length) {
17312 node.setAttribute(n, clean.join(';'));
17314 node.removeAttribute(n);
17320 for (var i = node.attributes.length-1; i > -1 ; i--) {
17321 var a = node.attributes[i];
17324 if (a.name.toLowerCase().substr(0,2)=='on') {
17325 node.removeAttribute(a.name);
17328 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17329 node.removeAttribute(a.name);
17332 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17333 cleanAttr(a.name,a.value); // fixme..
17336 if (a.name == 'style') {
17337 cleanStyle(a.name,a.value);
17340 /// clean up MS crap..
17341 // tecnically this should be a list of valid class'es..
17344 if (a.name == 'class') {
17345 if (a.value.match(/^Mso/)) {
17346 node.className = '';
17349 if (a.value.match(/body/)) {
17350 node.className = '';
17361 this.cleanUpChildren(node);
17366 * Clean up MS wordisms...
17368 cleanWord : function(node)
17371 var cleanWordChildren = function()
17373 if (!node.childNodes.length) {
17376 for (var i = node.childNodes.length-1; i > -1 ; i--) {
17377 _t.cleanWord(node.childNodes[i]);
17383 this.cleanWord(this.doc.body);
17386 if (node.nodeName == "#text") {
17387 // clean up silly Windows -- stuff?
17390 if (node.nodeName == "#comment") {
17391 node.parentNode.removeChild(node);
17392 // clean up silly Windows -- stuff?
17396 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17397 node.parentNode.removeChild(node);
17401 // remove - but keep children..
17402 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17403 while (node.childNodes.length) {
17404 var cn = node.childNodes[0];
17405 node.removeChild(cn);
17406 node.parentNode.insertBefore(cn, node);
17408 node.parentNode.removeChild(node);
17409 cleanWordChildren();
17413 if (node.className.length) {
17415 var cn = node.className.split(/\W+/);
17417 Roo.each(cn, function(cls) {
17418 if (cls.match(/Mso[a-zA-Z]+/)) {
17423 node.className = cna.length ? cna.join(' ') : '';
17425 node.removeAttribute("class");
17429 if (node.hasAttribute("lang")) {
17430 node.removeAttribute("lang");
17433 if (node.hasAttribute("style")) {
17435 var styles = node.getAttribute("style").split(";");
17437 Roo.each(styles, function(s) {
17438 if (!s.match(/:/)) {
17441 var kv = s.split(":");
17442 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17445 // what ever is left... we allow.
17448 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17449 if (!nstyle.length) {
17450 node.removeAttribute('style');
17454 cleanWordChildren();
17458 domToHTML : function(currentElement, depth, nopadtext) {
17460 depth = depth || 0;
17461 nopadtext = nopadtext || false;
17463 if (!currentElement) {
17464 return this.domToHTML(this.doc.body);
17467 //Roo.log(currentElement);
17469 var allText = false;
17470 var nodeName = currentElement.nodeName;
17471 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17473 if (nodeName == '#text') {
17474 return currentElement.nodeValue;
17479 if (nodeName != 'BODY') {
17482 // Prints the node tagName, such as <A>, <IMG>, etc
17485 for(i = 0; i < currentElement.attributes.length;i++) {
17487 var aname = currentElement.attributes.item(i).name;
17488 if (!currentElement.attributes.item(i).value.length) {
17491 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17494 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17503 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17506 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17511 // Traverse the tree
17513 var currentElementChild = currentElement.childNodes.item(i);
17514 var allText = true;
17515 var innerHTML = '';
17517 while (currentElementChild) {
17518 // Formatting code (indent the tree so it looks nice on the screen)
17519 var nopad = nopadtext;
17520 if (lastnode == 'SPAN') {
17524 if (currentElementChild.nodeName == '#text') {
17525 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17526 if (!nopad && toadd.length > 80) {
17527 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
17529 innerHTML += toadd;
17532 currentElementChild = currentElement.childNodes.item(i);
17538 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
17540 // Recursively traverse the tree structure of the child node
17541 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
17542 lastnode = currentElementChild.nodeName;
17544 currentElementChild=currentElement.childNodes.item(i);
17550 // The remaining code is mostly for formatting the tree
17551 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
17556 ret+= "</"+tagName+">";
17562 // hide stuff that is not compatible
17576 * @event specialkey
17580 * @cfg {String} fieldClass @hide
17583 * @cfg {String} focusClass @hide
17586 * @cfg {String} autoCreate @hide
17589 * @cfg {String} inputType @hide
17592 * @cfg {String} invalidClass @hide
17595 * @cfg {String} invalidText @hide
17598 * @cfg {String} msgFx @hide
17601 * @cfg {String} validateOnBlur @hide
17605 Roo.HtmlEditorCore.white = [
17606 'area', 'br', 'img', 'input', 'hr', 'wbr',
17608 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
17609 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
17610 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
17611 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
17612 'table', 'ul', 'xmp',
17614 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
17617 'dir', 'menu', 'ol', 'ul', 'dl',
17623 Roo.HtmlEditorCore.black = [
17624 // 'embed', 'object', // enable - backend responsiblity to clean thiese
17626 'base', 'basefont', 'bgsound', 'blink', 'body',
17627 'frame', 'frameset', 'head', 'html', 'ilayer',
17628 'iframe', 'layer', 'link', 'meta', 'object',
17629 'script', 'style' ,'title', 'xml' // clean later..
17631 Roo.HtmlEditorCore.clean = [
17632 'script', 'style', 'title', 'xml'
17634 Roo.HtmlEditorCore.remove = [
17639 Roo.HtmlEditorCore.ablack = [
17643 Roo.HtmlEditorCore.aclean = [
17644 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
17648 Roo.HtmlEditorCore.pwhite= [
17649 'http', 'https', 'mailto'
17652 // white listed style attributes.
17653 Roo.HtmlEditorCore.cwhite= [
17654 // 'text-align', /// default is to allow most things..
17660 // black listed style attributes.
17661 Roo.HtmlEditorCore.cblack= [
17662 // 'font-size' -- this can be set by the project
17666 Roo.HtmlEditorCore.swapCodes =[
17685 * @class Roo.bootstrap.HtmlEditor
17686 * @extends Roo.bootstrap.TextArea
17687 * Bootstrap HtmlEditor class
17690 * Create a new HtmlEditor
17691 * @param {Object} config The config object
17694 Roo.bootstrap.HtmlEditor = function(config){
17695 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17696 if (!this.toolbars) {
17697 this.toolbars = [];
17699 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17702 * @event initialize
17703 * Fires when the editor is fully initialized (including the iframe)
17704 * @param {HtmlEditor} this
17709 * Fires when the editor is first receives the focus. Any insertion must wait
17710 * until after this event.
17711 * @param {HtmlEditor} this
17715 * @event beforesync
17716 * Fires before the textarea is updated with content from the editor iframe. Return false
17717 * to cancel the sync.
17718 * @param {HtmlEditor} this
17719 * @param {String} html
17723 * @event beforepush
17724 * Fires before the iframe editor is updated with content from the textarea. Return false
17725 * to cancel the push.
17726 * @param {HtmlEditor} this
17727 * @param {String} html
17732 * Fires when the textarea is updated with content from the editor iframe.
17733 * @param {HtmlEditor} this
17734 * @param {String} html
17739 * Fires when the iframe editor is updated with content from the textarea.
17740 * @param {HtmlEditor} this
17741 * @param {String} html
17745 * @event editmodechange
17746 * Fires when the editor switches edit modes
17747 * @param {HtmlEditor} this
17748 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17750 editmodechange: true,
17752 * @event editorevent
17753 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17754 * @param {HtmlEditor} this
17758 * @event firstfocus
17759 * Fires when on first focus - needed by toolbars..
17760 * @param {HtmlEditor} this
17765 * Auto save the htmlEditor value as a file into Events
17766 * @param {HtmlEditor} this
17770 * @event savedpreview
17771 * preview the saved version of htmlEditor
17772 * @param {HtmlEditor} this
17779 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
17783 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17788 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
17793 * @cfg {Number} height (in pixels)
17797 * @cfg {Number} width (in pixels)
17802 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17805 stylesheets: false,
17810 // private properties
17811 validationEvent : false,
17813 initialized : false,
17816 onFocus : Roo.emptyFn,
17818 hideMode:'offsets',
17821 tbContainer : false,
17823 toolbarContainer :function() {
17824 return this.wrap.select('.x-html-editor-tb',true).first();
17828 * Protected method that will not generally be called directly. It
17829 * is called when the editor creates its toolbar. Override this method if you need to
17830 * add custom toolbar buttons.
17831 * @param {HtmlEditor} editor
17833 createToolbar : function(){
17835 Roo.log("create toolbars");
17837 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17838 this.toolbars[0].render(this.toolbarContainer());
17842 // if (!editor.toolbars || !editor.toolbars.length) {
17843 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17846 // for (var i =0 ; i < editor.toolbars.length;i++) {
17847 // editor.toolbars[i] = Roo.factory(
17848 // typeof(editor.toolbars[i]) == 'string' ?
17849 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
17850 // Roo.bootstrap.HtmlEditor);
17851 // editor.toolbars[i].init(editor);
17857 onRender : function(ct, position)
17859 // Roo.log("Call onRender: " + this.xtype);
17861 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17863 this.wrap = this.inputEl().wrap({
17864 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17867 this.editorcore.onRender(ct, position);
17869 if (this.resizable) {
17870 this.resizeEl = new Roo.Resizable(this.wrap, {
17874 minHeight : this.height,
17875 height: this.height,
17876 handles : this.resizable,
17879 resize : function(r, w, h) {
17880 _t.onResize(w,h); // -something
17886 this.createToolbar(this);
17889 if(!this.width && this.resizable){
17890 this.setSize(this.wrap.getSize());
17892 if (this.resizeEl) {
17893 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17894 // should trigger onReize..
17900 onResize : function(w, h)
17902 Roo.log('resize: ' +w + ',' + h );
17903 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17907 if(this.inputEl() ){
17908 if(typeof w == 'number'){
17909 var aw = w - this.wrap.getFrameWidth('lr');
17910 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17913 if(typeof h == 'number'){
17914 var tbh = -11; // fixme it needs to tool bar size!
17915 for (var i =0; i < this.toolbars.length;i++) {
17916 // fixme - ask toolbars for heights?
17917 tbh += this.toolbars[i].el.getHeight();
17918 //if (this.toolbars[i].footer) {
17919 // tbh += this.toolbars[i].footer.el.getHeight();
17927 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17928 ah -= 5; // knock a few pixes off for look..
17929 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17933 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17934 this.editorcore.onResize(ew,eh);
17939 * Toggles the editor between standard and source edit mode.
17940 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17942 toggleSourceEdit : function(sourceEditMode)
17944 this.editorcore.toggleSourceEdit(sourceEditMode);
17946 if(this.editorcore.sourceEditMode){
17947 Roo.log('editor - showing textarea');
17950 // Roo.log(this.syncValue());
17952 this.inputEl().removeClass(['hide', 'x-hidden']);
17953 this.inputEl().dom.removeAttribute('tabIndex');
17954 this.inputEl().focus();
17956 Roo.log('editor - hiding textarea');
17958 // Roo.log(this.pushValue());
17961 this.inputEl().addClass(['hide', 'x-hidden']);
17962 this.inputEl().dom.setAttribute('tabIndex', -1);
17963 //this.deferFocus();
17966 if(this.resizable){
17967 this.setSize(this.wrap.getSize());
17970 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
17973 // private (for BoxComponent)
17974 adjustSize : Roo.BoxComponent.prototype.adjustSize,
17976 // private (for BoxComponent)
17977 getResizeEl : function(){
17981 // private (for BoxComponent)
17982 getPositionEl : function(){
17987 initEvents : function(){
17988 this.originalValue = this.getValue();
17992 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17995 // markInvalid : Roo.emptyFn,
17997 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18000 // clearInvalid : Roo.emptyFn,
18002 setValue : function(v){
18003 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18004 this.editorcore.pushValue();
18009 deferFocus : function(){
18010 this.focus.defer(10, this);
18014 focus : function(){
18015 this.editorcore.focus();
18021 onDestroy : function(){
18027 for (var i =0; i < this.toolbars.length;i++) {
18028 // fixme - ask toolbars for heights?
18029 this.toolbars[i].onDestroy();
18032 this.wrap.dom.innerHTML = '';
18033 this.wrap.remove();
18038 onFirstFocus : function(){
18039 //Roo.log("onFirstFocus");
18040 this.editorcore.onFirstFocus();
18041 for (var i =0; i < this.toolbars.length;i++) {
18042 this.toolbars[i].onFirstFocus();
18048 syncValue : function()
18050 this.editorcore.syncValue();
18053 pushValue : function()
18055 this.editorcore.pushValue();
18059 // hide stuff that is not compatible
18073 * @event specialkey
18077 * @cfg {String} fieldClass @hide
18080 * @cfg {String} focusClass @hide
18083 * @cfg {String} autoCreate @hide
18086 * @cfg {String} inputType @hide
18089 * @cfg {String} invalidClass @hide
18092 * @cfg {String} invalidText @hide
18095 * @cfg {String} msgFx @hide
18098 * @cfg {String} validateOnBlur @hide
18107 Roo.namespace('Roo.bootstrap.htmleditor');
18109 * @class Roo.bootstrap.HtmlEditorToolbar1
18114 new Roo.bootstrap.HtmlEditor({
18117 new Roo.bootstrap.HtmlEditorToolbar1({
18118 disable : { fonts: 1 , format: 1, ..., ... , ...],
18124 * @cfg {Object} disable List of elements to disable..
18125 * @cfg {Array} btns List of additional buttons.
18129 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18132 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18135 Roo.apply(this, config);
18137 // default disabled, based on 'good practice'..
18138 this.disable = this.disable || {};
18139 Roo.applyIf(this.disable, {
18142 specialElements : true
18144 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18146 this.editor = config.editor;
18147 this.editorcore = config.editor.editorcore;
18149 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18151 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18152 // dont call parent... till later.
18154 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
18159 editorcore : false,
18164 "h1","h2","h3","h4","h5","h6",
18166 "abbr", "acronym", "address", "cite", "samp", "var",
18170 onRender : function(ct, position)
18172 // Roo.log("Call onRender: " + this.xtype);
18174 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18176 this.el.dom.style.marginBottom = '0';
18178 var editorcore = this.editorcore;
18179 var editor= this.editor;
18182 var btn = function(id,cmd , toggle, handler){
18184 var event = toggle ? 'toggle' : 'click';
18189 xns: Roo.bootstrap,
18192 enableToggle:toggle !== false,
18194 pressed : toggle ? false : null,
18197 a.listeners[toggle ? 'toggle' : 'click'] = function() {
18198 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
18207 xns: Roo.bootstrap,
18208 glyphicon : 'font',
18212 xns: Roo.bootstrap,
18216 Roo.each(this.formats, function(f) {
18217 style.menu.items.push({
18219 xns: Roo.bootstrap,
18220 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18225 editorcore.insertTag(this.tagname);
18232 children.push(style);
18235 btn('bold',false,true);
18236 btn('italic',false,true);
18237 btn('align-left', 'justifyleft',true);
18238 btn('align-center', 'justifycenter',true);
18239 btn('align-right' , 'justifyright',true);
18240 btn('link', false, false, function(btn) {
18241 //Roo.log("create link?");
18242 var url = prompt(this.createLinkText, this.defaultLinkValue);
18243 if(url && url != 'http:/'+'/'){
18244 this.editorcore.relayCmd('createlink', url);
18247 btn('list','insertunorderedlist',true);
18248 btn('pencil', false,true, function(btn){
18251 this.toggleSourceEdit(btn.pressed);
18257 xns: Roo.bootstrap,
18262 xns: Roo.bootstrap,
18267 cog.menu.items.push({
18269 xns: Roo.bootstrap,
18270 html : Clean styles,
18275 editorcore.insertTag(this.tagname);
18284 this.xtype = 'NavSimplebar';
18286 for(var i=0;i< children.length;i++) {
18288 this.buttons.add(this.addxtypeChild(children[i]));
18292 editor.on('editorevent', this.updateToolbar, this);
18294 onBtnClick : function(id)
18296 this.editorcore.relayCmd(id);
18297 this.editorcore.focus();
18301 * Protected method that will not generally be called directly. It triggers
18302 * a toolbar update by reading the markup state of the current selection in the editor.
18304 updateToolbar: function(){
18306 if(!this.editorcore.activated){
18307 this.editor.onFirstFocus(); // is this neeed?
18311 var btns = this.buttons;
18312 var doc = this.editorcore.doc;
18313 btns.get('bold').setActive(doc.queryCommandState('bold'));
18314 btns.get('italic').setActive(doc.queryCommandState('italic'));
18315 //btns.get('underline').setActive(doc.queryCommandState('underline'));
18317 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18318 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18319 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18321 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18322 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18325 var ans = this.editorcore.getAllAncestors();
18326 if (this.formatCombo) {
18329 var store = this.formatCombo.store;
18330 this.formatCombo.setValue("");
18331 for (var i =0; i < ans.length;i++) {
18332 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18334 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18342 // hides menus... - so this cant be on a menu...
18343 Roo.bootstrap.MenuMgr.hideAll();
18345 Roo.bootstrap.MenuMgr.hideAll();
18346 //this.editorsyncValue();
18348 onFirstFocus: function() {
18349 this.buttons.each(function(item){
18353 toggleSourceEdit : function(sourceEditMode){
18356 if(sourceEditMode){
18357 Roo.log("disabling buttons");
18358 this.buttons.each( function(item){
18359 if(item.cmd != 'pencil'){
18365 Roo.log("enabling buttons");
18366 if(this.editorcore.initialized){
18367 this.buttons.each( function(item){
18373 Roo.log("calling toggole on editor");
18374 // tell the editor that it's been pressed..
18375 this.editor.toggleSourceEdit(sourceEditMode);
18385 * @class Roo.bootstrap.Table.AbstractSelectionModel
18386 * @extends Roo.util.Observable
18387 * Abstract base class for grid SelectionModels. It provides the interface that should be
18388 * implemented by descendant classes. This class should not be directly instantiated.
18391 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18392 this.locked = false;
18393 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18397 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
18398 /** @ignore Called by the grid automatically. Do not call directly. */
18399 init : function(grid){
18405 * Locks the selections.
18408 this.locked = true;
18412 * Unlocks the selections.
18414 unlock : function(){
18415 this.locked = false;
18419 * Returns true if the selections are locked.
18420 * @return {Boolean}
18422 isLocked : function(){
18423 return this.locked;
18427 * @extends Roo.bootstrap.Table.AbstractSelectionModel
18428 * @class Roo.bootstrap.Table.RowSelectionModel
18429 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18430 * It supports multiple selections and keyboard selection/navigation.
18432 * @param {Object} config
18435 Roo.bootstrap.Table.RowSelectionModel = function(config){
18436 Roo.apply(this, config);
18437 this.selections = new Roo.util.MixedCollection(false, function(o){
18442 this.lastActive = false;
18446 * @event selectionchange
18447 * Fires when the selection changes
18448 * @param {SelectionModel} this
18450 "selectionchange" : true,
18452 * @event afterselectionchange
18453 * Fires after the selection changes (eg. by key press or clicking)
18454 * @param {SelectionModel} this
18456 "afterselectionchange" : true,
18458 * @event beforerowselect
18459 * Fires when a row is selected being selected, return false to cancel.
18460 * @param {SelectionModel} this
18461 * @param {Number} rowIndex The selected index
18462 * @param {Boolean} keepExisting False if other selections will be cleared
18464 "beforerowselect" : true,
18467 * Fires when a row is selected.
18468 * @param {SelectionModel} this
18469 * @param {Number} rowIndex The selected index
18470 * @param {Roo.data.Record} r The record
18472 "rowselect" : true,
18474 * @event rowdeselect
18475 * Fires when a row is deselected.
18476 * @param {SelectionModel} this
18477 * @param {Number} rowIndex The selected index
18479 "rowdeselect" : true
18481 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18482 this.locked = false;
18485 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
18487 * @cfg {Boolean} singleSelect
18488 * True to allow selection of only one row at a time (defaults to false)
18490 singleSelect : false,
18493 initEvents : function(){
18495 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18496 this.grid.on("mousedown", this.handleMouseDown, this);
18497 }else{ // allow click to work like normal
18498 this.grid.on("rowclick", this.handleDragableRowClick, this);
18501 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18502 "up" : function(e){
18504 this.selectPrevious(e.shiftKey);
18505 }else if(this.last !== false && this.lastActive !== false){
18506 var last = this.last;
18507 this.selectRange(this.last, this.lastActive-1);
18508 this.grid.getView().focusRow(this.lastActive);
18509 if(last !== false){
18513 this.selectFirstRow();
18515 this.fireEvent("afterselectionchange", this);
18517 "down" : function(e){
18519 this.selectNext(e.shiftKey);
18520 }else if(this.last !== false && this.lastActive !== false){
18521 var last = this.last;
18522 this.selectRange(this.last, this.lastActive+1);
18523 this.grid.getView().focusRow(this.lastActive);
18524 if(last !== false){
18528 this.selectFirstRow();
18530 this.fireEvent("afterselectionchange", this);
18535 var view = this.grid.view;
18536 view.on("refresh", this.onRefresh, this);
18537 view.on("rowupdated", this.onRowUpdated, this);
18538 view.on("rowremoved", this.onRemove, this);
18542 onRefresh : function(){
18543 var ds = this.grid.dataSource, i, v = this.grid.view;
18544 var s = this.selections;
18545 s.each(function(r){
18546 if((i = ds.indexOfId(r.id)) != -1){
18555 onRemove : function(v, index, r){
18556 this.selections.remove(r);
18560 onRowUpdated : function(v, index, r){
18561 if(this.isSelected(r)){
18562 v.onRowSelect(index);
18568 * @param {Array} records The records to select
18569 * @param {Boolean} keepExisting (optional) True to keep existing selections
18571 selectRecords : function(records, keepExisting){
18573 this.clearSelections();
18575 var ds = this.grid.dataSource;
18576 for(var i = 0, len = records.length; i < len; i++){
18577 this.selectRow(ds.indexOf(records[i]), true);
18582 * Gets the number of selected rows.
18585 getCount : function(){
18586 return this.selections.length;
18590 * Selects the first row in the grid.
18592 selectFirstRow : function(){
18597 * Select the last row.
18598 * @param {Boolean} keepExisting (optional) True to keep existing selections
18600 selectLastRow : function(keepExisting){
18601 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18605 * Selects the row immediately following the last selected row.
18606 * @param {Boolean} keepExisting (optional) True to keep existing selections
18608 selectNext : function(keepExisting){
18609 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18610 this.selectRow(this.last+1, keepExisting);
18611 this.grid.getView().focusRow(this.last);
18616 * Selects the row that precedes the last selected row.
18617 * @param {Boolean} keepExisting (optional) True to keep existing selections
18619 selectPrevious : function(keepExisting){
18621 this.selectRow(this.last-1, keepExisting);
18622 this.grid.getView().focusRow(this.last);
18627 * Returns the selected records
18628 * @return {Array} Array of selected records
18630 getSelections : function(){
18631 return [].concat(this.selections.items);
18635 * Returns the first selected record.
18638 getSelected : function(){
18639 return this.selections.itemAt(0);
18644 * Clears all selections.
18646 clearSelections : function(fast){
18647 if(this.locked) return;
18649 var ds = this.grid.dataSource;
18650 var s = this.selections;
18651 s.each(function(r){
18652 this.deselectRow(ds.indexOfId(r.id));
18656 this.selections.clear();
18663 * Selects all rows.
18665 selectAll : function(){
18666 if(this.locked) return;
18667 this.selections.clear();
18668 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18669 this.selectRow(i, true);
18674 * Returns True if there is a selection.
18675 * @return {Boolean}
18677 hasSelection : function(){
18678 return this.selections.length > 0;
18682 * Returns True if the specified row is selected.
18683 * @param {Number/Record} record The record or index of the record to check
18684 * @return {Boolean}
18686 isSelected : function(index){
18687 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18688 return (r && this.selections.key(r.id) ? true : false);
18692 * Returns True if the specified record id is selected.
18693 * @param {String} id The id of record to check
18694 * @return {Boolean}
18696 isIdSelected : function(id){
18697 return (this.selections.key(id) ? true : false);
18701 handleMouseDown : function(e, t){
18702 var view = this.grid.getView(), rowIndex;
18703 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18706 if(e.shiftKey && this.last !== false){
18707 var last = this.last;
18708 this.selectRange(last, rowIndex, e.ctrlKey);
18709 this.last = last; // reset the last
18710 view.focusRow(rowIndex);
18712 var isSelected = this.isSelected(rowIndex);
18713 if(e.button !== 0 && isSelected){
18714 view.focusRow(rowIndex);
18715 }else if(e.ctrlKey && isSelected){
18716 this.deselectRow(rowIndex);
18717 }else if(!isSelected){
18718 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18719 view.focusRow(rowIndex);
18722 this.fireEvent("afterselectionchange", this);
18725 handleDragableRowClick : function(grid, rowIndex, e)
18727 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18728 this.selectRow(rowIndex, false);
18729 grid.view.focusRow(rowIndex);
18730 this.fireEvent("afterselectionchange", this);
18735 * Selects multiple rows.
18736 * @param {Array} rows Array of the indexes of the row to select
18737 * @param {Boolean} keepExisting (optional) True to keep existing selections
18739 selectRows : function(rows, keepExisting){
18741 this.clearSelections();
18743 for(var i = 0, len = rows.length; i < len; i++){
18744 this.selectRow(rows[i], true);
18749 * Selects a range of rows. All rows in between startRow and endRow are also selected.
18750 * @param {Number} startRow The index of the first row in the range
18751 * @param {Number} endRow The index of the last row in the range
18752 * @param {Boolean} keepExisting (optional) True to retain existing selections
18754 selectRange : function(startRow, endRow, keepExisting){
18755 if(this.locked) return;
18757 this.clearSelections();
18759 if(startRow <= endRow){
18760 for(var i = startRow; i <= endRow; i++){
18761 this.selectRow(i, true);
18764 for(var i = startRow; i >= endRow; i--){
18765 this.selectRow(i, true);
18771 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18772 * @param {Number} startRow The index of the first row in the range
18773 * @param {Number} endRow The index of the last row in the range
18775 deselectRange : function(startRow, endRow, preventViewNotify){
18776 if(this.locked) return;
18777 for(var i = startRow; i <= endRow; i++){
18778 this.deselectRow(i, preventViewNotify);
18784 * @param {Number} row The index of the row to select
18785 * @param {Boolean} keepExisting (optional) True to keep existing selections
18787 selectRow : function(index, keepExisting, preventViewNotify){
18788 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18789 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18790 if(!keepExisting || this.singleSelect){
18791 this.clearSelections();
18793 var r = this.grid.dataSource.getAt(index);
18794 this.selections.add(r);
18795 this.last = this.lastActive = index;
18796 if(!preventViewNotify){
18797 this.grid.getView().onRowSelect(index);
18799 this.fireEvent("rowselect", this, index, r);
18800 this.fireEvent("selectionchange", this);
18806 * @param {Number} row The index of the row to deselect
18808 deselectRow : function(index, preventViewNotify){
18809 if(this.locked) return;
18810 if(this.last == index){
18813 if(this.lastActive == index){
18814 this.lastActive = false;
18816 var r = this.grid.dataSource.getAt(index);
18817 this.selections.remove(r);
18818 if(!preventViewNotify){
18819 this.grid.getView().onRowDeselect(index);
18821 this.fireEvent("rowdeselect", this, index);
18822 this.fireEvent("selectionchange", this);
18826 restoreLast : function(){
18828 this.last = this._last;
18833 acceptsNav : function(row, col, cm){
18834 return !cm.isHidden(col) && cm.isCellEditable(col, row);
18838 onEditorKey : function(field, e){
18839 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18844 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18846 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18848 }else if(k == e.ENTER && !e.ctrlKey){
18852 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18854 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18856 }else if(k == e.ESC){
18860 g.startEditing(newCell[0], newCell[1]);
18865 * Ext JS Library 1.1.1
18866 * Copyright(c) 2006-2007, Ext JS, LLC.
18868 * Originally Released Under LGPL - original licence link has changed is not relivant.
18871 * <script type="text/javascript">
18875 * @class Roo.bootstrap.PagingToolbar
18877 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18879 * Create a new PagingToolbar
18880 * @param {Object} config The config object
18882 Roo.bootstrap.PagingToolbar = function(config)
18884 // old args format still supported... - xtype is prefered..
18885 // created from xtype...
18886 var ds = config.dataSource;
18887 this.toolbarItems = [];
18888 if (config.items) {
18889 this.toolbarItems = config.items;
18890 // config.items = [];
18893 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18900 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18904 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18906 * @cfg {Roo.data.Store} dataSource
18907 * The underlying data store providing the paged data
18910 * @cfg {String/HTMLElement/Element} container
18911 * container The id or element that will contain the toolbar
18914 * @cfg {Boolean} displayInfo
18915 * True to display the displayMsg (defaults to false)
18918 * @cfg {Number} pageSize
18919 * The number of records to display per page (defaults to 20)
18923 * @cfg {String} displayMsg
18924 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18926 displayMsg : 'Displaying {0} - {1} of {2}',
18928 * @cfg {String} emptyMsg
18929 * The message to display when no records are found (defaults to "No data to display")
18931 emptyMsg : 'No data to display',
18933 * Customizable piece of the default paging text (defaults to "Page")
18936 beforePageText : "Page",
18938 * Customizable piece of the default paging text (defaults to "of %0")
18941 afterPageText : "of {0}",
18943 * Customizable piece of the default paging text (defaults to "First Page")
18946 firstText : "First Page",
18948 * Customizable piece of the default paging text (defaults to "Previous Page")
18951 prevText : "Previous Page",
18953 * Customizable piece of the default paging text (defaults to "Next Page")
18956 nextText : "Next Page",
18958 * Customizable piece of the default paging text (defaults to "Last Page")
18961 lastText : "Last Page",
18963 * Customizable piece of the default paging text (defaults to "Refresh")
18966 refreshText : "Refresh",
18970 onRender : function(ct, position)
18972 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
18973 this.navgroup.parentId = this.id;
18974 this.navgroup.onRender(this.el, null);
18975 // add the buttons to the navgroup
18977 if(this.displayInfo){
18978 Roo.log(this.el.select('ul.navbar-nav',true).first());
18979 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
18980 this.displayEl = this.el.select('.x-paging-info', true).first();
18981 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
18982 // this.displayEl = navel.el.select('span',true).first();
18988 Roo.each(_this.buttons, function(e){
18989 Roo.factory(e).onRender(_this.el, null);
18993 Roo.each(_this.toolbarItems, function(e) {
18994 _this.navgroup.addItem(e);
18997 this.first = this.navgroup.addItem({
18998 tooltip: this.firstText,
19000 icon : 'fa fa-backward',
19002 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19005 this.prev = this.navgroup.addItem({
19006 tooltip: this.prevText,
19008 icon : 'fa fa-step-backward',
19010 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
19012 //this.addSeparator();
19015 var field = this.navgroup.addItem( {
19017 cls : 'x-paging-position',
19019 html : this.beforePageText +
19020 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19021 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
19024 this.field = field.el.select('input', true).first();
19025 this.field.on("keydown", this.onPagingKeydown, this);
19026 this.field.on("focus", function(){this.dom.select();});
19029 this.afterTextEl = field.el.select('.x-paging-after',true).first();
19030 //this.field.setHeight(18);
19031 //this.addSeparator();
19032 this.next = this.navgroup.addItem({
19033 tooltip: this.nextText,
19035 html : ' <i class="fa fa-step-forward">',
19037 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
19039 this.last = this.navgroup.addItem({
19040 tooltip: this.lastText,
19041 icon : 'fa fa-forward',
19044 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
19046 //this.addSeparator();
19047 this.loading = this.navgroup.addItem({
19048 tooltip: this.refreshText,
19049 icon: 'fa fa-refresh',
19051 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19057 updateInfo : function(){
19058 if(this.displayEl){
19059 var count = this.ds.getCount();
19060 var msg = count == 0 ?
19064 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
19066 this.displayEl.update(msg);
19071 onLoad : function(ds, r, o){
19072 this.cursor = o.params ? o.params.start : 0;
19073 var d = this.getPageData(),
19077 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19078 this.field.dom.value = ap;
19079 this.first.setDisabled(ap == 1);
19080 this.prev.setDisabled(ap == 1);
19081 this.next.setDisabled(ap == ps);
19082 this.last.setDisabled(ap == ps);
19083 this.loading.enable();
19088 getPageData : function(){
19089 var total = this.ds.getTotalCount();
19092 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19093 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19098 onLoadError : function(){
19099 this.loading.enable();
19103 onPagingKeydown : function(e){
19104 var k = e.getKey();
19105 var d = this.getPageData();
19107 var v = this.field.dom.value, pageNum;
19108 if(!v || isNaN(pageNum = parseInt(v, 10))){
19109 this.field.dom.value = d.activePage;
19112 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19113 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19116 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))
19118 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19119 this.field.dom.value = pageNum;
19120 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19123 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19125 var v = this.field.dom.value, pageNum;
19126 var increment = (e.shiftKey) ? 10 : 1;
19127 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19129 if(!v || isNaN(pageNum = parseInt(v, 10))) {
19130 this.field.dom.value = d.activePage;
19133 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19135 this.field.dom.value = parseInt(v, 10) + increment;
19136 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19137 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19144 beforeLoad : function(){
19146 this.loading.disable();
19151 onClick : function(which){
19158 ds.load({params:{start: 0, limit: this.pageSize}});
19161 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19164 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19167 var total = ds.getTotalCount();
19168 var extra = total % this.pageSize;
19169 var lastStart = extra ? (total - extra) : total-this.pageSize;
19170 ds.load({params:{start: lastStart, limit: this.pageSize}});
19173 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19179 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19180 * @param {Roo.data.Store} store The data store to unbind
19182 unbind : function(ds){
19183 ds.un("beforeload", this.beforeLoad, this);
19184 ds.un("load", this.onLoad, this);
19185 ds.un("loadexception", this.onLoadError, this);
19186 ds.un("remove", this.updateInfo, this);
19187 ds.un("add", this.updateInfo, this);
19188 this.ds = undefined;
19192 * Binds the paging toolbar to the specified {@link Roo.data.Store}
19193 * @param {Roo.data.Store} store The data store to bind
19195 bind : function(ds){
19196 ds.on("beforeload", this.beforeLoad, this);
19197 ds.on("load", this.onLoad, this);
19198 ds.on("loadexception", this.onLoadError, this);
19199 ds.on("remove", this.updateInfo, this);
19200 ds.on("add", this.updateInfo, this);
19211 * @class Roo.bootstrap.MessageBar
19212 * @extends Roo.bootstrap.Component
19213 * Bootstrap MessageBar class
19214 * @cfg {String} html contents of the MessageBar
19215 * @cfg {String} weight (info | success | warning | danger) default info
19216 * @cfg {String} beforeClass insert the bar before the given class
19217 * @cfg {Boolean} closable (true | false) default false
19218 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19221 * Create a new Element
19222 * @param {Object} config The config object
19225 Roo.bootstrap.MessageBar = function(config){
19226 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19229 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
19235 beforeClass: 'bootstrap-sticky-wrap',
19237 getAutoCreate : function(){
19241 cls: 'alert alert-dismissable alert-' + this.weight,
19246 html: this.html || ''
19252 cfg.cls += ' alert-messages-fixed';
19266 onRender : function(ct, position)
19268 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19271 var cfg = Roo.apply({}, this.getAutoCreate());
19275 cfg.cls += ' ' + this.cls;
19278 cfg.style = this.style;
19280 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19282 this.el.setVisibilityMode(Roo.Element.DISPLAY);
19285 this.el.select('>button.close').on('click', this.hide, this);
19291 if (!this.rendered) {
19297 this.fireEvent('show', this);
19303 if (!this.rendered) {
19309 this.fireEvent('hide', this);
19312 update : function()
19314 // var e = this.el.dom.firstChild;
19316 // if(this.closable){
19317 // e = e.nextSibling;
19320 // e.data = this.html || '';
19322 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19338 * @class Roo.bootstrap.Graph
19339 * @extends Roo.bootstrap.Component
19340 * Bootstrap Graph class
19344 @cfg {String} graphtype bar | vbar | pie
19345 @cfg {number} g_x coodinator | centre x (pie)
19346 @cfg {number} g_y coodinator | centre y (pie)
19347 @cfg {number} g_r radius (pie)
19348 @cfg {number} g_height height of the chart (respected by all elements in the set)
19349 @cfg {number} g_width width of the chart (respected by all elements in the set)
19350 @cfg {Object} title The title of the chart
19353 -opts (object) options for the chart
19355 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19356 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19358 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.
19359 o stacked (boolean) whether or not to tread values as in a stacked bar chart
19361 o stretch (boolean)
19363 -opts (object) options for the pie
19366 o startAngle (number)
19367 o endAngle (number)
19371 * Create a new Input
19372 * @param {Object} config The config object
19375 Roo.bootstrap.Graph = function(config){
19376 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19382 * The img click event for the img.
19383 * @param {Roo.EventObject} e
19389 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
19400 //g_colors: this.colors,
19407 getAutoCreate : function(){
19418 onRender : function(ct,position){
19419 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19420 this.raphael = Raphael(this.el.dom);
19422 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19423 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19424 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19425 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19427 r.text(160, 10, "Single Series Chart").attr(txtattr);
19428 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19429 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19430 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19432 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19433 r.barchart(330, 10, 300, 220, data1);
19434 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19435 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19438 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19439 // r.barchart(30, 30, 560, 250, xdata, {
19440 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19441 // axis : "0 0 1 1",
19442 // axisxlabels : xdata
19443 // //yvalues : cols,
19446 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19448 // this.load(null,xdata,{
19449 // axis : "0 0 1 1",
19450 // axisxlabels : xdata
19455 load : function(graphtype,xdata,opts){
19456 this.raphael.clear();
19458 graphtype = this.graphtype;
19463 var r = this.raphael,
19464 fin = function () {
19465 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19467 fout = function () {
19468 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19470 pfin = function() {
19471 this.sector.stop();
19472 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19475 this.label[0].stop();
19476 this.label[0].attr({ r: 7.5 });
19477 this.label[1].attr({ "font-weight": 800 });
19480 pfout = function() {
19481 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19484 this.label[0].animate({ r: 5 }, 500, "bounce");
19485 this.label[1].attr({ "font-weight": 400 });
19491 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19494 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19497 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
19498 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19500 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19507 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19512 setTitle: function(o)
19517 initEvents: function() {
19520 this.el.on('click', this.onClick, this);
19524 onClick : function(e)
19526 Roo.log('img onclick');
19527 this.fireEvent('click', this, e);
19539 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19542 * @class Roo.bootstrap.dash.NumberBox
19543 * @extends Roo.bootstrap.Component
19544 * Bootstrap NumberBox class
19545 * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19546 * @cfg {String} headline Box headline
19547 * @cfg {String} content Box content
19548 * @cfg {String} icon Box icon
19549 * @cfg {String} footer Footer text
19550 * @cfg {String} fhref Footer href
19553 * Create a new NumberBox
19554 * @param {Object} config The config object
19558 Roo.bootstrap.dash.NumberBox = function(config){
19559 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19563 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
19573 getAutoCreate : function(){
19577 cls : 'small-box bg-' + this.bgcolor,
19585 cls : 'roo-headline',
19586 html : this.headline
19590 cls : 'roo-content',
19591 html : this.content
19605 cls : 'ion ' + this.icon
19614 cls : 'small-box-footer',
19615 href : this.fhref || '#',
19619 cfg.cn.push(footer);
19626 onRender : function(ct,position){
19627 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19634 setHeadline: function (value)
19636 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19639 setFooter: function (value, href)
19641 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19644 this.el.select('a.small-box-footer',true).first().attr('href', href);
19649 setContent: function (value)
19651 this.el.select('.roo-content',true).first().dom.innerHTML = value;
19654 initEvents: function()
19668 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19671 * @class Roo.bootstrap.dash.TabBox
19672 * @extends Roo.bootstrap.Component
19673 * Bootstrap TabBox class
19674 * @cfg {String} title Title of the TabBox
19675 * @cfg {String} icon Icon of the TabBox
19676 * @cfg {Boolean} showtabs (true|false) show the tabs default true
19679 * Create a new TabBox
19680 * @param {Object} config The config object
19684 Roo.bootstrap.dash.TabBox = function(config){
19685 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19690 * When a pane is added
19691 * @param {Roo.bootstrap.dash.TabPane} pane
19698 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
19704 getChildContainer : function()
19706 return this.el.select('.tab-content', true).first();
19709 getAutoCreate : function(){
19713 cls: 'pull-left header',
19721 cls: 'fa ' + this.icon
19728 cls: 'nav-tabs-custom',
19732 cls: 'nav nav-tabs pull-right',
19739 cls: 'tab-content no-padding',
19747 initEvents : function()
19749 //Roo.log('add add pane handler');
19750 this.on('addpane', this.onAddPane, this);
19753 * Updates the box title
19754 * @param {String} html to set the title to.
19756 setTitle : function(value)
19758 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19760 onAddPane : function(pane)
19762 //Roo.log('addpane');
19764 // tabs are rendere left to right..
19765 if(!this.showtabs){
19769 var ctr = this.el.select('.nav-tabs', true).first();
19772 var existing = ctr.select('.nav-tab',true);
19773 var qty = existing.getCount();;
19776 var tab = ctr.createChild({
19778 cls : 'nav-tab' + (qty ? '' : ' active'),
19786 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19789 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19791 pane.el.addClass('active');
19796 onTabClick : function(ev,un,ob,pane)
19798 //Roo.log('tab - prev default');
19799 ev.preventDefault();
19802 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19803 pane.tab.addClass('active');
19804 //Roo.log(pane.title);
19805 this.getChildContainer().select('.tab-pane',true).removeClass('active');
19806 // technically we should have a deactivate event.. but maybe add later.
19807 // and it should not de-activate the selected tab...
19809 pane.el.addClass('active');
19810 pane.fireEvent('activate');
19825 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19827 * @class Roo.bootstrap.TabPane
19828 * @extends Roo.bootstrap.Component
19829 * Bootstrap TabPane class
19830 * @cfg {Boolean} active (false | true) Default false
19831 * @cfg {String} title title of panel
19835 * Create a new TabPane
19836 * @param {Object} config The config object
19839 Roo.bootstrap.dash.TabPane = function(config){
19840 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19846 * When a pane is activated
19847 * @param {Roo.bootstrap.dash.TabPane} pane
19854 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
19859 // the tabBox that this is attached to.
19862 getAutoCreate : function()
19870 cfg.cls += ' active';
19875 initEvents : function()
19877 //Roo.log('trigger add pane handler');
19878 this.parent().fireEvent('addpane', this)
19882 * Updates the tab title
19883 * @param {String} html to set the title to.
19885 setTitle: function(str)
19891 this.tab.select('a', true).first().dom.innerHTML = str;
19908 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19911 * @class Roo.bootstrap.menu.Menu
19912 * @extends Roo.bootstrap.Component
19913 * Bootstrap Menu class - container for Menu
19914 * @cfg {String} html Text of the menu
19915 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19916 * @cfg {String} icon Font awesome icon
19917 * @cfg {String} pos Menu align to (top | bottom) default bottom
19921 * Create a new Menu
19922 * @param {Object} config The config object
19926 Roo.bootstrap.menu.Menu = function(config){
19927 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19931 * @event beforeshow
19932 * Fires before this menu is displayed
19933 * @param {Roo.bootstrap.menu.Menu} this
19937 * @event beforehide
19938 * Fires before this menu is hidden
19939 * @param {Roo.bootstrap.menu.Menu} this
19944 * Fires after this menu is displayed
19945 * @param {Roo.bootstrap.menu.Menu} this
19950 * Fires after this menu is hidden
19951 * @param {Roo.bootstrap.menu.Menu} this
19956 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19957 * @param {Roo.bootstrap.menu.Menu} this
19958 * @param {Roo.EventObject} e
19965 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
19969 weight : 'default',
19974 getChildContainer : function() {
19975 if(this.isSubMenu){
19979 return this.el.select('ul.dropdown-menu', true).first();
19982 getAutoCreate : function()
19987 cls : 'roo-menu-text',
19995 cls : 'fa ' + this.icon
20006 cls : 'dropdown-button btn btn-' + this.weight,
20011 cls : 'dropdown-toggle btn btn-' + this.weight,
20021 cls : 'dropdown-menu'
20027 if(this.pos == 'top'){
20028 cfg.cls += ' dropup';
20031 if(this.isSubMenu){
20034 cls : 'dropdown-menu'
20041 onRender : function(ct, position)
20043 this.isSubMenu = ct.hasClass('dropdown-submenu');
20045 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20048 initEvents : function()
20050 if(this.isSubMenu){
20054 this.hidden = true;
20056 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20057 this.triggerEl.on('click', this.onTriggerPress, this);
20059 this.buttonEl = this.el.select('button.dropdown-button', true).first();
20060 this.buttonEl.on('click', this.onClick, this);
20066 if(this.isSubMenu){
20070 return this.el.select('ul.dropdown-menu', true).first();
20073 onClick : function(e)
20075 this.fireEvent("click", this, e);
20078 onTriggerPress : function(e)
20080 if (this.isVisible()) {
20087 isVisible : function(){
20088 return !this.hidden;
20093 this.fireEvent("beforeshow", this);
20095 this.hidden = false;
20096 this.el.addClass('open');
20098 Roo.get(document).on("mouseup", this.onMouseUp, this);
20100 this.fireEvent("show", this);
20107 this.fireEvent("beforehide", this);
20109 this.hidden = true;
20110 this.el.removeClass('open');
20112 Roo.get(document).un("mouseup", this.onMouseUp);
20114 this.fireEvent("hide", this);
20117 onMouseUp : function()
20131 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20134 * @class Roo.bootstrap.menu.Item
20135 * @extends Roo.bootstrap.Component
20136 * Bootstrap MenuItem class
20137 * @cfg {Boolean} submenu (true | false) default false
20138 * @cfg {String} html text of the item
20139 * @cfg {String} href the link
20140 * @cfg {Boolean} disable (true | false) default false
20141 * @cfg {Boolean} preventDefault (true | false) default true
20142 * @cfg {String} icon Font awesome icon
20143 * @cfg {String} pos Submenu align to (left | right) default right
20147 * Create a new Item
20148 * @param {Object} config The config object
20152 Roo.bootstrap.menu.Item = function(config){
20153 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20157 * Fires when the mouse is hovering over this menu
20158 * @param {Roo.bootstrap.menu.Item} this
20159 * @param {Roo.EventObject} e
20164 * Fires when the mouse exits this menu
20165 * @param {Roo.bootstrap.menu.Item} this
20166 * @param {Roo.EventObject} e
20172 * The raw click event for the entire grid.
20173 * @param {Roo.EventObject} e
20179 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
20184 preventDefault: true,
20189 getAutoCreate : function()
20194 cls : 'roo-menu-item-text',
20202 cls : 'fa ' + this.icon
20211 href : this.href || '#',
20218 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20222 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20224 if(this.pos == 'left'){
20225 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20232 initEvents : function()
20234 this.el.on('mouseover', this.onMouseOver, this);
20235 this.el.on('mouseout', this.onMouseOut, this);
20237 this.el.select('a', true).first().on('click', this.onClick, this);
20241 onClick : function(e)
20243 if(this.preventDefault){
20244 e.preventDefault();
20247 this.fireEvent("click", this, e);
20250 onMouseOver : function(e)
20252 if(this.submenu && this.pos == 'left'){
20253 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20256 this.fireEvent("mouseover", this, e);
20259 onMouseOut : function(e)
20261 this.fireEvent("mouseout", this, e);
20273 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20276 * @class Roo.bootstrap.menu.Separator
20277 * @extends Roo.bootstrap.Component
20278 * Bootstrap Separator class
20281 * Create a new Separator
20282 * @param {Object} config The config object
20286 Roo.bootstrap.menu.Separator = function(config){
20287 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20290 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
20292 getAutoCreate : function(){