roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
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  
17  * 
18  * @constructor
19  * Do not use directly - it does not do anything..
20  * @param {Object} config The config object
21  */
22
23
24
25 Roo.bootstrap.Component = function(config){
26     Roo.bootstrap.Component.superclass.constructor.call(this, config);
27 };
28
29 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
30     
31     
32     allowDomMove : false, // to stop relocations in parent onRender...
33     
34     cls : false,
35     
36     style : false,
37     
38     autoCreate : false,
39     
40     initEvents : function() {  },
41     
42     xattr : false,
43     
44     parentId : false,
45     
46     parent: function() {
47         // returns the parent component..
48         return Roo.ComponentMgr.get(this.parentId)
49         
50         
51     },
52     
53     // private
54     onRender : function(ct, position)
55     {
56        // Roo.log("Call onRender: " + this.xtype);
57         
58         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
59         
60         if(this.el){
61             if (this.el.attr('xtype')) {
62                 this.el.dom.removeAttribute('xtype');
63                 this.initEvents();
64             }
65             
66             return;
67         }
68         
69          
70         
71         var cfg = Roo.apply({},  this.getAutoCreate());
72         cfg.id = Roo.id();
73         
74         // fill in the extra attributes 
75         if (this.xattr && typeof(this.xattr) =='object') {
76             for (var i in this.xattr) {
77                 cfg[i] = this.xattr[i];
78             }
79         }
80         
81         if (this.cls) {
82             cfg.cls += ' ' + this.cls;
83         }
84         if (this.style) { // fixme needs to support more complex style data.
85             cfg.style = this.style;
86         }
87         this.el = ct.createChild(cfg, position);
88         if(this.tabIndex !== undefined){
89             this.el.dom.setAttribute('tabIndex', this.tabIndex);
90         }
91         this.initEvents();
92         
93         
94     },
95     
96     getChildContainer : function()
97     {
98         return this.el;
99     },
100     
101     addxtype : function (tree, cntr) {
102         var cn = this;
103         cntr = typeof(cntr == 'undefined' ) ? 'getChildContainer' : cntr;
104         
105         // render the element if it's not BODY.
106         if (tree.xtype != 'Body') {
107             
108             cn = Roo.factory(tree);
109            // Roo.log(cn);
110             cn.parentType = this.xtype; //??
111             cn.parentId = this.id;
112             
113             // does the container contain child eleemnts with 'xtype' attributes.
114             // that match this xtype..
115             // note - when we render we create these as well..
116             // so we should check to see if body has xtype set.
117             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
118             
119                 var echild = Roo.get(this[cntr]()).child('*[xtype]');
120                 if (echild) {
121                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
122                     cn.el = echild;
123                     //echild.dom.removeAttribute('xtype');
124                 } else {
125                     Roo.log("missing child for " + this.xtype);
126                 }
127             }
128             cn.render(this[cntr]());
129             // then add the element..
130         }
131         
132         
133         // handle the kids..
134         
135         var nitems = [];
136         if (typeof (tree.menu) != 'undefined') {
137             tree.menu.parentType = cn.xtype;
138             tree.menu.triggerEl = cn.el;
139             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
140             
141         }
142         
143         if (!tree.items || !tree.items.length) {
144             cn.items = nitems;
145             return cn;
146         }
147         var items = tree.items;
148         delete tree.items;
149         
150         //Roo.log(items.length);
151             // add the items..
152         for(var i =0;i < items.length;i++) {
153             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
154         }
155         
156         cn.items = nitems;
157         
158         
159         return cn;
160     }
161     
162     
163     
164     
165 });
166
167  /*
168  * - LGPL
169  *
170  * page container.
171  * 
172  */ 
173 Roo.bootstrap.Body = function(config){
174     Roo.bootstrap.Body.superclass.constructor.call(this, config);
175     this.el = Roo.get(document.body);
176 };
177
178 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
179       
180         autoCreate : {
181         cls: 'container'
182     },
183     onRender : function(ct, position){
184         
185         
186         //this.el.addClass([this.fieldClass, this.cls]);
187         
188     }
189     
190     
191  
192    
193 });
194
195  /*
196  * - LGPL
197  *
198  * button group
199  * 
200  */
201
202
203 /**
204  * @class Roo.bootstrap.ButtonGroup
205  * @extends Roo.bootstrap.Component
206  * Bootstrap ButtonGroup class
207  * @cfg {String} size lg | sm | xs (default empty normal)
208  * @cfg {String} align vertical | justified  (default none)
209  * @cfg {String} direction up | down (default down)
210  * @cfg {Boolean} toolbar false | true
211  * @cfg {Boolean} btn true | false
212  * 
213  * 
214  * @constructor
215  * Create a new Input
216  * @param {Object} config The config object
217  */
218
219 Roo.bootstrap.ButtonGroup = function(config){
220     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
221 };
222
223 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
224     
225     size: '',
226     align: '',
227     direction: '',
228     toolbar: false,
229     btn: true,
230
231     getAutoCreate : function(){
232         var cfg = {
233             cls: 'btn-group',
234             html : null
235         }
236         
237         cfg.html = this.html || cfg.html;
238         
239         if (this.toolbar) {
240             cfg = {
241                 cls: 'btn-toolbar',
242                 html: null
243             }
244             
245             return cfg;
246         }
247         
248         if (['vertical','justified'].indexOf(this.align)!==-1) {
249             cfg.cls = 'btn-group-' + this.align;
250             
251             if (this.align == 'justified') {
252                 console.log(this.items);
253             }
254         }
255         
256         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
257             cfg.cls += ' btn-group-' + this.size;
258         }
259         
260         if (this.direction == 'up') {
261             cfg.cls += ' dropup' ;
262         }
263         
264         return cfg;
265     }
266    
267 });
268
269  /*
270  * - LGPL
271  *
272  * button
273  * 
274  */
275
276 /**
277  * @class Roo.bootstrap.Button
278  * @extends Roo.bootstrap.Component
279  * Bootstrap Button class
280  * @cfg {String} html The button content
281  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger
282  * @cfg {String} size empty | lg | sm | xs
283  * @cfg {String} tag empty | a | input | submit
284  * @cfg {String} href empty or href
285  * @cfg {Boolean} disabled false | true
286  * @cfg {Boolean} isClose false | true
287  * @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
288  * @cfg {String} badge text for badge
289  * @cfg {String} theme default (or empty) | glow
290  * @cfg {Boolean} inverse false | true
291  * @cfg {Boolean} toggle false | true
292  * @cfg {String} ontext text for on toggle state
293  * @cfg {String} offtext text for off toggle state
294  * @cfg {Boolean} defaulton true | false
295  * 
296  * @constructor
297  * Create a new button
298  * @param {Object} config The config object
299  */
300
301
302 Roo.bootstrap.Button = function(config){
303     Roo.bootstrap.Button.superclass.constructor.call(this, config);
304     this.addEvents({
305         // raw events
306         /**
307          * @event click
308          * The raw click event for the entire grid.
309          * @param {Roo.EventObject} e
310          */
311         "click" : true
312     });
313 };
314
315 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
316     html: false,
317     active: false,
318     weight: '',
319     size: '',
320     tag: 'button',
321     href: '',
322     disabled: false,
323     isClose: false,
324     glyphicon: '',
325     badge: '',
326     theme: 'default',
327     inverse: false,
328     
329     toggle: false,
330     ontext: 'ON',
331     offtext: 'OFF',
332     defaulton: true,
333     
334     getAutoCreate : function(){
335         
336         var cfg = {
337             tag : 'button',
338             cls : 'roo-button',
339             html: 'hello'
340         };
341         
342         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
343             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
344             this.tag = 'button';
345         } else {
346             cfg.tag = this.tag;
347         }
348         cfg.html = this.html || cfg.html;
349         
350         if (this.toggle===true) {
351             cfg={
352                 tag: 'div',
353                 cls: 'slider-frame roo-button',
354                 cn: [
355                     {
356                         tag: 'span',
357                         'data-on-text':'ON',
358                         'data-off-text':'OFF',
359                         cls: 'slider-button',
360                         html: this.offtext
361                     }
362                 ]
363             };
364             
365             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
366                 cfg.cls += ' '+this.weight;
367             }
368             
369             return cfg;
370         }
371         
372         if (this.isClose) {
373             cfg.cls += ' close';
374             
375             cfg["aria-hidden"] = true;
376             
377             cfg.html = "&times;";
378             
379             return cfg;
380         }
381         
382          
383         if (this.theme==='default') {
384             cfg.cls = 'btn roo-button';
385             
386             if (this.parentType != 'Navbar') {
387                 this.weight = this.weight.length ?  this.weight : 'default';
388             }
389             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
390                 
391                 cfg.cls += ' btn-' + this.weight;
392             }
393         } else if (this.theme==='glow') {
394             
395             cfg.tag = 'a';
396             cfg.cls = 'btn-glow roo-button';
397             
398             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
399                 
400                 cfg.cls += ' ' + this.weight;
401             }
402         }
403    
404         
405         if (this.inverse) {
406             this.cls += ' inverse';
407         }
408         
409         
410         if (this.active) {
411             cfg.cls += ' active';
412         }
413         
414         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
415          
416         //gsRoo.log(this.parentType);
417         if (this.parentType === 'Navbar') {
418             cfg.tag = 'li';
419             
420             cfg.cls = '';
421             cfg.cn =  [{
422                 tag : 'a',
423                 cls : 'roo-button',
424                 html : this.html,
425                 href : this.href || '#'
426             }];
427             if (this.menu) {
428                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
429                 cfg.cls += ' dropdown';
430             }   
431             
432             delete cfg.html;
433             
434         } else if (this.menu) {
435             cfg.tag = 'a';
436             cfg.cls += ' dropdown test';
437         }
438         
439         
440         
441         if (this.disabled) {
442             cfg.disabled = 'disabled';
443         }
444         //????
445         if (this.items) {
446             Roo.log('changing to ul' );
447             cfg.tag = 'ul';
448             this.glyphicon = 'caret';
449         }
450         
451         if (this.glyphicon) {
452             cfg.html = ' ' + cfg.html;
453             
454             cfg.cn = [
455                 {
456                     tag: 'span',
457                     cls: 'glyphicon glyphicon-' + this.glyphicon
458                 }
459             ];
460         }
461         
462         if (this.badge) {
463             cfg.html += ' ';
464             
465             cfg.tag = 'a';
466             
467             cfg.cls='btn roo-button';
468             
469             cfg.href=this.href;
470             
471             cfg.cn = [
472                 cfg.html,
473                 {
474                     tag: 'span',
475                     cls: 'badge',
476                     html: this.badge
477                 }
478             ];
479             
480             cfg.html='';
481         }
482         
483         if (cfg.tag !== 'a' && this.href !== '') {
484             throw "Tag must be a to set href.";
485         } else if (this.href.length > 0) {
486             cfg.href = this.href;
487         }
488         
489         return cfg;
490     },
491     initEvents: function() {
492        // Roo.log('init events?');
493        // Roo.log(this.el.dom);
494        if (this.el.hasClass('roo-button')) {
495             this.el.on('click', this.onClick, this);
496        } else {
497             this.el.select('.roo-button').on('click', this.onClick, this);
498        }
499        
500        
501         
502     },
503     onClick : function(e)
504     {
505         Roo.log('button on click ');
506         e.preventDefault();
507         this.fireEvent('click', this, e);
508     }
509     
510     
511 });
512
513  /*
514  * - LGPL
515  *
516  * column
517  * 
518  */
519
520 /**
521  * @class Roo.bootstrap.Column
522  * @extends Roo.bootstrap.Component
523  * Bootstrap Column class
524  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
525  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
526  * @cfg {Number} md colspan out of 12 for computer-sized screens
527  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
528  * @cfg {String} html content of column.
529  * 
530  * @constructor
531  * Create a new Column
532  * @param {Object} config The config object
533  */
534
535 Roo.bootstrap.Column = function(config){
536     Roo.bootstrap.Column.superclass.constructor.call(this, config);
537 };
538
539 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
540     
541     xs: null,
542     sm: null,
543     md: null,
544     lg: null,
545     html: '',
546     offset: 0,
547     
548     getAutoCreate : function(){
549         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
550         
551         cfg = {
552             tag: 'div',
553             cls: 'column'
554         };
555         
556         var settings=this;
557         ['xs','sm','md','lg'].map(function(size){
558             if (settings[size]) {
559                 cfg.cls += ' col-' + size + '-' + settings[size];
560             }
561         });
562         if (this.html.length) {
563             cfg.html = this.html;
564         }
565         
566         return cfg;
567     }
568    
569 });
570
571  
572
573  /*
574  * - LGPL
575  *
576  * page container.
577  * 
578  */
579
580
581 /**
582  * @class Roo.bootstrap.Container
583  * @extends Roo.bootstrap.Component
584  * Bootstrap Container class
585  * @cfg {Boolean} jumbotron is it a jumbotron element
586  * @cfg {String} html content of element
587  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
588  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
589  * @cfg {String} header content of header (for panel)
590  * @cfg {String} footer content of footer (for panel)
591  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
592  *     
593  * @constructor
594  * Create a new Container
595  * @param {Object} config The config object
596  */
597
598 Roo.bootstrap.Container = function(config){
599     Roo.bootstrap.Container.superclass.constructor.call(this, config);
600 };
601
602 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
603     
604     jumbotron : false,
605     well: '',
606     panel : '',
607     header: '',
608     footer : '',
609     sticky: '',
610   
611      
612     getChildContainer : function() {
613         if (this.panel.length) {
614             return this.el.select('.panel-body',true).first();
615         }
616         
617         return this.el;
618     },
619     
620     
621     getAutoCreate : function(){
622         
623         var cfg = {
624             html : '',
625             cls : ''
626         };
627         if (this.jumbotron) {
628             cfg.cls = 'jumbotron';
629         }
630         if (this.cls) {
631             cfg.cls = this.cls + '';
632         }
633         
634         if (this.sticky.length) {
635             var bd = Roo.get(document.body);
636             if (!bd.hasClass('bootstrap-sticky')) {
637                 bd.addClass('bootstrap-sticky');
638                 Roo.select('html',true).setStyle('height', '100%');
639             }
640              
641             cfg.cls += 'bootstrap-sticky-' + this.sticky;
642         }
643         
644         
645         if (this.well.length) {
646             switch (this.well) {
647                 case 'lg':
648                 case 'sm':
649                     cfg.cls +=' well well-' +this.well;
650                     break;
651                 default:
652                     cfg.cls +=' well';
653                     break;
654             }
655         }
656         
657         var body = cfg;
658         
659         if (this.panel.length) {
660             cfg.cls += 'panel panel-' + this.panel;
661             cfg.cn = [];
662             if (this.header.length) {
663                 cfg.cn.push({
664                     
665                     cls : 'panel-heading',
666                     cn : [{
667                         tag: 'h3',
668                         cls : 'panel-title',
669                         html : this.header
670                     }]
671                     
672                 });
673             }
674             body = false;
675             cfg.cn.push({
676                 cls : 'panel-body',
677                 html : this.html
678             });
679             
680             
681             if (this.footer.length) {
682                 cfg.cn.push({
683                     cls : 'panel-footer',
684                     html : this.footer
685                     
686                 });
687             }
688             
689         }
690         if (body) {
691             body.html = this.html || cfg.html;
692         }
693         if (!cfg.cls.length) {
694             cfg.cls =  'container';
695         }
696         
697         return cfg;
698     }
699    
700 });
701
702  /*
703  * - LGPL
704  *
705  * image
706  * 
707  */
708
709
710 /**
711  * @class Roo.bootstrap.Img
712  * @extends Roo.bootstrap.Component
713  * Bootstrap Img class
714  * @cfg {Boolean} imgResponsive false | true
715  * @cfg {String} border rounded | circle | thumbnail
716  * @cfg {String} src image source
717  * @cfg {String} alt image alternative text
718  * 
719  * @constructor
720  * Create a new Input
721  * @param {Object} config The config object
722  */
723
724 Roo.bootstrap.Img = function(config){
725     Roo.bootstrap.Img.superclass.constructor.call(this, config);
726 };
727
728 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
729     
730     imgResponsive: true,
731     border: '',
732     src: '',
733
734     getAutoCreate : function(){
735         
736         cfg = {
737             tag: 'img',
738             cls: 'img-responsive',
739             html : null
740         }
741         
742         cfg.html = this.html || cfg.html;
743         
744         cfg.src = this.src || cfg.src;
745         
746         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
747             cfg.cls += ' img-' + this.border;
748         }
749         
750         if(this.alt){
751             cfg.alt = this.alt;
752         }
753         
754         return cfg;
755     }
756    
757 });
758
759  /*
760  * - LGPL
761  *
762  * header
763  * 
764  */
765
766 /**
767  * @class Roo.bootstrap.Header
768  * @extends Roo.bootstrap.Component
769  * Bootstrap Header class
770  * @cfg {String} html content of header
771  * @cfg {Number} level (1|2|3|4|5|6) default 1
772  * 
773  * @constructor
774  * Create a new Header
775  * @param {Object} config The config object
776  */
777
778
779 Roo.bootstrap.Header  = function(config){
780     Roo.bootstrap.Header.superclass.constructor.call(this, config);
781 };
782
783 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
784     
785     //href : false,
786     html : false,
787     level : 1,
788     
789     
790     
791     getAutoCreate : function(){
792         
793         var cfg = {
794             tag: 'h' + (1 *this.level),
795             html: this.html || 'fill in html'
796         } ;
797         
798         return cfg;
799     }
800    
801 });
802
803  
804
805  /*
806  * - LGPL
807  *
808  * menu
809  * 
810  */
811
812 /**
813  * @class Roo.bootstrap.Menu
814  * @extends Roo.bootstrap.Component
815  * Bootstrap Menu class - container for MenuItems
816  * @cfg {String} type type of menu
817  * 
818  * @constructor
819  * Create a new Menu
820  * @param {Object} config The config object
821  */
822
823
824 Roo.bootstrap.Menu = function(config){
825     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
826 };
827
828 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
829     
830    /// html : false,
831     //align : '',
832     triggerEl : false,
833     type: false,
834     
835     
836     getChildContainer : function() {
837         return this.el;  
838     },
839     
840     getAutoCreate : function(){
841          
842         //if (['right'].indexOf(this.align)!==-1) {
843         //    cfg.cn[1].cls += ' pull-right'
844         //}
845         var cfg = {
846             tag : 'ul',
847             cls : 'dropdown-menu' 
848             
849         }
850         
851         if (this.type==='submenu') {
852             cfg.cls='submenu active'
853         }
854         
855         return cfg;
856     },
857     initEvents : function() {
858        // Roo.log("ADD event");
859        // Roo.log(this.triggerEl.dom);
860         this.triggerEl.on('click', this.toggle, this);
861         this.triggerEl.addClass('dropdown-toggle');
862         
863     },
864     toggle  : function(e)
865     {
866         //Roo.log(e.getTarget());
867        // Roo.log(this.triggerEl.dom);
868         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
869             return;
870         }
871         var isActive = this.triggerEl.hasClass('open');
872         // if disabled.. ingore
873         this.clearMenus(e);
874         //if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
875          // if mobile we use a backdrop because click events don't delegate
876         // $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
877         // }
878  
879        //var relatedTarget = { relatedTarget: this }
880        //$parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))
881  
882        //if (e.isDefaultPrevented()) return;
883         
884        this.triggerEl[isActive ? 'removeClass' : 'addClass']('open');
885        
886        //  .trigger('shown.bs.dropdown', relatedTarget)
887  
888        this.triggerEl.focus();
889        Roo.log(e);
890        e.preventDefault(); 
891         
892         
893     },
894     clearMenus : function()
895     {
896         //$(backdrop).remove()
897         Roo.select('.dropdown-toggle',true).each(function(aa) {
898             if (!aa.hasClass('open')) {
899                 return;
900             }
901             // triger close...
902             aa.removeClass('open');
903           //var parent = getParent($(this))
904           //var relatedTarget = { relatedTarget: this }
905           
906            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
907           //if (e.isDefaultPrevented()) return
908            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
909         })
910     }
911     
912    
913 });
914
915  
916
917  /*
918  * - LGPL
919  *
920  * menu item
921  * 
922  */
923
924
925 /**
926  * @class Roo.bootstrap.MenuItem
927  * @extends Roo.bootstrap.Component
928  * Bootstrap MenuItem class
929  * @cfg {String} html the menu label
930  * @cfg {String} href the link
931  * 
932  * 
933  * @constructor
934  * Create a new MenuItem
935  * @param {Object} config The config object
936  */
937
938
939 Roo.bootstrap.MenuItem = function(config){
940     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
941 };
942
943 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
944     
945     href : false,
946     html : false,
947     
948     getAutoCreate : function(){
949         var cfg= {
950             tag: 'li',
951             cn: [
952                 {
953                     tag : 'a',
954                     href : '#',
955                     html : 'Link'
956                 }
957             ]
958         };
959         
960         cfg.cn[0].href = this.href || cfg.cn[0].href ;
961         cfg.cn[0].html = this.html || cfg.cn[0].html ;
962         return cfg;
963     }
964    
965 });
966
967  
968
969  /*
970  * - LGPL
971  *
972  * menu separator
973  * 
974  */
975
976
977 /**
978  * @class Roo.bootstrap.MenuSeparator
979  * @extends Roo.bootstrap.Component
980  * Bootstrap MenuSeparator class
981  * 
982  * @constructor
983  * Create a new MenuItem
984  * @param {Object} config The config object
985  */
986
987
988 Roo.bootstrap.MenuSeparator = function(config){
989     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
990 };
991
992 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
993     
994     getAutoCreate : function(){
995         var cfg = {
996             cls: 'divider',
997             tag : 'li'
998         };
999         
1000         return cfg;
1001     }
1002    
1003 });
1004
1005  
1006
1007  
1008 /*
1009 <div class="modal fade">
1010   <div class="modal-dialog">
1011     <div class="modal-content">
1012       <div class="modal-header">
1013         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1014         <h4 class="modal-title">Modal title</h4>
1015       </div>
1016       <div class="modal-body">
1017         <p>One fine body&hellip;</p>
1018       </div>
1019       <div class="modal-footer">
1020         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1021         <button type="button" class="btn btn-primary">Save changes</button>
1022       </div>
1023     </div><!-- /.modal-content -->
1024   </div><!-- /.modal-dialog -->
1025 </div><!-- /.modal -->
1026 */
1027 /*
1028  * - LGPL
1029  *
1030  * page contgainer.
1031  * 
1032  */
1033
1034 /**
1035  * @class Roo.bootstrap.Modal
1036  * @extends Roo.bootstrap.Component
1037  * Bootstrap Modal class
1038  * @cfg {String} title Title of dialog
1039  * @cfg {Array} buttons Array of buttons or standard button set..
1040  * 
1041  * @constructor
1042  * Create a new Modal Dialog
1043  * @param {Object} config The config object
1044  */
1045
1046 Roo.bootstrap.Modal = function(config){
1047     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1048     this.addEvents({
1049         // raw events
1050         /**
1051          * @event click
1052          * The raw click event for the entire grid.
1053          * @param {Roo.EventObject} e
1054          */
1055         "btnclick" : true
1056     });
1057 };
1058
1059 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1060     
1061     title : 'test dialog',
1062    
1063     buttons : false,
1064
1065     onRender : function(ct, position)
1066     {
1067         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1068         if(!this.el){
1069             var cfg = Roo.apply({},  this.getAutoCreate());
1070             cfg.id = Roo.id();
1071             //if(!cfg.name){
1072             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1073             //}
1074             //if (!cfg.name.length) {
1075             //    delete cfg.name;
1076            // }
1077             if (this.cls) {
1078                 cfg.cls += ' ' + this.cls;
1079             }
1080             if (this.style) {
1081                 cfg.style = this.style;
1082             }
1083             this.el = Roo.get(document.body).createChild(cfg, position);
1084         }
1085         //var type = this.el.dom.type;
1086          
1087         if(this.tabIndex !== undefined){
1088             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1089         }
1090         
1091         
1092         
1093         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1094         this.maskEl.enableDisplayMode("block");
1095         this.maskEl.hide();
1096         //this.el.addClass("x-dlg-modal");
1097     
1098         
1099         if (this.buttons) {
1100             Roo.each(this.buttons, function(bb) {
1101                 b = Roo.apply({}, bb);
1102                 b.xns = b.xns || Roo.bootstrap;
1103                 b.xtype = b.xtype || 'Button';
1104                 if (typeof(b.listeners) == 'undefined') {
1105                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1106                 }
1107                 
1108                 var btn = Roo.factory(b);
1109                 
1110                 btn.onRender(this.el.select('.modal-footer').first());
1111                 
1112             },this);
1113         }
1114         
1115         
1116         
1117         this.initEvents();
1118         //this.el.addClass([this.fieldClass, this.cls]);
1119         
1120     },
1121     getAutoCreate : function(){
1122         
1123         
1124         var bdy = {
1125                 cls : 'modal-body',
1126                 html : this.html || ''
1127         };
1128         
1129          
1130         return {
1131             cls: "modal fade",
1132             cn : [
1133                 {
1134                     cls: "modal-dialog",
1135                     cn : [
1136                         {
1137                             cls : "modal-content",
1138                             cn : [
1139                                 {
1140                                     cls : 'modal-header',
1141                                     cn : [
1142                                         {
1143                                             tag: 'button',
1144                                             cls : 'close',
1145                                             html : '&times'
1146                                         },
1147                                         {
1148                                             tag: 'h4',
1149                                             cls : 'modal-title',
1150                                             html : this.title
1151                                         }
1152                                     
1153                                     ]
1154                                 },
1155                                 bdy,
1156                                 {
1157                                     cls : 'modal-footer' 
1158                                 }
1159                                 
1160                                 
1161                             ]
1162                             
1163                         }
1164                     ]
1165                         
1166                 }
1167             ]
1168             
1169             
1170         };
1171           
1172     },
1173     getChildContainer : function() {
1174          
1175          return this.el.select('.modal-body',true).first();
1176         
1177     },
1178     getButtonContainer : function() {
1179          return this.el.select('.modal-footer',true).first();
1180         
1181     },
1182     initEvents : function()
1183     {
1184         this.el.select('.modal-header .close').on('click', this.hide, this);
1185     },
1186     show : function() {
1187         if (!this.rendered) {
1188             this.render();
1189         }
1190        
1191         this.el.addClass('on');
1192         this.el.removeClass('fade');
1193         this.el.setStyle('display', 'block');
1194         Roo.get(document.body).addClass("x-body-masked");
1195         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1196         this.maskEl.show();
1197         this.el.setStyle('zIndex', '10001');
1198         
1199         
1200     },
1201     hide : function()
1202     {
1203         this.maskEl.hide();
1204         this.el.removeClass('on');
1205         this.el.addClass('fade');
1206         this.el.setStyle('display', 'none');
1207     },
1208     onButtonClick: function(btn,e)
1209     {
1210         //Roo.log([a,b,c]);
1211         this.fireEvent('btnclick', btn.name, e);
1212     }
1213 });
1214
1215
1216 Roo.apply(Roo.bootstrap.Modal,  {
1217     /**
1218          * Button config that displays a single OK button
1219          * @type Object
1220          */
1221         OK :  [{
1222             name : 'ok',
1223             weight : 'primary',
1224             html : 'OK'
1225         }], 
1226         /**
1227          * Button config that displays Yes and No buttons
1228          * @type Object
1229          */
1230         YESNO : [
1231             {
1232                 name  :'yes',
1233                 weight : 'primary',
1234                 html : 'Yes'
1235             },
1236             {
1237                 name  : 'no',
1238                 html : 'No'
1239             }
1240         ],
1241         
1242         /**
1243          * Button config that displays OK and Cancel buttons
1244          * @type Object
1245          */
1246         OKCANCEL : [
1247             {
1248                 name : 'ok',
1249                 weight : 'primary',
1250                 html : 'OK'
1251             },
1252             {
1253                name : 'cancel',
1254                 html : 'Cancel'
1255             }
1256         ],
1257         /**
1258          * Button config that displays Yes, No and Cancel buttons
1259          * @type Object
1260          */
1261         YESNOCANCEL : [
1262             {
1263                 name : 'yes',
1264                 weight : 'primary',
1265                 html : 'Yes'
1266             },
1267             {
1268                 name : 'no',
1269                 html : 'No'
1270             },
1271             {
1272                 name : 'cancel',
1273                 html : 'Cancel'
1274             }
1275         ]
1276 });
1277
1278  /*
1279  * - LGPL
1280  *
1281  * navbar
1282  * 
1283  */
1284
1285 /**
1286  * @class Roo.bootstrap.Navbar
1287  * @extends Roo.bootstrap.Component
1288  * Bootstrap Navbar class
1289  * @cfg {Boolean} sidebar has side bar
1290  * @cfg {Boolean} bar is a bar?
1291  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
1292  * @cfg {String} brand what is brand
1293  * @cfg {Boolean} inverse is inverted color
1294  * @cfg {String} type (nav | pills | tabs)
1295  * @cfg {Boolean} arrangement stacked | justified
1296  * @cfg {String} align (left | right) alignment
1297  *
1298  * 
1299  * @constructor
1300  * Create a new Navbar
1301  * @param {Object} config The config object
1302  */
1303
1304
1305 Roo.bootstrap.Navbar = function(config){
1306     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
1307 };
1308
1309 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
1310     
1311     sidebar: false,
1312     
1313     bar: false,
1314     brand: '',
1315     inverse: false,
1316     position: '',
1317     align : false,
1318     type: 'nav',
1319     arrangement: '',
1320     
1321     getAutoCreate : function(){
1322         var cfg = {
1323             cls : 'navbar'
1324         };
1325         
1326         if (this.sidebar === true) {
1327             cfg = {
1328                 tag: 'div',
1329                 cls: 'sidebar-nav'
1330             };
1331             return cfg;
1332         }
1333         
1334         if (this.bar === true) {
1335             cfg = {
1336                 tag: 'nav',
1337                 cls: 'navbar',
1338                 role: 'navigation',
1339                 cn: [
1340                     {
1341                         tag: 'div',
1342                         cls: 'navbar-header',
1343                         cn: [
1344                             {
1345                             tag: 'button',
1346                             type: 'button',
1347                             cls: 'navbar-toggle',
1348                             'data-toggle': 'collapse',
1349                             cn: [
1350                                 {
1351                                     tag: 'span',
1352                                     cls: 'sr-only',
1353                                     html: 'Toggle navigation'
1354                                 },
1355                                 {
1356                                     tag: 'span',
1357                                     cls: 'icon-bar'
1358                                 },
1359                                 {
1360                                     tag: 'span',
1361                                     cls: 'icon-bar'
1362                                 },
1363                                 {
1364                                     tag: 'span',
1365                                     cls: 'icon-bar'
1366                                 }
1367                             ]
1368                             }
1369                         ]
1370                     },
1371                     {
1372                     tag: 'div',
1373                     cls: 'collapse navbar-collapse'
1374                     }
1375                 ]
1376             };
1377             
1378             cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
1379             
1380             if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
1381             cfg.cls += ' navbar-' + this.position;
1382             cfg.tag = this.position  == 'fixed-bottom' ? 'footer' : 'header';
1383             }
1384             
1385             if (this.brand !== '') {
1386                 cfg.cn[0].cn.push({
1387                     tag: 'a',
1388                     href: '#',
1389                     cls: 'navbar-brand',
1390                     cn: [
1391                     this.brand
1392                     ]
1393                 });
1394             }
1395             
1396             return cfg;
1397         
1398         } else if (this.bar === false) {
1399             
1400         } else {
1401             Roo.log('Property \'bar\' in of Navbar must be either true or false')
1402         }
1403         
1404         cfg.cn = [
1405             {
1406                 cls: 'nav',
1407                 tag : 'ul'
1408             }
1409         ];
1410         
1411         if (['tabs','pills'].indexOf(this.type)!==-1) {
1412             cfg.cn[0].cls += ' nav-' + this.type
1413         } else {
1414             if (this.type!=='nav') {
1415             Roo.log('nav type must be nav/tabs/pills')
1416             }
1417             cfg.cn[0].cls += ' navbar-nav'
1418         }
1419         
1420         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
1421             cfg.cn[0].cls += ' nav-' + this.arrangement;
1422         }
1423         
1424         if (this.align === 'right') {
1425             cfg.cn[0].cls += ' navbar-right';
1426         }
1427         if (this.inverse) {
1428             cfg.cls += ' navbar-inverse';
1429             
1430         }
1431         
1432         
1433         return cfg;
1434     },
1435     
1436     getChildContainer : function() {
1437         if (this.bar === true) {
1438             return this.el.select('.collapse',true).first();
1439         }
1440         console.log(this);
1441         return this.el;
1442     }
1443    
1444 });
1445
1446  
1447
1448  /*
1449  * - LGPL
1450  *
1451  * nav group
1452  * 
1453  */
1454
1455 /**
1456  * @class Roo.bootstrap.NavGroup
1457  * @extends Roo.bootstrap.Component
1458  * Bootstrap NavGroup class
1459  * @cfg {String} align left | right
1460  * @cfg {Boolean} inverse false | true
1461  * 
1462  * @constructor
1463  * Create a new nav group
1464  * @param {Object} config The config object
1465  */
1466
1467 Roo.bootstrap.NavGroup = function(config){
1468     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
1469 };
1470
1471 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
1472     
1473     align: '',
1474     inverse: false,
1475     form: false,
1476     
1477     getAutoCreate : function(){
1478         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
1479         
1480         cfg = {
1481             tag : 'ul',
1482             cls: 'nav navbar-nav' 
1483         }
1484         
1485         if (this.parent().sidebar === true) {
1486             cfg = {
1487                 tag: 'ul',
1488                 cls: 'dashboard-menu'
1489             }
1490             
1491             return cfg;
1492         }
1493         
1494         if (this.form === true) {
1495             cfg = {
1496                 tag: 'form',
1497                 cls: 'navbar-form'
1498             }
1499             
1500             if (this.align === 'right') {
1501                 cfg.cls += ' navbar-right';
1502             } else {
1503                 cfg.cls += ' navbar-left';
1504             }
1505         }
1506         
1507         
1508         if (this.align === 'right') {
1509             cfg.cls += ' navbar-right';
1510         }
1511         
1512         if (this.inverse) {
1513             cfg.cls += ' navbar-inverse';
1514             
1515         }
1516         
1517         
1518         return cfg;
1519     }
1520    
1521 });
1522
1523  
1524
1525  /*
1526  * - LGPL
1527  *
1528  * row
1529  * 
1530  */
1531 /**
1532  * @class Roo.bootstrap.Navbar.Button
1533  * @extends Roo.bootstrap.Component
1534  * Bootstrap Navbar.Button class
1535  * @cfg {String} href  link to
1536  * @cfg {String} html content of button
1537     
1538  * @constructor
1539  * Create a new Navbar Button
1540  * @param {Object} config The config object
1541  */
1542
1543
1544 Roo.bootstrap.Navbar.Button = function(config){
1545     Roo.bootstrap.Navbar.Button.superclass.constructor.call(this, config);
1546 };
1547
1548 Roo.extend(Roo.bootstrap.Navbar.Button, Roo.bootstrap.Component,  {
1549     
1550     href : false,
1551     html : false,
1552     
1553     autoCreate : {
1554         cls: 'btn',
1555         tag : 'button',
1556         html: 'hello'
1557     },
1558     
1559     getAutoCreate : function(){
1560         
1561         var cfg = {
1562             cls: 'btn',
1563             tag : 'button',
1564             html: 'hello',
1565             cn : []
1566             
1567         } ;
1568         cfg.cn.push({
1569             html : this.html || ''
1570             //href : this.
1571              //       )
1572         });
1573         cfg.cn.push({
1574             tag: 'span',
1575             cls : 'carat'
1576         });
1577         
1578         return cfg;
1579     }
1580    
1581 });
1582
1583  
1584
1585  /*
1586  * - LGPL
1587  *
1588  * row
1589  * 
1590  */
1591
1592 /**
1593  * @class Roo.bootstrap.Navbar.Item
1594  * @extends Roo.bootstrap.Component
1595  * Bootstrap Navbar.Button class
1596  * @cfg {String} href  link to
1597  * @cfg {String} html content of button
1598  * @cfg {String} badge text inside badge
1599  * @cfg {String} glyphicon name of glyphicon
1600   
1601  * @constructor
1602  * Create a new Navbar Button
1603  * @param {Object} config The config object
1604  */
1605 Roo.bootstrap.Navbar.Item = function(config){
1606     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
1607 };
1608
1609 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
1610     
1611     href: false,
1612     html: '',
1613     badge: '',
1614     icon: false,
1615     glyphicon: false,
1616     
1617     getAutoCreate : function(){
1618         
1619         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
1620         
1621         if (this.parent().parent().sidebar === true) {
1622             cfg = {
1623                 tag: 'li',
1624                 cls: '',
1625                 cn: [
1626                     {
1627                         tag: 'p',
1628                         cls: ''
1629                     }
1630                 ]
1631             }
1632             
1633             if (this.html) {
1634                 cfg.cn[0].html = this.html;
1635             }
1636             
1637             if (this.active) {
1638                 this.cls += ' active';
1639             }
1640             
1641             if (this.menu) {
1642                 cfg.cn[0].cls += ' dropdown-toggle';
1643                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
1644             }
1645             
1646             if (this.href) {
1647                 cfg.cn[0].tag = 'a',
1648                 cfg.cn[0].href = this.href;
1649             }
1650             
1651             if (this.glyphicon) {
1652                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
1653             }
1654             
1655             return cfg;
1656         }
1657         
1658         cfg = {
1659             tag: 'li'
1660         }
1661         cfg.cn = [
1662             {
1663                 tag: 'p',
1664                 html: 'Text'
1665             }
1666         ];
1667         
1668         if (this.glyphicon) {
1669             if(cfg.html){cfg.html = ' ' + this.html};
1670             cfg.cn=[
1671                 {
1672                     tag: 'span',
1673                     cls: 'glyphicon glyphicon-' + this.glyphicon
1674                 }
1675             ];
1676         }
1677         
1678         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1679         if (this.menu) {
1680             cfg.cn[0].tag='a';
1681             cfg.cn[0].href='#';
1682             cfg.cn[0].html += " <span class='caret'></span>";
1683         //}else if (!this.href) {
1684         //    cfg.cn[0].tag='p';
1685         //    cfg.cn[0].cls='navbar-text';
1686         } else {
1687             cfg.cn[0].tag='a';
1688             cfg.cn[0].href=this.href||'#';
1689             cfg.cn[0].html=this.html;
1690         }
1691         
1692         if (this.badge !== '') {
1693             
1694             cfg.cn[0].cn=[
1695                 cfg.cn[0].html + ' ',
1696                 {
1697                     tag: 'span',
1698                     cls: 'badge',
1699                     html: this.badge
1700                 }
1701             ];
1702             cfg.cn[0].html=''
1703         }
1704          
1705         
1706         return cfg;
1707     },
1708     initEvents: function() {
1709        // Roo.log('init events?');
1710        // Roo.log(this.el.dom);
1711         this.el.select('a',true).on('click',
1712                 function(e) {
1713                     this.fireEvent('click', this);
1714                 },
1715                 this
1716         );
1717     }
1718    
1719 });
1720  
1721
1722  /*
1723  * - LGPL
1724  *
1725  * row
1726  * 
1727  */
1728
1729 /**
1730  * @class Roo.bootstrap.Row
1731  * @extends Roo.bootstrap.Component
1732  * Bootstrap Row class (contains columns...)
1733  * 
1734  * @constructor
1735  * Create a new Row
1736  * @param {Object} config The config object
1737  */
1738
1739 Roo.bootstrap.Row = function(config){
1740     Roo.bootstrap.Row.superclass.constructor.call(this, config);
1741 };
1742
1743 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
1744     
1745     getAutoCreate : function(){
1746        return {
1747             cls: 'row clearfix'
1748        };
1749     }
1750     
1751     
1752 });
1753
1754  
1755
1756  /*
1757  * - LGPL
1758  *
1759  * element
1760  * 
1761  */
1762
1763 /**
1764  * @class Roo.bootstrap.Element
1765  * @extends Roo.bootstrap.Component
1766  * Bootstrap Element class
1767  * @cfg {String} html contents of the element
1768  * @cfg {String} tag tag of the element
1769  * @cfg {String} cls class of the element
1770  * 
1771  * @constructor
1772  * Create a new Element
1773  * @param {Object} config The config object
1774  */
1775
1776 Roo.bootstrap.Element = function(config){
1777     Roo.bootstrap.Element.superclass.constructor.call(this, config);
1778 };
1779
1780 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
1781     
1782     tag: 'div',
1783     cls: '',
1784     html: '',
1785      
1786     
1787     getAutoCreate : function(){
1788         
1789         var cfg = {
1790             tag: this.tag,
1791             cls: '',
1792             html: this.html
1793         }
1794         
1795         return cfg;
1796     }
1797    
1798 });
1799
1800  
1801
1802  /*
1803  * - LGPL
1804  *
1805  * pagination
1806  * 
1807  */
1808
1809 /**
1810  * @class Roo.bootstrap.Pagination
1811  * @extends Roo.bootstrap.Component
1812  * Bootstrap Pagination class
1813  * @cfg {String} size xs | sm | md | lg
1814  * @cfg {Boolean} inverse false | true
1815  * @cfg {Number} from pagination starting number
1816  * @cfg {Number} to pagination ending number
1817  * @cfg {String} align empty or left | right
1818  * @cfg {Number} active active page number
1819  * 
1820  * @constructor
1821  * Create a new Pagination
1822  * @param {Object} config The config object
1823  */
1824
1825 Roo.bootstrap.Pagination = function(config){
1826     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
1827 };
1828
1829 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
1830     
1831     cls: false,
1832     size: false,
1833     inverse: false,
1834     from: 1,
1835     to: 4,
1836     align: false,
1837     active: 1,
1838     
1839     getAutoCreate : function(){
1840         cfg = {
1841             tag: 'ul',
1842                 cls: 'pagination',
1843                 cn: []
1844         };
1845         if (this.inverse) {
1846             cfg.cls += ' inverse';
1847         }
1848         if (this.html) {
1849             cfg.html=this.html;
1850         }
1851         if (this.cls) {
1852             cfg.cls=this.cls;
1853         }
1854         cfg.cn[0]={
1855             tag: 'li',
1856             cn: [
1857                 {
1858                     tag: 'a',
1859                     href:'#',
1860                     html: '&laquo;'
1861                 }
1862             ]
1863         };
1864         var from=this.from>0?this.from:1;
1865         var to=this.to-from<=10?this.to:from+10;
1866         var active=this.active>=from&&this.active<=to?this.active:null;
1867         for (var i=from;i<=to;i++) {
1868             cfg.cn.push(
1869                 {
1870                     tag: 'li',
1871                     cls: active===i?'active':'',
1872                     cn: [
1873                         {
1874                             tag: 'a',
1875                             href: '#',
1876                             html: i
1877                         }
1878                     ]
1879                 }
1880             );
1881         }
1882         
1883         cfg.cn.push(
1884             {
1885                 tag: 'li',
1886                 cn: [
1887                     {
1888                        tag: 'a',
1889                        href: '#',
1890                        html: '&raquo;'
1891                     }
1892                 ]
1893             }
1894         );
1895         
1896         return cfg;
1897     }
1898    
1899 });
1900
1901  
1902
1903  /*
1904  * - LGPL
1905  *
1906  * slider
1907  * 
1908  */
1909
1910
1911 /**
1912  * @class Roo.bootstrap.Slider
1913  * @extends Roo.bootstrap.Component
1914  * Bootstrap Slider class
1915  *    
1916  * @constructor
1917  * Create a new Slider
1918  * @param {Object} config The config object
1919  */
1920
1921 Roo.bootstrap.Slider = function(config){
1922     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
1923 };
1924
1925 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
1926     
1927     getAutoCreate : function(){
1928         
1929         var cfg = {
1930             tag: 'div',
1931             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
1932             cn: [
1933                 {
1934                     tag: 'a',
1935                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
1936                 }
1937             ]
1938         }
1939         
1940         return cfg;
1941     }
1942    
1943 });
1944
1945  /*
1946  * - LGPL
1947  *
1948  * table
1949  * 
1950  */
1951
1952 /**
1953  * @class Roo.bootstrap.Table
1954  * @extends Roo.bootstrap.Component
1955  * Bootstrap Table class
1956  * 
1957  * @constructor
1958  * Create a new Table
1959  * @param {Object} config The config object
1960  */
1961
1962 Roo.bootstrap.Table = function(config){
1963     Roo.bootstrap.Table.superclass.constructor.call(this, config);
1964 };
1965
1966 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
1967     
1968     html: false,
1969     cls: false,
1970     
1971     getAutoCreate : function(){
1972         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
1973         
1974         cfg = {
1975             tag: 'table',
1976             cn: [
1977                 {
1978                     tag: 'tbody'
1979                 }
1980             ]
1981         }
1982         if (this.html) {
1983             cfg.html=this.html
1984         }
1985         if (this.cls) {
1986             cfg.cls=this.cls
1987         }
1988         
1989         return cfg;
1990     }
1991    
1992 });
1993
1994  
1995
1996  /*
1997  * - LGPL
1998  *
1999  * table cell
2000  * 
2001  */
2002
2003 /**
2004  * @class Roo.bootstrap.TableCell
2005  * @extends Roo.bootstrap.Component
2006  * Bootstrap TableCell class
2007  * 
2008  * @constructor
2009  * Create a new TableCell
2010  * @param {Object} config The config object
2011  */
2012
2013 Roo.bootstrap.TableCell = function(config){
2014     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
2015 };
2016
2017 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
2018     
2019     getAutoCreate : function(){
2020         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
2021         
2022         cfg = {
2023             tag: 'td'
2024         }
2025         if (this.html) {
2026             cfg.html=this.html
2027         }
2028         if (this.cls) {
2029             cfg.cls=this.cls
2030         }
2031         
2032         return cfg;
2033     }
2034    
2035 });
2036
2037  
2038
2039  /*
2040  * - LGPL
2041  *
2042  * table row
2043  * 
2044  */
2045
2046 /**
2047  * @class Roo.bootstrap.TableRow
2048  * @extends Roo.bootstrap.Component
2049  * Bootstrap TableRow class
2050  * 
2051  * @constructor
2052  * Create a new TableRow
2053  * @param {Object} config The config object
2054  */
2055
2056 Roo.bootstrap.TableRow = function(config){
2057     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
2058 };
2059
2060 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
2061     
2062     getAutoCreate : function(){
2063         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
2064         
2065         cfg = {
2066             tag: 'tr'
2067         }
2068         
2069         return cfg;
2070     }
2071    
2072 });
2073
2074  
2075
2076  /*
2077  * Based on:
2078  * Ext JS Library 1.1.1
2079  * Copyright(c) 2006-2007, Ext JS, LLC.
2080  *
2081  * Originally Released Under LGPL - original licence link has changed is not relivant.
2082  *
2083  * Fork - LGPL
2084  * <script type="text/javascript">
2085  */
2086
2087 // as we use this in bootstrap.
2088 Roo.namespace('Roo.form');
2089  /**
2090  * @class Roo.form.Action
2091  * Internal Class used to handle form actions
2092  * @constructor
2093  * @param {Roo.form.BasicForm} el The form element or its id
2094  * @param {Object} config Configuration options
2095  */
2096
2097  
2098  
2099 // define the action interface
2100 Roo.form.Action = function(form, options){
2101     this.form = form;
2102     this.options = options || {};
2103 };
2104 /**
2105  * Client Validation Failed
2106  * @const 
2107  */
2108 Roo.form.Action.CLIENT_INVALID = 'client';
2109 /**
2110  * Server Validation Failed
2111  * @const 
2112  */
2113 Roo.form.Action.SERVER_INVALID = 'server';
2114  /**
2115  * Connect to Server Failed
2116  * @const 
2117  */
2118 Roo.form.Action.CONNECT_FAILURE = 'connect';
2119 /**
2120  * Reading Data from Server Failed
2121  * @const 
2122  */
2123 Roo.form.Action.LOAD_FAILURE = 'load';
2124
2125 Roo.form.Action.prototype = {
2126     type : 'default',
2127     failureType : undefined,
2128     response : undefined,
2129     result : undefined,
2130
2131     // interface method
2132     run : function(options){
2133
2134     },
2135
2136     // interface method
2137     success : function(response){
2138
2139     },
2140
2141     // interface method
2142     handleResponse : function(response){
2143
2144     },
2145
2146     // default connection failure
2147     failure : function(response){
2148         
2149         this.response = response;
2150         this.failureType = Roo.form.Action.CONNECT_FAILURE;
2151         this.form.afterAction(this, false);
2152     },
2153
2154     processResponse : function(response){
2155         this.response = response;
2156         if(!response.responseText){
2157             return true;
2158         }
2159         this.result = this.handleResponse(response);
2160         return this.result;
2161     },
2162
2163     // utility functions used internally
2164     getUrl : function(appendParams){
2165         var url = this.options.url || this.form.url || this.form.el.dom.action;
2166         if(appendParams){
2167             var p = this.getParams();
2168             if(p){
2169                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
2170             }
2171         }
2172         return url;
2173     },
2174
2175     getMethod : function(){
2176         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
2177     },
2178
2179     getParams : function(){
2180         var bp = this.form.baseParams;
2181         var p = this.options.params;
2182         if(p){
2183             if(typeof p == "object"){
2184                 p = Roo.urlEncode(Roo.applyIf(p, bp));
2185             }else if(typeof p == 'string' && bp){
2186                 p += '&' + Roo.urlEncode(bp);
2187             }
2188         }else if(bp){
2189             p = Roo.urlEncode(bp);
2190         }
2191         return p;
2192     },
2193
2194     createCallback : function(){
2195         return {
2196             success: this.success,
2197             failure: this.failure,
2198             scope: this,
2199             timeout: (this.form.timeout*1000),
2200             upload: this.form.fileUpload ? this.success : undefined
2201         };
2202     }
2203 };
2204
2205 Roo.form.Action.Submit = function(form, options){
2206     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
2207 };
2208
2209 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
2210     type : 'submit',
2211
2212     haveProgress : false,
2213     uploadComplete : false,
2214     
2215     // uploadProgress indicator.
2216     uploadProgress : function()
2217     {
2218         if (!this.form.progressUrl) {
2219             return;
2220         }
2221         
2222         if (!this.haveProgress) {
2223             Roo.MessageBox.progress("Uploading", "Uploading");
2224         }
2225         if (this.uploadComplete) {
2226            Roo.MessageBox.hide();
2227            return;
2228         }
2229         
2230         this.haveProgress = true;
2231    
2232         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
2233         
2234         var c = new Roo.data.Connection();
2235         c.request({
2236             url : this.form.progressUrl,
2237             params: {
2238                 id : uid
2239             },
2240             method: 'GET',
2241             success : function(req){
2242                //console.log(data);
2243                 var rdata = false;
2244                 var edata;
2245                 try  {
2246                    rdata = Roo.decode(req.responseText)
2247                 } catch (e) {
2248                     Roo.log("Invalid data from server..");
2249                     Roo.log(edata);
2250                     return;
2251                 }
2252                 if (!rdata || !rdata.success) {
2253                     Roo.log(rdata);
2254                     Roo.MessageBox.alert(Roo.encode(rdata));
2255                     return;
2256                 }
2257                 var data = rdata.data;
2258                 
2259                 if (this.uploadComplete) {
2260                    Roo.MessageBox.hide();
2261                    return;
2262                 }
2263                    
2264                 if (data){
2265                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
2266                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
2267                     );
2268                 }
2269                 this.uploadProgress.defer(2000,this);
2270             },
2271        
2272             failure: function(data) {
2273                 Roo.log('progress url failed ');
2274                 Roo.log(data);
2275             },
2276             scope : this
2277         });
2278            
2279     },
2280     
2281     
2282     run : function()
2283     {
2284         // run get Values on the form, so it syncs any secondary forms.
2285         this.form.getValues();
2286         
2287         var o = this.options;
2288         var method = this.getMethod();
2289         var isPost = method == 'POST';
2290         if(o.clientValidation === false || this.form.isValid()){
2291             
2292             if (this.form.progressUrl) {
2293                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
2294                     (new Date() * 1) + '' + Math.random());
2295                     
2296             } 
2297             
2298             
2299             Roo.Ajax.request(Roo.apply(this.createCallback(), {
2300                 form:this.form.el.dom,
2301                 url:this.getUrl(!isPost),
2302                 method: method,
2303                 params:isPost ? this.getParams() : null,
2304                 isUpload: this.form.fileUpload
2305             }));
2306             
2307             this.uploadProgress();
2308
2309         }else if (o.clientValidation !== false){ // client validation failed
2310             this.failureType = Roo.form.Action.CLIENT_INVALID;
2311             this.form.afterAction(this, false);
2312         }
2313     },
2314
2315     success : function(response)
2316     {
2317         this.uploadComplete= true;
2318         if (this.haveProgress) {
2319             Roo.MessageBox.hide();
2320         }
2321         
2322         
2323         var result = this.processResponse(response);
2324         if(result === true || result.success){
2325             this.form.afterAction(this, true);
2326             return;
2327         }
2328         if(result.errors){
2329             this.form.markInvalid(result.errors);
2330             this.failureType = Roo.form.Action.SERVER_INVALID;
2331         }
2332         this.form.afterAction(this, false);
2333     },
2334     failure : function(response)
2335     {
2336         this.uploadComplete= true;
2337         if (this.haveProgress) {
2338             Roo.MessageBox.hide();
2339         }
2340         
2341         this.response = response;
2342         this.failureType = Roo.form.Action.CONNECT_FAILURE;
2343         this.form.afterAction(this, false);
2344     },
2345     
2346     handleResponse : function(response){
2347         if(this.form.errorReader){
2348             var rs = this.form.errorReader.read(response);
2349             var errors = [];
2350             if(rs.records){
2351                 for(var i = 0, len = rs.records.length; i < len; i++) {
2352                     var r = rs.records[i];
2353                     errors[i] = r.data;
2354                 }
2355             }
2356             if(errors.length < 1){
2357                 errors = null;
2358             }
2359             return {
2360                 success : rs.success,
2361                 errors : errors
2362             };
2363         }
2364         var ret = false;
2365         try {
2366             ret = Roo.decode(response.responseText);
2367         } catch (e) {
2368             ret = {
2369                 success: false,
2370                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
2371                 errors : []
2372             };
2373         }
2374         return ret;
2375         
2376     }
2377 });
2378
2379
2380 Roo.form.Action.Load = function(form, options){
2381     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
2382     this.reader = this.form.reader;
2383 };
2384
2385 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
2386     type : 'load',
2387
2388     run : function(){
2389         
2390         Roo.Ajax.request(Roo.apply(
2391                 this.createCallback(), {
2392                     method:this.getMethod(),
2393                     url:this.getUrl(false),
2394                     params:this.getParams()
2395         }));
2396     },
2397
2398     success : function(response){
2399         
2400         var result = this.processResponse(response);
2401         if(result === true || !result.success || !result.data){
2402             this.failureType = Roo.form.Action.LOAD_FAILURE;
2403             this.form.afterAction(this, false);
2404             return;
2405         }
2406         this.form.clearInvalid();
2407         this.form.setValues(result.data);
2408         this.form.afterAction(this, true);
2409     },
2410
2411     handleResponse : function(response){
2412         if(this.form.reader){
2413             var rs = this.form.reader.read(response);
2414             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
2415             return {
2416                 success : rs.success,
2417                 data : data
2418             };
2419         }
2420         return Roo.decode(response.responseText);
2421     }
2422 });
2423
2424 Roo.form.Action.ACTION_TYPES = {
2425     'load' : Roo.form.Action.Load,
2426     'submit' : Roo.form.Action.Submit
2427 };/*
2428  * - LGPL
2429  *
2430  * form
2431  * 
2432  */
2433
2434 /**
2435  * @class Roo.bootstrap.Form
2436  * @extends Roo.bootstrap.Component
2437  * Bootstrap Form class
2438  * @cfg {String} method  GET | POST (default POST)
2439  * @cfg {String} labelAlign top | left (default top)
2440   * @cfg {String} align left  | right - for navbars
2441
2442  * 
2443  * @constructor
2444  * Create a new Form
2445  * @param {Object} config The config object
2446  */
2447
2448
2449 Roo.bootstrap.Form = function(config){
2450     Roo.bootstrap.Form.superclass.constructor.call(this, config);
2451     this.addEvents({
2452         /**
2453          * @event clientvalidation
2454          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
2455          * @param {Form} this
2456          * @param {Boolean} valid true if the form has passed client-side validation
2457          */
2458         clientvalidation: true,
2459         /**
2460          * @event beforeaction
2461          * Fires before any action is performed. Return false to cancel the action.
2462          * @param {Form} this
2463          * @param {Action} action The action to be performed
2464          */
2465         beforeaction: true,
2466         /**
2467          * @event actionfailed
2468          * Fires when an action fails.
2469          * @param {Form} this
2470          * @param {Action} action The action that failed
2471          */
2472         actionfailed : true,
2473         /**
2474          * @event actioncomplete
2475          * Fires when an action is completed.
2476          * @param {Form} this
2477          * @param {Action} action The action that completed
2478          */
2479         actioncomplete : true
2480     });
2481     
2482 };
2483
2484 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
2485       
2486      /**
2487      * @cfg {String} method
2488      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
2489      */
2490     method : 'POST',
2491     /**
2492      * @cfg {String} url
2493      * The URL to use for form actions if one isn't supplied in the action options.
2494      */
2495     /**
2496      * @cfg {Boolean} fileUpload
2497      * Set to true if this form is a file upload.
2498      */
2499      
2500     /**
2501      * @cfg {Object} baseParams
2502      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
2503      */
2504       
2505     /**
2506      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
2507      */
2508     timeout: 30,
2509     /**
2510      * @cfg {Sting} align (left|right) for navbar forms
2511      */
2512     align : 'left',
2513
2514     // private
2515     activeAction : null,
2516  
2517     /**
2518      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
2519      * element by passing it or its id or mask the form itself by passing in true.
2520      * @type Mixed
2521      */
2522     waitMsgTarget : false,
2523     
2524      
2525     
2526     /**
2527      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
2528      * element by passing it or its id or mask the form itself by passing in true.
2529      * @type Mixed
2530      */
2531     
2532     getAutoCreate : function(){
2533         
2534         var cfg = {
2535             tag: 'form',
2536             method : this.method || 'POST',
2537             id : this.id || Roo.id(),
2538             cls : ''
2539         }
2540         if (this.parent().xtype.match(/^Nav/)) {
2541             cfg.cls = 'navbar-form navbar-' + this.align;
2542             
2543         }
2544         
2545         if (this.labelAlign == 'left' ) {
2546             cfg.cls += ' form-horizontal';
2547         }
2548         
2549         
2550         return cfg;
2551     },
2552     initEvents : function()
2553     {
2554         this.el.on('submit', this.onSubmit, this);
2555         
2556         
2557     },
2558     // private
2559     onSubmit : function(e){
2560         e.stopEvent();
2561     },
2562     
2563      /**
2564      * Returns true if client-side validation on the form is successful.
2565      * @return Boolean
2566      */
2567     isValid : function(){
2568         var items = this.getItems();
2569         var valid = true;
2570         items.each(function(f){
2571            if(!f.validate()){
2572                valid = false;
2573                
2574            }
2575         });
2576         return valid;
2577     },
2578     /**
2579      * Returns true if any fields in this form have changed since their original load.
2580      * @return Boolean
2581      */
2582     isDirty : function(){
2583         var dirty = false;
2584         var items = this.getItems();
2585         items.each(function(f){
2586            if(f.isDirty()){
2587                dirty = true;
2588                return false;
2589            }
2590            return true;
2591         });
2592         return dirty;
2593     },
2594      /**
2595      * Performs a predefined action (submit or load) or custom actions you define on this form.
2596      * @param {String} actionName The name of the action type
2597      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
2598      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
2599      * accept other config options):
2600      * <pre>
2601 Property          Type             Description
2602 ----------------  ---------------  ----------------------------------------------------------------------------------
2603 url               String           The url for the action (defaults to the form's url)
2604 method            String           The form method to use (defaults to the form's method, or POST if not defined)
2605 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
2606 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
2607                                    validate the form on the client (defaults to false)
2608      * </pre>
2609      * @return {BasicForm} this
2610      */
2611     doAction : function(action, options){
2612         if(typeof action == 'string'){
2613             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
2614         }
2615         if(this.fireEvent('beforeaction', this, action) !== false){
2616             this.beforeAction(action);
2617             action.run.defer(100, action);
2618         }
2619         return this;
2620     },
2621     
2622     // private
2623     beforeAction : function(action){
2624         var o = action.options;
2625         
2626         // not really supported yet.. ??
2627         
2628         //if(this.waitMsgTarget === true){
2629             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
2630         //}else if(this.waitMsgTarget){
2631         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
2632         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
2633         //}else {
2634         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
2635        // }
2636          
2637     },
2638
2639     // private
2640     afterAction : function(action, success){
2641         this.activeAction = null;
2642         var o = action.options;
2643         
2644         //if(this.waitMsgTarget === true){
2645             this.el.unmask();
2646         //}else if(this.waitMsgTarget){
2647         //    this.waitMsgTarget.unmask();
2648         //}else{
2649         //    Roo.MessageBox.updateProgress(1);
2650         //    Roo.MessageBox.hide();
2651        // }
2652         // 
2653         if(success){
2654             if(o.reset){
2655                 this.reset();
2656             }
2657             Roo.callback(o.success, o.scope, [this, action]);
2658             this.fireEvent('actioncomplete', this, action);
2659             
2660         }else{
2661             
2662             // failure condition..
2663             // we have a scenario where updates need confirming.
2664             // eg. if a locking scenario exists..
2665             // we look for { errors : { needs_confirm : true }} in the response.
2666             if (
2667                 (typeof(action.result) != 'undefined')  &&
2668                 (typeof(action.result.errors) != 'undefined')  &&
2669                 (typeof(action.result.errors.needs_confirm) != 'undefined')
2670            ){
2671                 var _t = this;
2672                 Roo.log("not supported yet");
2673                  /*
2674                 
2675                 Roo.MessageBox.confirm(
2676                     "Change requires confirmation",
2677                     action.result.errorMsg,
2678                     function(r) {
2679                         if (r != 'yes') {
2680                             return;
2681                         }
2682                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
2683                     }
2684                     
2685                 );
2686                 */
2687                 
2688                 
2689                 return;
2690             }
2691             
2692             Roo.callback(o.failure, o.scope, [this, action]);
2693             // show an error message if no failed handler is set..
2694             if (!this.hasListener('actionfailed')) {
2695                 Roo.log("need to add dialog support");
2696                 /*
2697                 Roo.MessageBox.alert("Error",
2698                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
2699                         action.result.errorMsg :
2700                         "Saving Failed, please check your entries or try again"
2701                 );
2702                 */
2703             }
2704             
2705             this.fireEvent('actionfailed', this, action);
2706         }
2707         
2708     },
2709     /**
2710      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
2711      * @param {String} id The value to search for
2712      * @return Field
2713      */
2714     findField : function(id){
2715         var items = this.getItems();
2716         var field = items.get(id);
2717         if(!field){
2718              items.each(function(f){
2719                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
2720                     field = f;
2721                     return false;
2722                 }
2723                 return true;
2724             });
2725         }
2726         return field || null;
2727     },
2728      /**
2729      * Mark fields in this form invalid in bulk.
2730      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
2731      * @return {BasicForm} this
2732      */
2733     markInvalid : function(errors){
2734         if(errors instanceof Array){
2735             for(var i = 0, len = errors.length; i < len; i++){
2736                 var fieldError = errors[i];
2737                 var f = this.findField(fieldError.id);
2738                 if(f){
2739                     f.markInvalid(fieldError.msg);
2740                 }
2741             }
2742         }else{
2743             var field, id;
2744             for(id in errors){
2745                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
2746                     field.markInvalid(errors[id]);
2747                 }
2748             }
2749         }
2750         //Roo.each(this.childForms || [], function (f) {
2751         //    f.markInvalid(errors);
2752         //});
2753         
2754         return this;
2755     },
2756
2757     /**
2758      * Set values for fields in this form in bulk.
2759      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
2760      * @return {BasicForm} this
2761      */
2762     setValues : function(values){
2763         if(values instanceof Array){ // array of objects
2764             for(var i = 0, len = values.length; i < len; i++){
2765                 var v = values[i];
2766                 var f = this.findField(v.id);
2767                 if(f){
2768                     f.setValue(v.value);
2769                     if(this.trackResetOnLoad){
2770                         f.originalValue = f.getValue();
2771                     }
2772                 }
2773             }
2774         }else{ // object hash
2775             var field, id;
2776             for(id in values){
2777                 if(typeof values[id] != 'function' && (field = this.findField(id))){
2778                     
2779                     if (field.setFromData && 
2780                         field.valueField && 
2781                         field.displayField &&
2782                         // combos' with local stores can 
2783                         // be queried via setValue()
2784                         // to set their value..
2785                         (field.store && !field.store.isLocal)
2786                         ) {
2787                         // it's a combo
2788                         var sd = { };
2789                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
2790                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
2791                         field.setFromData(sd);
2792                         
2793                     } else {
2794                         field.setValue(values[id]);
2795                     }
2796                     
2797                     
2798                     if(this.trackResetOnLoad){
2799                         field.originalValue = field.getValue();
2800                     }
2801                 }
2802             }
2803         }
2804          
2805         //Roo.each(this.childForms || [], function (f) {
2806         //    f.setValues(values);
2807         //});
2808                 
2809         return this;
2810     },
2811
2812     /**
2813      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
2814      * they are returned as an array.
2815      * @param {Boolean} asString
2816      * @return {Object}
2817      */
2818     getValues : function(asString){
2819         //if (this.childForms) {
2820             // copy values from the child forms
2821         //    Roo.each(this.childForms, function (f) {
2822         //        this.setValues(f.getValues());
2823         //    }, this);
2824         //}
2825         
2826         
2827         
2828         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
2829         if(asString === true){
2830             return fs;
2831         }
2832         return Roo.urlDecode(fs);
2833     },
2834     
2835     /**
2836      * Returns the fields in this form as an object with key/value pairs. 
2837      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
2838      * @return {Object}
2839      */
2840     getFieldValues : function(with_hidden)
2841     {
2842         var items = this.getItems();
2843         var ret = {};
2844         items.each(function(f){
2845             if (!f.getName()) {
2846                 return;
2847             }
2848             var v = f.getValue();
2849             if (f.inputType =='radio') {
2850                 if (typeof(ret[f.getName()]) == 'undefined') {
2851                     ret[f.getName()] = ''; // empty..
2852                 }
2853                 
2854                 if (!f.el.dom.checked) {
2855                     return;
2856                     
2857                 }
2858                 v = f.el.dom.value;
2859                 
2860             }
2861             
2862             // not sure if this supported any more..
2863             if ((typeof(v) == 'object') && f.getRawValue) {
2864                 v = f.getRawValue() ; // dates..
2865             }
2866             // combo boxes where name != hiddenName...
2867             if (f.name != f.getName()) {
2868                 ret[f.name] = f.getRawValue();
2869             }
2870             ret[f.getName()] = v;
2871         });
2872         
2873         return ret;
2874     },
2875
2876     /**
2877      * Clears all invalid messages in this form.
2878      * @return {BasicForm} this
2879      */
2880     clearInvalid : function(){
2881         var items = this.getItems();
2882         
2883         items.each(function(f){
2884            f.clearInvalid();
2885         });
2886         
2887         
2888         
2889         return this;
2890     },
2891
2892     /**
2893      * Resets this form.
2894      * @return {BasicForm} this
2895      */
2896     reset : function(){
2897         var items = this.getItems();
2898         items.each(function(f){
2899             f.reset();
2900         });
2901         
2902         Roo.each(this.childForms || [], function (f) {
2903             f.reset();
2904         });
2905        
2906         
2907         return this;
2908     },
2909     getItems : function()
2910     {
2911         var r=new Roo.util.MixedCollection(false, function(o){
2912             return o.id || (o.id = Roo.id());
2913         });
2914         var iter = function(el) {
2915             if (el.inputEl) {
2916                 r.add(el);
2917             }
2918             if (!el.items) {
2919                 return;
2920             }
2921             Roo.each(el.items,function(e) {
2922                 iter(e);
2923             });
2924             
2925             
2926         };
2927         iter(this);
2928         return r;
2929         
2930         
2931         
2932         
2933     }
2934     
2935 });
2936
2937  
2938 /*
2939  * Based on:
2940  * Ext JS Library 1.1.1
2941  * Copyright(c) 2006-2007, Ext JS, LLC.
2942  *
2943  * Originally Released Under LGPL - original licence link has changed is not relivant.
2944  *
2945  * Fork - LGPL
2946  * <script type="text/javascript">
2947  */
2948 /**
2949  * @class Roo.form.VTypes
2950  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
2951  * @singleton
2952  */
2953 Roo.form.VTypes = function(){
2954     // closure these in so they are only created once.
2955     var alpha = /^[a-zA-Z_]+$/;
2956     var alphanum = /^[a-zA-Z0-9_]+$/;
2957     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
2958     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
2959
2960     // All these messages and functions are configurable
2961     return {
2962         /**
2963          * The function used to validate email addresses
2964          * @param {String} value The email address
2965          */
2966         'email' : function(v){
2967             return email.test(v);
2968         },
2969         /**
2970          * The error text to display when the email validation function returns false
2971          * @type String
2972          */
2973         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
2974         /**
2975          * The keystroke filter mask to be applied on email input
2976          * @type RegExp
2977          */
2978         'emailMask' : /[a-z0-9_\.\-@]/i,
2979
2980         /**
2981          * The function used to validate URLs
2982          * @param {String} value The URL
2983          */
2984         'url' : function(v){
2985             return url.test(v);
2986         },
2987         /**
2988          * The error text to display when the url validation function returns false
2989          * @type String
2990          */
2991         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
2992         
2993         /**
2994          * The function used to validate alpha values
2995          * @param {String} value The value
2996          */
2997         'alpha' : function(v){
2998             return alpha.test(v);
2999         },
3000         /**
3001          * The error text to display when the alpha validation function returns false
3002          * @type String
3003          */
3004         'alphaText' : 'This field should only contain letters and _',
3005         /**
3006          * The keystroke filter mask to be applied on alpha input
3007          * @type RegExp
3008          */
3009         'alphaMask' : /[a-z_]/i,
3010
3011         /**
3012          * The function used to validate alphanumeric values
3013          * @param {String} value The value
3014          */
3015         'alphanum' : function(v){
3016             return alphanum.test(v);
3017         },
3018         /**
3019          * The error text to display when the alphanumeric validation function returns false
3020          * @type String
3021          */
3022         'alphanumText' : 'This field should only contain letters, numbers and _',
3023         /**
3024          * The keystroke filter mask to be applied on alphanumeric input
3025          * @type RegExp
3026          */
3027         'alphanumMask' : /[a-z0-9_]/i
3028     };
3029 }();/*
3030  * - LGPL
3031  *
3032  * Input
3033  * 
3034  */
3035
3036 /**
3037  * @class Roo.bootstrap.Input
3038  * @extends Roo.bootstrap.Component
3039  * Bootstrap Input class
3040  * @cfg {Boolean} disabled is it disabled
3041  * @cfg {String} fieldLabel - the label associated
3042  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
3043  * @cfg {String} name name of the input
3044  * @cfg {string} fieldLabel - the label associated
3045  * @cfg {string}  inputType - input / file submit ...
3046  * @cfg {string} placeholder - placeholder to put in text.
3047  * @cfg {string}  before - input group add on before
3048  * @cfg {string} after - input group add on after
3049  * @cfg {string} size - (lg|sm) or leave empty..
3050  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
3051  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
3052  * @cfg {Number} md colspan out of 12 for computer-sized screens
3053  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
3054  * 
3055  * 
3056  * @constructor
3057  * Create a new Input
3058  * @param {Object} config The config object
3059  */
3060
3061 Roo.bootstrap.Input = function(config){
3062     Roo.bootstrap.Input.superclass.constructor.call(this, config);
3063    
3064         this.addEvents({
3065             /**
3066              * @event focus
3067              * Fires when this field receives input focus.
3068              * @param {Roo.form.Field} this
3069              */
3070             focus : true,
3071             /**
3072              * @event blur
3073              * Fires when this field loses input focus.
3074              * @param {Roo.form.Field} this
3075              */
3076             blur : true,
3077             /**
3078              * @event specialkey
3079              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
3080              * {@link Roo.EventObject#getKey} to determine which key was pressed.
3081              * @param {Roo.form.Field} this
3082              * @param {Roo.EventObject} e The event object
3083              */
3084             specialkey : true,
3085             /**
3086              * @event change
3087              * Fires just before the field blurs if the field value has changed.
3088              * @param {Roo.form.Field} this
3089              * @param {Mixed} newValue The new value
3090              * @param {Mixed} oldValue The original value
3091              */
3092             change : true,
3093             /**
3094              * @event invalid
3095              * Fires after the field has been marked as invalid.
3096              * @param {Roo.form.Field} this
3097              * @param {String} msg The validation message
3098              */
3099             invalid : true,
3100             /**
3101              * @event valid
3102              * Fires after the field has been validated with no errors.
3103              * @param {Roo.form.Field} this
3104              */
3105             valid : true,
3106              /**
3107              * @event keyup
3108              * Fires after the key up
3109              * @param {Roo.form.Field} this
3110              * @param {Roo.EventObject}  e The event Object
3111              */
3112             keyup : true
3113         });
3114 };
3115
3116 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
3117      /**
3118      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
3119       automatic validation (defaults to "keyup").
3120      */
3121     validationEvent : "keyup",
3122      /**
3123      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
3124      */
3125     validateOnBlur : true,
3126     /**
3127      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
3128      */
3129     validationDelay : 250,
3130      /**
3131      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
3132      */
3133     focusClass : "x-form-focus",  // not needed???
3134     
3135        
3136     /**
3137      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
3138      */
3139     invalidClass : "has-error",
3140     
3141     /**
3142      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
3143      */
3144     selectOnFocus : false,
3145     
3146      /**
3147      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
3148      */
3149     maskRe : null,
3150        /**
3151      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
3152      */
3153     vtype : null,
3154     
3155       /**
3156      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
3157      */
3158     disableKeyFilter : false,
3159     
3160        /**
3161      * @cfg {Boolean} disabled True to disable the field (defaults to false).
3162      */
3163     disabled : false,
3164      /**
3165      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
3166      */
3167     allowBlank : true,
3168     /**
3169      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
3170      */
3171     blankText : "This field is required",
3172     
3173      /**
3174      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
3175      */
3176     minLength : 0,
3177     /**
3178      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
3179      */
3180     maxLength : Number.MAX_VALUE,
3181     /**
3182      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
3183      */
3184     minLengthText : "The minimum length for this field is {0}",
3185     /**
3186      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
3187      */
3188     maxLengthText : "The maximum length for this field is {0}",
3189   
3190     
3191     /**
3192      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
3193      * If available, this function will be called only after the basic validators all return true, and will be passed the
3194      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
3195      */
3196     validator : null,
3197     /**
3198      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
3199      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
3200      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
3201      */
3202     regex : null,
3203     /**
3204      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
3205      */
3206     regexText : "",
3207     
3208     
3209     
3210     fieldLabel : '',
3211     inputType : 'text',
3212     
3213     name : false,
3214     placeholder: false,
3215     before : false,
3216     after : false,
3217     size : false,
3218     // private
3219     hasFocus : false,
3220     preventMark: false,
3221     isFormField : true,
3222     
3223     getAutoCreate : function(){
3224         
3225         var parent = this.parent();
3226         
3227         var align = parent.labelAlign;
3228         
3229         var id = Roo.id();
3230         
3231         var cfg = {
3232             cls: 'form-group' //input-group
3233         };
3234         
3235         var input =  {
3236             tag: 'input',
3237             id : id,
3238             type : this.inputType,
3239             cls : 'form-control',
3240             placeholder : this.placeholder || '' 
3241             
3242         };
3243         if (this.name) {
3244             input.name = this.name;
3245         }
3246         if (this.size) {
3247             input.cls += ' input-' + this.size;
3248         }
3249         var settings=this;
3250         ['xs','sm','md','lg'].map(function(size){
3251             if (settings[size]) {
3252                 cfg.cls += ' col-' + size + '-' + settings[size];
3253             }
3254         });
3255         
3256         var inputblock = input;
3257         
3258         if (this.before || this.after) {
3259             
3260             inputblock = {
3261                 cls : 'input-group',
3262                 cn :  [] 
3263             };
3264             if (this.before) {
3265                 inputblock.cn.push({
3266                     tag :'span',
3267                     cls : 'input-group-addon',
3268                     html : this.before
3269                 });
3270             }
3271             inputblock.cn.push(input);
3272             if (this.after) {
3273                 inputblock.cn.push({
3274                     tag :'span',
3275                     cls : 'input-group-addon',
3276                     html : this.after
3277                 });
3278             }
3279             
3280         }
3281         
3282         Roo.log(align);
3283         Roo.log(this.fieldLabel.length);
3284         
3285         if (align ==='left' && this.fieldLabel.length) {
3286                 Roo.log("left and has label");
3287                 cfg.cn = [
3288                     
3289                     {
3290                         tag: 'label',
3291                         'for' :  id,
3292                         cls : 'col-sm-2 control-label',
3293                         html : this.fieldLabel
3294                         
3295                     },
3296                     {
3297                         cls : "col-sm-10", 
3298                         cn: [
3299                             inputblock
3300                         ]
3301                     }
3302                     
3303                 ];
3304         } else if ( this.fieldLabel.length) {
3305                 Roo.log(" label");
3306                  cfg.cn = [
3307                    
3308                     {
3309                         tag: 'label',
3310                         //cls : 'input-group-addon',
3311                         html : this.fieldLabel
3312                         
3313                     },
3314                     
3315                     inputblock
3316                     
3317                 ];
3318
3319         } else {
3320             
3321                    Roo.log(" no label && no align");
3322                 cfg.cn = [
3323                     
3324                         inputblock
3325                     
3326                 ];
3327                 
3328                 
3329         }
3330          
3331         
3332         
3333         
3334         if (this.disabled) {
3335             input.disabled=true;
3336         }
3337         return cfg;
3338         
3339     },
3340     /**
3341      * return the real input element.
3342      */
3343     inputEl: function ()
3344     {
3345         return this.el.select('input.form-control',true).first();
3346     },
3347     setDisabled : function(v)
3348     {
3349         var i  = this.inputEl().dom;
3350         if (v) {
3351             i.removeAttribute('disabled');
3352             return;
3353             
3354         }
3355         i.setAttribute('disabled','true');
3356     },
3357     initEvents : function()
3358     {
3359         
3360         this.inputEl().on("keydown" , this.fireKey,  this);
3361         this.inputEl().on("focus", this.onFocus,  this);
3362         this.inputEl().on("blur", this.onBlur,  this);
3363         this.inputEl().relayEvent('keyup', this);
3364
3365         // reference to original value for reset
3366         this.originalValue = this.getValue();
3367         //Roo.form.TextField.superclass.initEvents.call(this);
3368         if(this.validationEvent == 'keyup'){
3369             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
3370             this.inputEl().on('keyup', this.filterValidation, this);
3371         }
3372         else if(this.validationEvent !== false){
3373             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
3374         }
3375         
3376         if(this.selectOnFocus){
3377             this.on("focus", this.preFocus, this);
3378             
3379         }
3380         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
3381             this.inputEl().on("keypress", this.filterKeys, this);
3382         }
3383        /* if(this.grow){
3384             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
3385             this.el.on("click", this.autoSize,  this);
3386         }
3387         */
3388         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
3389             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
3390         }
3391         
3392     },
3393     filterValidation : function(e){
3394         if(!e.isNavKeyPress()){
3395             this.validationTask.delay(this.validationDelay);
3396         }
3397     },
3398      /**
3399      * Validates the field value
3400      * @return {Boolean} True if the value is valid, else false
3401      */
3402     validate : function(){
3403         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
3404         if(this.disabled || this.validateValue(this.getRawValue())){
3405             this.clearInvalid();
3406             return true;
3407         }
3408         return false;
3409     },
3410     
3411     
3412     /**
3413      * Validates a value according to the field's validation rules and marks the field as invalid
3414      * if the validation fails
3415      * @param {Mixed} value The value to validate
3416      * @return {Boolean} True if the value is valid, else false
3417      */
3418     validateValue : function(value){
3419         if(value.length < 1)  { // if it's blank
3420              if(this.allowBlank){
3421                 this.clearInvalid();
3422                 return true;
3423              }else{
3424                 this.markInvalid(this.blankText);
3425                 return false;
3426              }
3427         }
3428         if(value.length < this.minLength){
3429             this.markInvalid(String.format(this.minLengthText, this.minLength));
3430             return false;
3431         }
3432         if(value.length > this.maxLength){
3433             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
3434             return false;
3435         }
3436         if(this.vtype){
3437             var vt = Roo.form.VTypes;
3438             if(!vt[this.vtype](value, this)){
3439                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
3440                 return false;
3441             }
3442         }
3443         if(typeof this.validator == "function"){
3444             var msg = this.validator(value);
3445             if(msg !== true){
3446                 this.markInvalid(msg);
3447                 return false;
3448             }
3449         }
3450         if(this.regex && !this.regex.test(value)){
3451             this.markInvalid(this.regexText);
3452             return false;
3453         }
3454         return true;
3455     },
3456
3457     
3458     
3459      // private
3460     fireKey : function(e){
3461         //Roo.log('field ' + e.getKey());
3462         if(e.isNavKeyPress()){
3463             this.fireEvent("specialkey", this, e);
3464         }
3465     },
3466     focus : function (selectText){
3467         if(this.rendered){
3468             this.inputEl().focus();
3469             if(selectText === true){
3470                 this.inputEl().dom.select();
3471             }
3472         }
3473         return this;
3474     } ,
3475     
3476     onFocus : function(){
3477         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
3478            // this.el.addClass(this.focusClass);
3479         }
3480         if(!this.hasFocus){
3481             this.hasFocus = true;
3482             this.startValue = this.getValue();
3483             this.fireEvent("focus", this);
3484         }
3485     },
3486     
3487     beforeBlur : Roo.emptyFn,
3488
3489     
3490     // private
3491     onBlur : function(){
3492         this.beforeBlur();
3493         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
3494             //this.el.removeClass(this.focusClass);
3495         }
3496         this.hasFocus = false;
3497         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
3498             this.validate();
3499         }
3500         var v = this.getValue();
3501         if(String(v) !== String(this.startValue)){
3502             this.fireEvent('change', this, v, this.startValue);
3503         }
3504         this.fireEvent("blur", this);
3505     },
3506     
3507     /**
3508      * Resets the current field value to the originally loaded value and clears any validation messages
3509      */
3510     reset : function(){
3511         this.setValue(this.originalValue);
3512         this.clearInvalid();
3513     },
3514      /**
3515      * Returns the name of the field
3516      * @return {Mixed} name The name field
3517      */
3518     getName: function(){
3519         return this.name;
3520     },
3521      /**
3522      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
3523      * @return {Mixed} value The field value
3524      */
3525     getValue : function(){
3526         var v = this.inputEl().getValue();
3527         return v;
3528     },
3529     /**
3530      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
3531      * @return {Mixed} value The field value
3532      */
3533     getRawValue : function(){
3534         var v = this.inputEl().getValue();
3535         
3536         return v;
3537     },
3538     /**
3539      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
3540      * @param {Mixed} value The value to set
3541      */
3542     setValue : function(v){
3543         this.value = v;
3544         if(this.rendered){
3545             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
3546             this.validate();
3547         }
3548     },
3549     
3550     /*
3551     processValue : function(value){
3552         if(this.stripCharsRe){
3553             var newValue = value.replace(this.stripCharsRe, '');
3554             if(newValue !== value){
3555                 this.setRawValue(newValue);
3556                 return newValue;
3557             }
3558         }
3559         return value;
3560     },
3561   */
3562     preFocus : function(){
3563         
3564         if(this.selectOnFocus){
3565             this.inputEl().dom.select();
3566         }
3567     },
3568     filterKeys : function(e){
3569         var k = e.getKey();
3570         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
3571             return;
3572         }
3573         var c = e.getCharCode(), cc = String.fromCharCode(c);
3574         if(Roo.isIE && (e.isSpecialKey() || !cc)){
3575             return;
3576         }
3577         if(!this.maskRe.test(cc)){
3578             e.stopEvent();
3579         }
3580     },
3581      /**
3582      * Clear any invalid styles/messages for this field
3583      */
3584     clearInvalid : function(){
3585         
3586         if(!this.el || this.preventMark){ // not rendered
3587             return;
3588         }
3589         this.el.removeClass(this.invalidClass);
3590         /*
3591         switch(this.msgTarget){
3592             case 'qtip':
3593                 this.el.dom.qtip = '';
3594                 break;
3595             case 'title':
3596                 this.el.dom.title = '';
3597                 break;
3598             case 'under':
3599                 if(this.errorEl){
3600                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
3601                 }
3602                 break;
3603             case 'side':
3604                 if(this.errorIcon){
3605                     this.errorIcon.dom.qtip = '';
3606                     this.errorIcon.hide();
3607                     this.un('resize', this.alignErrorIcon, this);
3608                 }
3609                 break;
3610             default:
3611                 var t = Roo.getDom(this.msgTarget);
3612                 t.innerHTML = '';
3613                 t.style.display = 'none';
3614                 break;
3615         }
3616         */
3617         this.fireEvent('valid', this);
3618     },
3619      /**
3620      * Mark this field as invalid
3621      * @param {String} msg The validation message
3622      */
3623     markInvalid : function(msg){
3624         if(!this.el  || this.preventMark){ // not rendered
3625             return;
3626         }
3627         this.el.addClass(this.invalidClass);
3628         /*
3629         msg = msg || this.invalidText;
3630         switch(this.msgTarget){
3631             case 'qtip':
3632                 this.el.dom.qtip = msg;
3633                 this.el.dom.qclass = 'x-form-invalid-tip';
3634                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
3635                     Roo.QuickTips.enable();
3636                 }
3637                 break;
3638             case 'title':
3639                 this.el.dom.title = msg;
3640                 break;
3641             case 'under':
3642                 if(!this.errorEl){
3643                     var elp = this.el.findParent('.x-form-element', 5, true);
3644                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
3645                     this.errorEl.setWidth(elp.getWidth(true)-20);
3646                 }
3647                 this.errorEl.update(msg);
3648                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
3649                 break;
3650             case 'side':
3651                 if(!this.errorIcon){
3652                     var elp = this.el.findParent('.x-form-element', 5, true);
3653                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
3654                 }
3655                 this.alignErrorIcon();
3656                 this.errorIcon.dom.qtip = msg;
3657                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
3658                 this.errorIcon.show();
3659                 this.on('resize', this.alignErrorIcon, this);
3660                 break;
3661             default:
3662                 var t = Roo.getDom(this.msgTarget);
3663                 t.innerHTML = msg;
3664                 t.style.display = this.msgDisplay;
3665                 break;
3666         }
3667         */
3668         this.fireEvent('invalid', this, msg);
3669     },
3670     // private
3671     SafariOnKeyDown : function(event)
3672     {
3673         // this is a workaround for a password hang bug on chrome/ webkit.
3674         
3675         var isSelectAll = false;
3676         
3677         if(this.inputEl().dom.selectionEnd > 0){
3678             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
3679         }
3680         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
3681             event.preventDefault();
3682             this.setValue('');
3683             return;
3684         }
3685         
3686         if(isSelectAll){ // backspace and delete key
3687             
3688             event.preventDefault();
3689             // this is very hacky as keydown always get's upper case.
3690             //
3691             var cc = String.fromCharCode(event.getCharCode());
3692             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
3693             
3694         }
3695         
3696         
3697     }
3698 });
3699
3700  
3701 /*
3702  * - LGPL
3703  *
3704  * trigger field - base class for combo..
3705  * 
3706  */
3707  
3708 /**
3709  * @class Roo.bootstrap.TriggerField
3710  * @extends Roo.bootstrap.Input
3711  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
3712  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
3713  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
3714  * for which you can provide a custom implementation.  For example:
3715  * <pre><code>
3716 var trigger = new Roo.bootstrap.TriggerField();
3717 trigger.onTriggerClick = myTriggerFn;
3718 trigger.applyTo('my-field');
3719 </code></pre>
3720  *
3721  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
3722  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
3723  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
3724  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
3725  * @constructor
3726  * Create a new TriggerField.
3727  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
3728  * to the base TextField)
3729  */
3730 Roo.bootstrap.TriggerField = function(config){
3731     this.mimicing = false;
3732     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
3733 };
3734
3735 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
3736     /**
3737      * @cfg {String} triggerClass A CSS class to apply to the trigger
3738      */
3739      /**
3740      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
3741      */
3742     hideTrigger:false,
3743
3744     /** @cfg {Boolean} grow @hide */
3745     /** @cfg {Number} growMin @hide */
3746     /** @cfg {Number} growMax @hide */
3747
3748     /**
3749      * @hide 
3750      * @method
3751      */
3752     autoSize: Roo.emptyFn,
3753     // private
3754     monitorTab : true,
3755     // private
3756     deferHeight : true,
3757
3758     
3759     actionMode : 'wrap',
3760     
3761     
3762     
3763     getAutoCreate : function(){
3764        
3765         var parent = this.parent();
3766         
3767         var align = parent.labelAlign;
3768         
3769         var id = Roo.id();
3770         
3771         var cfg = {
3772             cls: 'form-group' //input-group
3773         };
3774         
3775         
3776         var input =  {
3777             tag: 'input',
3778             id : id,
3779             type : this.inputType,
3780             cls : 'form-control',
3781             autocomplete: 'off',
3782             placeholder : this.placeholder || '' 
3783             
3784         };
3785         if (this.name) {
3786             input.name = this.name;
3787         }
3788         if (this.size) {
3789             input.cls += ' input-' + this.size;
3790         }
3791         var inputblock = {
3792             cls: 'combobox-container input-group',
3793             cn: [
3794                 {
3795                     tag: 'input',
3796                     type : 'hidden',
3797                     cls: 'form-hidden-field'
3798                 },
3799                 input,
3800                 {
3801                     tag: 'ul',
3802                     cls : 'typeahead typeahead-long dropdown-menu',
3803                     style : 'display:none'
3804                 },
3805                 {
3806                     tag :'span',
3807                     cls : 'input-group-addon btn dropdown-toggle',
3808                     cn : [
3809                         {
3810                             tag: 'span',
3811                             cls: 'caret'
3812                         },
3813                         {
3814                             tag: 'span',
3815                             cls: 'combobox-clear',
3816                             cn  : [
3817                                 {
3818                                     tag : 'i',
3819                                     cls: 'icon-remove'
3820                                 }
3821                             ]
3822                         }
3823                     ]
3824                         
3825                 }
3826             ]
3827         };
3828         
3829         
3830         
3831         
3832         if (align ==='left' && this.fieldLabel.length) {
3833                 
3834             
3835             
3836                 Roo.log("left and has label");
3837                 cfg.cn = [
3838                     
3839                     {
3840                         tag: 'label',
3841                         'for' :  id,
3842                         cls : 'col-sm-2 control-label',
3843                         html : this.fieldLabel
3844                         
3845                     },
3846                     {
3847                         cls : "col-sm-10", 
3848                         cn: [
3849                             inputblock
3850                         ]
3851                     }
3852                     
3853                 ];
3854         } else if ( this.fieldLabel.length) {
3855                 Roo.log(" label");
3856                  cfg.cn = [
3857                    
3858                     {
3859                         tag: 'label',
3860                         //cls : 'input-group-addon',
3861                         html : this.fieldLabel
3862                         
3863                     },
3864                     
3865                     inputblock
3866                     
3867                 ];
3868
3869         } else {
3870             
3871                 Roo.log(" no label && no align");
3872                 cfg = inputblock
3873                      
3874                 
3875         }
3876          
3877         var settings=this;
3878         ['xs','sm','md','lg'].map(function(size){
3879             if (settings[size]) {
3880                 cfg.cls += ' col-' + size + '-' + settings[size];
3881             }
3882         });
3883         
3884         
3885         
3886         if (this.disabled) {
3887             input.disabled=true;
3888         }
3889         return cfg;
3890         
3891     },
3892     
3893     
3894     
3895     // private
3896     onResize : function(w, h){
3897         Roo.boostrap.TriggerField.superclass.onResize.apply(this, arguments);
3898         if(typeof w == 'number'){
3899             var x = w - this.trigger.getWidth();
3900             this.inputEl().setWidth(this.adjustWidth('input', x));
3901             this.trigger.setStyle('left', x+'px');
3902         }
3903     },
3904
3905     // private
3906     adjustSize : Roo.BoxComponent.prototype.adjustSize,
3907
3908     // private
3909     getResizeEl : function(){
3910         return this.inputEl();
3911     },
3912
3913     // private
3914     getPositionEl : function(){
3915         return this.inputEl();
3916     },
3917
3918     // private
3919     alignErrorIcon : function(){
3920         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
3921     },
3922
3923     // private
3924     initEvents : function(){
3925         
3926         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
3927         
3928         this.trigger = this.el.select('span.dropdown-toggle',true).first();
3929         if(this.hideTrigger){
3930             this.trigger.setDisplayed(false);
3931         }
3932         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
3933         //this.trigger.addClassOnOver('x-form-trigger-over');
3934         //this.trigger.addClassOnClick('x-form-trigger-click');
3935         
3936         //if(!this.width){
3937         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
3938         //}
3939     },
3940
3941     // private
3942     initTrigger : function(){
3943        
3944     },
3945
3946     // private
3947     onDestroy : function(){
3948         if(this.trigger){
3949             this.trigger.removeAllListeners();
3950           //  this.trigger.remove();
3951         }
3952         //if(this.wrap){
3953         //    this.wrap.remove();
3954         //}
3955         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
3956     },
3957
3958     // private
3959     onFocus : function(){
3960         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
3961         /*
3962         if(!this.mimicing){
3963             this.wrap.addClass('x-trigger-wrap-focus');
3964             this.mimicing = true;
3965             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
3966             if(this.monitorTab){
3967                 this.el.on("keydown", this.checkTab, this);
3968             }
3969         }
3970         */
3971     },
3972
3973     // private
3974     checkTab : function(e){
3975         if(e.getKey() == e.TAB){
3976             this.triggerBlur();
3977         }
3978     },
3979
3980     // private
3981     onBlur : function(){
3982         // do nothing
3983     },
3984
3985     // private
3986     mimicBlur : function(e, t){
3987         /*
3988         if(!this.wrap.contains(t) && this.validateBlur()){
3989             this.triggerBlur();
3990         }
3991         */
3992     },
3993
3994     // private
3995     triggerBlur : function(){
3996         this.mimicing = false;
3997         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
3998         if(this.monitorTab){
3999             this.el.un("keydown", this.checkTab, this);
4000         }
4001         //this.wrap.removeClass('x-trigger-wrap-focus');
4002         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
4003     },
4004
4005     // private
4006     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
4007     validateBlur : function(e, t){
4008         return true;
4009     },
4010
4011     // private
4012     onDisable : function(){
4013         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
4014         //if(this.wrap){
4015         //    this.wrap.addClass('x-item-disabled');
4016         //}
4017     },
4018
4019     // private
4020     onEnable : function(){
4021         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
4022         //if(this.wrap){
4023         //    this.el.removeClass('x-item-disabled');
4024         //}
4025     },
4026
4027     // private
4028     onShow : function(){
4029         var ae = this.getActionEl();
4030         
4031         if(ae){
4032             ae.dom.style.display = '';
4033             ae.dom.style.visibility = 'visible';
4034         }
4035     },
4036
4037     // private
4038     
4039     onHide : function(){
4040         var ae = this.getActionEl();
4041         ae.dom.style.display = 'none';
4042     },
4043
4044     /**
4045      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
4046      * by an implementing function.
4047      * @method
4048      * @param {EventObject} e
4049      */
4050     onTriggerClick : Roo.emptyFn
4051 });
4052  /*
4053  * Based on:
4054  * Ext JS Library 1.1.1
4055  * Copyright(c) 2006-2007, Ext JS, LLC.
4056  *
4057  * Originally Released Under LGPL - original licence link has changed is not relivant.
4058  *
4059  * Fork - LGPL
4060  * <script type="text/javascript">
4061  */
4062
4063 /**
4064  * @class Roo.View
4065  * @extends Roo.util.Observable
4066  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
4067  * This class also supports single and multi selection modes. <br>
4068  * Create a data model bound view:
4069  <pre><code>
4070  var store = new Roo.data.Store(...);
4071
4072  var view = new Roo.View({
4073     el : "my-element",
4074     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
4075  
4076     singleSelect: true,
4077     selectedClass: "ydataview-selected",
4078     store: store
4079  });
4080
4081  // listen for node click?
4082  view.on("click", function(vw, index, node, e){
4083  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4084  });
4085
4086  // load XML data
4087  dataModel.load("foobar.xml");
4088  </code></pre>
4089  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4090  * <br><br>
4091  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4092  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4093  * 
4094  * Note: old style constructor is still suported (container, template, config)
4095  * 
4096  * @constructor
4097  * Create a new View
4098  * @param {Object} config The config object
4099  * 
4100  */
4101 Roo.View = function(config, depreciated_tpl, depreciated_config){
4102     
4103     if (typeof(depreciated_tpl) == 'undefined') {
4104         // new way.. - universal constructor.
4105         Roo.apply(this, config);
4106         this.el  = Roo.get(this.el);
4107     } else {
4108         // old format..
4109         this.el  = Roo.get(config);
4110         this.tpl = depreciated_tpl;
4111         Roo.apply(this, depreciated_config);
4112     }
4113     this.wrapEl  = this.el.wrap().wrap();
4114     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4115     
4116     
4117     if(typeof(this.tpl) == "string"){
4118         this.tpl = new Roo.Template(this.tpl);
4119     } else {
4120         // support xtype ctors..
4121         this.tpl = new Roo.factory(this.tpl, Roo);
4122     }
4123     
4124     
4125     this.tpl.compile();
4126    
4127   
4128     
4129      
4130     /** @private */
4131     this.addEvents({
4132         /**
4133          * @event beforeclick
4134          * Fires before a click is processed. Returns false to cancel the default action.
4135          * @param {Roo.View} this
4136          * @param {Number} index The index of the target node
4137          * @param {HTMLElement} node The target node
4138          * @param {Roo.EventObject} e The raw event object
4139          */
4140             "beforeclick" : true,
4141         /**
4142          * @event click
4143          * Fires when a template node is clicked.
4144          * @param {Roo.View} this
4145          * @param {Number} index The index of the target node
4146          * @param {HTMLElement} node The target node
4147          * @param {Roo.EventObject} e The raw event object
4148          */
4149             "click" : true,
4150         /**
4151          * @event dblclick
4152          * Fires when a template node is double clicked.
4153          * @param {Roo.View} this
4154          * @param {Number} index The index of the target node
4155          * @param {HTMLElement} node The target node
4156          * @param {Roo.EventObject} e The raw event object
4157          */
4158             "dblclick" : true,
4159         /**
4160          * @event contextmenu
4161          * Fires when a template node is right clicked.
4162          * @param {Roo.View} this
4163          * @param {Number} index The index of the target node
4164          * @param {HTMLElement} node The target node
4165          * @param {Roo.EventObject} e The raw event object
4166          */
4167             "contextmenu" : true,
4168         /**
4169          * @event selectionchange
4170          * Fires when the selected nodes change.
4171          * @param {Roo.View} this
4172          * @param {Array} selections Array of the selected nodes
4173          */
4174             "selectionchange" : true,
4175     
4176         /**
4177          * @event beforeselect
4178          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4179          * @param {Roo.View} this
4180          * @param {HTMLElement} node The node to be selected
4181          * @param {Array} selections Array of currently selected nodes
4182          */
4183             "beforeselect" : true,
4184         /**
4185          * @event preparedata
4186          * Fires on every row to render, to allow you to change the data.
4187          * @param {Roo.View} this
4188          * @param {Object} data to be rendered (change this)
4189          */
4190           "preparedata" : true
4191           
4192           
4193         });
4194
4195
4196
4197     this.el.on({
4198         "click": this.onClick,
4199         "dblclick": this.onDblClick,
4200         "contextmenu": this.onContextMenu,
4201         scope:this
4202     });
4203
4204     this.selections = [];
4205     this.nodes = [];
4206     this.cmp = new Roo.CompositeElementLite([]);
4207     if(this.store){
4208         this.store = Roo.factory(this.store, Roo.data);
4209         this.setStore(this.store, true);
4210     }
4211     
4212     if ( this.footer && this.footer.xtype) {
4213            
4214          var fctr = this.wrapEl.appendChild(document.createElement("div"));
4215         
4216         this.footer.dataSource = this.store
4217         this.footer.container = fctr;
4218         this.footer = Roo.factory(this.footer, Roo);
4219         fctr.insertFirst(this.el);
4220         
4221         // this is a bit insane - as the paging toolbar seems to detach the el..
4222 //        dom.parentNode.parentNode.parentNode
4223          // they get detached?
4224     }
4225     
4226     
4227     Roo.View.superclass.constructor.call(this);
4228     
4229     
4230 };
4231
4232 Roo.extend(Roo.View, Roo.util.Observable, {
4233     
4234      /**
4235      * @cfg {Roo.data.Store} store Data store to load data from.
4236      */
4237     store : false,
4238     
4239     /**
4240      * @cfg {String|Roo.Element} el The container element.
4241      */
4242     el : '',
4243     
4244     /**
4245      * @cfg {String|Roo.Template} tpl The template used by this View 
4246      */
4247     tpl : false,
4248     /**
4249      * @cfg {String} dataName the named area of the template to use as the data area
4250      *                          Works with domtemplates roo-name="name"
4251      */
4252     dataName: false,
4253     /**
4254      * @cfg {String} selectedClass The css class to add to selected nodes
4255      */
4256     selectedClass : "x-view-selected",
4257      /**
4258      * @cfg {String} emptyText The empty text to show when nothing is loaded.
4259      */
4260     emptyText : "",
4261     
4262     /**
4263      * @cfg {String} text to display on mask (default Loading)
4264      */
4265     mask : false,
4266     /**
4267      * @cfg {Boolean} multiSelect Allow multiple selection
4268      */
4269     multiSelect : false,
4270     /**
4271      * @cfg {Boolean} singleSelect Allow single selection
4272      */
4273     singleSelect:  false,
4274     
4275     /**
4276      * @cfg {Boolean} toggleSelect - selecting 
4277      */
4278     toggleSelect : false,
4279     
4280     /**
4281      * Returns the element this view is bound to.
4282      * @return {Roo.Element}
4283      */
4284     getEl : function(){
4285         return this.wrapEl;
4286     },
4287     
4288     
4289
4290     /**
4291      * Refreshes the view. - called by datachanged on the store. - do not call directly.
4292      */
4293     refresh : function(){
4294         var t = this.tpl;
4295         
4296         // if we are using something like 'domtemplate', then
4297         // the what gets used is:
4298         // t.applySubtemplate(NAME, data, wrapping data..)
4299         // the outer template then get' applied with
4300         //     the store 'extra data'
4301         // and the body get's added to the
4302         //      roo-name="data" node?
4303         //      <span class='roo-tpl-{name}'></span> ?????
4304         
4305         
4306         
4307         this.clearSelections();
4308         this.el.update("");
4309         var html = [];
4310         var records = this.store.getRange();
4311         if(records.length < 1) {
4312             
4313             // is this valid??  = should it render a template??
4314             
4315             this.el.update(this.emptyText);
4316             return;
4317         }
4318         var el = this.el;
4319         if (this.dataName) {
4320             this.el.update(t.apply(this.store.meta)); //????
4321             el = this.el.child('.roo-tpl-' + this.dataName);
4322         }
4323         
4324         for(var i = 0, len = records.length; i < len; i++){
4325             var data = this.prepareData(records[i].data, i, records[i]);
4326             this.fireEvent("preparedata", this, data, i, records[i]);
4327             html[html.length] = Roo.util.Format.trim(
4328                 this.dataName ?
4329                     t.applySubtemplate(this.dataName, data, this.store.meta) :
4330                     t.apply(data)
4331             );
4332         }
4333         
4334         
4335         
4336         el.update(html.join(""));
4337         this.nodes = el.dom.childNodes;
4338         this.updateIndexes(0);
4339     },
4340
4341     /**
4342      * Function to override to reformat the data that is sent to
4343      * the template for each node.
4344      * DEPRICATED - use the preparedata event handler.
4345      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4346      * a JSON object for an UpdateManager bound view).
4347      */
4348     prepareData : function(data, index, record)
4349     {
4350         this.fireEvent("preparedata", this, data, index, record);
4351         return data;
4352     },
4353
4354     onUpdate : function(ds, record){
4355         this.clearSelections();
4356         var index = this.store.indexOf(record);
4357         var n = this.nodes[index];
4358         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4359         n.parentNode.removeChild(n);
4360         this.updateIndexes(index, index);
4361     },
4362
4363     
4364     
4365 // --------- FIXME     
4366     onAdd : function(ds, records, index)
4367     {
4368         this.clearSelections();
4369         if(this.nodes.length == 0){
4370             this.refresh();
4371             return;
4372         }
4373         var n = this.nodes[index];
4374         for(var i = 0, len = records.length; i < len; i++){
4375             var d = this.prepareData(records[i].data, i, records[i]);
4376             if(n){
4377                 this.tpl.insertBefore(n, d);
4378             }else{
4379                 
4380                 this.tpl.append(this.el, d);
4381             }
4382         }
4383         this.updateIndexes(index);
4384     },
4385
4386     onRemove : function(ds, record, index){
4387         this.clearSelections();
4388         var el = this.dataName  ?
4389             this.el.child('.roo-tpl-' + this.dataName) :
4390             this.el; 
4391         el.dom.removeChild(this.nodes[index]);
4392         this.updateIndexes(index);
4393     },
4394
4395     /**
4396      * Refresh an individual node.
4397      * @param {Number} index
4398      */
4399     refreshNode : function(index){
4400         this.onUpdate(this.store, this.store.getAt(index));
4401     },
4402
4403     updateIndexes : function(startIndex, endIndex){
4404         var ns = this.nodes;
4405         startIndex = startIndex || 0;
4406         endIndex = endIndex || ns.length - 1;
4407         for(var i = startIndex; i <= endIndex; i++){
4408             ns[i].nodeIndex = i;
4409         }
4410     },
4411
4412     /**
4413      * Changes the data store this view uses and refresh the view.
4414      * @param {Store} store
4415      */
4416     setStore : function(store, initial){
4417         if(!initial && this.store){
4418             this.store.un("datachanged", this.refresh);
4419             this.store.un("add", this.onAdd);
4420             this.store.un("remove", this.onRemove);
4421             this.store.un("update", this.onUpdate);
4422             this.store.un("clear", this.refresh);
4423             this.store.un("beforeload", this.onBeforeLoad);
4424             this.store.un("load", this.onLoad);
4425             this.store.un("loadexception", this.onLoad);
4426         }
4427         if(store){
4428           
4429             store.on("datachanged", this.refresh, this);
4430             store.on("add", this.onAdd, this);
4431             store.on("remove", this.onRemove, this);
4432             store.on("update", this.onUpdate, this);
4433             store.on("clear", this.refresh, this);
4434             store.on("beforeload", this.onBeforeLoad, this);
4435             store.on("load", this.onLoad, this);
4436             store.on("loadexception", this.onLoad, this);
4437         }
4438         
4439         if(store){
4440             this.refresh();
4441         }
4442     },
4443     /**
4444      * onbeforeLoad - masks the loading area.
4445      *
4446      */
4447     onBeforeLoad : function()
4448     {
4449         this.el.update("");
4450         this.el.mask(this.mask ? this.mask : "Loading" ); 
4451     },
4452     onLoad : function ()
4453     {
4454         this.el.unmask();
4455     },
4456     
4457
4458     /**
4459      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4460      * @param {HTMLElement} node
4461      * @return {HTMLElement} The template node
4462      */
4463     findItemFromChild : function(node){
4464         var el = this.dataName  ?
4465             this.el.child('.roo-tpl-' + this.dataName,true) :
4466             this.el.dom; 
4467         
4468         if(!node || node.parentNode == el){
4469                     return node;
4470             }
4471             var p = node.parentNode;
4472             while(p && p != el){
4473             if(p.parentNode == el){
4474                 return p;
4475             }
4476             p = p.parentNode;
4477         }
4478             return null;
4479     },
4480
4481     /** @ignore */
4482     onClick : function(e){
4483         var item = this.findItemFromChild(e.getTarget());
4484         if(item){
4485             var index = this.indexOf(item);
4486             if(this.onItemClick(item, index, e) !== false){
4487                 this.fireEvent("click", this, index, item, e);
4488             }
4489         }else{
4490             this.clearSelections();
4491         }
4492     },
4493
4494     /** @ignore */
4495     onContextMenu : function(e){
4496         var item = this.findItemFromChild(e.getTarget());
4497         if(item){
4498             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4499         }
4500     },
4501
4502     /** @ignore */
4503     onDblClick : function(e){
4504         var item = this.findItemFromChild(e.getTarget());
4505         if(item){
4506             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4507         }
4508     },
4509
4510     onItemClick : function(item, index, e)
4511     {
4512         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4513             return false;
4514         }
4515         if (this.toggleSelect) {
4516             var m = this.isSelected(item) ? 'unselect' : 'select';
4517             Roo.log(m);
4518             var _t = this;
4519             _t[m](item, true, false);
4520             return true;
4521         }
4522         if(this.multiSelect || this.singleSelect){
4523             if(this.multiSelect && e.shiftKey && this.lastSelection){
4524                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4525             }else{
4526                 this.select(item, this.multiSelect && e.ctrlKey);
4527                 this.lastSelection = item;
4528             }
4529             e.preventDefault();
4530         }
4531         return true;
4532     },
4533
4534     /**
4535      * Get the number of selected nodes.
4536      * @return {Number}
4537      */
4538     getSelectionCount : function(){
4539         return this.selections.length;
4540     },
4541
4542     /**
4543      * Get the currently selected nodes.
4544      * @return {Array} An array of HTMLElements
4545      */
4546     getSelectedNodes : function(){
4547         return this.selections;
4548     },
4549
4550     /**
4551      * Get the indexes of the selected nodes.
4552      * @return {Array}
4553      */
4554     getSelectedIndexes : function(){
4555         var indexes = [], s = this.selections;
4556         for(var i = 0, len = s.length; i < len; i++){
4557             indexes.push(s[i].nodeIndex);
4558         }
4559         return indexes;
4560     },
4561
4562     /**
4563      * Clear all selections
4564      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4565      */
4566     clearSelections : function(suppressEvent){
4567         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4568             this.cmp.elements = this.selections;
4569             this.cmp.removeClass(this.selectedClass);
4570             this.selections = [];
4571             if(!suppressEvent){
4572                 this.fireEvent("selectionchange", this, this.selections);
4573             }
4574         }
4575     },
4576
4577     /**
4578      * Returns true if the passed node is selected
4579      * @param {HTMLElement/Number} node The node or node index
4580      * @return {Boolean}
4581      */
4582     isSelected : function(node){
4583         var s = this.selections;
4584         if(s.length < 1){
4585             return false;
4586         }
4587         node = this.getNode(node);
4588         return s.indexOf(node) !== -1;
4589     },
4590
4591     /**
4592      * Selects nodes.
4593      * @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
4594      * @param {Boolean} keepExisting (optional) true to keep existing selections
4595      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4596      */
4597     select : function(nodeInfo, keepExisting, suppressEvent){
4598         if(nodeInfo instanceof Array){
4599             if(!keepExisting){
4600                 this.clearSelections(true);
4601             }
4602             for(var i = 0, len = nodeInfo.length; i < len; i++){
4603                 this.select(nodeInfo[i], true, true);
4604             }
4605             return;
4606         } 
4607         var node = this.getNode(nodeInfo);
4608         if(!node || this.isSelected(node)){
4609             return; // already selected.
4610         }
4611         if(!keepExisting){
4612             this.clearSelections(true);
4613         }
4614         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4615             Roo.fly(node).addClass(this.selectedClass);
4616             this.selections.push(node);
4617             if(!suppressEvent){
4618                 this.fireEvent("selectionchange", this, this.selections);
4619             }
4620         }
4621         
4622         
4623     },
4624       /**
4625      * Unselects nodes.
4626      * @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
4627      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4628      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4629      */
4630     unselect : function(nodeInfo, keepExisting, suppressEvent)
4631     {
4632         if(nodeInfo instanceof Array){
4633             Roo.each(this.selections, function(s) {
4634                 this.unselect(s, nodeInfo);
4635             }, this);
4636             return;
4637         }
4638         var node = this.getNode(nodeInfo);
4639         if(!node || !this.isSelected(node)){
4640             Roo.log("not selected");
4641             return; // not selected.
4642         }
4643         // fireevent???
4644         var ns = [];
4645         Roo.each(this.selections, function(s) {
4646             if (s == node ) {
4647                 Roo.fly(node).removeClass(this.selectedClass);
4648
4649                 return;
4650             }
4651             ns.push(s);
4652         },this);
4653         
4654         this.selections= ns;
4655         this.fireEvent("selectionchange", this, this.selections);
4656     },
4657
4658     /**
4659      * Gets a template node.
4660      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4661      * @return {HTMLElement} The node or null if it wasn't found
4662      */
4663     getNode : function(nodeInfo){
4664         if(typeof nodeInfo == "string"){
4665             return document.getElementById(nodeInfo);
4666         }else if(typeof nodeInfo == "number"){
4667             return this.nodes[nodeInfo];
4668         }
4669         return nodeInfo;
4670     },
4671
4672     /**
4673      * Gets a range template nodes.
4674      * @param {Number} startIndex
4675      * @param {Number} endIndex
4676      * @return {Array} An array of nodes
4677      */
4678     getNodes : function(start, end){
4679         var ns = this.nodes;
4680         start = start || 0;
4681         end = typeof end == "undefined" ? ns.length - 1 : end;
4682         var nodes = [];
4683         if(start <= end){
4684             for(var i = start; i <= end; i++){
4685                 nodes.push(ns[i]);
4686             }
4687         } else{
4688             for(var i = start; i >= end; i--){
4689                 nodes.push(ns[i]);
4690             }
4691         }
4692         return nodes;
4693     },
4694
4695     /**
4696      * Finds the index of the passed node
4697      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4698      * @return {Number} The index of the node or -1
4699      */
4700     indexOf : function(node){
4701         node = this.getNode(node);
4702         if(typeof node.nodeIndex == "number"){
4703             return node.nodeIndex;
4704         }
4705         var ns = this.nodes;
4706         for(var i = 0, len = ns.length; i < len; i++){
4707             if(ns[i] == node){
4708                 return i;
4709             }
4710         }
4711         return -1;
4712     }
4713 });
4714 /*
4715  * - LGPL
4716  * * 
4717  */
4718
4719 /**
4720  * @class Roo.bootstrap.ComboBox
4721  * @extends Roo.bootstrap.TriggerField
4722  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
4723  * @constructor
4724  * Create a new ComboBox.
4725  * @param {Object} config Configuration options
4726  */
4727 Roo.bootstrap.ComboBox = function(config){
4728     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
4729     this.addEvents({
4730         /**
4731          * @event expand
4732          * Fires when the dropdown list is expanded
4733              * @param {Roo.bootstrap.ComboBox} combo This combo box
4734              */
4735         'expand' : true,
4736         /**
4737          * @event collapse
4738          * Fires when the dropdown list is collapsed
4739              * @param {Roo.bootstrap.ComboBox} combo This combo box
4740              */
4741         'collapse' : true,
4742         /**
4743          * @event beforeselect
4744          * Fires before a list item is selected. Return false to cancel the selection.
4745              * @param {Roo.bootstrap.ComboBox} combo This combo box
4746              * @param {Roo.data.Record} record The data record returned from the underlying store
4747              * @param {Number} index The index of the selected item in the dropdown list
4748              */
4749         'beforeselect' : true,
4750         /**
4751          * @event select
4752          * Fires when a list item is selected
4753              * @param {Roo.bootstrap.ComboBox} combo This combo box
4754              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
4755              * @param {Number} index The index of the selected item in the dropdown list
4756              */
4757         'select' : true,
4758         /**
4759          * @event beforequery
4760          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
4761          * The event object passed has these properties:
4762              * @param {Roo.bootstrap.ComboBox} combo This combo box
4763              * @param {String} query The query
4764              * @param {Boolean} forceAll true to force "all" query
4765              * @param {Boolean} cancel true to cancel the query
4766              * @param {Object} e The query event object
4767              */
4768         'beforequery': true,
4769          /**
4770          * @event add
4771          * Fires when the 'add' icon is pressed (add a listener to enable add button)
4772              * @param {Roo.bootstrap.ComboBox} combo This combo box
4773              */
4774         'add' : true,
4775         /**
4776          * @event edit
4777          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
4778              * @param {Roo.bootstrap.ComboBox} combo This combo box
4779              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
4780              */
4781         'edit' : true
4782         
4783         
4784     });
4785     
4786     
4787     this.selectedIndex = -1;
4788     if(this.mode == 'local'){
4789         if(config.queryDelay === undefined){
4790             this.queryDelay = 10;
4791         }
4792         if(config.minChars === undefined){
4793             this.minChars = 0;
4794         }
4795     }
4796 };
4797
4798 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
4799      
4800     /**
4801      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
4802      * rendering into an Roo.Editor, defaults to false)
4803      */
4804     /**
4805      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
4806      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
4807      */
4808     /**
4809      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
4810      */
4811     /**
4812      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
4813      * the dropdown list (defaults to undefined, with no header element)
4814      */
4815
4816      /**
4817      * @cfg {String/Roo.Template} tpl The template to use to render the output
4818      */
4819      
4820      /**
4821      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
4822      */
4823     listWidth: undefined,
4824     /**
4825      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
4826      * mode = 'remote' or 'text' if mode = 'local')
4827      */
4828     displayField: undefined,
4829     /**
4830      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
4831      * mode = 'remote' or 'value' if mode = 'local'). 
4832      * Note: use of a valueField requires the user make a selection
4833      * in order for a value to be mapped.
4834      */
4835     valueField: undefined,
4836     
4837     
4838     /**
4839      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
4840      * field's data value (defaults to the underlying DOM element's name)
4841      */
4842     hiddenName: undefined,
4843     /**
4844      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
4845      */
4846     listClass: '',
4847     /**
4848      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
4849      */
4850     selectedClass: 'active',
4851     
4852     /**
4853      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
4854      */
4855     shadow:'sides',
4856     /**
4857      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
4858      * anchor positions (defaults to 'tl-bl')
4859      */
4860     listAlign: 'tl-bl?',
4861     /**
4862      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
4863      */
4864     maxHeight: 300,
4865     /**
4866      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
4867      * query specified by the allQuery config option (defaults to 'query')
4868      */
4869     triggerAction: 'query',
4870     /**
4871      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
4872      * (defaults to 4, does not apply if editable = false)
4873      */
4874     minChars : 4,
4875     /**
4876      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
4877      * delay (typeAheadDelay) if it matches a known value (defaults to false)
4878      */
4879     typeAhead: false,
4880     /**
4881      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
4882      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
4883      */
4884     queryDelay: 500,
4885     /**
4886      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
4887      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
4888      */
4889     pageSize: 0,
4890     /**
4891      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
4892      * when editable = true (defaults to false)
4893      */
4894     selectOnFocus:false,
4895     /**
4896      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
4897      */
4898     queryParam: 'query',
4899     /**
4900      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
4901      * when mode = 'remote' (defaults to 'Loading...')
4902      */
4903     loadingText: 'Loading...',
4904     /**
4905      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
4906      */
4907     resizable: false,
4908     /**
4909      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
4910      */
4911     handleHeight : 8,
4912     /**
4913      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
4914      * traditional select (defaults to true)
4915      */
4916     editable: true,
4917     /**
4918      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
4919      */
4920     allQuery: '',
4921     /**
4922      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
4923      */
4924     mode: 'remote',
4925     /**
4926      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
4927      * listWidth has a higher value)
4928      */
4929     minListWidth : 70,
4930     /**
4931      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
4932      * allow the user to set arbitrary text into the field (defaults to false)
4933      */
4934     forceSelection:false,
4935     /**
4936      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
4937      * if typeAhead = true (defaults to 250)
4938      */
4939     typeAheadDelay : 250,
4940     /**
4941      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
4942      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
4943      */
4944     valueNotFoundText : undefined,
4945     /**
4946      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
4947      */
4948     blockFocus : false,
4949     
4950     /**
4951      * @cfg {Boolean} disableClear Disable showing of clear button.
4952      */
4953     disableClear : false,
4954     /**
4955      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
4956      */
4957     alwaysQuery : false,
4958     
4959     //private
4960     addicon : false,
4961     editicon: false,
4962     
4963     // element that contains real text value.. (when hidden is used..)
4964      
4965     // private
4966     initEvents: function(){
4967         
4968         if (!this.store) {
4969             throw "can not find store for combo";
4970         }
4971         this.store = Roo.factory(this.store, Roo.data);
4972         
4973         
4974         
4975         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
4976         
4977         
4978         if(this.hiddenName){
4979             
4980             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
4981             
4982             this.hiddenField.dom.value =
4983                 this.hiddenValue !== undefined ? this.hiddenValue :
4984                 this.value !== undefined ? this.value : '';
4985
4986             // prevent input submission
4987             this.el.dom.removeAttribute('name');
4988             this.hiddenField.dom.setAttribute('name', this.hiddenName);
4989              
4990              
4991         }
4992         //if(Roo.isGecko){
4993         //    this.el.dom.setAttribute('autocomplete', 'off');
4994         //}
4995
4996         var cls = 'x-combo-list';
4997         this.list = this.el.select('ul',true).first();
4998
4999         //this.list = new Roo.Layer({
5000         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
5001         //});
5002         
5003         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
5004         this.list.setWidth(lw);
5005         /*
5006         this.list.swallowEvent('mousewheel');
5007         this.assetHeight = 0;
5008
5009         if(this.title){
5010             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
5011             this.assetHeight += this.header.getHeight();
5012         }
5013
5014         this.innerList = this.list.createChild({cls:cls+'-inner'});
5015         this.innerList.on('mouseover', this.onViewOver, this);
5016         this.innerList.on('mousemove', this.onViewMove, this);
5017         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
5018         
5019         if(this.allowBlank && !this.pageSize && !this.disableClear){
5020             this.footer = this.list.createChild({cls:cls+'-ft'});
5021             this.pageTb = new Roo.Toolbar(this.footer);
5022            
5023         }
5024         if(this.pageSize){
5025             this.footer = this.list.createChild({cls:cls+'-ft'});
5026             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
5027                     {pageSize: this.pageSize});
5028             
5029         }
5030         
5031         if (this.pageTb && this.allowBlank && !this.disableClear) {
5032             var _this = this;
5033             this.pageTb.add(new Roo.Toolbar.Fill(), {
5034                 cls: 'x-btn-icon x-btn-clear',
5035                 text: '&#160;',
5036                 handler: function()
5037                 {
5038                     _this.collapse();
5039                     _this.clearValue();
5040                     _this.onSelect(false, -1);
5041                 }
5042             });
5043         }
5044         if (this.footer) {
5045             this.assetHeight += this.footer.getHeight();
5046         }
5047         */
5048             
5049         if(!this.tpl){
5050             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
5051         }
5052
5053         this.view = new Roo.View(this.el.select('ul',true).first(), this.tpl, {
5054             singleSelect:true, store: this.store, selectedClass: this.selectedClass
5055         });
5056         //this.view.wrapEl.setDisplayed(false);
5057         this.view.on('click', this.onViewClick, this);
5058         
5059         
5060         
5061         this.store.on('beforeload', this.onBeforeLoad, this);
5062         this.store.on('load', this.onLoad, this);
5063         this.store.on('loadexception', this.onLoadException, this);
5064         /*
5065         if(this.resizable){
5066             this.resizer = new Roo.Resizable(this.list,  {
5067                pinned:true, handles:'se'
5068             });
5069             this.resizer.on('resize', function(r, w, h){
5070                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
5071                 this.listWidth = w;
5072                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
5073                 this.restrictHeight();
5074             }, this);
5075             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
5076         }
5077         */
5078         if(!this.editable){
5079             this.editable = true;
5080             this.setEditable(false);
5081         }
5082         
5083         /*
5084         
5085         if (typeof(this.events.add.listeners) != 'undefined') {
5086             
5087             this.addicon = this.wrap.createChild(
5088                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
5089        
5090             this.addicon.on('click', function(e) {
5091                 this.fireEvent('add', this);
5092             }, this);
5093         }
5094         if (typeof(this.events.edit.listeners) != 'undefined') {
5095             
5096             this.editicon = this.wrap.createChild(
5097                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
5098             if (this.addicon) {
5099                 this.editicon.setStyle('margin-left', '40px');
5100             }
5101             this.editicon.on('click', function(e) {
5102                 
5103                 // we fire even  if inothing is selected..
5104                 this.fireEvent('edit', this, this.lastData );
5105                 
5106             }, this);
5107         }
5108         */
5109         
5110  
5111         this.keyNav = new Roo.KeyNav(this.inputEl(), {
5112             "up" : function(e){
5113                 this.inKeyMode = true;
5114                 this.selectPrev();
5115             },
5116
5117             "down" : function(e){
5118                 if(!this.isExpanded()){
5119                     this.onTriggerClick();
5120                 }else{
5121                     this.inKeyMode = true;
5122                     this.selectNext();
5123                 }
5124             },
5125
5126             "enter" : function(e){
5127                 this.onViewClick();
5128                 //return true;
5129             },
5130
5131             "esc" : function(e){
5132                 this.collapse();
5133             },
5134
5135             "tab" : function(e){
5136                 this.onViewClick(false);
5137                 this.fireEvent("specialkey", this, e);
5138                 return true;
5139             },
5140
5141             scope : this,
5142
5143             doRelay : function(foo, bar, hname){
5144                 if(hname == 'down' || this.scope.isExpanded()){
5145                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
5146                 }
5147                 return true;
5148             },
5149
5150             forceKeyDown: true
5151         });
5152         
5153         
5154         this.queryDelay = Math.max(this.queryDelay || 10,
5155                 this.mode == 'local' ? 10 : 250);
5156         
5157         
5158         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
5159         
5160         if(this.typeAhead){
5161             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
5162         }
5163         if(this.editable !== false){
5164             this.inputEl().on("keyup", this.onKeyUp, this);
5165         }
5166         if(this.forceSelection){
5167             this.on('blur', this.doForce, this);
5168         }
5169     },
5170
5171     onDestroy : function(){
5172         if(this.view){
5173             this.view.setStore(null);
5174             this.view.el.removeAllListeners();
5175             this.view.el.remove();
5176             this.view.purgeListeners();
5177         }
5178         if(this.list){
5179             this.list.dom.innerHTML  = '';
5180         }
5181         if(this.store){
5182             this.store.un('beforeload', this.onBeforeLoad, this);
5183             this.store.un('load', this.onLoad, this);
5184             this.store.un('loadexception', this.onLoadException, this);
5185         }
5186         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
5187     },
5188
5189     // private
5190     fireKey : function(e){
5191         if(e.isNavKeyPress() && !this.list.isVisible()){
5192             this.fireEvent("specialkey", this, e);
5193         }
5194     },
5195
5196     // private
5197     onResize: function(w, h){
5198         Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
5199         
5200         if(typeof w != 'number'){
5201             // we do not handle it!?!?
5202             return;
5203         }
5204         var tw = this.trigger.getWidth();
5205        // tw += this.addicon ? this.addicon.getWidth() : 0;
5206        // tw += this.editicon ? this.editicon.getWidth() : 0;
5207         var x = w - tw;
5208         this.inputEl().setWidth( this.adjustWidth('input', x));
5209             
5210         //this.trigger.setStyle('left', x+'px');
5211         
5212         if(this.list && this.listWidth === undefined){
5213             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
5214             this.list.setWidth(lw);
5215             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
5216         }
5217         
5218     
5219         
5220     },
5221
5222     /**
5223      * Allow or prevent the user from directly editing the field text.  If false is passed,
5224      * the user will only be able to select from the items defined in the dropdown list.  This method
5225      * is the runtime equivalent of setting the 'editable' config option at config time.
5226      * @param {Boolean} value True to allow the user to directly edit the field text
5227      */
5228     setEditable : function(value){
5229         if(value == this.editable){
5230             return;
5231         }
5232         this.editable = value;
5233         if(!value){
5234             this.inputEl().dom.setAttribute('readOnly', true);
5235             this.inputEl().on('mousedown', this.onTriggerClick,  this);
5236             this.inputEl().addClass('x-combo-noedit');
5237         }else{
5238             this.inputEl().dom.setAttribute('readOnly', false);
5239             this.inputEl().un('mousedown', this.onTriggerClick,  this);
5240             this.inputEl().removeClass('x-combo-noedit');
5241         }
5242     },
5243
5244     // private
5245     onBeforeLoad : function(){
5246         if(!this.hasFocus){
5247             return;
5248         }
5249         //this.innerList.update(this.loadingText ?
5250         //       '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
5251         this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
5252         
5253         this.restrictHeight();
5254         this.selectedIndex = -1;
5255     },
5256
5257     // private
5258     onLoad : function(){
5259         if(!this.hasFocus){
5260             return;
5261         }
5262         if(this.store.getCount() > 0){
5263             this.expand();
5264             this.restrictHeight();
5265             if(this.lastQuery == this.allQuery){
5266                 if(this.editable){
5267                     this.inputEl().dom.select();
5268                 }
5269                 if(!this.selectByValue(this.value, true)){
5270                     this.select(0, true);
5271                 }
5272             }else{
5273                 this.selectNext();
5274                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
5275                     this.taTask.delay(this.typeAheadDelay);
5276                 }
5277             }
5278         }else{
5279             this.onEmptyResults();
5280         }
5281         //this.el.focus();
5282     },
5283     // private
5284     onLoadException : function()
5285     {
5286         this.collapse();
5287         Roo.log(this.store.reader.jsonData);
5288         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5289             // fixme
5290             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5291         }
5292         
5293         
5294     },
5295     // private
5296     onTypeAhead : function(){
5297         if(this.store.getCount() > 0){
5298             var r = this.store.getAt(0);
5299             var newValue = r.data[this.displayField];
5300             var len = newValue.length;
5301             var selStart = this.getRawValue().length;
5302             if(selStart != len){
5303                 this.setRawValue(newValue);
5304                 this.selectText(selStart, newValue.length);
5305             }
5306         }
5307     },
5308
5309     // private
5310     onSelect : function(record, index){
5311         if(this.fireEvent('beforeselect', this, record, index) !== false){
5312             this.setFromData(index > -1 ? record.data : false);
5313             this.collapse();
5314             this.fireEvent('select', this, record, index);
5315         }
5316     },
5317
5318     /**
5319      * Returns the currently selected field value or empty string if no value is set.
5320      * @return {String} value The selected value
5321      */
5322     getValue : function(){
5323         if(this.valueField){
5324             return typeof this.value != 'undefined' ? this.value : '';
5325         }else{
5326             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
5327         }
5328     },
5329
5330     /**
5331      * Clears any text/value currently set in the field
5332      */
5333     clearValue : function(){
5334         if(this.hiddenField){
5335             this.hiddenField.dom.value = '';
5336         }
5337         this.value = '';
5338         this.setRawValue('');
5339         this.lastSelectionText = '';
5340         
5341     },
5342
5343     /**
5344      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
5345      * will be displayed in the field.  If the value does not match the data value of an existing item,
5346      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
5347      * Otherwise the field will be blank (although the value will still be set).
5348      * @param {String} value The value to match
5349      */
5350     setValue : function(v){
5351         var text = v;
5352         if(this.valueField){
5353             var r = this.findRecord(this.valueField, v);
5354             if(r){
5355                 text = r.data[this.displayField];
5356             }else if(this.valueNotFoundText !== undefined){
5357                 text = this.valueNotFoundText;
5358             }
5359         }
5360         this.lastSelectionText = text;
5361         if(this.hiddenField){
5362             this.hiddenField.dom.value = v;
5363         }
5364         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
5365         this.value = v;
5366     },
5367     /**
5368      * @property {Object} the last set data for the element
5369      */
5370     
5371     lastData : false,
5372     /**
5373      * Sets the value of the field based on a object which is related to the record format for the store.
5374      * @param {Object} value the value to set as. or false on reset?
5375      */
5376     setFromData : function(o){
5377         var dv = ''; // display value
5378         var vv = ''; // value value..
5379         this.lastData = o;
5380         if (this.displayField) {
5381             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
5382         } else {
5383             // this is an error condition!!!
5384             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
5385         }
5386         
5387         if(this.valueField){
5388             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
5389         }
5390         if(this.hiddenField){
5391             this.hiddenField.dom.value = vv;
5392             
5393             this.lastSelectionText = dv;
5394             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
5395             this.value = vv;
5396             return;
5397         }
5398         // no hidden field.. - we store the value in 'value', but still display
5399         // display field!!!!
5400         this.lastSelectionText = dv;
5401         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
5402         this.value = vv;
5403         
5404         
5405     },
5406     // private
5407     reset : function(){
5408         // overridden so that last data is reset..
5409         this.setValue(this.originalValue);
5410         this.clearInvalid();
5411         this.lastData = false;
5412         if (this.view) {
5413             this.view.clearSelections();
5414         }
5415     },
5416     // private
5417     findRecord : function(prop, value){
5418         var record;
5419         if(this.store.getCount() > 0){
5420             this.store.each(function(r){
5421                 if(r.data[prop] == value){
5422                     record = r;
5423                     return false;
5424                 }
5425                 return true;
5426             });
5427         }
5428         return record;
5429     },
5430     
5431     getName: function()
5432     {
5433         // returns hidden if it's set..
5434         if (!this.rendered) {return ''};
5435         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
5436         
5437     },
5438     // private
5439     onViewMove : function(e, t){
5440         this.inKeyMode = false;
5441     },
5442
5443     // private
5444     onViewOver : function(e, t){
5445         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
5446             return;
5447         }
5448         var item = this.view.findItemFromChild(t);
5449         if(item){
5450             var index = this.view.indexOf(item);
5451             this.select(index, false);
5452         }
5453     },
5454
5455     // private
5456     onViewClick : function(doFocus)
5457     {
5458         var index = this.view.getSelectedIndexes()[0];
5459         var r = this.store.getAt(index);
5460         if(r){
5461             this.onSelect(r, index);
5462         }
5463         if(doFocus !== false && !this.blockFocus){
5464             this.inputEl().focus();
5465         }
5466     },
5467
5468     // private
5469     restrictHeight : function(){
5470         //this.innerList.dom.style.height = '';
5471         //var inner = this.innerList.dom;
5472         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
5473         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
5474         //this.list.beginUpdate();
5475         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
5476         this.list.alignTo(this.inputEl(), this.listAlign);
5477         //this.list.endUpdate();
5478     },
5479
5480     // private
5481     onEmptyResults : function(){
5482         this.collapse();
5483     },
5484
5485     /**
5486      * Returns true if the dropdown list is expanded, else false.
5487      */
5488     isExpanded : function(){
5489         return this.list.isVisible();
5490     },
5491
5492     /**
5493      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
5494      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
5495      * @param {String} value The data value of the item to select
5496      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
5497      * selected item if it is not currently in view (defaults to true)
5498      * @return {Boolean} True if the value matched an item in the list, else false
5499      */
5500     selectByValue : function(v, scrollIntoView){
5501         if(v !== undefined && v !== null){
5502             var r = this.findRecord(this.valueField || this.displayField, v);
5503             if(r){
5504                 this.select(this.store.indexOf(r), scrollIntoView);
5505                 return true;
5506             }
5507         }
5508         return false;
5509     },
5510
5511     /**
5512      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
5513      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
5514      * @param {Number} index The zero-based index of the list item to select
5515      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
5516      * selected item if it is not currently in view (defaults to true)
5517      */
5518     select : function(index, scrollIntoView){
5519         this.selectedIndex = index;
5520         this.view.select(index);
5521         if(scrollIntoView !== false){
5522             var el = this.view.getNode(index);
5523             if(el){
5524                 //this.innerList.scrollChildIntoView(el, false);
5525                 
5526             }
5527         }
5528     },
5529
5530     // private
5531     selectNext : function(){
5532         var ct = this.store.getCount();
5533         if(ct > 0){
5534             if(this.selectedIndex == -1){
5535                 this.select(0);
5536             }else if(this.selectedIndex < ct-1){
5537                 this.select(this.selectedIndex+1);
5538             }
5539         }
5540     },
5541
5542     // private
5543     selectPrev : function(){
5544         var ct = this.store.getCount();
5545         if(ct > 0){
5546             if(this.selectedIndex == -1){
5547                 this.select(0);
5548             }else if(this.selectedIndex != 0){
5549                 this.select(this.selectedIndex-1);
5550             }
5551         }
5552     },
5553
5554     // private
5555     onKeyUp : function(e){
5556         if(this.editable !== false && !e.isSpecialKey()){
5557             this.lastKey = e.getKey();
5558             this.dqTask.delay(this.queryDelay);
5559         }
5560     },
5561
5562     // private
5563     validateBlur : function(){
5564         return !this.list || !this.list.isVisible();   
5565     },
5566
5567     // private
5568     initQuery : function(){
5569         this.doQuery(this.getRawValue());
5570     },
5571
5572     // private
5573     doForce : function(){
5574         if(this.el.dom.value.length > 0){
5575             this.el.dom.value =
5576                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
5577              
5578         }
5579     },
5580
5581     /**
5582      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
5583      * query allowing the query action to be canceled if needed.
5584      * @param {String} query The SQL query to execute
5585      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
5586      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
5587      * saved in the current store (defaults to false)
5588      */
5589     doQuery : function(q, forceAll){
5590         if(q === undefined || q === null){
5591             q = '';
5592         }
5593         var qe = {
5594             query: q,
5595             forceAll: forceAll,
5596             combo: this,
5597             cancel:false
5598         };
5599         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
5600             return false;
5601         }
5602         q = qe.query;
5603         forceAll = qe.forceAll;
5604         if(forceAll === true || (q.length >= this.minChars)){
5605             if(this.lastQuery != q || this.alwaysQuery){
5606                 this.lastQuery = q;
5607                 if(this.mode == 'local'){
5608                     this.selectedIndex = -1;
5609                     if(forceAll){
5610                         this.store.clearFilter();
5611                     }else{
5612                         this.store.filter(this.displayField, q);
5613                     }
5614                     this.onLoad();
5615                 }else{
5616                     this.store.baseParams[this.queryParam] = q;
5617                     this.store.load({
5618                         params: this.getParams(q)
5619                     });
5620                     this.expand();
5621                 }
5622             }else{
5623                 this.selectedIndex = -1;
5624                 this.onLoad();   
5625             }
5626         }
5627     },
5628
5629     // private
5630     getParams : function(q){
5631         var p = {};
5632         //p[this.queryParam] = q;
5633         if(this.pageSize){
5634             p.start = 0;
5635             p.limit = this.pageSize;
5636         }
5637         return p;
5638     },
5639
5640     /**
5641      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
5642      */
5643     collapse : function(){
5644         if(!this.isExpanded()){
5645             return;
5646         }
5647         this.list.hide();
5648         Roo.get(document).un('mousedown', this.collapseIf, this);
5649         Roo.get(document).un('mousewheel', this.collapseIf, this);
5650         if (!this.editable) {
5651             Roo.get(document).un('keydown', this.listKeyPress, this);
5652         }
5653         this.fireEvent('collapse', this);
5654     },
5655
5656     // private
5657     collapseIf : function(e){
5658         if(!e.within(this.el) && !e.within(this.el)){
5659             this.collapse();
5660         }
5661     },
5662
5663     /**
5664      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
5665      */
5666     expand : function(){
5667         Roo.log('expand');
5668         if(this.isExpanded() || !this.hasFocus){
5669             return;
5670         }
5671         this.list.alignTo(this.inputEl(), this.listAlign);
5672         this.list.show();
5673         Roo.get(document).on('mousedown', this.collapseIf, this);
5674         Roo.get(document).on('mousewheel', this.collapseIf, this);
5675         if (!this.editable) {
5676             Roo.get(document).on('keydown', this.listKeyPress, this);
5677         }
5678         
5679         this.fireEvent('expand', this);
5680     },
5681
5682     // private
5683     // Implements the default empty TriggerField.onTriggerClick function
5684     onTriggerClick : function()
5685     {
5686         Roo.log('trigger click');
5687         
5688         if(this.disabled){
5689             return;
5690         }
5691         if(this.isExpanded()){
5692             this.collapse();
5693             if (!this.blockFocus) {
5694                 this.inputEl().focus();
5695             }
5696             
5697         }else {
5698             this.hasFocus = true;
5699             if(this.triggerAction == 'all') {
5700                 this.doQuery(this.allQuery, true);
5701             } else {
5702                 this.doQuery(this.getRawValue());
5703             }
5704             if (!this.blockFocus) {
5705                 this.inputEl().focus();
5706             }
5707         }
5708     },
5709     listKeyPress : function(e)
5710     {
5711         //Roo.log('listkeypress');
5712         // scroll to first matching element based on key pres..
5713         if (e.isSpecialKey()) {
5714             return false;
5715         }
5716         var k = String.fromCharCode(e.getKey()).toUpperCase();
5717         //Roo.log(k);
5718         var match  = false;
5719         var csel = this.view.getSelectedNodes();
5720         var cselitem = false;
5721         if (csel.length) {
5722             var ix = this.view.indexOf(csel[0]);
5723             cselitem  = this.store.getAt(ix);
5724             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
5725                 cselitem = false;
5726             }
5727             
5728         }
5729         
5730         this.store.each(function(v) { 
5731             if (cselitem) {
5732                 // start at existing selection.
5733                 if (cselitem.id == v.id) {
5734                     cselitem = false;
5735                 }
5736                 return true;
5737             }
5738                 
5739             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
5740                 match = this.store.indexOf(v);
5741                 return false;
5742             }
5743             return true;
5744         }, this);
5745         
5746         if (match === false) {
5747             return true; // no more action?
5748         }
5749         // scroll to?
5750         this.view.select(match);
5751         var sn = Roo.get(this.view.getSelectedNodes()[0])
5752         //sn.scrollIntoView(sn.dom.parentNode, false);
5753     }
5754
5755     /** 
5756     * @cfg {Boolean} grow 
5757     * @hide 
5758     */
5759     /** 
5760     * @cfg {Number} growMin 
5761     * @hide 
5762     */
5763     /** 
5764     * @cfg {Number} growMax 
5765     * @hide 
5766     */
5767     /**
5768      * @hide
5769      * @method autoSize
5770      */
5771 });/*
5772  * - LGPL
5773  *
5774  * based on jquery fullcalendar
5775  * 
5776  */
5777
5778
5779 /**
5780  * @class Roo.bootstrap.Calendar
5781  * @extends Roo.bootstrap.Component
5782  * Bootstrap Calendar class
5783     
5784  * @constructor
5785  * Create a new Container
5786  * @param {Object} config The config object
5787  */
5788
5789 Roo.bootstrap.Calendar = function(config){
5790     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
5791      this.addEvents({
5792         /**
5793              * @event select
5794              * Fires when a date is selected
5795              * @param {DatePicker} this
5796              * @param {Date} date The selected date
5797              */
5798         'select': true,
5799         /**
5800              * @event monthchange
5801              * Fires when the displayed month changes 
5802              * @param {DatePicker} this
5803              * @param {Date} date The selected month
5804              */
5805         'monthchange': true,
5806         /**
5807              * @event evententer
5808              * Fires when mouse over an event
5809              * @param {Calendar} this
5810              * @param {event} Event
5811              */
5812         'evententer': true,
5813         /**
5814              * @event eventleave
5815              * Fires when the mouse leaves an
5816              * @param {Calendar} this
5817              * @param {event}
5818              */
5819         'eventleave': true
5820         
5821     });
5822
5823 };
5824
5825 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
5826     
5827      /**
5828      * @cfg {Number} startDay
5829      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5830      */
5831     startDay : 0,
5832       
5833     getAutoCreate : function(){
5834         
5835         
5836         fc_button = function(name, corner, style, content ) {
5837             return Roo.apply({},{
5838                 tag : 'span',
5839                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
5840                          (corner.length ?
5841                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
5842                             ''
5843                         ),
5844                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
5845                 unselectable: 'on'
5846             });
5847         };
5848         
5849         var header = {
5850             tag : 'table',
5851             cls : 'fc-header',
5852             style : 'width:100%',
5853             cn : [
5854                 {
5855                     tag: 'tr',
5856                     cn : [
5857                         {
5858                             tag : 'td',
5859                             cls : 'fc-header-left',
5860                             cn : [
5861                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
5862                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
5863                                 { tag: 'span', cls: 'fc-header-space' },
5864                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
5865                                 
5866                                 
5867                             ]
5868                         },
5869                         
5870                         {
5871                             tag : 'td',
5872                             cls : 'fc-header-center',
5873                             cn : [
5874                                 {
5875                                     tag: 'span',
5876                                     cls: 'fc-header-title',
5877                                     cn : {
5878                                         tag: 'H2',
5879                                         html : 'month / year'
5880                                     }
5881                                 }
5882                                 
5883                             ]
5884                         },
5885                         {
5886                             tag : 'td',
5887                             cls : 'fc-header-right',
5888                             cn : [
5889                           /*      fc_button('month', 'left', '', 'month' ),
5890                                 fc_button('week', '', '', 'week' ),
5891                                 fc_button('day', 'right', '', 'day' )
5892                             */    
5893                                 
5894                             ]
5895                         }
5896                         
5897                     ]
5898                 }
5899             ]
5900         };
5901         
5902        
5903         var cal_heads = function() {
5904             var ret = [];
5905             // fixme - handle this.
5906             
5907             for (var i =0; i < Date.dayNames.length; i++) {
5908                 var d = Date.dayNames[i];
5909                 ret.push({
5910                     tag: 'th',
5911                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
5912                     html : d.substring(0,3)
5913                 });
5914                 
5915             }
5916             ret[0].cls += ' fc-first';
5917             ret[6].cls += ' fc-last';
5918             return ret;
5919         };
5920         var cal_cell = function(n) {
5921             return  {
5922                 tag: 'td',
5923                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
5924                 cn : [
5925                     {
5926                         cn : [
5927                             {
5928                                 cls: 'fc-day-number',
5929                                 html: 'D'
5930                             },
5931                             {
5932                                 cls: 'fc-day-content',
5933                              
5934                                 cn : [
5935                                      {
5936                                         style: 'position: relative;' // height: 17px;
5937                                     }
5938                                 ]
5939                             }
5940                             
5941                             
5942                         ]
5943                     }
5944                 ]
5945                 
5946             }
5947         };
5948         var cal_rows = function() {
5949             var ret = []
5950             for (var r = 0; r < 6; r++) {
5951                 var row= {
5952                     tag : 'tr',
5953                     cls : 'fc-week',
5954                     cn : []
5955                 };
5956                 
5957                 for (var i =0; i < Date.dayNames.length; i++) {
5958                     var d = Date.dayNames[i];
5959                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
5960
5961                 }
5962                 row.cn[0].cls+=' fc-first';
5963                 row.cn[0].cn[0].style = 'min-height:90px';
5964                 row.cn[6].cls+=' fc-last';
5965                 ret.push(row);
5966                 
5967             }
5968             ret[0].cls += ' fc-first';
5969            
5970             ret[5].cls += ' fc-last';
5971             return ret;
5972             
5973         };
5974         var cal_table = {
5975             tag: 'table',
5976             cls: 'fc-border-separate',
5977             style : 'width:100%',
5978             cellspacing  : 0,
5979             cn : [
5980                 { 
5981                     tag: 'thead',
5982                     cn : [
5983                         { 
5984                             tag: 'tr',
5985                             cls : 'fc-first fc-last',
5986                             cn : cal_heads()
5987                         }
5988                     ]
5989                 },
5990                 { 
5991                     tag: 'tbody',
5992                     cn : cal_rows()
5993                 }
5994                   
5995             ]
5996         };
5997          
5998          var cfg = {
5999             cls : 'fc fc-ltr',
6000             cn : [
6001                 header,
6002                 {
6003                     cls : 'fc-content',
6004                     style : "position: relative;",
6005                     cn : [
6006                         {
6007                             cls : 'fc-view fc-view-month fc-grid',
6008                             style : 'position: relative',
6009                             unselectable : 'on',
6010                             cn : [
6011                                 {
6012                                     cls : 'fc-event-container',
6013                                     style : 'position:absolute;z-index:8;top:0;left:0;'
6014                                 },
6015                                 cal_table
6016                             ]
6017                         }
6018                     ]
6019     
6020                 }
6021            ] 
6022             
6023         };
6024         
6025          
6026         
6027         return cfg;
6028     },
6029     
6030     
6031     initEvents : function()
6032     {
6033         this.resize();
6034         this.cells = this.el.select('.fc-day',true);
6035         this.textNodes = this.el.query('.fc-day-number');
6036         this.update(new Date().clearTime());
6037         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
6038         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
6039         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
6040         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
6041         this.cells.addClassOnOver('fc-state-hover');
6042         
6043         
6044         // move to ... 
6045         this.calevents = [];
6046         
6047         this.addItem({
6048             start: new Date(),
6049             end : new Date().add(Date.DAY, 2),
6050             title : 'test'
6051         });
6052         this.addItem({
6053             start: new Date().add(Date.DAY, -5), 
6054             end : new Date().add(Date.DAY, 2),
6055             title : 'test'
6056         });
6057         this.addItem({
6058             start: new Date(), 
6059             end : new Date().add(Date.HOUR, 2),
6060             title : 'test'
6061         });
6062          this.addItem({
6063             start: new Date(), 
6064             end : new Date().add(Date.HOUR, 2),
6065             title : 'test'
6066         });
6067         this.renderEvents();
6068     },
6069     resize : function() {
6070         var sz  = this.el.getSize();
6071         
6072         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
6073         this.el.select('.fc-day-content div',true).setHeight(34);
6074     },
6075     
6076     
6077     // private
6078     showPrevMonth : function(e){
6079         this.update(this.activeDate.add("mo", -1));
6080     },
6081     showToday : function(e){
6082         this.update(new Date().clearTime());
6083     },
6084     // private
6085     showNextMonth : function(e){
6086         this.update(this.activeDate.add("mo", 1));
6087     },
6088
6089     // private
6090     showPrevYear : function(){
6091         this.update(this.activeDate.add("y", -1));
6092     },
6093
6094     // private
6095     showNextYear : function(){
6096         this.update(this.activeDate.add("y", 1));
6097     },
6098
6099     
6100    // private
6101     update : function(date)
6102     {
6103        
6104         var vd = this.activeDate;
6105         this.activeDate = date;
6106         if(vd && this.el){
6107             var t = date.getTime();
6108             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
6109                 Roo.log('using add remove');
6110                 this.cells.removeClass("fc-state-highlight");
6111                 this.cells.each(function(c){
6112                    if(c.dateValue == t){
6113                        c.addClass("fc-state-highlight");
6114                        setTimeout(function(){
6115                             try{c.dom.firstChild.focus();}catch(e){}
6116                        }, 50);
6117                        return false;
6118                    }
6119                    return true;
6120                 });
6121                 return;
6122             }
6123         }
6124         
6125         var days = date.getDaysInMonth();
6126         var firstOfMonth = date.getFirstDateOfMonth();
6127         var startingPos = firstOfMonth.getDay()-this.startDay;
6128
6129         if(startingPos <= this.startDay){
6130             startingPos += 7;
6131         }
6132
6133         var pm = date.add("mo", -1);
6134         var prevStart = pm.getDaysInMonth()-startingPos;
6135
6136         var cells = this.cells.elements;
6137         var textEls = this.textNodes;
6138         days += startingPos;
6139
6140         // convert everything to numbers so it's fast
6141         var day = 86400000;
6142         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
6143         var today = new Date().clearTime().getTime();
6144         var sel = date.clearTime().getTime();
6145         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
6146         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
6147         var ddMatch = this.disabledDatesRE;
6148         var ddText = this.disabledDatesText;
6149         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
6150         var ddaysText = this.disabledDaysText;
6151         var format = this.format;
6152
6153         var setCellClass = function(cal, cell){
6154             cell.title = "";
6155             var t = d.getTime();
6156             cell.dateValue = t;
6157             if(t == today){
6158                 cell.className += " fc-today";
6159                 cell.title = cal.todayText;
6160             }
6161             if(t == sel){
6162                 cell.className += " fc-state-highlight";
6163                 //setTimeout(function(){
6164                 //    try{cell.firstChild.focus();}catch(e){}
6165                 //}, 50);
6166             }
6167             // disabling
6168             if(t < min) {
6169                 cell.className = " fc-state-disabled";
6170                 cell.title = cal.minText;
6171                 return;
6172             }
6173             if(t > max) {
6174                 cell.className = " fc-state-disabled";
6175                 cell.title = cal.maxText;
6176                 return;
6177             }
6178             if(ddays){
6179                 if(ddays.indexOf(d.getDay()) != -1){
6180                     cell.title = ddaysText;
6181                     cell.className = " fc-state-disabled";
6182                 }
6183             }
6184             if(ddMatch && format){
6185                 var fvalue = d.dateFormat(format);
6186                 if(ddMatch.test(fvalue)){
6187                     cell.title = ddText.replace("%0", fvalue);
6188                     cell.className = " fc-state-disabled";
6189                 }
6190             }
6191             if (!cell.initialClassName) {
6192                 cell.initialClassName = cell.dom.className;
6193             }
6194             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
6195         };
6196
6197         var i = 0;
6198         for(; i < startingPos; i++) {
6199             textEls[i].innerHTML = (++prevStart);
6200             d.setDate(d.getDate()+1);
6201             cells[i].className = "fc-past fc-other-month";
6202             setCellClass(this, cells[i]);
6203         }
6204         for(; i < days; i++){
6205             intDay = i - startingPos + 1;
6206             textEls[i].innerHTML = (intDay);
6207             d.setDate(d.getDate()+1);
6208             cells[i].className = ''; // "x-date-active";
6209             setCellClass(this, cells[i]);
6210         }
6211         var extraDays = 0;
6212         for(; i < 42; i++) {
6213             textEls[i].innerHTML = (++extraDays);
6214             d.setDate(d.getDate()+1);
6215             cells[i].className = "fc-future fc-other-month";
6216             setCellClass(this, cells[i]);
6217         }
6218
6219         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
6220         this.fireEvent('monthchange', this, date);
6221         
6222         
6223         /*
6224         if(!this.internalRender){
6225             var main = this.el.dom.firstChild;
6226             var w = main.offsetWidth;
6227             this.el.setWidth(w + this.el.getBorderWidth("lr"));
6228             Roo.fly(main).setWidth(w);
6229             this.internalRender = true;
6230             // opera does not respect the auto grow header center column
6231             // then, after it gets a width opera refuses to recalculate
6232             // without a second pass
6233             if(Roo.isOpera && !this.secondPass){
6234                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
6235                 this.secondPass = true;
6236                 this.update.defer(10, this, [date]);
6237             }
6238         }
6239         */
6240         
6241     },
6242     
6243     findCell : function(dt) {
6244         dt = dt.clearTime().getTime();
6245         var ret = false;
6246         this.cells.each(function(c){
6247             //Roo.log("check " +c.dateValue + '?=' + dt);
6248             if(c.dateValue == dt){
6249                 ret = c;
6250                 return false;
6251             }
6252             return true;
6253         });
6254         Roo.log(ret);
6255         return ret;
6256     },
6257     
6258     findCells : function(ev) {
6259         var s = ev.start.clone().clearTime().getTime();
6260         var e= ev.end.clone().clearTime().getTime();
6261         var ret = [];
6262         this.cells.each(function(c){
6263             //Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
6264             
6265             if(c.dateValue > e){
6266                 return ;
6267             }
6268             if(c.dateValue < s){
6269                 return ;
6270             }
6271             ret.push(c);
6272         });
6273         //Roo.log(ret);
6274         return ret;    
6275     },
6276     
6277     findBestRow: function(cells)
6278     {
6279         var ret = 0;
6280         for (var i =0 ; i < cells.length;i++) {
6281             ret  = Math.max(cells[i].rows || 0,ret);
6282         }
6283         return ret;
6284         //d.setDate(d.ev()+1);
6285         
6286     },
6287     
6288     
6289     addItem : function(ev)
6290     {
6291         
6292         // look for vertical location slot in
6293         var cells = this.findCells(ev);
6294         ev.row = this.findBestRow(cells);
6295         
6296         // work out the location.
6297         
6298         var crow = false;
6299         var rows = [];
6300         for(var i =0; i < cells.length; i++) {
6301             if (!crow) {
6302                 crow = {
6303                     start : cells[i],
6304                     end :  cells[i]
6305                 };
6306                 continue;
6307             }
6308             if (crow.start.getY() == cells[i].getY()) {
6309                 // on same row.
6310                 crow.end = cells[i];
6311                 continue;
6312             }
6313             // different row.
6314             rows.push(crow);
6315             crow = {
6316                 start: cells[i],
6317                 end : cells[i]
6318             };
6319             
6320         }
6321         rows.push(crow);
6322         ev.els = [];
6323         ev.rows = rows;
6324         ev.cells = cells;
6325         for (var i = 0; i < cells.length;i++) {
6326             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
6327             
6328         }
6329         
6330         this.calevents.push(ev);
6331     },
6332     
6333     clearEvents: function() {
6334         Roo.each(this.calevents, function(e) {
6335             Roo.each(e.els, function(el) {
6336                 el.un('mouseenter' ,this.onEventEnter, this);
6337                 el.un('mouseleave' ,this.onEventLeave, this);
6338                 el.remove();
6339             },this);
6340         },this);
6341         
6342     },
6343     
6344     renderEvents: function()
6345     {
6346         this.clearEvents();
6347         // first make sure there is enough space..
6348         this.cells.each(function(c) {
6349             Roo.log(c.select('.fc-day-content div',true).first(), Math.max(34, c.rows * 20));
6350             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
6351         });
6352         
6353         for (var e = 0; e < this.calevents.length; e++) {
6354             var ev = this.calevents[e];
6355             var cells = ev.cells;
6356             var rows = ev.rows;
6357             
6358             for(var i =0; i < rows.length; i++) {
6359                 
6360                  
6361                 // how many rows should it span..
6362                 
6363                 var  cfg = {
6364                     cls : 'fc-event fc-event-hori fc-event-draggable ui-draggable',
6365                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
6366                     
6367                     unselectable : "on",
6368                     cn : [
6369                         {
6370                             cls: 'fc-event-inner',
6371                             cn : [
6372                                 {
6373                                   tag:'span',
6374                                   cls: 'fc-event-time',
6375                                   html : cells.length > 1 ? '' : '7pm'
6376                                 },
6377                                 {
6378                                   tag:'span',
6379                                   cls: 'fc-event-title',
6380                                   html : String.format('{0}', ev.title)
6381                                 }
6382                                 
6383                                 
6384                             ]
6385                         },
6386                         {
6387                             cls: 'ui-resizable-handle ui-resizable-e',
6388                             html : '&nbsp;&nbsp;&nbsp'
6389                         }
6390                         
6391                     ]
6392                 };
6393                 if (i == 0) {
6394                     cfg.cls += ' fc-event-start';
6395                 }
6396                 if ((i+1) == rows.length) {
6397                     cfg.cls += ' fc-event-end';
6398                 }
6399                 
6400                 var ctr = this.el.select('.fc-event-container',true).first();
6401                 var cg = ctr.createChild(cfg);
6402                 
6403                 
6404                 cg.on('mouseenter' ,this.onEventEnter, this);
6405                 cg.on('mouseleave' ,this.onEventLeave, this);
6406                 
6407                 ev.els.push(cg);
6408                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
6409                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
6410                 //Roo.log(cg);
6411                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
6412                 cg.setWidth(ebox.right - sbox.x -2);
6413             }
6414             
6415         }
6416         
6417     },
6418     
6419     onEventEnter: function (e, el,c,d) {
6420         Roo.log(e,el,c,d)
6421         this.fireEvent('evententer', this, el);
6422         //code
6423     },
6424     onEventLeave: function (e, el,c,d) {
6425         Roo.log(e,el,c,d)
6426         this.fireEvent('eventleave', this, el);
6427         //code
6428     } 
6429 });
6430
6431  
6432