Roo/bootstrap/Table.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  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr]());
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr]());
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr]());
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         if (typeof (tree.menu) != 'undefined') {
249             tree.menu.parentType = cn.xtype;
250             tree.menu.triggerEl = cn.el;
251             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
252             
253         }
254         
255         if (!tree.items || !tree.items.length) {
256             cn.items = nitems;
257             return cn;
258         }
259         var items = tree.items;
260         delete tree.items;
261         
262         //Roo.log(items.length);
263             // add the items..
264         for(var i =0;i < items.length;i++) {
265             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
266         }
267         
268         cn.items = nitems;
269         
270         return cn;
271     }
272     
273     
274     
275     
276 });
277
278  /*
279  * - LGPL
280  *
281  * Body
282  * 
283  */
284
285 /**
286  * @class Roo.bootstrap.Body
287  * @extends Roo.bootstrap.Component
288  * Bootstrap Body class
289  * 
290  * @constructor
291  * Create a new body
292  * @param {Object} config The config object
293  */
294
295 Roo.bootstrap.Body = function(config){
296     Roo.bootstrap.Body.superclass.constructor.call(this, config);
297     this.el = Roo.get(document.body);
298 };
299
300 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
301       
302         autoCreate : {
303         cls: 'container'
304     },
305     onRender : function(ct, position){
306         
307         
308         //this.el.addClass([this.fieldClass, this.cls]);
309         
310     }
311     
312     
313  
314    
315 });
316
317  /*
318  * - LGPL
319  *
320  * button group
321  * 
322  */
323
324
325 /**
326  * @class Roo.bootstrap.ButtonGroup
327  * @extends Roo.bootstrap.Component
328  * Bootstrap ButtonGroup class
329  * @cfg {String} size lg | sm | xs (default empty normal)
330  * @cfg {String} align vertical | justified  (default none)
331  * @cfg {String} direction up | down (default down)
332  * @cfg {Boolean} toolbar false | true
333  * @cfg {Boolean} btn true | false
334  * 
335  * 
336  * @constructor
337  * Create a new Input
338  * @param {Object} config The config object
339  */
340
341 Roo.bootstrap.ButtonGroup = function(config){
342     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
343 };
344
345 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
346     
347     size: '',
348     align: '',
349     direction: '',
350     toolbar: false,
351     btn: true,
352
353     getAutoCreate : function(){
354         var cfg = {
355             cls: 'btn-group',
356             html : null
357         }
358         
359         cfg.html = this.html || cfg.html;
360         
361         if (this.toolbar) {
362             cfg = {
363                 cls: 'btn-toolbar',
364                 html: null
365             }
366             
367             return cfg;
368         }
369         
370         if (['vertical','justified'].indexOf(this.align)!==-1) {
371             cfg.cls = 'btn-group-' + this.align;
372             
373             if (this.align == 'justified') {
374                 console.log(this.items);
375             }
376         }
377         
378         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
379             cfg.cls += ' btn-group-' + this.size;
380         }
381         
382         if (this.direction == 'up') {
383             cfg.cls += ' dropup' ;
384         }
385         
386         return cfg;
387     }
388    
389 });
390
391  /*
392  * - LGPL
393  *
394  * button
395  * 
396  */
397
398 /**
399  * @class Roo.bootstrap.Button
400  * @extends Roo.bootstrap.Component
401  * Bootstrap Button class
402  * @cfg {String} html The button content
403  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
404  * @cfg {String} size empty | lg | sm | xs
405  * @cfg {String} tag empty | a | input | submit
406  * @cfg {String} href empty or href
407  * @cfg {Boolean} disabled false | true
408  * @cfg {Boolean} isClose false | true
409  * @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
410  * @cfg {String} badge text for badge
411  * @cfg {String} theme default (or empty) | glow
412  * @cfg {Boolean} inverse false | true
413  * @cfg {Boolean} toggle false | true
414  * @cfg {String} ontext text for on toggle state
415  * @cfg {String} offtext text for off toggle state
416  * @cfg {Boolean} defaulton true | false
417  * @cfg {Boolean} preventDefault (true | false) default true
418  * @cfg {Boolean} removeClass true | false remove the standard class..
419  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
420  * 
421  * @constructor
422  * Create a new button
423  * @param {Object} config The config object
424  */
425
426
427 Roo.bootstrap.Button = function(config){
428     Roo.bootstrap.Button.superclass.constructor.call(this, config);
429     this.addEvents({
430         // raw events
431         /**
432          * @event click
433          * When a butotn is pressed
434          * @param {Roo.EventObject} e
435          */
436         "click" : true,
437          /**
438          * @event toggle
439          * After the button has been toggles
440          * @param {Roo.EventObject} e
441          * @param {boolean} pressed (also available as button.pressed)
442          */
443         "toggle" : true
444     });
445 };
446
447 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
448     html: false,
449     active: false,
450     weight: '',
451     size: '',
452     tag: 'button',
453     href: '',
454     disabled: false,
455     isClose: false,
456     glyphicon: '',
457     badge: '',
458     theme: 'default',
459     inverse: false,
460     
461     toggle: false,
462     ontext: 'ON',
463     offtext: 'OFF',
464     defaulton: true,
465     preventDefault: true,
466     removeClass: false,
467     name: false,
468     target: false,
469     
470     
471     pressed : null,
472     
473     
474     getAutoCreate : function(){
475         
476         var cfg = {
477             tag : 'button',
478             cls : 'roo-button',
479             html: ''
480         };
481         
482         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
483             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
484             this.tag = 'button';
485         } else {
486             cfg.tag = this.tag;
487         }
488         cfg.html = this.html || cfg.html;
489         
490         if (this.toggle == true) {
491             cfg={
492                 tag: 'div',
493                 cls: 'slider-frame roo-button',
494                 cn: [
495                     {
496                         tag: 'span',
497                         'data-on-text':'ON',
498                         'data-off-text':'OFF',
499                         cls: 'slider-button',
500                         html: this.offtext
501                     }
502                 ]
503             };
504             
505             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
506                 cfg.cls += ' '+this.weight;
507             }
508             
509             return cfg;
510         }
511         
512         if (this.isClose) {
513             cfg.cls += ' close';
514             
515             cfg["aria-hidden"] = true;
516             
517             cfg.html = "&times;";
518             
519             return cfg;
520         }
521         
522          
523         if (this.theme==='default') {
524             cfg.cls = 'btn roo-button';
525             
526             //if (this.parentType != 'Navbar') {
527             this.weight = this.weight.length ?  this.weight : 'default';
528             //}
529             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
530                 
531                 cfg.cls += ' btn-' + this.weight;
532             }
533         } else if (this.theme==='glow') {
534             
535             cfg.tag = 'a';
536             cfg.cls = 'btn-glow roo-button';
537             
538             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
539                 
540                 cfg.cls += ' ' + this.weight;
541             }
542         }
543    
544         
545         if (this.inverse) {
546             this.cls += ' inverse';
547         }
548         
549         
550         if (this.active) {
551             cfg.cls += ' active';
552         }
553         
554         if (this.disabled) {
555             cfg.disabled = 'disabled';
556         }
557         
558         if (this.items) {
559             Roo.log('changing to ul' );
560             cfg.tag = 'ul';
561             this.glyphicon = 'caret';
562         }
563         
564         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
565          
566         //gsRoo.log(this.parentType);
567         if (this.parentType === 'Navbar' && !this.parent().bar) {
568             Roo.log('changing to li?');
569             
570             cfg.tag = 'li';
571             
572             cfg.cls = '';
573             cfg.cn =  [{
574                 tag : 'a',
575                 cls : 'roo-button',
576                 html : this.html,
577                 href : this.href || '#'
578             }];
579             if (this.menu) {
580                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
581                 cfg.cls += ' dropdown';
582             }   
583             
584             delete cfg.html;
585             
586         }
587         
588        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
589         
590         if (this.glyphicon) {
591             cfg.html = ' ' + cfg.html;
592             
593             cfg.cn = [
594                 {
595                     tag: 'span',
596                     cls: 'glyphicon glyphicon-' + this.glyphicon
597                 }
598             ];
599         }
600         
601         if (this.badge) {
602             cfg.html += ' ';
603             
604             cfg.tag = 'a';
605             
606 //            cfg.cls='btn roo-button';
607             
608             cfg.href=this.href;
609             
610             var value = cfg.html;
611             
612             if(this.glyphicon){
613                 value = {
614                             tag: 'span',
615                             cls: 'glyphicon glyphicon-' + this.glyphicon,
616                             html: this.html
617                         };
618                 
619             }
620             
621             cfg.cn = [
622                 value,
623                 {
624                     tag: 'span',
625                     cls: 'badge',
626                     html: this.badge
627                 }
628             ];
629             
630             cfg.html='';
631         }
632         
633         if (this.menu) {
634             cfg.cls += ' dropdown';
635             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
636         }
637         
638         if (cfg.tag !== 'a' && this.href !== '') {
639             throw "Tag must be a to set href.";
640         } else if (this.href.length > 0) {
641             cfg.href = this.href;
642         }
643         
644         if(this.removeClass){
645             cfg.cls = '';
646         }
647         
648         if(this.target){
649             cfg.target = this.target;
650         }
651         
652         return cfg;
653     },
654     initEvents: function() {
655        // Roo.log('init events?');
656 //        Roo.log(this.el.dom);
657        if (this.el.hasClass('roo-button')) {
658             this.el.on('click', this.onClick, this);
659        } else {
660             this.el.select('.roo-button').on('click', this.onClick, this);
661        }
662        
663        
664         
665     },
666     onClick : function(e)
667     {
668         if (this.disabled) {
669             return;
670         }
671         
672         Roo.log('button on click ');
673         if(this.preventDefault){
674             e.preventDefault();
675         }
676         if (this.pressed === true || this.pressed === false) {
677             this.pressed = !this.pressed;
678             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
679             this.fireEvent('toggle', this, e, this.pressed);
680         }
681         
682         
683         this.fireEvent('click', this, e);
684     },
685     
686     /**
687      * Enables this button
688      */
689     enable : function()
690     {
691         this.disabled = false;
692         this.el.removeClass('disabled');
693     },
694     
695     /**
696      * Disable this button
697      */
698     disable : function()
699     {
700         this.disabled = true;
701         this.el.addClass('disabled');
702     },
703      /**
704      * sets the active state on/off, 
705      * @param {Boolean} state (optional) Force a particular state
706      */
707     setActive : function(v) {
708         
709         this.el[v ? 'addClass' : 'removeClass']('active');
710     },
711      /**
712      * toggles the current active state 
713      */
714     toggleActive : function()
715     {
716        var active = this.el.hasClass('active');
717        this.setActive(!active);
718        
719         
720     }
721     
722     
723     
724 });
725
726  /*
727  * - LGPL
728  *
729  * column
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.Column
735  * @extends Roo.bootstrap.Component
736  * Bootstrap Column class
737  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
738  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
739  * @cfg {Number} md colspan out of 12 for computer-sized screens
740  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
741  * @cfg {String} html content of column.
742  * 
743  * @constructor
744  * Create a new Column
745  * @param {Object} config The config object
746  */
747
748 Roo.bootstrap.Column = function(config){
749     Roo.bootstrap.Column.superclass.constructor.call(this, config);
750 };
751
752 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
753     
754     xs: null,
755     sm: null,
756     md: null,
757     lg: null,
758     html: '',
759     offset: 0,
760     
761     getAutoCreate : function(){
762         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
763         
764         cfg = {
765             tag: 'div',
766             cls: 'column'
767         };
768         
769         var settings=this;
770         ['xs','sm','md','lg'].map(function(size){
771             if (settings[size]) {
772                 cfg.cls += ' col-' + size + '-' + settings[size];
773             }
774         });
775         if (this.html.length) {
776             cfg.html = this.html;
777         }
778         
779         return cfg;
780     }
781    
782 });
783
784  
785
786  /*
787  * - LGPL
788  *
789  * page container.
790  * 
791  */
792
793
794 /**
795  * @class Roo.bootstrap.Container
796  * @extends Roo.bootstrap.Component
797  * Bootstrap Container class
798  * @cfg {Boolean} jumbotron is it a jumbotron element
799  * @cfg {String} html content of element
800  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
801  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
802  * @cfg {String} header content of header (for panel)
803  * @cfg {String} footer content of footer (for panel)
804  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
805  *     
806  * @constructor
807  * Create a new Container
808  * @param {Object} config The config object
809  */
810
811 Roo.bootstrap.Container = function(config){
812     Roo.bootstrap.Container.superclass.constructor.call(this, config);
813 };
814
815 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
816     
817     jumbotron : false,
818     well: '',
819     panel : '',
820     header: '',
821     footer : '',
822     sticky: '',
823   
824      
825     getChildContainer : function() {
826         
827         if(!this.el){
828             return false;
829         }
830         
831         if (this.panel.length) {
832             return this.el.select('.panel-body',true).first();
833         }
834         
835         return this.el;
836     },
837     
838     
839     getAutoCreate : function(){
840         
841         var cfg = {
842             html : '',
843             cls : ''
844         };
845         if (this.jumbotron) {
846             cfg.cls = 'jumbotron';
847         }
848         if (this.cls) {
849             cfg.cls = this.cls + '';
850         }
851         
852         if (this.sticky.length) {
853             
854             var bd = Roo.get(document.body);
855             if (!bd.hasClass('bootstrap-sticky')) {
856                 bd.addClass('bootstrap-sticky');
857                 Roo.select('html',true).setStyle('height', '100%');
858             }
859              
860             cfg.cls += 'bootstrap-sticky-' + this.sticky;
861         }
862         
863         
864         if (this.well.length) {
865             switch (this.well) {
866                 case 'lg':
867                 case 'sm':
868                     cfg.cls +=' well well-' +this.well;
869                     break;
870                 default:
871                     cfg.cls +=' well';
872                     break;
873             }
874         }
875         
876         var body = cfg;
877         
878         if (this.panel.length) {
879             cfg.cls += ' panel panel-' + this.panel;
880             cfg.cn = [];
881             if (this.header.length) {
882                 cfg.cn.push({
883                     
884                     cls : 'panel-heading',
885                     cn : [{
886                         tag: 'h3',
887                         cls : 'panel-title',
888                         html : this.header
889                     }]
890                     
891                 });
892             }
893             body = false;
894             cfg.cn.push({
895                 cls : 'panel-body',
896                 html : this.html
897             });
898             
899             
900             if (this.footer.length) {
901                 cfg.cn.push({
902                     cls : 'panel-footer',
903                     html : this.footer
904                     
905                 });
906             }
907             
908         }
909         if (body) {
910             body.html = this.html || cfg.html;
911         }
912         if (!cfg.cls.length) {
913             cfg.cls =  'container';
914         }
915         
916         return cfg;
917     }
918    
919 });
920
921  /*
922  * - LGPL
923  *
924  * image
925  * 
926  */
927
928
929 /**
930  * @class Roo.bootstrap.Img
931  * @extends Roo.bootstrap.Component
932  * Bootstrap Img class
933  * @cfg {Boolean} imgResponsive false | true
934  * @cfg {String} border rounded | circle | thumbnail
935  * @cfg {String} src image source
936  * @cfg {String} alt image alternative text
937  * @cfg {String} href a tag href
938  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
939  * 
940  * @constructor
941  * Create a new Input
942  * @param {Object} config The config object
943  */
944
945 Roo.bootstrap.Img = function(config){
946     Roo.bootstrap.Img.superclass.constructor.call(this, config);
947     
948     this.addEvents({
949         // img events
950         /**
951          * @event click
952          * The img click event for the img.
953          * @param {Roo.EventObject} e
954          */
955         "click" : true
956     });
957 };
958
959 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
960     
961     imgResponsive: true,
962     border: '',
963     src: '',
964     href: false,
965     target: false,
966
967     getAutoCreate : function(){
968         
969         var cfg = {
970             tag: 'img',
971             cls: 'img-responsive',
972             html : null
973         }
974         
975         cfg.html = this.html || cfg.html;
976         
977         cfg.src = this.src || cfg.src;
978         
979         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
980             cfg.cls += ' img-' + this.border;
981         }
982         
983         if(this.alt){
984             cfg.alt = this.alt;
985         }
986         
987         if(this.href){
988             var a = {
989                 tag: 'a',
990                 href: this.href,
991                 cn: [
992                     cfg
993                 ]
994             }
995             
996             if(this.target){
997                 a.target = this.target;
998             }
999             
1000         }
1001         
1002         
1003         return (this.href) ? a : cfg;
1004     },
1005     
1006     initEvents: function() {
1007         
1008         if(!this.href){
1009             this.el.on('click', this.onClick, this);
1010         }
1011     },
1012     
1013     onClick : function(e)
1014     {
1015         Roo.log('img onclick');
1016         this.fireEvent('click', this, e);
1017     }
1018    
1019 });
1020
1021  /*
1022  * - LGPL
1023  *
1024  * header
1025  * 
1026  */
1027
1028 /**
1029  * @class Roo.bootstrap.Header
1030  * @extends Roo.bootstrap.Component
1031  * Bootstrap Header class
1032  * @cfg {String} html content of header
1033  * @cfg {Number} level (1|2|3|4|5|6) default 1
1034  * 
1035  * @constructor
1036  * Create a new Header
1037  * @param {Object} config The config object
1038  */
1039
1040
1041 Roo.bootstrap.Header  = function(config){
1042     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1043 };
1044
1045 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1046     
1047     //href : false,
1048     html : false,
1049     level : 1,
1050     
1051     
1052     
1053     getAutoCreate : function(){
1054         
1055         var cfg = {
1056             tag: 'h' + (1 *this.level),
1057             html: this.html || 'fill in html'
1058         } ;
1059         
1060         return cfg;
1061     }
1062    
1063 });
1064
1065  
1066
1067  /*
1068  * Based on:
1069  * Ext JS Library 1.1.1
1070  * Copyright(c) 2006-2007, Ext JS, LLC.
1071  *
1072  * Originally Released Under LGPL - original licence link has changed is not relivant.
1073  *
1074  * Fork - LGPL
1075  * <script type="text/javascript">
1076  */
1077  
1078 /**
1079  * @class Roo.bootstrap.MenuMgr
1080  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1081  * @singleton
1082  */
1083 Roo.bootstrap.MenuMgr = function(){
1084    var menus, active, groups = {}, attached = false, lastShow = new Date();
1085
1086    // private - called when first menu is created
1087    function init(){
1088        menus = {};
1089        active = new Roo.util.MixedCollection();
1090        Roo.get(document).addKeyListener(27, function(){
1091            if(active.length > 0){
1092                hideAll();
1093            }
1094        });
1095    }
1096
1097    // private
1098    function hideAll(){
1099        if(active && active.length > 0){
1100            var c = active.clone();
1101            c.each(function(m){
1102                m.hide();
1103            });
1104        }
1105    }
1106
1107    // private
1108    function onHide(m){
1109        active.remove(m);
1110        if(active.length < 1){
1111            Roo.get(document).un("mouseup", onMouseDown);
1112             
1113            attached = false;
1114        }
1115    }
1116
1117    // private
1118    function onShow(m){
1119        var last = active.last();
1120        lastShow = new Date();
1121        active.add(m);
1122        if(!attached){
1123           Roo.get(document).on("mouseup", onMouseDown);
1124            
1125            attached = true;
1126        }
1127        if(m.parentMenu){
1128           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1129           m.parentMenu.activeChild = m;
1130        }else if(last && last.isVisible()){
1131           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1132        }
1133    }
1134
1135    // private
1136    function onBeforeHide(m){
1137        if(m.activeChild){
1138            m.activeChild.hide();
1139        }
1140        if(m.autoHideTimer){
1141            clearTimeout(m.autoHideTimer);
1142            delete m.autoHideTimer;
1143        }
1144    }
1145
1146    // private
1147    function onBeforeShow(m){
1148        var pm = m.parentMenu;
1149        if(!pm && !m.allowOtherMenus){
1150            hideAll();
1151        }else if(pm && pm.activeChild && active != m){
1152            pm.activeChild.hide();
1153        }
1154    }
1155
1156    // private
1157    function onMouseDown(e){
1158         Roo.log("on MouseDown");
1159         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1160            hideAll();
1161         }
1162         
1163         
1164    }
1165
1166    // private
1167    function onBeforeCheck(mi, state){
1168        if(state){
1169            var g = groups[mi.group];
1170            for(var i = 0, l = g.length; i < l; i++){
1171                if(g[i] != mi){
1172                    g[i].setChecked(false);
1173                }
1174            }
1175        }
1176    }
1177
1178    return {
1179
1180        /**
1181         * Hides all menus that are currently visible
1182         */
1183        hideAll : function(){
1184             hideAll();  
1185        },
1186
1187        // private
1188        register : function(menu){
1189            if(!menus){
1190                init();
1191            }
1192            menus[menu.id] = menu;
1193            menu.on("beforehide", onBeforeHide);
1194            menu.on("hide", onHide);
1195            menu.on("beforeshow", onBeforeShow);
1196            menu.on("show", onShow);
1197            var g = menu.group;
1198            if(g && menu.events["checkchange"]){
1199                if(!groups[g]){
1200                    groups[g] = [];
1201                }
1202                groups[g].push(menu);
1203                menu.on("checkchange", onCheck);
1204            }
1205        },
1206
1207         /**
1208          * Returns a {@link Roo.menu.Menu} object
1209          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1210          * be used to generate and return a new Menu instance.
1211          */
1212        get : function(menu){
1213            if(typeof menu == "string"){ // menu id
1214                return menus[menu];
1215            }else if(menu.events){  // menu instance
1216                return menu;
1217            }
1218            /*else if(typeof menu.length == 'number'){ // array of menu items?
1219                return new Roo.bootstrap.Menu({items:menu});
1220            }else{ // otherwise, must be a config
1221                return new Roo.bootstrap.Menu(menu);
1222            }
1223            */
1224            return false;
1225        },
1226
1227        // private
1228        unregister : function(menu){
1229            delete menus[menu.id];
1230            menu.un("beforehide", onBeforeHide);
1231            menu.un("hide", onHide);
1232            menu.un("beforeshow", onBeforeShow);
1233            menu.un("show", onShow);
1234            var g = menu.group;
1235            if(g && menu.events["checkchange"]){
1236                groups[g].remove(menu);
1237                menu.un("checkchange", onCheck);
1238            }
1239        },
1240
1241        // private
1242        registerCheckable : function(menuItem){
1243            var g = menuItem.group;
1244            if(g){
1245                if(!groups[g]){
1246                    groups[g] = [];
1247                }
1248                groups[g].push(menuItem);
1249                menuItem.on("beforecheckchange", onBeforeCheck);
1250            }
1251        },
1252
1253        // private
1254        unregisterCheckable : function(menuItem){
1255            var g = menuItem.group;
1256            if(g){
1257                groups[g].remove(menuItem);
1258                menuItem.un("beforecheckchange", onBeforeCheck);
1259            }
1260        }
1261    };
1262 }();/*
1263  * - LGPL
1264  *
1265  * menu
1266  * 
1267  */
1268
1269 /**
1270  * @class Roo.bootstrap.Menu
1271  * @extends Roo.bootstrap.Component
1272  * Bootstrap Menu class - container for MenuItems
1273  * @cfg {String} type type of menu
1274  * 
1275  * @constructor
1276  * Create a new Menu
1277  * @param {Object} config The config object
1278  */
1279
1280
1281 Roo.bootstrap.Menu = function(config){
1282     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1283     if (this.registerMenu) {
1284         Roo.bootstrap.MenuMgr.register(this);
1285     }
1286     this.addEvents({
1287         /**
1288          * @event beforeshow
1289          * Fires before this menu is displayed
1290          * @param {Roo.menu.Menu} this
1291          */
1292         beforeshow : true,
1293         /**
1294          * @event beforehide
1295          * Fires before this menu is hidden
1296          * @param {Roo.menu.Menu} this
1297          */
1298         beforehide : true,
1299         /**
1300          * @event show
1301          * Fires after this menu is displayed
1302          * @param {Roo.menu.Menu} this
1303          */
1304         show : true,
1305         /**
1306          * @event hide
1307          * Fires after this menu is hidden
1308          * @param {Roo.menu.Menu} this
1309          */
1310         hide : true,
1311         /**
1312          * @event click
1313          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1314          * @param {Roo.menu.Menu} this
1315          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1316          * @param {Roo.EventObject} e
1317          */
1318         click : true,
1319         /**
1320          * @event mouseover
1321          * Fires when the mouse is hovering over this menu
1322          * @param {Roo.menu.Menu} this
1323          * @param {Roo.EventObject} e
1324          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1325          */
1326         mouseover : true,
1327         /**
1328          * @event mouseout
1329          * Fires when the mouse exits this menu
1330          * @param {Roo.menu.Menu} this
1331          * @param {Roo.EventObject} e
1332          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1333          */
1334         mouseout : true,
1335         /**
1336          * @event itemclick
1337          * Fires when a menu item contained in this menu is clicked
1338          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1339          * @param {Roo.EventObject} e
1340          */
1341         itemclick: true
1342     });
1343     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1344 };
1345
1346 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1347     
1348    /// html : false,
1349     //align : '',
1350     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1351     type: false,
1352     /**
1353      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1354      */
1355     registerMenu : true,
1356     
1357     menuItems :false, // stores the menu items..
1358     
1359     hidden:true,
1360     
1361     parentMenu : false,
1362     
1363     getChildContainer : function() {
1364         return this.el;  
1365     },
1366     
1367     getAutoCreate : function(){
1368          
1369         //if (['right'].indexOf(this.align)!==-1) {
1370         //    cfg.cn[1].cls += ' pull-right'
1371         //}
1372         var cfg = {
1373             tag : 'ul',
1374             cls : 'dropdown-menu' ,
1375             style : 'z-index:1000'
1376             
1377         }
1378         
1379         if (this.type === 'submenu') {
1380             cfg.cls = 'submenu active'
1381         }
1382         
1383         return cfg;
1384     },
1385     initEvents : function() {
1386         
1387        // Roo.log("ADD event");
1388        // Roo.log(this.triggerEl.dom);
1389         this.triggerEl.on('click', this.onTriggerPress, this);
1390         this.triggerEl.addClass('dropdown-toggle');
1391         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1392
1393         this.el.on("mouseover", this.onMouseOver, this);
1394         this.el.on("mouseout", this.onMouseOut, this);
1395         
1396         
1397     },
1398     findTargetItem : function(e){
1399         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1400         if(!t){
1401             return false;
1402         }
1403         //Roo.log(t);         Roo.log(t.id);
1404         if(t && t.id){
1405             //Roo.log(this.menuitems);
1406             return this.menuitems.get(t.id);
1407             
1408             //return this.items.get(t.menuItemId);
1409         }
1410         
1411         return false;
1412     },
1413     onClick : function(e){
1414         Roo.log("menu.onClick");
1415         var t = this.findTargetItem(e);
1416         if(!t){
1417             return;
1418         }
1419         Roo.log(e);
1420         /*
1421         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1422             if(t == this.activeItem && t.shouldDeactivate(e)){
1423                 this.activeItem.deactivate();
1424                 delete this.activeItem;
1425                 return;
1426             }
1427             if(t.canActivate){
1428                 this.setActiveItem(t, true);
1429             }
1430             return;
1431             
1432             
1433         }
1434         */
1435         Roo.log('pass click event');
1436         
1437         t.onClick(e);
1438         
1439         this.fireEvent("click", this, t, e);
1440         
1441         this.hide();
1442     },
1443      onMouseOver : function(e){
1444         var t  = this.findTargetItem(e);
1445         //Roo.log(t);
1446         //if(t){
1447         //    if(t.canActivate && !t.disabled){
1448         //        this.setActiveItem(t, true);
1449         //    }
1450         //}
1451         
1452         this.fireEvent("mouseover", this, e, t);
1453     },
1454     isVisible : function(){
1455         return !this.hidden;
1456     },
1457      onMouseOut : function(e){
1458         var t  = this.findTargetItem(e);
1459         
1460         //if(t ){
1461         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1462         //        this.activeItem.deactivate();
1463         //        delete this.activeItem;
1464         //    }
1465         //}
1466         this.fireEvent("mouseout", this, e, t);
1467     },
1468     
1469     
1470     /**
1471      * Displays this menu relative to another element
1472      * @param {String/HTMLElement/Roo.Element} element The element to align to
1473      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1474      * the element (defaults to this.defaultAlign)
1475      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1476      */
1477     show : function(el, pos, parentMenu){
1478         this.parentMenu = parentMenu;
1479         if(!this.el){
1480             this.render();
1481         }
1482         this.fireEvent("beforeshow", this);
1483         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1484     },
1485      /**
1486      * Displays this menu at a specific xy position
1487      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1488      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1489      */
1490     showAt : function(xy, parentMenu, /* private: */_e){
1491         this.parentMenu = parentMenu;
1492         if(!this.el){
1493             this.render();
1494         }
1495         if(_e !== false){
1496             this.fireEvent("beforeshow", this);
1497             
1498             //xy = this.el.adjustForConstraints(xy);
1499         }
1500         //this.el.setXY(xy);
1501         //this.el.show();
1502         this.hideMenuItems();
1503         this.hidden = false;
1504         this.triggerEl.addClass('open');
1505         this.focus();
1506         this.fireEvent("show", this);
1507     },
1508     
1509     focus : function(){
1510         return;
1511         if(!this.hidden){
1512             this.doFocus.defer(50, this);
1513         }
1514     },
1515
1516     doFocus : function(){
1517         if(!this.hidden){
1518             this.focusEl.focus();
1519         }
1520     },
1521
1522     /**
1523      * Hides this menu and optionally all parent menus
1524      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1525      */
1526     hide : function(deep){
1527         
1528         this.hideMenuItems();
1529         if(this.el && this.isVisible()){
1530             this.fireEvent("beforehide", this);
1531             if(this.activeItem){
1532                 this.activeItem.deactivate();
1533                 this.activeItem = null;
1534             }
1535             this.triggerEl.removeClass('open');;
1536             this.hidden = true;
1537             this.fireEvent("hide", this);
1538         }
1539         if(deep === true && this.parentMenu){
1540             this.parentMenu.hide(true);
1541         }
1542     },
1543     
1544     onTriggerPress  : function(e)
1545     {
1546         
1547         Roo.log('trigger press');
1548         //Roo.log(e.getTarget());
1549        // Roo.log(this.triggerEl.dom);
1550         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1551             return;
1552         }
1553         if (this.isVisible()) {
1554             Roo.log('hide');
1555             this.hide();
1556         } else {
1557             this.show(this.triggerEl, false, false);
1558         }
1559         
1560         
1561     },
1562     
1563          
1564        
1565     
1566     hideMenuItems : function()
1567     {
1568         //$(backdrop).remove()
1569         Roo.select('.open',true).each(function(aa) {
1570             
1571             aa.removeClass('open');
1572           //var parent = getParent($(this))
1573           //var relatedTarget = { relatedTarget: this }
1574           
1575            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1576           //if (e.isDefaultPrevented()) return
1577            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1578         })
1579     },
1580     addxtypeChild : function (tree, cntr) {
1581         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1582           
1583         this.menuitems.add(comp);
1584         return comp;
1585
1586     },
1587     getEl : function()
1588     {
1589         Roo.log(this.el);
1590         return this.el;
1591     }
1592 });
1593
1594  
1595  /*
1596  * - LGPL
1597  *
1598  * menu item
1599  * 
1600  */
1601
1602
1603 /**
1604  * @class Roo.bootstrap.MenuItem
1605  * @extends Roo.bootstrap.Component
1606  * Bootstrap MenuItem class
1607  * @cfg {String} html the menu label
1608  * @cfg {String} href the link
1609  * @cfg {Boolean} preventDefault (true | false) default true
1610  * 
1611  * 
1612  * @constructor
1613  * Create a new MenuItem
1614  * @param {Object} config The config object
1615  */
1616
1617
1618 Roo.bootstrap.MenuItem = function(config){
1619     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1620     this.addEvents({
1621         // raw events
1622         /**
1623          * @event click
1624          * The raw click event for the entire grid.
1625          * @param {Roo.EventObject} e
1626          */
1627         "click" : true
1628     });
1629 };
1630
1631 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1632     
1633     href : false,
1634     html : false,
1635     preventDefault: true,
1636     
1637     getAutoCreate : function(){
1638         var cfg= {
1639             tag: 'li',
1640         cls: 'dropdown-menu-item',
1641             cn: [
1642             {
1643                 tag : 'a',
1644                 href : '#',
1645                 html : 'Link'
1646             }
1647             ]
1648     };
1649         
1650         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1651         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1652         return cfg;
1653     },
1654     
1655     initEvents: function() {
1656         
1657         //this.el.select('a').on('click', this.onClick, this);
1658         
1659     },
1660     onClick : function(e)
1661     {
1662         Roo.log('item on click ');
1663         //if(this.preventDefault){
1664         //    e.preventDefault();
1665         //}
1666         //this.parent().hideMenuItems();
1667         
1668         this.fireEvent('click', this, e);
1669     },
1670     getEl : function()
1671     {
1672         return this.el;
1673     }
1674 });
1675
1676  
1677
1678  /*
1679  * - LGPL
1680  *
1681  * menu separator
1682  * 
1683  */
1684
1685
1686 /**
1687  * @class Roo.bootstrap.MenuSeparator
1688  * @extends Roo.bootstrap.Component
1689  * Bootstrap MenuSeparator class
1690  * 
1691  * @constructor
1692  * Create a new MenuItem
1693  * @param {Object} config The config object
1694  */
1695
1696
1697 Roo.bootstrap.MenuSeparator = function(config){
1698     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1699 };
1700
1701 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1702     
1703     getAutoCreate : function(){
1704         var cfg = {
1705             cls: 'divider',
1706             tag : 'li'
1707         };
1708         
1709         return cfg;
1710     }
1711    
1712 });
1713
1714  
1715
1716  
1717 /*
1718 <div class="modal fade">
1719   <div class="modal-dialog">
1720     <div class="modal-content">
1721       <div class="modal-header">
1722         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1723         <h4 class="modal-title">Modal title</h4>
1724       </div>
1725       <div class="modal-body">
1726         <p>One fine body&hellip;</p>
1727       </div>
1728       <div class="modal-footer">
1729         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1730         <button type="button" class="btn btn-primary">Save changes</button>
1731       </div>
1732     </div><!-- /.modal-content -->
1733   </div><!-- /.modal-dialog -->
1734 </div><!-- /.modal -->
1735 */
1736 /*
1737  * - LGPL
1738  *
1739  * page contgainer.
1740  * 
1741  */
1742
1743 /**
1744  * @class Roo.bootstrap.Modal
1745  * @extends Roo.bootstrap.Component
1746  * Bootstrap Modal class
1747  * @cfg {String} title Title of dialog
1748  * @cfg {Array} buttons Array of buttons or standard button set..
1749  * 
1750  * @constructor
1751  * Create a new Modal Dialog
1752  * @param {Object} config The config object
1753  */
1754
1755 Roo.bootstrap.Modal = function(config){
1756     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1757     this.addEvents({
1758         // raw events
1759         /**
1760          * @event btnclick
1761          * The raw btnclick event for the button
1762          * @param {Roo.EventObject} e
1763          */
1764         "btnclick" : true
1765     });
1766 };
1767
1768 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1769     
1770     title : 'test dialog',
1771    
1772     buttons : false,
1773     
1774     onRender : function(ct, position)
1775     {
1776         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1777      
1778         if(!this.el){
1779             var cfg = Roo.apply({},  this.getAutoCreate());
1780             cfg.id = Roo.id();
1781             //if(!cfg.name){
1782             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1783             //}
1784             //if (!cfg.name.length) {
1785             //    delete cfg.name;
1786            // }
1787             if (this.cls) {
1788                 cfg.cls += ' ' + this.cls;
1789             }
1790             if (this.style) {
1791                 cfg.style = this.style;
1792             }
1793             this.el = Roo.get(document.body).createChild(cfg, position);
1794         }
1795         //var type = this.el.dom.type;
1796         
1797         if(this.tabIndex !== undefined){
1798             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1799         }
1800         
1801         
1802         
1803         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1804         this.maskEl.enableDisplayMode("block");
1805         this.maskEl.hide();
1806         //this.el.addClass("x-dlg-modal");
1807     
1808         if (this.buttons) {
1809             Roo.each(this.buttons, function(bb) {
1810                 b = Roo.apply({}, bb);
1811                 b.xns = b.xns || Roo.bootstrap;
1812                 b.xtype = b.xtype || 'Button';
1813                 if (typeof(b.listeners) == 'undefined') {
1814                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1815                 }
1816                 
1817                 var btn = Roo.factory(b);
1818                 
1819                 btn.onRender(this.el.select('.modal-footer').first());
1820                 
1821             },this);
1822         }
1823         // render the children.
1824         var nitems = [];
1825         
1826         if(typeof(this.items) != 'undefined'){
1827             var items = this.items;
1828             delete this.items;
1829
1830             for(var i =0;i < items.length;i++) {
1831                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1832             }
1833         }
1834         
1835         this.items = nitems;
1836         this.initEvents();
1837         //this.el.addClass([this.fieldClass, this.cls]);
1838         
1839     },
1840     getAutoCreate : function(){
1841         
1842         
1843         var bdy = {
1844                 cls : 'modal-body',
1845                 html : this.html || ''
1846         };
1847         
1848          
1849         return modal = {
1850             cls: "modal fade",
1851             cn : [
1852                 {
1853                     cls: "modal-dialog",
1854                     cn : [
1855                         {
1856                             cls : "modal-content",
1857                             cn : [
1858                                 {
1859                                     cls : 'modal-header',
1860                                     cn : [
1861                                         {
1862                                             tag: 'button',
1863                                             cls : 'close',
1864                                             html : '&times'
1865                                         },
1866                                         {
1867                                             tag: 'h4',
1868                                             cls : 'modal-title',
1869                                             html : this.title
1870                                         }
1871                                     
1872                                     ]
1873                                 },
1874                                 bdy,
1875                                 {
1876                                     cls : 'modal-footer' 
1877                                 }
1878                                 
1879                                 
1880                             ]
1881                             
1882                         }
1883                     ]
1884                         
1885                 }
1886             ]
1887             
1888             
1889         };
1890           
1891     },
1892     getChildContainer : function() {
1893          
1894          return this.el.select('.modal-body',true).first();
1895         
1896     },
1897     getButtonContainer : function() {
1898          return this.el.select('.modal-footer',true).first();
1899         
1900     },
1901     initEvents : function()
1902     {
1903         this.el.select('.modal-header .close').on('click', this.hide, this);
1904 //        
1905 //        this.addxtype(this);
1906     },
1907     show : function() {
1908         
1909         if (!this.rendered) {
1910             this.render();
1911         }
1912        
1913         this.el.addClass('on');
1914         this.el.removeClass('fade');
1915         this.el.setStyle('display', 'block');
1916         Roo.get(document.body).addClass("x-body-masked");
1917         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1918         this.maskEl.show();
1919         this.el.setStyle('zIndex', '10001');
1920         this.fireEvent('show', this);
1921         
1922         
1923     },
1924     hide : function()
1925     {
1926         Roo.log('Modal hide?!');
1927         this.maskEl.hide();
1928         Roo.get(document.body).removeClass("x-body-masked");
1929         this.el.removeClass('on');
1930         this.el.addClass('fade');
1931         this.el.setStyle('display', 'none');
1932         this.fireEvent('hide', this);
1933     },
1934     onButtonClick: function(btn,e)
1935     {
1936         //Roo.log([a,b,c]);
1937         this.fireEvent('btnclick', btn.name, e);
1938     }
1939 });
1940
1941
1942 Roo.apply(Roo.bootstrap.Modal,  {
1943     /**
1944          * Button config that displays a single OK button
1945          * @type Object
1946          */
1947         OK :  [{
1948             name : 'ok',
1949             weight : 'primary',
1950             html : 'OK'
1951         }], 
1952         /**
1953          * Button config that displays Yes and No buttons
1954          * @type Object
1955          */
1956         YESNO : [
1957             {
1958                 name  : 'no',
1959                 html : 'No'
1960             },
1961             {
1962                 name  :'yes',
1963                 weight : 'primary',
1964                 html : 'Yes'
1965             }
1966         ],
1967         
1968         /**
1969          * Button config that displays OK and Cancel buttons
1970          * @type Object
1971          */
1972         OKCANCEL : [
1973             {
1974                name : 'cancel',
1975                 html : 'Cancel'
1976             },
1977             {
1978                 name : 'ok',
1979                 weight : 'primary',
1980                 html : 'OK'
1981             }
1982         ],
1983         /**
1984          * Button config that displays Yes, No and Cancel buttons
1985          * @type Object
1986          */
1987         YESNOCANCEL : [
1988             {
1989                 name : 'yes',
1990                 weight : 'primary',
1991                 html : 'Yes'
1992             },
1993             {
1994                 name : 'no',
1995                 html : 'No'
1996             },
1997             {
1998                 name : 'cancel',
1999                 html : 'Cancel'
2000             }
2001         ]
2002 });
2003  /*
2004  * - LGPL
2005  *
2006  * navbar
2007  * 
2008  */
2009
2010 /**
2011  * @class Roo.bootstrap.Navbar
2012  * @extends Roo.bootstrap.Component
2013  * Bootstrap Navbar class
2014  * @cfg {Boolean} sidebar has side bar
2015  * @cfg {Boolean} bar is a bar?
2016  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2017  * @cfg {String} brand what is brand
2018  * @cfg {Boolean} inverse is inverted color
2019  * @cfg {String} type (nav | pills | tabs)
2020  * @cfg {Boolean} arrangement stacked | justified
2021  * @cfg {String} align (left | right) alignment
2022  * @cfg {String} brand_href href of the brand
2023  * @cfg {Boolean} main (true|false) main nav bar? default false
2024  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2025  *
2026  * 
2027  * @constructor
2028  * Create a new Navbar
2029  * @param {Object} config The config object
2030  */
2031
2032
2033 Roo.bootstrap.Navbar = function(config){
2034     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2035 };
2036
2037 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2038     
2039     sidebar: false,
2040     
2041     bar: false,
2042     brand: '',
2043     inverse: false,
2044     position: '',
2045     align : false,
2046     type: 'nav',
2047     arrangement: '',
2048     brand_href: false,
2049     main : false,
2050     loadMask : false,
2051     
2052     getAutoCreate : function(){
2053         var cfg = {
2054             cls : 'navbar'
2055         };
2056         
2057         if (this.sidebar === true) {
2058             cfg = {
2059                 tag: 'div',
2060                 cls: 'sidebar-nav'
2061             };
2062             return cfg;
2063         }
2064         
2065         if (this.bar === true) {
2066             cfg = {
2067                 tag: 'nav',
2068                 cls: 'navbar',
2069                 role: 'navigation',
2070                 cn: [
2071                     {
2072                         tag: 'div',
2073                         cls: 'navbar-header',
2074                         cn: [
2075                             {
2076                             tag: 'button',
2077                             type: 'button',
2078                             cls: 'navbar-toggle',
2079                             'data-toggle': 'collapse',
2080                             cn: [
2081                                 {
2082                                     tag: 'span',
2083                                     cls: 'sr-only',
2084                                     html: 'Toggle navigation'
2085                                 },
2086                                 {
2087                                     tag: 'span',
2088                                     cls: 'icon-bar'
2089                                 },
2090                                 {
2091                                     tag: 'span',
2092                                     cls: 'icon-bar'
2093                                 },
2094                                 {
2095                                     tag: 'span',
2096                                     cls: 'icon-bar'
2097                                 }
2098                             ]
2099                             }
2100                         ]
2101                     },
2102                     {
2103                     tag: 'div',
2104                     cls: 'collapse navbar-collapse'
2105                     }
2106                 ]
2107             };
2108             
2109             cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2110             
2111             if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2112                 cfg.cls += ' navbar-' + this.position;
2113                 cfg.tag = this.position  == 'fixed-bottom' ? 'footer' : 'header';
2114             }
2115             
2116             if (this.brand !== '') {
2117                 cfg.cn[0].cn.push({
2118                     tag: 'a',
2119                     href: this.brand_href ? this.brand_href : '#',
2120                     cls: 'navbar-brand',
2121                     cn: [
2122                     this.brand
2123                     ]
2124                 });
2125             }
2126             
2127             if(this.main){
2128                 cfg.cls += ' main-nav';
2129             }
2130             
2131             
2132             return cfg;
2133         
2134         } else if (this.bar === false) {
2135             
2136         } else {
2137             Roo.log('Property \'bar\' in of Navbar must be either true or false')
2138         }
2139         
2140         cfg.cn = [
2141             {
2142                 cls: 'nav',
2143                 tag : 'ul'
2144             }
2145         ];
2146         
2147         if (['tabs','pills'].indexOf(this.type)!==-1) {
2148             cfg.cn[0].cls += ' nav-' + this.type
2149         } else {
2150             if (this.type!=='nav') {
2151             Roo.log('nav type must be nav/tabs/pills')
2152             }
2153             cfg.cn[0].cls += ' navbar-nav'
2154         }
2155         
2156         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2157             cfg.cn[0].cls += ' nav-' + this.arrangement;
2158         }
2159         
2160         if (this.align === 'right') {
2161             cfg.cn[0].cls += ' navbar-right';
2162         }
2163         if (this.inverse) {
2164             cfg.cls += ' navbar-inverse';
2165             
2166         }
2167         
2168         
2169         return cfg;
2170     },
2171     
2172     initEvents :function ()
2173     {
2174         //Roo.log(this.el.select('.navbar-toggle',true));
2175         this.el.select('.navbar-toggle',true).on('click', function() {
2176            // Roo.log('click');
2177             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2178         }, this);
2179         
2180         var mark = {
2181             tag: "div",
2182             cls:"x-dlg-mask"
2183         }
2184         
2185         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2186         
2187         var size = this.el.getSize();
2188         this.maskEl.setSize(size.width, size.height);
2189         this.maskEl.enableDisplayMode("block");
2190         this.maskEl.hide();
2191         
2192         if(this.loadMask){
2193             this.maskEl.show();
2194         }
2195     },
2196     
2197     
2198     getChildContainer : function()
2199     {
2200         if (this.bar === true) {
2201             return this.el.select('.collapse',true).first();
2202         }
2203         
2204         return this.el;
2205     },
2206     
2207     mask : function()
2208     {
2209         this.maskEl.show();
2210     },
2211     
2212     unmask : function()
2213     {
2214         this.maskEl.hide();
2215     }
2216    
2217 });
2218
2219  
2220
2221  /*
2222  * - LGPL
2223  *
2224  * nav group
2225  * 
2226  */
2227
2228 /**
2229  * @class Roo.bootstrap.NavGroup
2230  * @extends Roo.bootstrap.Component
2231  * Bootstrap NavGroup class
2232  * @cfg {String} align left | right
2233  * @cfg {Boolean} inverse false | true
2234  * @cfg {String} type (nav|pills|tab) default nav
2235  * 
2236  * @constructor
2237  * Create a new nav group
2238  * @param {Object} config The config object
2239  */
2240
2241 Roo.bootstrap.NavGroup = function(config){
2242     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2243 };
2244
2245 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
2246     
2247     align: '',
2248     inverse: false,
2249     form: false,
2250     type: 'nav',
2251     
2252     getAutoCreate : function(){
2253         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2254         
2255         cfg = {
2256             tag : 'ul',
2257             cls: 'nav' 
2258         }
2259         
2260         if (['tabs','pills'].indexOf(this.type)!==-1) {
2261             cfg.cls += ' nav-' + this.type
2262         } else {
2263             if (this.type!=='nav') {
2264                 Roo.log('nav type must be nav/tabs/pills')
2265             }
2266             cfg.cls += ' navbar-nav'
2267         }
2268         
2269         if (this.parent().sidebar === true) {
2270             cfg = {
2271                 tag: 'ul',
2272                 cls: 'dashboard-menu'
2273             }
2274             
2275             return cfg;
2276         }
2277         
2278         if (this.form === true) {
2279             cfg = {
2280                 tag: 'form',
2281                 cls: 'navbar-form'
2282             }
2283             
2284             if (this.align === 'right') {
2285                 cfg.cls += ' navbar-right';
2286             } else {
2287                 cfg.cls += ' navbar-left';
2288             }
2289         }
2290         
2291         if (this.align === 'right') {
2292             cfg.cls += ' navbar-right';
2293         }
2294         
2295         if (this.inverse) {
2296             cfg.cls += ' navbar-inverse';
2297             
2298         }
2299         
2300         
2301         return cfg;
2302     }
2303    
2304 });
2305
2306  
2307
2308  /*
2309  * - LGPL
2310  *
2311  * row
2312  * 
2313  */
2314
2315 /**
2316  * @class Roo.bootstrap.Navbar.Item
2317  * @extends Roo.bootstrap.Component
2318  * Bootstrap Navbar.Button class
2319  * @cfg {String} href  link to
2320  * @cfg {String} html content of button
2321  * @cfg {String} badge text inside badge
2322  * @cfg {String} glyphicon name of glyphicon
2323  * @cfg {String} icon name of font awesome icon
2324  * @cfg {Boolena} active Is item active
2325  * @cfg {Boolean} preventDefault (true | false) default false
2326   
2327  * @constructor
2328  * Create a new Navbar Button
2329  * @param {Object} config The config object
2330  */
2331 Roo.bootstrap.Navbar.Item = function(config){
2332     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
2333     this.addEvents({
2334         // raw events
2335         /**
2336          * @event click
2337          * The raw click event for the entire grid.
2338          * @param {Roo.EventObject} e
2339          */
2340         "click" : true
2341     });
2342 };
2343
2344 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
2345     
2346     href: false,
2347     html: '',
2348     badge: '',
2349     icon: false,
2350     glyphicon: false,
2351     icon: false,
2352     active: false,
2353     preventDefault : false,
2354     
2355     getAutoCreate : function(){
2356         
2357         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
2358         
2359         if (this.parent().parent().sidebar === true) {
2360             cfg = {
2361                 tag: 'li',
2362                 cls: '',
2363                 cn: [
2364                     {
2365                         tag: 'p',
2366                         cls: ''
2367                     }
2368                 ]
2369             }
2370             
2371             if (this.html) {
2372                 cfg.cn[0].html = this.html;
2373             }
2374             
2375             if (this.active) {
2376                 this.cls += ' active';
2377             }
2378             
2379             if (this.menu) {
2380                 cfg.cn[0].cls += ' dropdown-toggle';
2381                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
2382             }
2383             
2384             if (this.href) {
2385                 cfg.cn[0].tag = 'a',
2386                 cfg.cn[0].href = this.href;
2387             }
2388             
2389             if (this.glyphicon) {
2390                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2391             }
2392             
2393             if (this.icon) {
2394                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2395             }
2396             
2397             return cfg;
2398         }
2399         
2400         cfg = {
2401             tag: 'li',
2402             cls: 'nav-item'
2403         }
2404         
2405         if (this.active) {
2406             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
2407         }
2408             
2409         cfg.cn = [
2410             {
2411                 tag: 'p',
2412                 html: 'Text'
2413             }
2414         ];
2415         
2416         if (this.glyphicon) {
2417             if(cfg.html){cfg.html = ' ' + this.html};
2418             cfg.cn=[
2419                 {
2420                     tag: 'span',
2421                     cls: 'glyphicon glyphicon-' + this.glyphicon
2422                 }
2423             ];
2424         }
2425         
2426         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2427         
2428         if (this.menu) {
2429             cfg.cn[0].tag='a';
2430             cfg.cn[0].href='#';
2431             cfg.cn[0].html += " <span class='caret'></span>";
2432         //}else if (!this.href) {
2433         //    cfg.cn[0].tag='p';
2434         //    cfg.cn[0].cls='navbar-text';
2435         } else {
2436             cfg.cn[0].tag='a';
2437             cfg.cn[0].href=this.href||'#';
2438             cfg.cn[0].html=this.html;
2439         }
2440         
2441         if (this.badge !== '') {
2442             
2443             cfg.cn[0].cn=[
2444                 cfg.cn[0].html + ' ',
2445                 {
2446                     tag: 'span',
2447                     cls: 'badge',
2448                     html: this.badge
2449                 }
2450             ];
2451             cfg.cn[0].html=''
2452         }
2453          
2454         if (this.icon) {
2455             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2456         }
2457         
2458         return cfg;
2459     },
2460     initEvents: function() {
2461        // Roo.log('init events?');
2462        // Roo.log(this.el.dom);
2463         this.el.select('a',true).on('click', this.onClick, this);
2464     },
2465     
2466     onClick : function(e)
2467     {
2468         if(this.preventDefault){
2469             e.preventDefault();
2470         }
2471         
2472         if(this.fireEvent('click', this, e) === false){
2473             return;
2474         };
2475         
2476         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
2477             this.onTabsClick(e);
2478         } 
2479     },
2480     
2481     onTabsClick : function(e)
2482     {
2483         Roo.each(this.parent().el.select('.active',true).elements, function(v){
2484             v.removeClass('active');
2485         })
2486
2487         this.el.addClass('active');
2488
2489         if(this.href && this.href.substring(0,1) == '#'){
2490             var tab = Roo.select('[tabId=' + this.href + ']', true).first();
2491
2492             Roo.each(tab.findParent('.tab-content', 0, true).select('.active', true).elements, function(v){
2493                 v.removeClass('active');
2494             });
2495
2496             tab.addClass('active');
2497         }
2498     }
2499    
2500 });
2501  
2502
2503  /*
2504  * - LGPL
2505  *
2506  * row
2507  * 
2508  */
2509
2510 /**
2511  * @class Roo.bootstrap.Row
2512  * @extends Roo.bootstrap.Component
2513  * Bootstrap Row class (contains columns...)
2514  * 
2515  * @constructor
2516  * Create a new Row
2517  * @param {Object} config The config object
2518  */
2519
2520 Roo.bootstrap.Row = function(config){
2521     Roo.bootstrap.Row.superclass.constructor.call(this, config);
2522 };
2523
2524 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
2525     
2526     getAutoCreate : function(){
2527        return {
2528             cls: 'row clearfix'
2529        };
2530     }
2531     
2532     
2533 });
2534
2535  
2536
2537  /*
2538  * - LGPL
2539  *
2540  * element
2541  * 
2542  */
2543
2544 /**
2545  * @class Roo.bootstrap.Element
2546  * @extends Roo.bootstrap.Component
2547  * Bootstrap Element class
2548  * @cfg {String} html contents of the element
2549  * @cfg {String} tag tag of the element
2550  * @cfg {String} cls class of the element
2551  * 
2552  * @constructor
2553  * Create a new Element
2554  * @param {Object} config The config object
2555  */
2556
2557 Roo.bootstrap.Element = function(config){
2558     Roo.bootstrap.Element.superclass.constructor.call(this, config);
2559 };
2560
2561 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
2562     
2563     tag: 'div',
2564     cls: '',
2565     html: '',
2566      
2567     
2568     getAutoCreate : function(){
2569         
2570         var cfg = {
2571             tag: this.tag,
2572             cls: this.cls,
2573             html: this.html
2574         }
2575         
2576         
2577         
2578         return cfg;
2579     }
2580    
2581 });
2582
2583  
2584
2585  /*
2586  * - LGPL
2587  *
2588  * pagination
2589  * 
2590  */
2591
2592 /**
2593  * @class Roo.bootstrap.Pagination
2594  * @extends Roo.bootstrap.Component
2595  * Bootstrap Pagination class
2596  * @cfg {String} size xs | sm | md | lg
2597  * @cfg {Boolean} inverse false | true
2598  * 
2599  * @constructor
2600  * Create a new Pagination
2601  * @param {Object} config The config object
2602  */
2603
2604 Roo.bootstrap.Pagination = function(config){
2605     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
2606 };
2607
2608 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
2609     
2610     cls: false,
2611     size: false,
2612     inverse: false,
2613     
2614     getAutoCreate : function(){
2615         var cfg = {
2616             tag: 'ul',
2617                 cls: 'pagination'
2618         };
2619         if (this.inverse) {
2620             cfg.cls += ' inverse';
2621         }
2622         if (this.html) {
2623             cfg.html=this.html;
2624         }
2625         if (this.cls) {
2626             cfg.cls += " " + this.cls;
2627         }
2628         return cfg;
2629     }
2630    
2631 });
2632
2633  
2634
2635  /*
2636  * - LGPL
2637  *
2638  * Pagination item
2639  * 
2640  */
2641
2642
2643 /**
2644  * @class Roo.bootstrap.PaginationItem
2645  * @extends Roo.bootstrap.Component
2646  * Bootstrap PaginationItem class
2647  * @cfg {String} html text
2648  * @cfg {String} href the link
2649  * @cfg {Boolean} preventDefault (true | false) default true
2650  * @cfg {Boolean} active (true | false) default false
2651  * 
2652  * 
2653  * @constructor
2654  * Create a new PaginationItem
2655  * @param {Object} config The config object
2656  */
2657
2658
2659 Roo.bootstrap.PaginationItem = function(config){
2660     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
2661     this.addEvents({
2662         // raw events
2663         /**
2664          * @event click
2665          * The raw click event for the entire grid.
2666          * @param {Roo.EventObject} e
2667          */
2668         "click" : true
2669     });
2670 };
2671
2672 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
2673     
2674     href : false,
2675     html : false,
2676     preventDefault: true,
2677     active : false,
2678     cls : false,
2679     
2680     getAutoCreate : function(){
2681         var cfg= {
2682             tag: 'li',
2683             cn: [
2684                 {
2685                     tag : 'a',
2686                     href : this.href ? this.href : '#',
2687                     html : this.html ? this.html : ''
2688                 }
2689             ]
2690         };
2691         
2692         if(this.cls){
2693             cfg.cls = this.cls;
2694         }
2695         
2696         if(this.active){
2697             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
2698         }
2699         
2700         return cfg;
2701     },
2702     
2703     initEvents: function() {
2704         
2705         this.el.on('click', this.onClick, this);
2706         
2707     },
2708     onClick : function(e)
2709     {
2710         Roo.log('PaginationItem on click ');
2711         if(this.preventDefault){
2712             e.preventDefault();
2713         }
2714         
2715         this.fireEvent('click', this, e);
2716     }
2717    
2718 });
2719
2720  
2721
2722  /*
2723  * - LGPL
2724  *
2725  * slider
2726  * 
2727  */
2728
2729
2730 /**
2731  * @class Roo.bootstrap.Slider
2732  * @extends Roo.bootstrap.Component
2733  * Bootstrap Slider class
2734  *    
2735  * @constructor
2736  * Create a new Slider
2737  * @param {Object} config The config object
2738  */
2739
2740 Roo.bootstrap.Slider = function(config){
2741     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2742 };
2743
2744 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
2745     
2746     getAutoCreate : function(){
2747         
2748         var cfg = {
2749             tag: 'div',
2750             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2751             cn: [
2752                 {
2753                     tag: 'a',
2754                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
2755                 }
2756             ]
2757         }
2758         
2759         return cfg;
2760     }
2761    
2762 });
2763
2764  /*
2765  * - LGPL
2766  *
2767  * table
2768  * 
2769  */
2770
2771 /**
2772  * @class Roo.bootstrap.Table
2773  * @extends Roo.bootstrap.Component
2774  * Bootstrap Table class
2775  * @cfg {String} cls table class
2776  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
2777  * @cfg {String} bgcolor Specifies the background color for a table
2778  * @cfg {Number} border Specifies whether the table cells should have borders or not
2779  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
2780  * @cfg {Number} cellspacing Specifies the space between cells
2781  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
2782  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
2783  * @cfg {String} sortable Specifies that the table should be sortable
2784  * @cfg {String} summary Specifies a summary of the content of a table
2785  * @cfg {Number} width Specifies the width of a table
2786  * 
2787  * @cfg {boolean} striped Should the rows be alternative striped
2788  * @cfg {boolean} bordered Add borders to the table
2789  * @cfg {boolean} hover Add hover highlighting
2790  * @cfg {boolean} condensed Format condensed
2791  * @cfg {boolean} responsive Format condensed
2792  *
2793  
2794  
2795  * 
2796  * @constructor
2797  * Create a new Table
2798  * @param {Object} config The config object
2799  */
2800
2801 Roo.bootstrap.Table = function(config){
2802     Roo.bootstrap.Table.superclass.constructor.call(this, config);
2803     
2804     if (this.sm) {
2805         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
2806         this.sm = this.selModel;
2807         this.sm.xmodule = this.xmodule || false;
2808     }
2809     if (this.cm && typeof(this.cm.config) == 'undefined') {
2810         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
2811         this.cm = this.colModel;
2812         this.cm.xmodule = this.xmodule || false;
2813     }
2814     if (this.store) {
2815         this.store= Roo.factory(this.store, Roo.data);
2816         this.ds = this.store;
2817         this.ds.xmodule = this.xmodule || false;
2818          
2819     }
2820 };
2821
2822 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
2823     
2824     cls: false,
2825     align: false,
2826     bgcolor: false,
2827     border: false,
2828     cellpadding: false,
2829     cellspacing: false,
2830     frame: false,
2831     rules: false,
2832     sortable: false,
2833     summary: false,
2834     width: false,
2835     striped : false,
2836     bordered: false,
2837     hover:  false,
2838     condensed : false,
2839     responsive : false,
2840     sm : false,
2841     cm : false,
2842     store : false,
2843     
2844     getAutoCreate : function(){
2845         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2846         
2847         cfg = {
2848             tag: 'table',
2849             cls : 'table',
2850             cn : []
2851         }
2852             
2853         if (this.striped) {
2854             cfg.cls += ' table-striped';
2855         }
2856         if (this.hover) {
2857             cfg.cls += ' table-hover';
2858         }
2859         if (this.bordered) {
2860             cfg.cls += ' table-bordered';
2861         }
2862         if (this.condensed) {
2863             cfg.cls += ' table-condensed';
2864         }
2865         if (this.responsive) {
2866             cfg.cls += ' table-responsive';
2867         }
2868         
2869           
2870         
2871         
2872         if (this.cls) {
2873             cfg.cls+=  ' ' +this.cls;
2874         }
2875         
2876         // this lot should be simplifed...
2877         
2878         if (this.align) {
2879             cfg.align=this.align;
2880         }
2881         if (this.bgcolor) {
2882             cfg.bgcolor=this.bgcolor;
2883         }
2884         if (this.border) {
2885             cfg.border=this.border;
2886         }
2887         if (this.cellpadding) {
2888             cfg.cellpadding=this.cellpadding;
2889         }
2890         if (this.cellspacing) {
2891             cfg.cellspacing=this.cellspacing;
2892         }
2893         if (this.frame) {
2894             cfg.frame=this.frame;
2895         }
2896         if (this.rules) {
2897             cfg.rules=this.rules;
2898         }
2899         if (this.sortable) {
2900             cfg.sortable=this.sortable;
2901         }
2902         if (this.summary) {
2903             cfg.summary=this.summary;
2904         }
2905         if (this.width) {
2906             cfg.width=this.width;
2907         }
2908         
2909         if(this.store || this.cm){
2910             cfg.cn.push(this.renderHeader());
2911             cfg.cn.push(this.renderBody());
2912             cfg.cn.push(this.renderFooter());
2913             
2914             cfg.cls+=  ' TableGrid';
2915         }
2916         
2917         return cfg;
2918     },
2919 //    
2920 //    initTableGrid : function()
2921 //    {
2922 //        var cfg = {};
2923 //        
2924 //        var header = {
2925 //            tag: 'thead',
2926 //            cn : []
2927 //        };
2928 //        
2929 //        var cm = this.cm;
2930 //        
2931 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2932 //            header.cn.push({
2933 //                tag: 'th',
2934 //                html: cm.getColumnHeader(i)
2935 //            })
2936 //        }
2937 //        
2938 //        cfg.push(header);
2939 //        
2940 //        return cfg;
2941 //        
2942 //        
2943 //    },
2944     
2945     initEvents : function()
2946     {   
2947         if(!this.store || !this.cm){
2948             return;
2949         }
2950         
2951         Roo.log('initEvents with ds!!!!');
2952         
2953         var _this = this;
2954         
2955         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
2956             e.on('click', _this.sort, _this);
2957         });
2958 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2959 //        this.maskEl.enableDisplayMode("block");
2960 //        this.maskEl.show();
2961         
2962         this.store.on('load', this.onLoad, this);
2963         this.store.on('beforeload', this.onBeforeLoad, this);
2964         
2965         this.store.load();
2966         
2967         
2968         
2969     },
2970     
2971     sort : function(e,el)
2972     {
2973         var col = Roo.get(el)
2974         
2975         if(!col.hasClass('sortable')){
2976             return;
2977         }
2978         
2979         var sort = col.attr('sort');
2980         var dir = 'ASC';
2981         
2982         if(col.hasClass('glyphicon-arrow-up')){
2983             dir = 'DESC';
2984         }
2985         
2986         this.store.sortInfo = {field : sort, direction : dir};
2987         
2988         this.store.load();
2989     },
2990     
2991     renderHeader : function()
2992     {
2993         var header = {
2994             tag: 'thead',
2995             cn : []
2996         };
2997         
2998         var cm = this.cm;
2999         
3000         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3001             
3002             var config = cm.config[i];
3003             
3004             var c = {
3005                 tag: 'th',
3006                 html: cm.getColumnHeader(i)
3007             };
3008             
3009             if(typeof(config.dataIndex) != 'undefined'){
3010                 c.sort = config.dataIndex;
3011             }
3012             
3013             if(typeof(config.sortable) != 'undefined' && config.sortable){
3014                 c.cls = 'sortable';
3015             }
3016             
3017             header.cn.push(c)
3018         }
3019         
3020         return header;
3021     },
3022     
3023     renderBody : function()
3024     {
3025         var body = {
3026             tag: 'tbody',
3027             cn : []
3028         };
3029         
3030         return body;
3031     },
3032     
3033     renderFooter : function()
3034     {
3035         var footer = {
3036             tag: 'tfoot',
3037             cn : []
3038         };
3039         
3040         return footer;
3041     },
3042     
3043     onLoad : function()
3044     {
3045         Roo.log('ds onload');
3046         
3047         var _this = this;
3048         var cm = this.cm;
3049         
3050         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3051             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
3052             
3053             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
3054                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
3055             }
3056             
3057             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
3058                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
3059             }
3060         });
3061         
3062         var tbody = this.el.select('tbody', true).first();
3063         
3064         var renders = [];
3065         
3066         if(this.store.getCount() > 0){
3067             this.store.data.each(function(d){
3068                 var row = {
3069                     tag : 'tr',
3070                     cn : []
3071                 };
3072                 
3073                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3074                     var renderer = cm.getRenderer(i);
3075                     var config = cm.config[i];
3076                     var value = '';
3077                     var id = Roo.id();
3078                     
3079                     if(typeof(renderer) !== 'undefined'){
3080                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
3081                     }
3082                     
3083                     if(typeof(value) === 'object'){
3084                         renders.push({
3085                             id : id,
3086                             cfg : value 
3087                         })
3088                     }
3089                     
3090                     var td = {
3091                         tag: 'td',
3092                         id: id,
3093                         html: (typeof(value) === 'object') ? '' : value
3094                     };
3095                     
3096                     if(typeof(config.width) != 'undefined'){
3097                         td.width = config.width;
3098                     }
3099                     
3100                     row.cn.push(td);
3101                    
3102                 }
3103                 
3104                 tbody.createChild(row);
3105                 
3106             });
3107         }
3108         
3109         
3110         if(renders.length){
3111             var _this = this;
3112             Roo.each(renders, function(r){
3113                 _this.renderColumn(r);
3114             })
3115         }
3116 //        
3117 //        if(this.loadMask){
3118 //            this.maskEl.hide();
3119 //        }
3120     },
3121     
3122     onBeforeLoad : function()
3123     {
3124         Roo.log('ds onBeforeLoad');
3125         
3126 //        this.clear();
3127         
3128 //        if(this.loadMask){
3129 //            this.maskEl.show();
3130 //        }
3131     },
3132     
3133     clear : function()
3134     {
3135         this.el.select('tbody', true).first().dom.innerHTML = '';
3136     },
3137     
3138     getSelectionModel : function(){
3139         if(!this.selModel){
3140             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3141         }
3142         return this.selModel;
3143     },
3144     
3145     renderColumn : function(r)
3146     {
3147         var _this = this;
3148         r.cfg.render(Roo.get(r.id));
3149         
3150         if(r.cfg.cn){
3151             Roo.each(r.cfg.cn, function(c){
3152                 var child = {
3153                     id: r.id,
3154                     cfg: c
3155                 }
3156                 _this.renderColumn(child);
3157             })
3158         }
3159     }
3160    
3161 });
3162
3163  
3164
3165  /*
3166  * - LGPL
3167  *
3168  * table cell
3169  * 
3170  */
3171
3172 /**
3173  * @class Roo.bootstrap.TableCell
3174  * @extends Roo.bootstrap.Component
3175  * Bootstrap TableCell class
3176  * @cfg {String} html cell contain text
3177  * @cfg {String} cls cell class
3178  * @cfg {String} tag cell tag (td|th) default td
3179  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3180  * @cfg {String} align Aligns the content in a cell
3181  * @cfg {String} axis Categorizes cells
3182  * @cfg {String} bgcolor Specifies the background color of a cell
3183  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3184  * @cfg {Number} colspan Specifies the number of columns a cell should span
3185  * @cfg {String} headers Specifies one or more header cells a cell is related to
3186  * @cfg {Number} height Sets the height of a cell
3187  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3188  * @cfg {Number} rowspan Sets the number of rows a cell should span
3189  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3190  * @cfg {String} valign Vertical aligns the content in a cell
3191  * @cfg {Number} width Specifies the width of a cell
3192  * 
3193  * @constructor
3194  * Create a new TableCell
3195  * @param {Object} config The config object
3196  */
3197
3198 Roo.bootstrap.TableCell = function(config){
3199     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3200 };
3201
3202 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3203     
3204     html: false,
3205     cls: false,
3206     tag: false,
3207     abbr: false,
3208     align: false,
3209     axis: false,
3210     bgcolor: false,
3211     charoff: false,
3212     colspan: false,
3213     headers: false,
3214     height: false,
3215     nowrap: false,
3216     rowspan: false,
3217     scope: false,
3218     valign: false,
3219     width: false,
3220     
3221     
3222     getAutoCreate : function(){
3223         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3224         
3225         cfg = {
3226             tag: 'td'
3227         }
3228         
3229         if(this.tag){
3230             cfg.tag = this.tag;
3231         }
3232         
3233         if (this.html) {
3234             cfg.html=this.html
3235         }
3236         if (this.cls) {
3237             cfg.cls=this.cls
3238         }
3239         if (this.abbr) {
3240             cfg.abbr=this.abbr
3241         }
3242         if (this.align) {
3243             cfg.align=this.align
3244         }
3245         if (this.axis) {
3246             cfg.axis=this.axis
3247         }
3248         if (this.bgcolor) {
3249             cfg.bgcolor=this.bgcolor
3250         }
3251         if (this.charoff) {
3252             cfg.charoff=this.charoff
3253         }
3254         if (this.colspan) {
3255             cfg.colspan=this.colspan
3256         }
3257         if (this.headers) {
3258             cfg.headers=this.headers
3259         }
3260         if (this.height) {
3261             cfg.height=this.height
3262         }
3263         if (this.nowrap) {
3264             cfg.nowrap=this.nowrap
3265         }
3266         if (this.rowspan) {
3267             cfg.rowspan=this.rowspan
3268         }
3269         if (this.scope) {
3270             cfg.scope=this.scope
3271         }
3272         if (this.valign) {
3273             cfg.valign=this.valign
3274         }
3275         if (this.width) {
3276             cfg.width=this.width
3277         }
3278         
3279         
3280         return cfg;
3281     }
3282    
3283 });
3284
3285  
3286
3287  /*
3288  * - LGPL
3289  *
3290  * table row
3291  * 
3292  */
3293
3294 /**
3295  * @class Roo.bootstrap.TableRow
3296  * @extends Roo.bootstrap.Component
3297  * Bootstrap TableRow class
3298  * @cfg {String} cls row class
3299  * @cfg {String} align Aligns the content in a table row
3300  * @cfg {String} bgcolor Specifies a background color for a table row
3301  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3302  * @cfg {String} valign Vertical aligns the content in a table row
3303  * 
3304  * @constructor
3305  * Create a new TableRow
3306  * @param {Object} config The config object
3307  */
3308
3309 Roo.bootstrap.TableRow = function(config){
3310     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3311 };
3312
3313 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
3314     
3315     cls: false,
3316     align: false,
3317     bgcolor: false,
3318     charoff: false,
3319     valign: false,
3320     
3321     getAutoCreate : function(){
3322         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3323         
3324         cfg = {
3325             tag: 'tr'
3326         }
3327             
3328         if(this.cls){
3329             cfg.cls = this.cls;
3330         }
3331         if(this.align){
3332             cfg.align = this.align;
3333         }
3334         if(this.bgcolor){
3335             cfg.bgcolor = this.bgcolor;
3336         }
3337         if(this.charoff){
3338             cfg.charoff = this.charoff;
3339         }
3340         if(this.valign){
3341             cfg.valign = this.valign;
3342         }
3343         
3344         return cfg;
3345     }
3346    
3347 });
3348
3349  
3350
3351  /*
3352  * - LGPL
3353  *
3354  * table body
3355  * 
3356  */
3357
3358 /**
3359  * @class Roo.bootstrap.TableBody
3360  * @extends Roo.bootstrap.Component
3361  * Bootstrap TableBody class
3362  * @cfg {String} cls element class
3363  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3364  * @cfg {String} align Aligns the content inside the element
3365  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3366  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3367  * 
3368  * @constructor
3369  * Create a new TableBody
3370  * @param {Object} config The config object
3371  */
3372
3373 Roo.bootstrap.TableBody = function(config){
3374     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3375 };
3376
3377 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
3378     
3379     cls: false,
3380     tag: false,
3381     align: false,
3382     charoff: false,
3383     valign: false,
3384     
3385     getAutoCreate : function(){
3386         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3387         
3388         cfg = {
3389             tag: 'tbody'
3390         }
3391             
3392         if (this.cls) {
3393             cfg.cls=this.cls
3394         }
3395         if(this.tag){
3396             cfg.tag = this.tag;
3397         }
3398         
3399         if(this.align){
3400             cfg.align = this.align;
3401         }
3402         if(this.charoff){
3403             cfg.charoff = this.charoff;
3404         }
3405         if(this.valign){
3406             cfg.valign = this.valign;
3407         }
3408         
3409         return cfg;
3410     }
3411     
3412     
3413 //    initEvents : function()
3414 //    {
3415 //        
3416 //        if(!this.store){
3417 //            return;
3418 //        }
3419 //        
3420 //        this.store = Roo.factory(this.store, Roo.data);
3421 //        this.store.on('load', this.onLoad, this);
3422 //        
3423 //        this.store.load();
3424 //        
3425 //    },
3426 //    
3427 //    onLoad: function () 
3428 //    {   
3429 //        this.fireEvent('load', this);
3430 //    }
3431 //    
3432 //   
3433 });
3434
3435  
3436
3437  /*
3438  * Based on:
3439  * Ext JS Library 1.1.1
3440  * Copyright(c) 2006-2007, Ext JS, LLC.
3441  *
3442  * Originally Released Under LGPL - original licence link has changed is not relivant.
3443  *
3444  * Fork - LGPL
3445  * <script type="text/javascript">
3446  */
3447
3448 // as we use this in bootstrap.
3449 Roo.namespace('Roo.form');
3450  /**
3451  * @class Roo.form.Action
3452  * Internal Class used to handle form actions
3453  * @constructor
3454  * @param {Roo.form.BasicForm} el The form element or its id
3455  * @param {Object} config Configuration options
3456  */
3457
3458  
3459  
3460 // define the action interface
3461 Roo.form.Action = function(form, options){
3462     this.form = form;
3463     this.options = options || {};
3464 };
3465 /**
3466  * Client Validation Failed
3467  * @const 
3468  */
3469 Roo.form.Action.CLIENT_INVALID = 'client';
3470 /**
3471  * Server Validation Failed
3472  * @const 
3473  */
3474 Roo.form.Action.SERVER_INVALID = 'server';
3475  /**
3476  * Connect to Server Failed
3477  * @const 
3478  */
3479 Roo.form.Action.CONNECT_FAILURE = 'connect';
3480 /**
3481  * Reading Data from Server Failed
3482  * @const 
3483  */
3484 Roo.form.Action.LOAD_FAILURE = 'load';
3485
3486 Roo.form.Action.prototype = {
3487     type : 'default',
3488     failureType : undefined,
3489     response : undefined,
3490     result : undefined,
3491
3492     // interface method
3493     run : function(options){
3494
3495     },
3496
3497     // interface method
3498     success : function(response){
3499
3500     },
3501
3502     // interface method
3503     handleResponse : function(response){
3504
3505     },
3506
3507     // default connection failure
3508     failure : function(response){
3509         
3510         this.response = response;
3511         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3512         this.form.afterAction(this, false);
3513     },
3514
3515     processResponse : function(response){
3516         this.response = response;
3517         if(!response.responseText){
3518             return true;
3519         }
3520         this.result = this.handleResponse(response);
3521         return this.result;
3522     },
3523
3524     // utility functions used internally
3525     getUrl : function(appendParams){
3526         var url = this.options.url || this.form.url || this.form.el.dom.action;
3527         if(appendParams){
3528             var p = this.getParams();
3529             if(p){
3530                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3531             }
3532         }
3533         return url;
3534     },
3535
3536     getMethod : function(){
3537         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3538     },
3539
3540     getParams : function(){
3541         var bp = this.form.baseParams;
3542         var p = this.options.params;
3543         if(p){
3544             if(typeof p == "object"){
3545                 p = Roo.urlEncode(Roo.applyIf(p, bp));
3546             }else if(typeof p == 'string' && bp){
3547                 p += '&' + Roo.urlEncode(bp);
3548             }
3549         }else if(bp){
3550             p = Roo.urlEncode(bp);
3551         }
3552         return p;
3553     },
3554
3555     createCallback : function(){
3556         return {
3557             success: this.success,
3558             failure: this.failure,
3559             scope: this,
3560             timeout: (this.form.timeout*1000),
3561             upload: this.form.fileUpload ? this.success : undefined
3562         };
3563     }
3564 };
3565
3566 Roo.form.Action.Submit = function(form, options){
3567     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3568 };
3569
3570 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3571     type : 'submit',
3572
3573     haveProgress : false,
3574     uploadComplete : false,
3575     
3576     // uploadProgress indicator.
3577     uploadProgress : function()
3578     {
3579         if (!this.form.progressUrl) {
3580             return;
3581         }
3582         
3583         if (!this.haveProgress) {
3584             Roo.MessageBox.progress("Uploading", "Uploading");
3585         }
3586         if (this.uploadComplete) {
3587            Roo.MessageBox.hide();
3588            return;
3589         }
3590         
3591         this.haveProgress = true;
3592    
3593         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3594         
3595         var c = new Roo.data.Connection();
3596         c.request({
3597             url : this.form.progressUrl,
3598             params: {
3599                 id : uid
3600             },
3601             method: 'GET',
3602             success : function(req){
3603                //console.log(data);
3604                 var rdata = false;
3605                 var edata;
3606                 try  {
3607                    rdata = Roo.decode(req.responseText)
3608                 } catch (e) {
3609                     Roo.log("Invalid data from server..");
3610                     Roo.log(edata);
3611                     return;
3612                 }
3613                 if (!rdata || !rdata.success) {
3614                     Roo.log(rdata);
3615                     Roo.MessageBox.alert(Roo.encode(rdata));
3616                     return;
3617                 }
3618                 var data = rdata.data;
3619                 
3620                 if (this.uploadComplete) {
3621                    Roo.MessageBox.hide();
3622                    return;
3623                 }
3624                    
3625                 if (data){
3626                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3627                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3628                     );
3629                 }
3630                 this.uploadProgress.defer(2000,this);
3631             },
3632        
3633             failure: function(data) {
3634                 Roo.log('progress url failed ');
3635                 Roo.log(data);
3636             },
3637             scope : this
3638         });
3639            
3640     },
3641     
3642     
3643     run : function()
3644     {
3645         // run get Values on the form, so it syncs any secondary forms.
3646         this.form.getValues();
3647         
3648         var o = this.options;
3649         var method = this.getMethod();
3650         var isPost = method == 'POST';
3651         if(o.clientValidation === false || this.form.isValid()){
3652             
3653             if (this.form.progressUrl) {
3654                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3655                     (new Date() * 1) + '' + Math.random());
3656                     
3657             } 
3658             
3659             
3660             Roo.Ajax.request(Roo.apply(this.createCallback(), {
3661                 form:this.form.el.dom,
3662                 url:this.getUrl(!isPost),
3663                 method: method,
3664                 params:isPost ? this.getParams() : null,
3665                 isUpload: this.form.fileUpload
3666             }));
3667             
3668             this.uploadProgress();
3669
3670         }else if (o.clientValidation !== false){ // client validation failed
3671             this.failureType = Roo.form.Action.CLIENT_INVALID;
3672             this.form.afterAction(this, false);
3673         }
3674     },
3675
3676     success : function(response)
3677     {
3678         this.uploadComplete= true;
3679         if (this.haveProgress) {
3680             Roo.MessageBox.hide();
3681         }
3682         
3683         
3684         var result = this.processResponse(response);
3685         if(result === true || result.success){
3686             this.form.afterAction(this, true);
3687             return;
3688         }
3689         if(result.errors){
3690             this.form.markInvalid(result.errors);
3691             this.failureType = Roo.form.Action.SERVER_INVALID;
3692         }
3693         this.form.afterAction(this, false);
3694     },
3695     failure : function(response)
3696     {
3697         this.uploadComplete= true;
3698         if (this.haveProgress) {
3699             Roo.MessageBox.hide();
3700         }
3701         
3702         this.response = response;
3703         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3704         this.form.afterAction(this, false);
3705     },
3706     
3707     handleResponse : function(response){
3708         if(this.form.errorReader){
3709             var rs = this.form.errorReader.read(response);
3710             var errors = [];
3711             if(rs.records){
3712                 for(var i = 0, len = rs.records.length; i < len; i++) {
3713                     var r = rs.records[i];
3714                     errors[i] = r.data;
3715                 }
3716             }
3717             if(errors.length < 1){
3718                 errors = null;
3719             }
3720             return {
3721                 success : rs.success,
3722                 errors : errors
3723             };
3724         }
3725         var ret = false;
3726         try {
3727             ret = Roo.decode(response.responseText);
3728         } catch (e) {
3729             ret = {
3730                 success: false,
3731                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3732                 errors : []
3733             };
3734         }
3735         return ret;
3736         
3737     }
3738 });
3739
3740
3741 Roo.form.Action.Load = function(form, options){
3742     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3743     this.reader = this.form.reader;
3744 };
3745
3746 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3747     type : 'load',
3748
3749     run : function(){
3750         
3751         Roo.Ajax.request(Roo.apply(
3752                 this.createCallback(), {
3753                     method:this.getMethod(),
3754                     url:this.getUrl(false),
3755                     params:this.getParams()
3756         }));
3757     },
3758
3759     success : function(response){
3760         
3761         var result = this.processResponse(response);
3762         if(result === true || !result.success || !result.data){
3763             this.failureType = Roo.form.Action.LOAD_FAILURE;
3764             this.form.afterAction(this, false);
3765             return;
3766         }
3767         this.form.clearInvalid();
3768         this.form.setValues(result.data);
3769         this.form.afterAction(this, true);
3770     },
3771
3772     handleResponse : function(response){
3773         if(this.form.reader){
3774             var rs = this.form.reader.read(response);
3775             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3776             return {
3777                 success : rs.success,
3778                 data : data
3779             };
3780         }
3781         return Roo.decode(response.responseText);
3782     }
3783 });
3784
3785 Roo.form.Action.ACTION_TYPES = {
3786     'load' : Roo.form.Action.Load,
3787     'submit' : Roo.form.Action.Submit
3788 };/*
3789  * - LGPL
3790  *
3791  * form
3792  * 
3793  */
3794
3795 /**
3796  * @class Roo.bootstrap.Form
3797  * @extends Roo.bootstrap.Component
3798  * Bootstrap Form class
3799  * @cfg {String} method  GET | POST (default POST)
3800  * @cfg {String} labelAlign top | left (default top)
3801   * @cfg {String} align left  | right - for navbars
3802
3803  * 
3804  * @constructor
3805  * Create a new Form
3806  * @param {Object} config The config object
3807  */
3808
3809
3810 Roo.bootstrap.Form = function(config){
3811     Roo.bootstrap.Form.superclass.constructor.call(this, config);
3812     this.addEvents({
3813         /**
3814          * @event clientvalidation
3815          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3816          * @param {Form} this
3817          * @param {Boolean} valid true if the form has passed client-side validation
3818          */
3819         clientvalidation: true,
3820         /**
3821          * @event beforeaction
3822          * Fires before any action is performed. Return false to cancel the action.
3823          * @param {Form} this
3824          * @param {Action} action The action to be performed
3825          */
3826         beforeaction: true,
3827         /**
3828          * @event actionfailed
3829          * Fires when an action fails.
3830          * @param {Form} this
3831          * @param {Action} action The action that failed
3832          */
3833         actionfailed : true,
3834         /**
3835          * @event actioncomplete
3836          * Fires when an action is completed.
3837          * @param {Form} this
3838          * @param {Action} action The action that completed
3839          */
3840         actioncomplete : true
3841     });
3842     
3843 };
3844
3845 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
3846       
3847      /**
3848      * @cfg {String} method
3849      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3850      */
3851     method : 'POST',
3852     /**
3853      * @cfg {String} url
3854      * The URL to use for form actions if one isn't supplied in the action options.
3855      */
3856     /**
3857      * @cfg {Boolean} fileUpload
3858      * Set to true if this form is a file upload.
3859      */
3860      
3861     /**
3862      * @cfg {Object} baseParams
3863      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3864      */
3865       
3866     /**
3867      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3868      */
3869     timeout: 30,
3870     /**
3871      * @cfg {Sting} align (left|right) for navbar forms
3872      */
3873     align : 'left',
3874
3875     // private
3876     activeAction : null,
3877  
3878     /**
3879      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3880      * element by passing it or its id or mask the form itself by passing in true.
3881      * @type Mixed
3882      */
3883     waitMsgTarget : false,
3884     
3885      
3886     
3887     /**
3888      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3889      * element by passing it or its id or mask the form itself by passing in true.
3890      * @type Mixed
3891      */
3892     
3893     getAutoCreate : function(){
3894         
3895         var cfg = {
3896             tag: 'form',
3897             method : this.method || 'POST',
3898             id : this.id || Roo.id(),
3899             cls : ''
3900         }
3901         if (this.parent().xtype.match(/^Nav/)) {
3902             cfg.cls = 'navbar-form navbar-' + this.align;
3903             
3904         }
3905         
3906         if (this.labelAlign == 'left' ) {
3907             cfg.cls += ' form-horizontal';
3908         }
3909         
3910         
3911         return cfg;
3912     },
3913     initEvents : function()
3914     {
3915         this.el.on('submit', this.onSubmit, this);
3916         
3917         
3918     },
3919     // private
3920     onSubmit : function(e){
3921         e.stopEvent();
3922     },
3923     
3924      /**
3925      * Returns true if client-side validation on the form is successful.
3926      * @return Boolean
3927      */
3928     isValid : function(){
3929         var items = this.getItems();
3930         var valid = true;
3931         items.each(function(f){
3932            if(!f.validate()){
3933                valid = false;
3934                
3935            }
3936         });
3937         return valid;
3938     },
3939     /**
3940      * Returns true if any fields in this form have changed since their original load.
3941      * @return Boolean
3942      */
3943     isDirty : function(){
3944         var dirty = false;
3945         var items = this.getItems();
3946         items.each(function(f){
3947            if(f.isDirty()){
3948                dirty = true;
3949                return false;
3950            }
3951            return true;
3952         });
3953         return dirty;
3954     },
3955      /**
3956      * Performs a predefined action (submit or load) or custom actions you define on this form.
3957      * @param {String} actionName The name of the action type
3958      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
3959      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3960      * accept other config options):
3961      * <pre>
3962 Property          Type             Description
3963 ----------------  ---------------  ----------------------------------------------------------------------------------
3964 url               String           The url for the action (defaults to the form's url)
3965 method            String           The form method to use (defaults to the form's method, or POST if not defined)
3966 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
3967 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
3968                                    validate the form on the client (defaults to false)
3969      * </pre>
3970      * @return {BasicForm} this
3971      */
3972     doAction : function(action, options){
3973         if(typeof action == 'string'){
3974             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3975         }
3976         if(this.fireEvent('beforeaction', this, action) !== false){
3977             this.beforeAction(action);
3978             action.run.defer(100, action);
3979         }
3980         return this;
3981     },
3982     
3983     // private
3984     beforeAction : function(action){
3985         var o = action.options;
3986         
3987         // not really supported yet.. ??
3988         
3989         //if(this.waitMsgTarget === true){
3990             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3991         //}else if(this.waitMsgTarget){
3992         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3993         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3994         //}else {
3995         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3996        // }
3997          
3998     },
3999
4000     // private
4001     afterAction : function(action, success){
4002         this.activeAction = null;
4003         var o = action.options;
4004         
4005         //if(this.waitMsgTarget === true){
4006             this.el.unmask();
4007         //}else if(this.waitMsgTarget){
4008         //    this.waitMsgTarget.unmask();
4009         //}else{
4010         //    Roo.MessageBox.updateProgress(1);
4011         //    Roo.MessageBox.hide();
4012        // }
4013         // 
4014         if(success){
4015             if(o.reset){
4016                 this.reset();
4017             }
4018             Roo.callback(o.success, o.scope, [this, action]);
4019             this.fireEvent('actioncomplete', this, action);
4020             
4021         }else{
4022             
4023             // failure condition..
4024             // we have a scenario where updates need confirming.
4025             // eg. if a locking scenario exists..
4026             // we look for { errors : { needs_confirm : true }} in the response.
4027             if (
4028                 (typeof(action.result) != 'undefined')  &&
4029                 (typeof(action.result.errors) != 'undefined')  &&
4030                 (typeof(action.result.errors.needs_confirm) != 'undefined')
4031            ){
4032                 var _t = this;
4033                 Roo.log("not supported yet");
4034                  /*
4035                 
4036                 Roo.MessageBox.confirm(
4037                     "Change requires confirmation",
4038                     action.result.errorMsg,
4039                     function(r) {
4040                         if (r != 'yes') {
4041                             return;
4042                         }
4043                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
4044                     }
4045                     
4046                 );
4047                 */
4048                 
4049                 
4050                 return;
4051             }
4052             
4053             Roo.callback(o.failure, o.scope, [this, action]);
4054             // show an error message if no failed handler is set..
4055             if (!this.hasListener('actionfailed')) {
4056                 Roo.log("need to add dialog support");
4057                 /*
4058                 Roo.MessageBox.alert("Error",
4059                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4060                         action.result.errorMsg :
4061                         "Saving Failed, please check your entries or try again"
4062                 );
4063                 */
4064             }
4065             
4066             this.fireEvent('actionfailed', this, action);
4067         }
4068         
4069     },
4070     /**
4071      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4072      * @param {String} id The value to search for
4073      * @return Field
4074      */
4075     findField : function(id){
4076         var items = this.getItems();
4077         var field = items.get(id);
4078         if(!field){
4079              items.each(function(f){
4080                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4081                     field = f;
4082                     return false;
4083                 }
4084                 return true;
4085             });
4086         }
4087         return field || null;
4088     },
4089      /**
4090      * Mark fields in this form invalid in bulk.
4091      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4092      * @return {BasicForm} this
4093      */
4094     markInvalid : function(errors){
4095         if(errors instanceof Array){
4096             for(var i = 0, len = errors.length; i < len; i++){
4097                 var fieldError = errors[i];
4098                 var f = this.findField(fieldError.id);
4099                 if(f){
4100                     f.markInvalid(fieldError.msg);
4101                 }
4102             }
4103         }else{
4104             var field, id;
4105             for(id in errors){
4106                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4107                     field.markInvalid(errors[id]);
4108                 }
4109             }
4110         }
4111         //Roo.each(this.childForms || [], function (f) {
4112         //    f.markInvalid(errors);
4113         //});
4114         
4115         return this;
4116     },
4117
4118     /**
4119      * Set values for fields in this form in bulk.
4120      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4121      * @return {BasicForm} this
4122      */
4123     setValues : function(values){
4124         if(values instanceof Array){ // array of objects
4125             for(var i = 0, len = values.length; i < len; i++){
4126                 var v = values[i];
4127                 var f = this.findField(v.id);
4128                 if(f){
4129                     f.setValue(v.value);
4130                     if(this.trackResetOnLoad){
4131                         f.originalValue = f.getValue();
4132                     }
4133                 }
4134             }
4135         }else{ // object hash
4136             var field, id;
4137             for(id in values){
4138                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4139                     
4140                     if (field.setFromData && 
4141                         field.valueField && 
4142                         field.displayField &&
4143                         // combos' with local stores can 
4144                         // be queried via setValue()
4145                         // to set their value..
4146                         (field.store && !field.store.isLocal)
4147                         ) {
4148                         // it's a combo
4149                         var sd = { };
4150                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4151                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4152                         field.setFromData(sd);
4153                         
4154                     } else {
4155                         field.setValue(values[id]);
4156                     }
4157                     
4158                     
4159                     if(this.trackResetOnLoad){
4160                         field.originalValue = field.getValue();
4161                     }
4162                 }
4163             }
4164         }
4165          
4166         //Roo.each(this.childForms || [], function (f) {
4167         //    f.setValues(values);
4168         //});
4169                 
4170         return this;
4171     },
4172
4173     /**
4174      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4175      * they are returned as an array.
4176      * @param {Boolean} asString
4177      * @return {Object}
4178      */
4179     getValues : function(asString){
4180         //if (this.childForms) {
4181             // copy values from the child forms
4182         //    Roo.each(this.childForms, function (f) {
4183         //        this.setValues(f.getValues());
4184         //    }, this);
4185         //}
4186         
4187         
4188         
4189         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4190         if(asString === true){
4191             return fs;
4192         }
4193         return Roo.urlDecode(fs);
4194     },
4195     
4196     /**
4197      * Returns the fields in this form as an object with key/value pairs. 
4198      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4199      * @return {Object}
4200      */
4201     getFieldValues : function(with_hidden)
4202     {
4203         var items = this.getItems();
4204         var ret = {};
4205         items.each(function(f){
4206             if (!f.getName()) {
4207                 return;
4208             }
4209             var v = f.getValue();
4210             if (f.inputType =='radio') {
4211                 if (typeof(ret[f.getName()]) == 'undefined') {
4212                     ret[f.getName()] = ''; // empty..
4213                 }
4214                 
4215                 if (!f.el.dom.checked) {
4216                     return;
4217                     
4218                 }
4219                 v = f.el.dom.value;
4220                 
4221             }
4222             
4223             // not sure if this supported any more..
4224             if ((typeof(v) == 'object') && f.getRawValue) {
4225                 v = f.getRawValue() ; // dates..
4226             }
4227             // combo boxes where name != hiddenName...
4228             if (f.name != f.getName()) {
4229                 ret[f.name] = f.getRawValue();
4230             }
4231             ret[f.getName()] = v;
4232         });
4233         
4234         return ret;
4235     },
4236
4237     /**
4238      * Clears all invalid messages in this form.
4239      * @return {BasicForm} this
4240      */
4241     clearInvalid : function(){
4242         var items = this.getItems();
4243         
4244         items.each(function(f){
4245            f.clearInvalid();
4246         });
4247         
4248         
4249         
4250         return this;
4251     },
4252
4253     /**
4254      * Resets this form.
4255      * @return {BasicForm} this
4256      */
4257     reset : function(){
4258         var items = this.getItems();
4259         items.each(function(f){
4260             f.reset();
4261         });
4262         
4263         Roo.each(this.childForms || [], function (f) {
4264             f.reset();
4265         });
4266        
4267         
4268         return this;
4269     },
4270     getItems : function()
4271     {
4272         var r=new Roo.util.MixedCollection(false, function(o){
4273             return o.id || (o.id = Roo.id());
4274         });
4275         var iter = function(el) {
4276             if (el.inputEl) {
4277                 r.add(el);
4278             }
4279             if (!el.items) {
4280                 return;
4281             }
4282             Roo.each(el.items,function(e) {
4283                 iter(e);
4284             });
4285             
4286             
4287         };
4288         iter(this);
4289         return r;
4290         
4291         
4292         
4293         
4294     }
4295     
4296 });
4297
4298  
4299 /*
4300  * Based on:
4301  * Ext JS Library 1.1.1
4302  * Copyright(c) 2006-2007, Ext JS, LLC.
4303  *
4304  * Originally Released Under LGPL - original licence link has changed is not relivant.
4305  *
4306  * Fork - LGPL
4307  * <script type="text/javascript">
4308  */
4309 /**
4310  * @class Roo.form.VTypes
4311  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4312  * @singleton
4313  */
4314 Roo.form.VTypes = function(){
4315     // closure these in so they are only created once.
4316     var alpha = /^[a-zA-Z_]+$/;
4317     var alphanum = /^[a-zA-Z0-9_]+$/;
4318     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4319     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4320
4321     // All these messages and functions are configurable
4322     return {
4323         /**
4324          * The function used to validate email addresses
4325          * @param {String} value The email address
4326          */
4327         'email' : function(v){
4328             return email.test(v);
4329         },
4330         /**
4331          * The error text to display when the email validation function returns false
4332          * @type String
4333          */
4334         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4335         /**
4336          * The keystroke filter mask to be applied on email input
4337          * @type RegExp
4338          */
4339         'emailMask' : /[a-z0-9_\.\-@]/i,
4340
4341         /**
4342          * The function used to validate URLs
4343          * @param {String} value The URL
4344          */
4345         'url' : function(v){
4346             return url.test(v);
4347         },
4348         /**
4349          * The error text to display when the url validation function returns false
4350          * @type String
4351          */
4352         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4353         
4354         /**
4355          * The function used to validate alpha values
4356          * @param {String} value The value
4357          */
4358         'alpha' : function(v){
4359             return alpha.test(v);
4360         },
4361         /**
4362          * The error text to display when the alpha validation function returns false
4363          * @type String
4364          */
4365         'alphaText' : 'This field should only contain letters and _',
4366         /**
4367          * The keystroke filter mask to be applied on alpha input
4368          * @type RegExp
4369          */
4370         'alphaMask' : /[a-z_]/i,
4371
4372         /**
4373          * The function used to validate alphanumeric values
4374          * @param {String} value The value
4375          */
4376         'alphanum' : function(v){
4377             return alphanum.test(v);
4378         },
4379         /**
4380          * The error text to display when the alphanumeric validation function returns false
4381          * @type String
4382          */
4383         'alphanumText' : 'This field should only contain letters, numbers and _',
4384         /**
4385          * The keystroke filter mask to be applied on alphanumeric input
4386          * @type RegExp
4387          */
4388         'alphanumMask' : /[a-z0-9_]/i
4389     };
4390 }();/*
4391  * - LGPL
4392  *
4393  * Input
4394  * 
4395  */
4396
4397 /**
4398  * @class Roo.bootstrap.Input
4399  * @extends Roo.bootstrap.Component
4400  * Bootstrap Input class
4401  * @cfg {Boolean} disabled is it disabled
4402  * @cfg {String} fieldLabel - the label associated
4403  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4404  * @cfg {String} name name of the input
4405  * @cfg {string} fieldLabel - the label associated
4406  * @cfg {string}  inputType - input / file submit ...
4407  * @cfg {string} placeholder - placeholder to put in text.
4408  * @cfg {string}  before - input group add on before
4409  * @cfg {string} after - input group add on after
4410  * @cfg {string} size - (lg|sm) or leave empty..
4411  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4412  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4413  * @cfg {Number} md colspan out of 12 for computer-sized screens
4414  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4415  * @cfg {string} value default value of the input
4416  * @cfg {Number} labelWidth set the width of label (0-12)
4417  * @cfg {String} labelAlign (top|left)
4418  * @cfg {Boolean} readOnly Specifies that the field should be read-only
4419  * 
4420  * 
4421  * @constructor
4422  * Create a new Input
4423  * @param {Object} config The config object
4424  */
4425
4426 Roo.bootstrap.Input = function(config){
4427     Roo.bootstrap.Input.superclass.constructor.call(this, config);
4428    
4429         this.addEvents({
4430             /**
4431              * @event focus
4432              * Fires when this field receives input focus.
4433              * @param {Roo.form.Field} this
4434              */
4435             focus : true,
4436             /**
4437              * @event blur
4438              * Fires when this field loses input focus.
4439              * @param {Roo.form.Field} this
4440              */
4441             blur : true,
4442             /**
4443              * @event specialkey
4444              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
4445              * {@link Roo.EventObject#getKey} to determine which key was pressed.
4446              * @param {Roo.form.Field} this
4447              * @param {Roo.EventObject} e The event object
4448              */
4449             specialkey : true,
4450             /**
4451              * @event change
4452              * Fires just before the field blurs if the field value has changed.
4453              * @param {Roo.form.Field} this
4454              * @param {Mixed} newValue The new value
4455              * @param {Mixed} oldValue The original value
4456              */
4457             change : true,
4458             /**
4459              * @event invalid
4460              * Fires after the field has been marked as invalid.
4461              * @param {Roo.form.Field} this
4462              * @param {String} msg The validation message
4463              */
4464             invalid : true,
4465             /**
4466              * @event valid
4467              * Fires after the field has been validated with no errors.
4468              * @param {Roo.form.Field} this
4469              */
4470             valid : true,
4471              /**
4472              * @event keyup
4473              * Fires after the key up
4474              * @param {Roo.form.Field} this
4475              * @param {Roo.EventObject}  e The event Object
4476              */
4477             keyup : true
4478         });
4479 };
4480
4481 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
4482      /**
4483      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4484       automatic validation (defaults to "keyup").
4485      */
4486     validationEvent : "keyup",
4487      /**
4488      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4489      */
4490     validateOnBlur : true,
4491     /**
4492      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4493      */
4494     validationDelay : 250,
4495      /**
4496      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4497      */
4498     focusClass : "x-form-focus",  // not needed???
4499     
4500        
4501     /**
4502      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4503      */
4504     invalidClass : "has-error",
4505     
4506     /**
4507      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4508      */
4509     selectOnFocus : false,
4510     
4511      /**
4512      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4513      */
4514     maskRe : null,
4515        /**
4516      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4517      */
4518     vtype : null,
4519     
4520       /**
4521      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4522      */
4523     disableKeyFilter : false,
4524     
4525        /**
4526      * @cfg {Boolean} disabled True to disable the field (defaults to false).
4527      */
4528     disabled : false,
4529      /**
4530      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4531      */
4532     allowBlank : true,
4533     /**
4534      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4535      */
4536     blankText : "This field is required",
4537     
4538      /**
4539      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4540      */
4541     minLength : 0,
4542     /**
4543      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4544      */
4545     maxLength : Number.MAX_VALUE,
4546     /**
4547      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4548      */
4549     minLengthText : "The minimum length for this field is {0}",
4550     /**
4551      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4552      */
4553     maxLengthText : "The maximum length for this field is {0}",
4554   
4555     
4556     /**
4557      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4558      * If available, this function will be called only after the basic validators all return true, and will be passed the
4559      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4560      */
4561     validator : null,
4562     /**
4563      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4564      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4565      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
4566      */
4567     regex : null,
4568     /**
4569      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4570      */
4571     regexText : "",
4572     
4573     
4574     
4575     fieldLabel : '',
4576     inputType : 'text',
4577     
4578     name : false,
4579     placeholder: false,
4580     before : false,
4581     after : false,
4582     size : false,
4583     // private
4584     hasFocus : false,
4585     preventMark: false,
4586     isFormField : true,
4587     value : '',
4588     labelWidth : 2,
4589     labelAlign : false,
4590     readOnly : false,
4591     
4592     parentLabelAlign : function()
4593     {
4594         var parent = this;
4595         while (parent.parent()) {
4596             parent = parent.parent();
4597             if (typeof(parent.labelAlign) !='undefined') {
4598                 return parent.labelAlign;
4599             }
4600         }
4601         return 'left';
4602         
4603     },
4604     
4605     getAutoCreate : function(){
4606         
4607         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4608         
4609         var id = Roo.id();
4610         
4611         var cfg = {};
4612         
4613         if(this.inputType != 'hidden'){
4614             cfg.cls = 'form-group' //input-group
4615         }
4616         
4617         var input =  {
4618             tag: 'input',
4619             id : id,
4620             type : this.inputType,
4621             value : this.value,
4622             cls : 'form-control',
4623             placeholder : this.placeholder || ''
4624             
4625         };
4626         
4627         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4628             input.maxLength = this.maxLength;
4629         }
4630         
4631         if (this.disabled) {
4632             input.disabled=true;
4633         }
4634         
4635         if (this.readOnly) {
4636             input.readonly=true;
4637         }
4638         
4639         if (this.name) {
4640             input.name = this.name;
4641         }
4642         if (this.size) {
4643             input.cls += ' input-' + this.size;
4644         }
4645         var settings=this;
4646         ['xs','sm','md','lg'].map(function(size){
4647             if (settings[size]) {
4648                 cfg.cls += ' col-' + size + '-' + settings[size];
4649             }
4650         });
4651         
4652         var inputblock = input;
4653         
4654         if (this.before || this.after) {
4655             
4656             inputblock = {
4657                 cls : 'input-group',
4658                 cn :  [] 
4659             };
4660             if (this.before) {
4661                 inputblock.cn.push({
4662                     tag :'span',
4663                     cls : 'input-group-addon',
4664                     html : this.before
4665                 });
4666             }
4667             inputblock.cn.push(input);
4668             if (this.after) {
4669                 inputblock.cn.push({
4670                     tag :'span',
4671                     cls : 'input-group-addon',
4672                     html : this.after
4673                 });
4674             }
4675             
4676         };
4677         
4678         if (align ==='left' && this.fieldLabel.length) {
4679                 Roo.log("left and has label");
4680                 cfg.cn = [
4681                     
4682                     {
4683                         tag: 'label',
4684                         'for' :  id,
4685                         cls : 'control-label col-sm-' + this.labelWidth,
4686                         html : this.fieldLabel
4687                         
4688                     },
4689                     {
4690                         cls : "col-sm-" + (12 - this.labelWidth), 
4691                         cn: [
4692                             inputblock
4693                         ]
4694                     }
4695                     
4696                 ];
4697         } else if ( this.fieldLabel.length) {
4698                 Roo.log(" label");
4699                  cfg.cn = [
4700                    
4701                     {
4702                         tag: 'label',
4703                         //cls : 'input-group-addon',
4704                         html : this.fieldLabel
4705                         
4706                     },
4707                     
4708                     inputblock
4709                     
4710                 ];
4711
4712         } else {
4713             
4714                 Roo.log(" no label && no align");
4715                 cfg.cn = [
4716                     
4717                         inputblock
4718                     
4719                 ];
4720                 
4721                 
4722         };
4723         Roo.log('input-parentType: ' + this.parentType);
4724         
4725         if (this.parentType === 'Navbar' &&  this.parent().bar) {
4726            cfg.cls += ' navbar-form';
4727            Roo.log(cfg);
4728         }
4729         
4730         return cfg;
4731         
4732     },
4733     /**
4734      * return the real input element.
4735      */
4736     inputEl: function ()
4737     {
4738         return this.el.select('input.form-control',true).first();
4739     },
4740     setDisabled : function(v)
4741     {
4742         var i  = this.inputEl().dom;
4743         if (!v) {
4744             i.removeAttribute('disabled');
4745             return;
4746             
4747         }
4748         i.setAttribute('disabled','true');
4749     },
4750     initEvents : function()
4751     {
4752         
4753         this.inputEl().on("keydown" , this.fireKey,  this);
4754         this.inputEl().on("focus", this.onFocus,  this);
4755         this.inputEl().on("blur", this.onBlur,  this);
4756         
4757         this.inputEl().relayEvent('keyup', this);
4758
4759         // reference to original value for reset
4760         this.originalValue = this.getValue();
4761         //Roo.form.TextField.superclass.initEvents.call(this);
4762         if(this.validationEvent == 'keyup'){
4763             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4764             this.inputEl().on('keyup', this.filterValidation, this);
4765         }
4766         else if(this.validationEvent !== false){
4767             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4768         }
4769         
4770         if(this.selectOnFocus){
4771             this.on("focus", this.preFocus, this);
4772             
4773         }
4774         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4775             this.inputEl().on("keypress", this.filterKeys, this);
4776         }
4777        /* if(this.grow){
4778             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
4779             this.el.on("click", this.autoSize,  this);
4780         }
4781         */
4782         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4783             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4784         }
4785         
4786     },
4787     filterValidation : function(e){
4788         if(!e.isNavKeyPress()){
4789             this.validationTask.delay(this.validationDelay);
4790         }
4791     },
4792      /**
4793      * Validates the field value
4794      * @return {Boolean} True if the value is valid, else false
4795      */
4796     validate : function(){
4797         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4798         if(this.disabled || this.validateValue(this.getRawValue())){
4799             this.clearInvalid();
4800             return true;
4801         }
4802         return false;
4803     },
4804     
4805     
4806     /**
4807      * Validates a value according to the field's validation rules and marks the field as invalid
4808      * if the validation fails
4809      * @param {Mixed} value The value to validate
4810      * @return {Boolean} True if the value is valid, else false
4811      */
4812     validateValue : function(value){
4813         if(value.length < 1)  { // if it's blank
4814              if(this.allowBlank){
4815                 this.clearInvalid();
4816                 return true;
4817              }else{
4818                 this.markInvalid(this.blankText);
4819                 return false;
4820              }
4821         }
4822         if(value.length < this.minLength){
4823             this.markInvalid(String.format(this.minLengthText, this.minLength));
4824             return false;
4825         }
4826         if(value.length > this.maxLength){
4827             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4828             return false;
4829         }
4830         if(this.vtype){
4831             var vt = Roo.form.VTypes;
4832             if(!vt[this.vtype](value, this)){
4833                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4834                 return false;
4835             }
4836         }
4837         if(typeof this.validator == "function"){
4838             var msg = this.validator(value);
4839             if(msg !== true){
4840                 this.markInvalid(msg);
4841                 return false;
4842             }
4843         }
4844         if(this.regex && !this.regex.test(value)){
4845             this.markInvalid(this.regexText);
4846             return false;
4847         }
4848         return true;
4849     },
4850
4851     
4852     
4853      // private
4854     fireKey : function(e){
4855         //Roo.log('field ' + e.getKey());
4856         if(e.isNavKeyPress()){
4857             this.fireEvent("specialkey", this, e);
4858         }
4859     },
4860     focus : function (selectText){
4861         if(this.rendered){
4862             this.inputEl().focus();
4863             if(selectText === true){
4864                 this.inputEl().dom.select();
4865             }
4866         }
4867         return this;
4868     } ,
4869     
4870     onFocus : function(){
4871         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4872            // this.el.addClass(this.focusClass);
4873         }
4874         if(!this.hasFocus){
4875             this.hasFocus = true;
4876             this.startValue = this.getValue();
4877             this.fireEvent("focus", this);
4878         }
4879     },
4880     
4881     beforeBlur : Roo.emptyFn,
4882
4883     
4884     // private
4885     onBlur : function(){
4886         this.beforeBlur();
4887         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4888             //this.el.removeClass(this.focusClass);
4889         }
4890         this.hasFocus = false;
4891         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4892             this.validate();
4893         }
4894         var v = this.getValue();
4895         if(String(v) !== String(this.startValue)){
4896             this.fireEvent('change', this, v, this.startValue);
4897         }
4898         this.fireEvent("blur", this);
4899     },
4900     
4901     /**
4902      * Resets the current field value to the originally loaded value and clears any validation messages
4903      */
4904     reset : function(){
4905         this.setValue(this.originalValue);
4906         this.clearInvalid();
4907     },
4908      /**
4909      * Returns the name of the field
4910      * @return {Mixed} name The name field
4911      */
4912     getName: function(){
4913         return this.name;
4914     },
4915      /**
4916      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
4917      * @return {Mixed} value The field value
4918      */
4919     getValue : function(){
4920         return this.inputEl().getValue();
4921     },
4922     /**
4923      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
4924      * @return {Mixed} value The field value
4925      */
4926     getRawValue : function(){
4927         var v = this.inputEl().getValue();
4928         
4929         return v;
4930     },
4931     
4932     /**
4933      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
4934      * @param {Mixed} value The value to set
4935      */
4936     setRawValue : function(v){
4937         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4938     },
4939     
4940     selectText : function(start, end){
4941         var v = this.getRawValue();
4942         if(v.length > 0){
4943             start = start === undefined ? 0 : start;
4944             end = end === undefined ? v.length : end;
4945             var d = this.inputEl().dom;
4946             if(d.setSelectionRange){
4947                 d.setSelectionRange(start, end);
4948             }else if(d.createTextRange){
4949                 var range = d.createTextRange();
4950                 range.moveStart("character", start);
4951                 range.moveEnd("character", v.length-end);
4952                 range.select();
4953             }
4954         }
4955     },
4956     
4957     /**
4958      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
4959      * @param {Mixed} value The value to set
4960      */
4961     setValue : function(v){
4962         this.value = v;
4963         if(this.rendered){
4964             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4965             this.validate();
4966         }
4967     },
4968     
4969     /*
4970     processValue : function(value){
4971         if(this.stripCharsRe){
4972             var newValue = value.replace(this.stripCharsRe, '');
4973             if(newValue !== value){
4974                 this.setRawValue(newValue);
4975                 return newValue;
4976             }
4977         }
4978         return value;
4979     },
4980   */
4981     preFocus : function(){
4982         
4983         if(this.selectOnFocus){
4984             this.inputEl().dom.select();
4985         }
4986     },
4987     filterKeys : function(e){
4988         var k = e.getKey();
4989         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4990             return;
4991         }
4992         var c = e.getCharCode(), cc = String.fromCharCode(c);
4993         if(Roo.isIE && (e.isSpecialKey() || !cc)){
4994             return;
4995         }
4996         if(!this.maskRe.test(cc)){
4997             e.stopEvent();
4998         }
4999     },
5000      /**
5001      * Clear any invalid styles/messages for this field
5002      */
5003     clearInvalid : function(){
5004         
5005         if(!this.el || this.preventMark){ // not rendered
5006             return;
5007         }
5008         this.el.removeClass(this.invalidClass);
5009         /*
5010         switch(this.msgTarget){
5011             case 'qtip':
5012                 this.el.dom.qtip = '';
5013                 break;
5014             case 'title':
5015                 this.el.dom.title = '';
5016                 break;
5017             case 'under':
5018                 if(this.errorEl){
5019                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
5020                 }
5021                 break;
5022             case 'side':
5023                 if(this.errorIcon){
5024                     this.errorIcon.dom.qtip = '';
5025                     this.errorIcon.hide();
5026                     this.un('resize', this.alignErrorIcon, this);
5027                 }
5028                 break;
5029             default:
5030                 var t = Roo.getDom(this.msgTarget);
5031                 t.innerHTML = '';
5032                 t.style.display = 'none';
5033                 break;
5034         }
5035         */
5036         this.fireEvent('valid', this);
5037     },
5038      /**
5039      * Mark this field as invalid
5040      * @param {String} msg The validation message
5041      */
5042     markInvalid : function(msg){
5043         if(!this.el  || this.preventMark){ // not rendered
5044             return;
5045         }
5046         this.el.addClass(this.invalidClass);
5047         /*
5048         msg = msg || this.invalidText;
5049         switch(this.msgTarget){
5050             case 'qtip':
5051                 this.el.dom.qtip = msg;
5052                 this.el.dom.qclass = 'x-form-invalid-tip';
5053                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5054                     Roo.QuickTips.enable();
5055                 }
5056                 break;
5057             case 'title':
5058                 this.el.dom.title = msg;
5059                 break;
5060             case 'under':
5061                 if(!this.errorEl){
5062                     var elp = this.el.findParent('.x-form-element', 5, true);
5063                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5064                     this.errorEl.setWidth(elp.getWidth(true)-20);
5065                 }
5066                 this.errorEl.update(msg);
5067                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5068                 break;
5069             case 'side':
5070                 if(!this.errorIcon){
5071                     var elp = this.el.findParent('.x-form-element', 5, true);
5072                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5073                 }
5074                 this.alignErrorIcon();
5075                 this.errorIcon.dom.qtip = msg;
5076                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5077                 this.errorIcon.show();
5078                 this.on('resize', this.alignErrorIcon, this);
5079                 break;
5080             default:
5081                 var t = Roo.getDom(this.msgTarget);
5082                 t.innerHTML = msg;
5083                 t.style.display = this.msgDisplay;
5084                 break;
5085         }
5086         */
5087         this.fireEvent('invalid', this, msg);
5088     },
5089     // private
5090     SafariOnKeyDown : function(event)
5091     {
5092         // this is a workaround for a password hang bug on chrome/ webkit.
5093         
5094         var isSelectAll = false;
5095         
5096         if(this.inputEl().dom.selectionEnd > 0){
5097             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5098         }
5099         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5100             event.preventDefault();
5101             this.setValue('');
5102             return;
5103         }
5104         
5105         if(isSelectAll){ // backspace and delete key
5106             
5107             event.preventDefault();
5108             // this is very hacky as keydown always get's upper case.
5109             //
5110             var cc = String.fromCharCode(event.getCharCode());
5111             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
5112             
5113         }
5114     },
5115     adjustWidth : function(tag, w){
5116         tag = tag.toLowerCase();
5117         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5118             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5119                 if(tag == 'input'){
5120                     return w + 2;
5121                 }
5122                 if(tag == 'textarea'){
5123                     return w-2;
5124                 }
5125             }else if(Roo.isOpera){
5126                 if(tag == 'input'){
5127                     return w + 2;
5128                 }
5129                 if(tag == 'textarea'){
5130                     return w-2;
5131                 }
5132             }
5133         }
5134         return w;
5135     }
5136     
5137 });
5138
5139  
5140 /*
5141  * - LGPL
5142  *
5143  * Input
5144  * 
5145  */
5146
5147 /**
5148  * @class Roo.bootstrap.TextArea
5149  * @extends Roo.bootstrap.Input
5150  * Bootstrap TextArea class
5151  * @cfg {Number} cols Specifies the visible width of a text area
5152  * @cfg {Number} rows Specifies the visible number of lines in a text area
5153  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5154  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5155  * @cfg {string} html text
5156  * 
5157  * @constructor
5158  * Create a new TextArea
5159  * @param {Object} config The config object
5160  */
5161
5162 Roo.bootstrap.TextArea = function(config){
5163     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5164    
5165 };
5166
5167 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5168      
5169     cols : false,
5170     rows : 5,
5171     readOnly : false,
5172     warp : 'soft',
5173     resize : false,
5174     value: false,
5175     html: false,
5176     
5177     getAutoCreate : function(){
5178         
5179         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5180         
5181         var id = Roo.id();
5182         
5183         var cfg = {};
5184         
5185         var input =  {
5186             tag: 'textarea',
5187             id : id,
5188             warp : this.warp,
5189             rows : this.rows,
5190             value : this.value || '',
5191             html: this.html || '',
5192             cls : 'form-control',
5193             placeholder : this.placeholder || '' 
5194             
5195         };
5196         
5197         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5198             input.maxLength = this.maxLength;
5199         }
5200         
5201         if(this.resize){
5202             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5203         }
5204         
5205         if(this.cols){
5206             input.cols = this.cols;
5207         }
5208         
5209         if (this.readOnly) {
5210             input.readonly = true;
5211         }
5212         
5213         if (this.name) {
5214             input.name = this.name;
5215         }
5216         
5217         if (this.size) {
5218             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5219         }
5220         
5221         var settings=this;
5222         ['xs','sm','md','lg'].map(function(size){
5223             if (settings[size]) {
5224                 cfg.cls += ' col-' + size + '-' + settings[size];
5225             }
5226         });
5227         
5228         var inputblock = input;
5229         
5230         if (this.before || this.after) {
5231             
5232             inputblock = {
5233                 cls : 'input-group',
5234                 cn :  [] 
5235             };
5236             if (this.before) {
5237                 inputblock.cn.push({
5238                     tag :'span',
5239                     cls : 'input-group-addon',
5240                     html : this.before
5241                 });
5242             }
5243             inputblock.cn.push(input);
5244             if (this.after) {
5245                 inputblock.cn.push({
5246                     tag :'span',
5247                     cls : 'input-group-addon',
5248                     html : this.after
5249                 });
5250             }
5251             
5252         }
5253         
5254         if (align ==='left' && this.fieldLabel.length) {
5255                 Roo.log("left and has label");
5256                 cfg.cn = [
5257                     
5258                     {
5259                         tag: 'label',
5260                         'for' :  id,
5261                         cls : 'control-label col-sm-' + this.labelWidth,
5262                         html : this.fieldLabel
5263                         
5264                     },
5265                     {
5266                         cls : "col-sm-" + (12 - this.labelWidth), 
5267                         cn: [
5268                             inputblock
5269                         ]
5270                     }
5271                     
5272                 ];
5273         } else if ( this.fieldLabel.length) {
5274                 Roo.log(" label");
5275                  cfg.cn = [
5276                    
5277                     {
5278                         tag: 'label',
5279                         //cls : 'input-group-addon',
5280                         html : this.fieldLabel
5281                         
5282                     },
5283                     
5284                     inputblock
5285                     
5286                 ];
5287
5288         } else {
5289             
5290                    Roo.log(" no label && no align");
5291                 cfg.cn = [
5292                     
5293                         inputblock
5294                     
5295                 ];
5296                 
5297                 
5298         }
5299         
5300         if (this.disabled) {
5301             input.disabled=true;
5302         }
5303         
5304         return cfg;
5305         
5306     },
5307     /**
5308      * return the real textarea element.
5309      */
5310     inputEl: function ()
5311     {
5312         return this.el.select('textarea.form-control',true).first();
5313     }
5314 });
5315
5316  
5317 /*
5318  * - LGPL
5319  *
5320  * trigger field - base class for combo..
5321  * 
5322  */
5323  
5324 /**
5325  * @class Roo.bootstrap.TriggerField
5326  * @extends Roo.bootstrap.Input
5327  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5328  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5329  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5330  * for which you can provide a custom implementation.  For example:
5331  * <pre><code>
5332 var trigger = new Roo.bootstrap.TriggerField();
5333 trigger.onTriggerClick = myTriggerFn;
5334 trigger.applyTo('my-field');
5335 </code></pre>
5336  *
5337  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5338  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5339  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
5340  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5341  * @constructor
5342  * Create a new TriggerField.
5343  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5344  * to the base TextField)
5345  */
5346 Roo.bootstrap.TriggerField = function(config){
5347     this.mimicing = false;
5348     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5349 };
5350
5351 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
5352     /**
5353      * @cfg {String} triggerClass A CSS class to apply to the trigger
5354      */
5355      /**
5356      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5357      */
5358     hideTrigger:false,
5359
5360     /** @cfg {Boolean} grow @hide */
5361     /** @cfg {Number} growMin @hide */
5362     /** @cfg {Number} growMax @hide */
5363
5364     /**
5365      * @hide 
5366      * @method
5367      */
5368     autoSize: Roo.emptyFn,
5369     // private
5370     monitorTab : true,
5371     // private
5372     deferHeight : true,
5373
5374     
5375     actionMode : 'wrap',
5376     
5377     
5378     
5379     getAutoCreate : function(){
5380        
5381         var parent = this.parent();
5382         
5383         var align = this.parentLabelAlign();
5384         
5385         var id = Roo.id();
5386         
5387         var cfg = {
5388             cls: 'form-group' //input-group
5389         };
5390         
5391         
5392         var input =  {
5393             tag: 'input',
5394             id : id,
5395             type : this.inputType,
5396             cls : 'form-control',
5397             autocomplete: 'off',
5398             placeholder : this.placeholder || '' 
5399             
5400         };
5401         if (this.name) {
5402             input.name = this.name;
5403         }
5404         if (this.size) {
5405             input.cls += ' input-' + this.size;
5406         }
5407         
5408         if (this.disabled) {
5409             input.disabled=true;
5410         }
5411         
5412         var inputblock = input;
5413         
5414         if (this.before || this.after) {
5415             
5416             inputblock = {
5417                 cls : 'input-group',
5418                 cn :  [] 
5419             };
5420             if (this.before) {
5421                 inputblock.cn.push({
5422                     tag :'span',
5423                     cls : 'input-group-addon',
5424                     html : this.before
5425                 });
5426             }
5427             inputblock.cn.push(input);
5428             if (this.after) {
5429                 inputblock.cn.push({
5430                     tag :'span',
5431                     cls : 'input-group-addon',
5432                     html : this.after
5433                 });
5434             }
5435             
5436         };
5437         
5438         var box = {
5439             tag: 'div',
5440             cn: [
5441                 {
5442                     tag: 'input',
5443                     type : 'hidden',
5444                     cls: 'form-hidden-field'
5445                 },
5446                 inputblock
5447             ]
5448             
5449         };
5450         
5451         if(this.multiple){
5452             Roo.log('multiple');
5453             
5454             box = {
5455                 tag: 'div',
5456                 cn: [
5457                     {
5458                         tag: 'input',
5459                         type : 'hidden',
5460                         cls: 'form-hidden-field'
5461                     },
5462                     {
5463                         tag: 'ul',
5464                         cls: 'select2-choices',
5465                         cn:[
5466                             {
5467                                 tag: 'li',
5468                                 cls: 'select2-search-field',
5469                                 cn: [
5470
5471                                     inputblock
5472                                 ]
5473                             }
5474                         ]
5475                     }
5476                 ]
5477             }
5478         };
5479         
5480         var combobox = {
5481             cls: 'select2-container input-group',
5482             cn: [
5483                 box,
5484                 {
5485                     tag: 'ul',
5486                     cls: 'typeahead typeahead-long dropdown-menu',
5487                     style: 'display:none'
5488                 }
5489             ]
5490         };
5491         
5492         if(!this.multiple){
5493             combobox.cn.push({
5494                 tag :'span',
5495                 cls : 'input-group-addon btn dropdown-toggle',
5496                 cn : [
5497                     {
5498                         tag: 'span',
5499                         cls: 'caret'
5500                     },
5501                     {
5502                         tag: 'span',
5503                         cls: 'combobox-clear',
5504                         cn  : [
5505                             {
5506                                 tag : 'i',
5507                                 cls: 'icon-remove'
5508                             }
5509                         ]
5510                     }
5511                 ]
5512
5513             })
5514         }
5515         
5516         if(this.multiple){
5517             combobox.cls += ' select2-container-multi';
5518         }
5519         
5520         if (align ==='left' && this.fieldLabel.length) {
5521             
5522                 Roo.log("left and has label");
5523                 cfg.cn = [
5524                     
5525                     {
5526                         tag: 'label',
5527                         'for' :  id,
5528                         cls : 'control-label col-sm-' + this.labelWidth,
5529                         html : this.fieldLabel
5530                         
5531                     },
5532                     {
5533                         cls : "col-sm-" + (12 - this.labelWidth), 
5534                         cn: [
5535                             combobox
5536                         ]
5537                     }
5538                     
5539                 ];
5540         } else if ( this.fieldLabel.length) {
5541                 Roo.log(" label");
5542                  cfg.cn = [
5543                    
5544                     {
5545                         tag: 'label',
5546                         //cls : 'input-group-addon',
5547                         html : this.fieldLabel
5548                         
5549                     },
5550                     
5551                     combobox
5552                     
5553                 ];
5554
5555         } else {
5556             
5557                 Roo.log(" no label && no align");
5558                 cfg = combobox
5559                      
5560                 
5561         }
5562          
5563         var settings=this;
5564         ['xs','sm','md','lg'].map(function(size){
5565             if (settings[size]) {
5566                 cfg.cls += ' col-' + size + '-' + settings[size];
5567             }
5568         });
5569         
5570         return cfg;
5571         
5572     },
5573     
5574     
5575     
5576     // private
5577     onResize : function(w, h){
5578 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5579 //        if(typeof w == 'number'){
5580 //            var x = w - this.trigger.getWidth();
5581 //            this.inputEl().setWidth(this.adjustWidth('input', x));
5582 //            this.trigger.setStyle('left', x+'px');
5583 //        }
5584     },
5585
5586     // private
5587     adjustSize : Roo.BoxComponent.prototype.adjustSize,
5588
5589     // private
5590     getResizeEl : function(){
5591         return this.inputEl();
5592     },
5593
5594     // private
5595     getPositionEl : function(){
5596         return this.inputEl();
5597     },
5598
5599     // private
5600     alignErrorIcon : function(){
5601         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5602     },
5603
5604     // private
5605     initEvents : function(){
5606         
5607         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5608         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5609         if(!this.multiple){
5610             this.trigger = this.el.select('span.dropdown-toggle',true).first();
5611             if(this.hideTrigger){
5612                 this.trigger.setDisplayed(false);
5613             }
5614             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5615         }
5616         
5617         if(this.multiple){
5618             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5619         }
5620         
5621         //this.trigger.addClassOnOver('x-form-trigger-over');
5622         //this.trigger.addClassOnClick('x-form-trigger-click');
5623         
5624         //if(!this.width){
5625         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5626         //}
5627     },
5628
5629     // private
5630     initTrigger : function(){
5631        
5632     },
5633
5634     // private
5635     onDestroy : function(){
5636         if(this.trigger){
5637             this.trigger.removeAllListeners();
5638           //  this.trigger.remove();
5639         }
5640         //if(this.wrap){
5641         //    this.wrap.remove();
5642         //}
5643         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5644     },
5645
5646     // private
5647     onFocus : function(){
5648         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5649         /*
5650         if(!this.mimicing){
5651             this.wrap.addClass('x-trigger-wrap-focus');
5652             this.mimicing = true;
5653             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5654             if(this.monitorTab){
5655                 this.el.on("keydown", this.checkTab, this);
5656             }
5657         }
5658         */
5659     },
5660
5661     // private
5662     checkTab : function(e){
5663         if(e.getKey() == e.TAB){
5664             this.triggerBlur();
5665         }
5666     },
5667
5668     // private
5669     onBlur : function(){
5670         // do nothing
5671     },
5672
5673     // private
5674     mimicBlur : function(e, t){
5675         /*
5676         if(!this.wrap.contains(t) && this.validateBlur()){
5677             this.triggerBlur();
5678         }
5679         */
5680     },
5681
5682     // private
5683     triggerBlur : function(){
5684         this.mimicing = false;
5685         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5686         if(this.monitorTab){
5687             this.el.un("keydown", this.checkTab, this);
5688         }
5689         //this.wrap.removeClass('x-trigger-wrap-focus');
5690         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5691     },
5692
5693     // private
5694     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5695     validateBlur : function(e, t){
5696         return true;
5697     },
5698
5699     // private
5700     onDisable : function(){
5701         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5702         //if(this.wrap){
5703         //    this.wrap.addClass('x-item-disabled');
5704         //}
5705     },
5706
5707     // private
5708     onEnable : function(){
5709         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5710         //if(this.wrap){
5711         //    this.el.removeClass('x-item-disabled');
5712         //}
5713     },
5714
5715     // private
5716     onShow : function(){
5717         var ae = this.getActionEl();
5718         
5719         if(ae){
5720             ae.dom.style.display = '';
5721             ae.dom.style.visibility = 'visible';
5722         }
5723     },
5724
5725     // private
5726     
5727     onHide : function(){
5728         var ae = this.getActionEl();
5729         ae.dom.style.display = 'none';
5730     },
5731
5732     /**
5733      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
5734      * by an implementing function.
5735      * @method
5736      * @param {EventObject} e
5737      */
5738     onTriggerClick : Roo.emptyFn
5739 });
5740  /*
5741  * Based on:
5742  * Ext JS Library 1.1.1
5743  * Copyright(c) 2006-2007, Ext JS, LLC.
5744  *
5745  * Originally Released Under LGPL - original licence link has changed is not relivant.
5746  *
5747  * Fork - LGPL
5748  * <script type="text/javascript">
5749  */
5750
5751
5752 /**
5753  * @class Roo.data.SortTypes
5754  * @singleton
5755  * Defines the default sorting (casting?) comparison functions used when sorting data.
5756  */
5757 Roo.data.SortTypes = {
5758     /**
5759      * Default sort that does nothing
5760      * @param {Mixed} s The value being converted
5761      * @return {Mixed} The comparison value
5762      */
5763     none : function(s){
5764         return s;
5765     },
5766     
5767     /**
5768      * The regular expression used to strip tags
5769      * @type {RegExp}
5770      * @property
5771      */
5772     stripTagsRE : /<\/?[^>]+>/gi,
5773     
5774     /**
5775      * Strips all HTML tags to sort on text only
5776      * @param {Mixed} s The value being converted
5777      * @return {String} The comparison value
5778      */
5779     asText : function(s){
5780         return String(s).replace(this.stripTagsRE, "");
5781     },
5782     
5783     /**
5784      * Strips all HTML tags to sort on text only - Case insensitive
5785      * @param {Mixed} s The value being converted
5786      * @return {String} The comparison value
5787      */
5788     asUCText : function(s){
5789         return String(s).toUpperCase().replace(this.stripTagsRE, "");
5790     },
5791     
5792     /**
5793      * Case insensitive string
5794      * @param {Mixed} s The value being converted
5795      * @return {String} The comparison value
5796      */
5797     asUCString : function(s) {
5798         return String(s).toUpperCase();
5799     },
5800     
5801     /**
5802      * Date sorting
5803      * @param {Mixed} s The value being converted
5804      * @return {Number} The comparison value
5805      */
5806     asDate : function(s) {
5807         if(!s){
5808             return 0;
5809         }
5810         if(s instanceof Date){
5811             return s.getTime();
5812         }
5813         return Date.parse(String(s));
5814     },
5815     
5816     /**
5817      * Float sorting
5818      * @param {Mixed} s The value being converted
5819      * @return {Float} The comparison value
5820      */
5821     asFloat : function(s) {
5822         var val = parseFloat(String(s).replace(/,/g, ""));
5823         if(isNaN(val)) val = 0;
5824         return val;
5825     },
5826     
5827     /**
5828      * Integer sorting
5829      * @param {Mixed} s The value being converted
5830      * @return {Number} The comparison value
5831      */
5832     asInt : function(s) {
5833         var val = parseInt(String(s).replace(/,/g, ""));
5834         if(isNaN(val)) val = 0;
5835         return val;
5836     }
5837 };/*
5838  * Based on:
5839  * Ext JS Library 1.1.1
5840  * Copyright(c) 2006-2007, Ext JS, LLC.
5841  *
5842  * Originally Released Under LGPL - original licence link has changed is not relivant.
5843  *
5844  * Fork - LGPL
5845  * <script type="text/javascript">
5846  */
5847
5848 /**
5849 * @class Roo.data.Record
5850  * Instances of this class encapsulate both record <em>definition</em> information, and record
5851  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5852  * to access Records cached in an {@link Roo.data.Store} object.<br>
5853  * <p>
5854  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5855  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5856  * objects.<br>
5857  * <p>
5858  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5859  * @constructor
5860  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5861  * {@link #create}. The parameters are the same.
5862  * @param {Array} data An associative Array of data values keyed by the field name.
5863  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5864  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5865  * not specified an integer id is generated.
5866  */
5867 Roo.data.Record = function(data, id){
5868     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5869     this.data = data;
5870 };
5871
5872 /**
5873  * Generate a constructor for a specific record layout.
5874  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5875  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5876  * Each field definition object may contain the following properties: <ul>
5877  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
5878  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5879  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5880  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5881  * is being used, then this is a string containing the javascript expression to reference the data relative to 
5882  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5883  * to the data item relative to the record element. If the mapping expression is the same as the field name,
5884  * this may be omitted.</p></li>
5885  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5886  * <ul><li>auto (Default, implies no conversion)</li>
5887  * <li>string</li>
5888  * <li>int</li>
5889  * <li>float</li>
5890  * <li>boolean</li>
5891  * <li>date</li></ul></p></li>
5892  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5893  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5894  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5895  * by the Reader into an object that will be stored in the Record. It is passed the
5896  * following parameters:<ul>
5897  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5898  * </ul></p></li>
5899  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5900  * </ul>
5901  * <br>usage:<br><pre><code>
5902 var TopicRecord = Roo.data.Record.create(
5903     {name: 'title', mapping: 'topic_title'},
5904     {name: 'author', mapping: 'username'},
5905     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5906     {name: 'lastPost', mapping: 'post_time', type: 'date'},
5907     {name: 'lastPoster', mapping: 'user2'},
5908     {name: 'excerpt', mapping: 'post_text'}
5909 );
5910
5911 var myNewRecord = new TopicRecord({
5912     title: 'Do my job please',
5913     author: 'noobie',
5914     totalPosts: 1,
5915     lastPost: new Date(),
5916     lastPoster: 'Animal',
5917     excerpt: 'No way dude!'
5918 });
5919 myStore.add(myNewRecord);
5920 </code></pre>
5921  * @method create
5922  * @static
5923  */
5924 Roo.data.Record.create = function(o){
5925     var f = function(){
5926         f.superclass.constructor.apply(this, arguments);
5927     };
5928     Roo.extend(f, Roo.data.Record);
5929     var p = f.prototype;
5930     p.fields = new Roo.util.MixedCollection(false, function(field){
5931         return field.name;
5932     });
5933     for(var i = 0, len = o.length; i < len; i++){
5934         p.fields.add(new Roo.data.Field(o[i]));
5935     }
5936     f.getField = function(name){
5937         return p.fields.get(name);  
5938     };
5939     return f;
5940 };
5941
5942 Roo.data.Record.AUTO_ID = 1000;
5943 Roo.data.Record.EDIT = 'edit';
5944 Roo.data.Record.REJECT = 'reject';
5945 Roo.data.Record.COMMIT = 'commit';
5946
5947 Roo.data.Record.prototype = {
5948     /**
5949      * Readonly flag - true if this record has been modified.
5950      * @type Boolean
5951      */
5952     dirty : false,
5953     editing : false,
5954     error: null,
5955     modified: null,
5956
5957     // private
5958     join : function(store){
5959         this.store = store;
5960     },
5961
5962     /**
5963      * Set the named field to the specified value.
5964      * @param {String} name The name of the field to set.
5965      * @param {Object} value The value to set the field to.
5966      */
5967     set : function(name, value){
5968         if(this.data[name] == value){
5969             return;
5970         }
5971         this.dirty = true;
5972         if(!this.modified){
5973             this.modified = {};
5974         }
5975         if(typeof this.modified[name] == 'undefined'){
5976             this.modified[name] = this.data[name];
5977         }
5978         this.data[name] = value;
5979         if(!this.editing && this.store){
5980             this.store.afterEdit(this);
5981         }       
5982     },
5983
5984     /**
5985      * Get the value of the named field.
5986      * @param {String} name The name of the field to get the value of.
5987      * @return {Object} The value of the field.
5988      */
5989     get : function(name){
5990         return this.data[name]; 
5991     },
5992
5993     // private
5994     beginEdit : function(){
5995         this.editing = true;
5996         this.modified = {}; 
5997     },
5998
5999     // private
6000     cancelEdit : function(){
6001         this.editing = false;
6002         delete this.modified;
6003     },
6004
6005     // private
6006     endEdit : function(){
6007         this.editing = false;
6008         if(this.dirty && this.store){
6009             this.store.afterEdit(this);
6010         }
6011     },
6012
6013     /**
6014      * Usually called by the {@link Roo.data.Store} which owns the Record.
6015      * Rejects all changes made to the Record since either creation, or the last commit operation.
6016      * Modified fields are reverted to their original values.
6017      * <p>
6018      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6019      * of reject operations.
6020      */
6021     reject : function(){
6022         var m = this.modified;
6023         for(var n in m){
6024             if(typeof m[n] != "function"){
6025                 this.data[n] = m[n];
6026             }
6027         }
6028         this.dirty = false;
6029         delete this.modified;
6030         this.editing = false;
6031         if(this.store){
6032             this.store.afterReject(this);
6033         }
6034     },
6035
6036     /**
6037      * Usually called by the {@link Roo.data.Store} which owns the Record.
6038      * Commits all changes made to the Record since either creation, or the last commit operation.
6039      * <p>
6040      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6041      * of commit operations.
6042      */
6043     commit : function(){
6044         this.dirty = false;
6045         delete this.modified;
6046         this.editing = false;
6047         if(this.store){
6048             this.store.afterCommit(this);
6049         }
6050     },
6051
6052     // private
6053     hasError : function(){
6054         return this.error != null;
6055     },
6056
6057     // private
6058     clearError : function(){
6059         this.error = null;
6060     },
6061
6062     /**
6063      * Creates a copy of this record.
6064      * @param {String} id (optional) A new record id if you don't want to use this record's id
6065      * @return {Record}
6066      */
6067     copy : function(newId) {
6068         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6069     }
6070 };/*
6071  * Based on:
6072  * Ext JS Library 1.1.1
6073  * Copyright(c) 2006-2007, Ext JS, LLC.
6074  *
6075  * Originally Released Under LGPL - original licence link has changed is not relivant.
6076  *
6077  * Fork - LGPL
6078  * <script type="text/javascript">
6079  */
6080
6081
6082
6083 /**
6084  * @class Roo.data.Store
6085  * @extends Roo.util.Observable
6086  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6087  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6088  * <p>
6089  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
6090  * has no knowledge of the format of the data returned by the Proxy.<br>
6091  * <p>
6092  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6093  * instances from the data object. These records are cached and made available through accessor functions.
6094  * @constructor
6095  * Creates a new Store.
6096  * @param {Object} config A config object containing the objects needed for the Store to access data,
6097  * and read the data into Records.
6098  */
6099 Roo.data.Store = function(config){
6100     this.data = new Roo.util.MixedCollection(false);
6101     this.data.getKey = function(o){
6102         return o.id;
6103     };
6104     this.baseParams = {};
6105     // private
6106     this.paramNames = {
6107         "start" : "start",
6108         "limit" : "limit",
6109         "sort" : "sort",
6110         "dir" : "dir",
6111         "multisort" : "_multisort"
6112     };
6113
6114     if(config && config.data){
6115         this.inlineData = config.data;
6116         delete config.data;
6117     }
6118
6119     Roo.apply(this, config);
6120     
6121     if(this.reader){ // reader passed
6122         this.reader = Roo.factory(this.reader, Roo.data);
6123         this.reader.xmodule = this.xmodule || false;
6124         if(!this.recordType){
6125             this.recordType = this.reader.recordType;
6126         }
6127         if(this.reader.onMetaChange){
6128             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6129         }
6130     }
6131
6132     if(this.recordType){
6133         this.fields = this.recordType.prototype.fields;
6134     }
6135     this.modified = [];
6136
6137     this.addEvents({
6138         /**
6139          * @event datachanged
6140          * Fires when the data cache has changed, and a widget which is using this Store
6141          * as a Record cache should refresh its view.
6142          * @param {Store} this
6143          */
6144         datachanged : true,
6145         /**
6146          * @event metachange
6147          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6148          * @param {Store} this
6149          * @param {Object} meta The JSON metadata
6150          */
6151         metachange : true,
6152         /**
6153          * @event add
6154          * Fires when Records have been added to the Store
6155          * @param {Store} this
6156          * @param {Roo.data.Record[]} records The array of Records added
6157          * @param {Number} index The index at which the record(s) were added
6158          */
6159         add : true,
6160         /**
6161          * @event remove
6162          * Fires when a Record has been removed from the Store
6163          * @param {Store} this
6164          * @param {Roo.data.Record} record The Record that was removed
6165          * @param {Number} index The index at which the record was removed
6166          */
6167         remove : true,
6168         /**
6169          * @event update
6170          * Fires when a Record has been updated
6171          * @param {Store} this
6172          * @param {Roo.data.Record} record The Record that was updated
6173          * @param {String} operation The update operation being performed.  Value may be one of:
6174          * <pre><code>
6175  Roo.data.Record.EDIT
6176  Roo.data.Record.REJECT
6177  Roo.data.Record.COMMIT
6178          * </code></pre>
6179          */
6180         update : true,
6181         /**
6182          * @event clear
6183          * Fires when the data cache has been cleared.
6184          * @param {Store} this
6185          */
6186         clear : true,
6187         /**
6188          * @event beforeload
6189          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6190          * the load action will be canceled.
6191          * @param {Store} this
6192          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6193          */
6194         beforeload : true,
6195         /**
6196          * @event beforeloadadd
6197          * Fires after a new set of Records has been loaded.
6198          * @param {Store} this
6199          * @param {Roo.data.Record[]} records The Records that were loaded
6200          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6201          */
6202         beforeloadadd : true,
6203         /**
6204          * @event load
6205          * Fires after a new set of Records has been loaded, before they are added to the store.
6206          * @param {Store} this
6207          * @param {Roo.data.Record[]} records The Records that were loaded
6208          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6209          * @params {Object} return from reader
6210          */
6211         load : true,
6212         /**
6213          * @event loadexception
6214          * Fires if an exception occurs in the Proxy during loading.
6215          * Called with the signature of the Proxy's "loadexception" event.
6216          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6217          * 
6218          * @param {Proxy} 
6219          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6220          * @param {Object} load options 
6221          * @param {Object} jsonData from your request (normally this contains the Exception)
6222          */
6223         loadexception : true
6224     });
6225     
6226     if(this.proxy){
6227         this.proxy = Roo.factory(this.proxy, Roo.data);
6228         this.proxy.xmodule = this.xmodule || false;
6229         this.relayEvents(this.proxy,  ["loadexception"]);
6230     }
6231     this.sortToggle = {};
6232     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6233
6234     Roo.data.Store.superclass.constructor.call(this);
6235
6236     if(this.inlineData){
6237         this.loadData(this.inlineData);
6238         delete this.inlineData;
6239     }
6240 };
6241
6242 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6243      /**
6244     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6245     * without a remote query - used by combo/forms at present.
6246     */
6247     
6248     /**
6249     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6250     */
6251     /**
6252     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6253     */
6254     /**
6255     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6256     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6257     */
6258     /**
6259     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6260     * on any HTTP request
6261     */
6262     /**
6263     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6264     */
6265     /**
6266     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6267     */
6268     multiSort: false,
6269     /**
6270     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6271     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6272     */
6273     remoteSort : false,
6274
6275     /**
6276     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6277      * loaded or when a record is removed. (defaults to false).
6278     */
6279     pruneModifiedRecords : false,
6280
6281     // private
6282     lastOptions : null,
6283
6284     /**
6285      * Add Records to the Store and fires the add event.
6286      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6287      */
6288     add : function(records){
6289         records = [].concat(records);
6290         for(var i = 0, len = records.length; i < len; i++){
6291             records[i].join(this);
6292         }
6293         var index = this.data.length;
6294         this.data.addAll(records);
6295         this.fireEvent("add", this, records, index);
6296     },
6297
6298     /**
6299      * Remove a Record from the Store and fires the remove event.
6300      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6301      */
6302     remove : function(record){
6303         var index = this.data.indexOf(record);
6304         this.data.removeAt(index);
6305         if(this.pruneModifiedRecords){
6306             this.modified.remove(record);
6307         }
6308         this.fireEvent("remove", this, record, index);
6309     },
6310
6311     /**
6312      * Remove all Records from the Store and fires the clear event.
6313      */
6314     removeAll : function(){
6315         this.data.clear();
6316         if(this.pruneModifiedRecords){
6317             this.modified = [];
6318         }
6319         this.fireEvent("clear", this);
6320     },
6321
6322     /**
6323      * Inserts Records to the Store at the given index and fires the add event.
6324      * @param {Number} index The start index at which to insert the passed Records.
6325      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6326      */
6327     insert : function(index, records){
6328         records = [].concat(records);
6329         for(var i = 0, len = records.length; i < len; i++){
6330             this.data.insert(index, records[i]);
6331             records[i].join(this);
6332         }
6333         this.fireEvent("add", this, records, index);
6334     },
6335
6336     /**
6337      * Get the index within the cache of the passed Record.
6338      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6339      * @return {Number} The index of the passed Record. Returns -1 if not found.
6340      */
6341     indexOf : function(record){
6342         return this.data.indexOf(record);
6343     },
6344
6345     /**
6346      * Get the index within the cache of the Record with the passed id.
6347      * @param {String} id The id of the Record to find.
6348      * @return {Number} The index of the Record. Returns -1 if not found.
6349      */
6350     indexOfId : function(id){
6351         return this.data.indexOfKey(id);
6352     },
6353
6354     /**
6355      * Get the Record with the specified id.
6356      * @param {String} id The id of the Record to find.
6357      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6358      */
6359     getById : function(id){
6360         return this.data.key(id);
6361     },
6362
6363     /**
6364      * Get the Record at the specified index.
6365      * @param {Number} index The index of the Record to find.
6366      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6367      */
6368     getAt : function(index){
6369         return this.data.itemAt(index);
6370     },
6371
6372     /**
6373      * Returns a range of Records between specified indices.
6374      * @param {Number} startIndex (optional) The starting index (defaults to 0)
6375      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6376      * @return {Roo.data.Record[]} An array of Records
6377      */
6378     getRange : function(start, end){
6379         return this.data.getRange(start, end);
6380     },
6381
6382     // private
6383     storeOptions : function(o){
6384         o = Roo.apply({}, o);
6385         delete o.callback;
6386         delete o.scope;
6387         this.lastOptions = o;
6388     },
6389
6390     /**
6391      * Loads the Record cache from the configured Proxy using the configured Reader.
6392      * <p>
6393      * If using remote paging, then the first load call must specify the <em>start</em>
6394      * and <em>limit</em> properties in the options.params property to establish the initial
6395      * position within the dataset, and the number of Records to cache on each read from the Proxy.
6396      * <p>
6397      * <strong>It is important to note that for remote data sources, loading is asynchronous,
6398      * and this call will return before the new data has been loaded. Perform any post-processing
6399      * in a callback function, or in a "load" event handler.</strong>
6400      * <p>
6401      * @param {Object} options An object containing properties which control loading options:<ul>
6402      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6403      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6404      * passed the following arguments:<ul>
6405      * <li>r : Roo.data.Record[]</li>
6406      * <li>options: Options object from the load call</li>
6407      * <li>success: Boolean success indicator</li></ul></li>
6408      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6409      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6410      * </ul>
6411      */
6412     load : function(options){
6413         options = options || {};
6414         if(this.fireEvent("beforeload", this, options) !== false){
6415             this.storeOptions(options);
6416             var p = Roo.apply(options.params || {}, this.baseParams);
6417             // if meta was not loaded from remote source.. try requesting it.
6418             if (!this.reader.metaFromRemote) {
6419                 p._requestMeta = 1;
6420             }
6421             if(this.sortInfo && this.remoteSort){
6422                 var pn = this.paramNames;
6423                 p[pn["sort"]] = this.sortInfo.field;
6424                 p[pn["dir"]] = this.sortInfo.direction;
6425             }
6426             if (this.multiSort) {
6427                 var pn = this.paramNames;
6428                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6429             }
6430             
6431             this.proxy.load(p, this.reader, this.loadRecords, this, options);
6432         }
6433     },
6434
6435     /**
6436      * Reloads the Record cache from the configured Proxy using the configured Reader and
6437      * the options from the last load operation performed.
6438      * @param {Object} options (optional) An object containing properties which may override the options
6439      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6440      * the most recently used options are reused).
6441      */
6442     reload : function(options){
6443         this.load(Roo.applyIf(options||{}, this.lastOptions));
6444     },
6445
6446     // private
6447     // Called as a callback by the Reader during a load operation.
6448     loadRecords : function(o, options, success){
6449         if(!o || success === false){
6450             if(success !== false){
6451                 this.fireEvent("load", this, [], options, o);
6452             }
6453             if(options.callback){
6454                 options.callback.call(options.scope || this, [], options, false);
6455             }
6456             return;
6457         }
6458         // if data returned failure - throw an exception.
6459         if (o.success === false) {
6460             // show a message if no listener is registered.
6461             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6462                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6463             }
6464             // loadmask wil be hooked into this..
6465             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6466             return;
6467         }
6468         var r = o.records, t = o.totalRecords || r.length;
6469         
6470         this.fireEvent("beforeloadadd", this, r, options, o);
6471         
6472         if(!options || options.add !== true){
6473             if(this.pruneModifiedRecords){
6474                 this.modified = [];
6475             }
6476             for(var i = 0, len = r.length; i < len; i++){
6477                 r[i].join(this);
6478             }
6479             if(this.snapshot){
6480                 this.data = this.snapshot;
6481                 delete this.snapshot;
6482             }
6483             this.data.clear();
6484             this.data.addAll(r);
6485             this.totalLength = t;
6486             this.applySort();
6487             this.fireEvent("datachanged", this);
6488         }else{
6489             this.totalLength = Math.max(t, this.data.length+r.length);
6490             this.add(r);
6491         }
6492         this.fireEvent("load", this, r, options, o);
6493         if(options.callback){
6494             options.callback.call(options.scope || this, r, options, true);
6495         }
6496     },
6497
6498
6499     /**
6500      * Loads data from a passed data block. A Reader which understands the format of the data
6501      * must have been configured in the constructor.
6502      * @param {Object} data The data block from which to read the Records.  The format of the data expected
6503      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6504      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6505      */
6506     loadData : function(o, append){
6507         var r = this.reader.readRecords(o);
6508         this.loadRecords(r, {add: append}, true);
6509     },
6510
6511     /**
6512      * Gets the number of cached records.
6513      * <p>
6514      * <em>If using paging, this may not be the total size of the dataset. If the data object
6515      * used by the Reader contains the dataset size, then the getTotalCount() function returns
6516      * the data set size</em>
6517      */
6518     getCount : function(){
6519         return this.data.length || 0;
6520     },
6521
6522     /**
6523      * Gets the total number of records in the dataset as returned by the server.
6524      * <p>
6525      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6526      * the dataset size</em>
6527      */
6528     getTotalCount : function(){
6529         return this.totalLength || 0;
6530     },
6531
6532     /**
6533      * Returns the sort state of the Store as an object with two properties:
6534      * <pre><code>
6535  field {String} The name of the field by which the Records are sorted
6536  direction {String} The sort order, "ASC" or "DESC"
6537      * </code></pre>
6538      */
6539     getSortState : function(){
6540         return this.sortInfo;
6541     },
6542
6543     // private
6544     applySort : function(){
6545         if(this.sortInfo && !this.remoteSort){
6546             var s = this.sortInfo, f = s.field;
6547             var st = this.fields.get(f).sortType;
6548             var fn = function(r1, r2){
6549                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6550                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6551             };
6552             this.data.sort(s.direction, fn);
6553             if(this.snapshot && this.snapshot != this.data){
6554                 this.snapshot.sort(s.direction, fn);
6555             }
6556         }
6557     },
6558
6559     /**
6560      * Sets the default sort column and order to be used by the next load operation.
6561      * @param {String} fieldName The name of the field to sort by.
6562      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6563      */
6564     setDefaultSort : function(field, dir){
6565         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6566     },
6567
6568     /**
6569      * Sort the Records.
6570      * If remote sorting is used, the sort is performed on the server, and the cache is
6571      * reloaded. If local sorting is used, the cache is sorted internally.
6572      * @param {String} fieldName The name of the field to sort by.
6573      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6574      */
6575     sort : function(fieldName, dir){
6576         var f = this.fields.get(fieldName);
6577         if(!dir){
6578             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6579             
6580             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6581                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6582             }else{
6583                 dir = f.sortDir;
6584             }
6585         }
6586         this.sortToggle[f.name] = dir;
6587         this.sortInfo = {field: f.name, direction: dir};
6588         if(!this.remoteSort){
6589             this.applySort();
6590             this.fireEvent("datachanged", this);
6591         }else{
6592             this.load(this.lastOptions);
6593         }
6594     },
6595
6596     /**
6597      * Calls the specified function for each of the Records in the cache.
6598      * @param {Function} fn The function to call. The Record is passed as the first parameter.
6599      * Returning <em>false</em> aborts and exits the iteration.
6600      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6601      */
6602     each : function(fn, scope){
6603         this.data.each(fn, scope);
6604     },
6605
6606     /**
6607      * Gets all records modified since the last commit.  Modified records are persisted across load operations
6608      * (e.g., during paging).
6609      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6610      */
6611     getModifiedRecords : function(){
6612         return this.modified;
6613     },
6614
6615     // private
6616     createFilterFn : function(property, value, anyMatch){
6617         if(!value.exec){ // not a regex
6618             value = String(value);
6619             if(value.length == 0){
6620                 return false;
6621             }
6622             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6623         }
6624         return function(r){
6625             return value.test(r.data[property]);
6626         };
6627     },
6628
6629     /**
6630      * Sums the value of <i>property</i> for each record between start and end and returns the result.
6631      * @param {String} property A field on your records
6632      * @param {Number} start The record index to start at (defaults to 0)
6633      * @param {Number} end The last record index to include (defaults to length - 1)
6634      * @return {Number} The sum
6635      */
6636     sum : function(property, start, end){
6637         var rs = this.data.items, v = 0;
6638         start = start || 0;
6639         end = (end || end === 0) ? end : rs.length-1;
6640
6641         for(var i = start; i <= end; i++){
6642             v += (rs[i].data[property] || 0);
6643         }
6644         return v;
6645     },
6646
6647     /**
6648      * Filter the records by a specified property.
6649      * @param {String} field A field on your records
6650      * @param {String/RegExp} value Either a string that the field
6651      * should start with or a RegExp to test against the field
6652      * @param {Boolean} anyMatch True to match any part not just the beginning
6653      */
6654     filter : function(property, value, anyMatch){
6655         var fn = this.createFilterFn(property, value, anyMatch);
6656         return fn ? this.filterBy(fn) : this.clearFilter();
6657     },
6658
6659     /**
6660      * Filter by a function. The specified function will be called with each
6661      * record in this data source. If the function returns true the record is included,
6662      * otherwise it is filtered.
6663      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6664      * @param {Object} scope (optional) The scope of the function (defaults to this)
6665      */
6666     filterBy : function(fn, scope){
6667         this.snapshot = this.snapshot || this.data;
6668         this.data = this.queryBy(fn, scope||this);
6669         this.fireEvent("datachanged", this);
6670     },
6671
6672     /**
6673      * Query the records by a specified property.
6674      * @param {String} field A field on your records
6675      * @param {String/RegExp} value Either a string that the field
6676      * should start with or a RegExp to test against the field
6677      * @param {Boolean} anyMatch True to match any part not just the beginning
6678      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6679      */
6680     query : function(property, value, anyMatch){
6681         var fn = this.createFilterFn(property, value, anyMatch);
6682         return fn ? this.queryBy(fn) : this.data.clone();
6683     },
6684
6685     /**
6686      * Query by a function. The specified function will be called with each
6687      * record in this data source. If the function returns true the record is included
6688      * in the results.
6689      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6690      * @param {Object} scope (optional) The scope of the function (defaults to this)
6691       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6692      **/
6693     queryBy : function(fn, scope){
6694         var data = this.snapshot || this.data;
6695         return data.filterBy(fn, scope||this);
6696     },
6697
6698     /**
6699      * Collects unique values for a particular dataIndex from this store.
6700      * @param {String} dataIndex The property to collect
6701      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6702      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6703      * @return {Array} An array of the unique values
6704      **/
6705     collect : function(dataIndex, allowNull, bypassFilter){
6706         var d = (bypassFilter === true && this.snapshot) ?
6707                 this.snapshot.items : this.data.items;
6708         var v, sv, r = [], l = {};
6709         for(var i = 0, len = d.length; i < len; i++){
6710             v = d[i].data[dataIndex];
6711             sv = String(v);
6712             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6713                 l[sv] = true;
6714                 r[r.length] = v;
6715             }
6716         }
6717         return r;
6718     },
6719
6720     /**
6721      * Revert to a view of the Record cache with no filtering applied.
6722      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6723      */
6724     clearFilter : function(suppressEvent){
6725         if(this.snapshot && this.snapshot != this.data){
6726             this.data = this.snapshot;
6727             delete this.snapshot;
6728             if(suppressEvent !== true){
6729                 this.fireEvent("datachanged", this);
6730             }
6731         }
6732     },
6733
6734     // private
6735     afterEdit : function(record){
6736         if(this.modified.indexOf(record) == -1){
6737             this.modified.push(record);
6738         }
6739         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6740     },
6741     
6742     // private
6743     afterReject : function(record){
6744         this.modified.remove(record);
6745         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6746     },
6747
6748     // private
6749     afterCommit : function(record){
6750         this.modified.remove(record);
6751         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6752     },
6753
6754     /**
6755      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6756      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6757      */
6758     commitChanges : function(){
6759         var m = this.modified.slice(0);
6760         this.modified = [];
6761         for(var i = 0, len = m.length; i < len; i++){
6762             m[i].commit();
6763         }
6764     },
6765
6766     /**
6767      * Cancel outstanding changes on all changed records.
6768      */
6769     rejectChanges : function(){
6770         var m = this.modified.slice(0);
6771         this.modified = [];
6772         for(var i = 0, len = m.length; i < len; i++){
6773             m[i].reject();
6774         }
6775     },
6776
6777     onMetaChange : function(meta, rtype, o){
6778         this.recordType = rtype;
6779         this.fields = rtype.prototype.fields;
6780         delete this.snapshot;
6781         this.sortInfo = meta.sortInfo || this.sortInfo;
6782         this.modified = [];
6783         this.fireEvent('metachange', this, this.reader.meta);
6784     },
6785     
6786     moveIndex : function(data, type)
6787     {
6788         var index = this.indexOf(data);
6789         
6790         var newIndex = index + type;
6791         
6792         this.remove(data);
6793         
6794         this.insert(newIndex, data);
6795         
6796     }
6797 });/*
6798  * Based on:
6799  * Ext JS Library 1.1.1
6800  * Copyright(c) 2006-2007, Ext JS, LLC.
6801  *
6802  * Originally Released Under LGPL - original licence link has changed is not relivant.
6803  *
6804  * Fork - LGPL
6805  * <script type="text/javascript">
6806  */
6807
6808 /**
6809  * @class Roo.data.SimpleStore
6810  * @extends Roo.data.Store
6811  * Small helper class to make creating Stores from Array data easier.
6812  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6813  * @cfg {Array} fields An array of field definition objects, or field name strings.
6814  * @cfg {Array} data The multi-dimensional array of data
6815  * @constructor
6816  * @param {Object} config
6817  */
6818 Roo.data.SimpleStore = function(config){
6819     Roo.data.SimpleStore.superclass.constructor.call(this, {
6820         isLocal : true,
6821         reader: new Roo.data.ArrayReader({
6822                 id: config.id
6823             },
6824             Roo.data.Record.create(config.fields)
6825         ),
6826         proxy : new Roo.data.MemoryProxy(config.data)
6827     });
6828     this.load();
6829 };
6830 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6831  * Based on:
6832  * Ext JS Library 1.1.1
6833  * Copyright(c) 2006-2007, Ext JS, LLC.
6834  *
6835  * Originally Released Under LGPL - original licence link has changed is not relivant.
6836  *
6837  * Fork - LGPL
6838  * <script type="text/javascript">
6839  */
6840
6841 /**
6842 /**
6843  * @extends Roo.data.Store
6844  * @class Roo.data.JsonStore
6845  * Small helper class to make creating Stores for JSON data easier. <br/>
6846 <pre><code>
6847 var store = new Roo.data.JsonStore({
6848     url: 'get-images.php',
6849     root: 'images',
6850     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6851 });
6852 </code></pre>
6853  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6854  * JsonReader and HttpProxy (unless inline data is provided).</b>
6855  * @cfg {Array} fields An array of field definition objects, or field name strings.
6856  * @constructor
6857  * @param {Object} config
6858  */
6859 Roo.data.JsonStore = function(c){
6860     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6861         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6862         reader: new Roo.data.JsonReader(c, c.fields)
6863     }));
6864 };
6865 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6866  * Based on:
6867  * Ext JS Library 1.1.1
6868  * Copyright(c) 2006-2007, Ext JS, LLC.
6869  *
6870  * Originally Released Under LGPL - original licence link has changed is not relivant.
6871  *
6872  * Fork - LGPL
6873  * <script type="text/javascript">
6874  */
6875
6876  
6877 Roo.data.Field = function(config){
6878     if(typeof config == "string"){
6879         config = {name: config};
6880     }
6881     Roo.apply(this, config);
6882     
6883     if(!this.type){
6884         this.type = "auto";
6885     }
6886     
6887     var st = Roo.data.SortTypes;
6888     // named sortTypes are supported, here we look them up
6889     if(typeof this.sortType == "string"){
6890         this.sortType = st[this.sortType];
6891     }
6892     
6893     // set default sortType for strings and dates
6894     if(!this.sortType){
6895         switch(this.type){
6896             case "string":
6897                 this.sortType = st.asUCString;
6898                 break;
6899             case "date":
6900                 this.sortType = st.asDate;
6901                 break;
6902             default:
6903                 this.sortType = st.none;
6904         }
6905     }
6906
6907     // define once
6908     var stripRe = /[\$,%]/g;
6909
6910     // prebuilt conversion function for this field, instead of
6911     // switching every time we're reading a value
6912     if(!this.convert){
6913         var cv, dateFormat = this.dateFormat;
6914         switch(this.type){
6915             case "":
6916             case "auto":
6917             case undefined:
6918                 cv = function(v){ return v; };
6919                 break;
6920             case "string":
6921                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6922                 break;
6923             case "int":
6924                 cv = function(v){
6925                     return v !== undefined && v !== null && v !== '' ?
6926                            parseInt(String(v).replace(stripRe, ""), 10) : '';
6927                     };
6928                 break;
6929             case "float":
6930                 cv = function(v){
6931                     return v !== undefined && v !== null && v !== '' ?
6932                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
6933                     };
6934                 break;
6935             case "bool":
6936             case "boolean":
6937                 cv = function(v){ return v === true || v === "true" || v == 1; };
6938                 break;
6939             case "date":
6940                 cv = function(v){
6941                     if(!v){
6942                         return '';
6943                     }
6944                     if(v instanceof Date){
6945                         return v;
6946                     }
6947                     if(dateFormat){
6948                         if(dateFormat == "timestamp"){
6949                             return new Date(v*1000);
6950                         }
6951                         return Date.parseDate(v, dateFormat);
6952                     }
6953                     var parsed = Date.parse(v);
6954                     return parsed ? new Date(parsed) : null;
6955                 };
6956              break;
6957             
6958         }
6959         this.convert = cv;
6960     }
6961 };
6962
6963 Roo.data.Field.prototype = {
6964     dateFormat: null,
6965     defaultValue: "",
6966     mapping: null,
6967     sortType : null,
6968     sortDir : "ASC"
6969 };/*
6970  * Based on:
6971  * Ext JS Library 1.1.1
6972  * Copyright(c) 2006-2007, Ext JS, LLC.
6973  *
6974  * Originally Released Under LGPL - original licence link has changed is not relivant.
6975  *
6976  * Fork - LGPL
6977  * <script type="text/javascript">
6978  */
6979  
6980 // Base class for reading structured data from a data source.  This class is intended to be
6981 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6982
6983 /**
6984  * @class Roo.data.DataReader
6985  * Base class for reading structured data from a data source.  This class is intended to be
6986  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6987  */
6988
6989 Roo.data.DataReader = function(meta, recordType){
6990     
6991     this.meta = meta;
6992     
6993     this.recordType = recordType instanceof Array ? 
6994         Roo.data.Record.create(recordType) : recordType;
6995 };
6996
6997 Roo.data.DataReader.prototype = {
6998      /**
6999      * Create an empty record
7000      * @param {Object} data (optional) - overlay some values
7001      * @return {Roo.data.Record} record created.
7002      */
7003     newRow :  function(d) {
7004         var da =  {};
7005         this.recordType.prototype.fields.each(function(c) {
7006             switch( c.type) {
7007                 case 'int' : da[c.name] = 0; break;
7008                 case 'date' : da[c.name] = new Date(); break;
7009                 case 'float' : da[c.name] = 0.0; break;
7010                 case 'boolean' : da[c.name] = false; break;
7011                 default : da[c.name] = ""; break;
7012             }
7013             
7014         });
7015         return new this.recordType(Roo.apply(da, d));
7016     }
7017     
7018 };/*
7019  * Based on:
7020  * Ext JS Library 1.1.1
7021  * Copyright(c) 2006-2007, Ext JS, LLC.
7022  *
7023  * Originally Released Under LGPL - original licence link has changed is not relivant.
7024  *
7025  * Fork - LGPL
7026  * <script type="text/javascript">
7027  */
7028
7029 /**
7030  * @class Roo.data.DataProxy
7031  * @extends Roo.data.Observable
7032  * This class is an abstract base class for implementations which provide retrieval of
7033  * unformatted data objects.<br>
7034  * <p>
7035  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7036  * (of the appropriate type which knows how to parse the data object) to provide a block of
7037  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7038  * <p>
7039  * Custom implementations must implement the load method as described in
7040  * {@link Roo.data.HttpProxy#load}.
7041  */
7042 Roo.data.DataProxy = function(){
7043     this.addEvents({
7044         /**
7045          * @event beforeload
7046          * Fires before a network request is made to retrieve a data object.
7047          * @param {Object} This DataProxy object.
7048          * @param {Object} params The params parameter to the load function.
7049          */
7050         beforeload : true,
7051         /**
7052          * @event load
7053          * Fires before the load method's callback is called.
7054          * @param {Object} This DataProxy object.
7055          * @param {Object} o The data object.
7056          * @param {Object} arg The callback argument object passed to the load function.
7057          */
7058         load : true,
7059         /**
7060          * @event loadexception
7061          * Fires if an Exception occurs during data retrieval.
7062          * @param {Object} This DataProxy object.
7063          * @param {Object} o The data object.
7064          * @param {Object} arg The callback argument object passed to the load function.
7065          * @param {Object} e The Exception.
7066          */
7067         loadexception : true
7068     });
7069     Roo.data.DataProxy.superclass.constructor.call(this);
7070 };
7071
7072 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7073
7074     /**
7075      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7076      */
7077 /*
7078  * Based on:
7079  * Ext JS Library 1.1.1
7080  * Copyright(c) 2006-2007, Ext JS, LLC.
7081  *
7082  * Originally Released Under LGPL - original licence link has changed is not relivant.
7083  *
7084  * Fork - LGPL
7085  * <script type="text/javascript">
7086  */
7087 /**
7088  * @class Roo.data.MemoryProxy
7089  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7090  * to the Reader when its load method is called.
7091  * @constructor
7092  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7093  */
7094 Roo.data.MemoryProxy = function(data){
7095     if (data.data) {
7096         data = data.data;
7097     }
7098     Roo.data.MemoryProxy.superclass.constructor.call(this);
7099     this.data = data;
7100 };
7101
7102 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7103     /**
7104      * Load data from the requested source (in this case an in-memory
7105      * data object passed to the constructor), read the data object into
7106      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7107      * process that block using the passed callback.
7108      * @param {Object} params This parameter is not used by the MemoryProxy class.
7109      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7110      * object into a block of Roo.data.Records.
7111      * @param {Function} callback The function into which to pass the block of Roo.data.records.
7112      * The function must be passed <ul>
7113      * <li>The Record block object</li>
7114      * <li>The "arg" argument from the load function</li>
7115      * <li>A boolean success indicator</li>
7116      * </ul>
7117      * @param {Object} scope The scope in which to call the callback
7118      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7119      */
7120     load : function(params, reader, callback, scope, arg){
7121         params = params || {};
7122         var result;
7123         try {
7124             result = reader.readRecords(this.data);
7125         }catch(e){
7126             this.fireEvent("loadexception", this, arg, null, e);
7127             callback.call(scope, null, arg, false);
7128             return;
7129         }
7130         callback.call(scope, result, arg, true);
7131     },
7132     
7133     // private
7134     update : function(params, records){
7135         
7136     }
7137 });/*
7138  * Based on:
7139  * Ext JS Library 1.1.1
7140  * Copyright(c) 2006-2007, Ext JS, LLC.
7141  *
7142  * Originally Released Under LGPL - original licence link has changed is not relivant.
7143  *
7144  * Fork - LGPL
7145  * <script type="text/javascript">
7146  */
7147 /**
7148  * @class Roo.data.HttpProxy
7149  * @extends Roo.data.DataProxy
7150  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7151  * configured to reference a certain URL.<br><br>
7152  * <p>
7153  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7154  * from which the running page was served.<br><br>
7155  * <p>
7156  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7157  * <p>
7158  * Be aware that to enable the browser to parse an XML document, the server must set
7159  * the Content-Type header in the HTTP response to "text/xml".
7160  * @constructor
7161  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7162  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7163  * will be used to make the request.
7164  */
7165 Roo.data.HttpProxy = function(conn){
7166     Roo.data.HttpProxy.superclass.constructor.call(this);
7167     // is conn a conn config or a real conn?
7168     this.conn = conn;
7169     this.useAjax = !conn || !conn.events;
7170   
7171 };
7172
7173 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7174     // thse are take from connection...
7175     
7176     /**
7177      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7178      */
7179     /**
7180      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7181      * extra parameters to each request made by this object. (defaults to undefined)
7182      */
7183     /**
7184      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7185      *  to each request made by this object. (defaults to undefined)
7186      */
7187     /**
7188      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
7189      */
7190     /**
7191      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7192      */
7193      /**
7194      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7195      * @type Boolean
7196      */
7197   
7198
7199     /**
7200      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7201      * @type Boolean
7202      */
7203     /**
7204      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7205      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7206      * a finer-grained basis than the DataProxy events.
7207      */
7208     getConnection : function(){
7209         return this.useAjax ? Roo.Ajax : this.conn;
7210     },
7211
7212     /**
7213      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7214      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7215      * process that block using the passed callback.
7216      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7217      * for the request to the remote server.
7218      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7219      * object into a block of Roo.data.Records.
7220      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7221      * The function must be passed <ul>
7222      * <li>The Record block object</li>
7223      * <li>The "arg" argument from the load function</li>
7224      * <li>A boolean success indicator</li>
7225      * </ul>
7226      * @param {Object} scope The scope in which to call the callback
7227      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7228      */
7229     load : function(params, reader, callback, scope, arg){
7230         if(this.fireEvent("beforeload", this, params) !== false){
7231             var  o = {
7232                 params : params || {},
7233                 request: {
7234                     callback : callback,
7235                     scope : scope,
7236                     arg : arg
7237                 },
7238                 reader: reader,
7239                 callback : this.loadResponse,
7240                 scope: this
7241             };
7242             if(this.useAjax){
7243                 Roo.applyIf(o, this.conn);
7244                 if(this.activeRequest){
7245                     Roo.Ajax.abort(this.activeRequest);
7246                 }
7247                 this.activeRequest = Roo.Ajax.request(o);
7248             }else{
7249                 this.conn.request(o);
7250             }
7251         }else{
7252             callback.call(scope||this, null, arg, false);
7253         }
7254     },
7255
7256     // private
7257     loadResponse : function(o, success, response){
7258         delete this.activeRequest;
7259         if(!success){
7260             this.fireEvent("loadexception", this, o, response);
7261             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7262             return;
7263         }
7264         var result;
7265         try {
7266             result = o.reader.read(response);
7267         }catch(e){
7268             this.fireEvent("loadexception", this, o, response, e);
7269             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7270             return;
7271         }
7272         
7273         this.fireEvent("load", this, o, o.request.arg);
7274         o.request.callback.call(o.request.scope, result, o.request.arg, true);
7275     },
7276
7277     // private
7278     update : function(dataSet){
7279
7280     },
7281
7282     // private
7283     updateResponse : function(dataSet){
7284
7285     }
7286 });/*
7287  * Based on:
7288  * Ext JS Library 1.1.1
7289  * Copyright(c) 2006-2007, Ext JS, LLC.
7290  *
7291  * Originally Released Under LGPL - original licence link has changed is not relivant.
7292  *
7293  * Fork - LGPL
7294  * <script type="text/javascript">
7295  */
7296
7297 /**
7298  * @class Roo.data.ScriptTagProxy
7299  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7300  * other than the originating domain of the running page.<br><br>
7301  * <p>
7302  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
7303  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7304  * <p>
7305  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7306  * source code that is used as the source inside a &lt;script> tag.<br><br>
7307  * <p>
7308  * In order for the browser to process the returned data, the server must wrap the data object
7309  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7310  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7311  * depending on whether the callback name was passed:
7312  * <p>
7313  * <pre><code>
7314 boolean scriptTag = false;
7315 String cb = request.getParameter("callback");
7316 if (cb != null) {
7317     scriptTag = true;
7318     response.setContentType("text/javascript");
7319 } else {
7320     response.setContentType("application/x-json");
7321 }
7322 Writer out = response.getWriter();
7323 if (scriptTag) {
7324     out.write(cb + "(");
7325 }
7326 out.print(dataBlock.toJsonString());
7327 if (scriptTag) {
7328     out.write(");");
7329 }
7330 </pre></code>
7331  *
7332  * @constructor
7333  * @param {Object} config A configuration object.
7334  */
7335 Roo.data.ScriptTagProxy = function(config){
7336     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7337     Roo.apply(this, config);
7338     this.head = document.getElementsByTagName("head")[0];
7339 };
7340
7341 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7342
7343 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7344     /**
7345      * @cfg {String} url The URL from which to request the data object.
7346      */
7347     /**
7348      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7349      */
7350     timeout : 30000,
7351     /**
7352      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7353      * the server the name of the callback function set up by the load call to process the returned data object.
7354      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7355      * javascript output which calls this named function passing the data object as its only parameter.
7356      */
7357     callbackParam : "callback",
7358     /**
7359      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7360      * name to the request.
7361      */
7362     nocache : true,
7363
7364     /**
7365      * Load data from the configured URL, read the data object into
7366      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7367      * process that block using the passed callback.
7368      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7369      * for the request to the remote server.
7370      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7371      * object into a block of Roo.data.Records.
7372      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7373      * The function must be passed <ul>
7374      * <li>The Record block object</li>
7375      * <li>The "arg" argument from the load function</li>
7376      * <li>A boolean success indicator</li>
7377      * </ul>
7378      * @param {Object} scope The scope in which to call the callback
7379      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7380      */
7381     load : function(params, reader, callback, scope, arg){
7382         if(this.fireEvent("beforeload", this, params) !== false){
7383
7384             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7385
7386             var url = this.url;
7387             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7388             if(this.nocache){
7389                 url += "&_dc=" + (new Date().getTime());
7390             }
7391             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7392             var trans = {
7393                 id : transId,
7394                 cb : "stcCallback"+transId,
7395                 scriptId : "stcScript"+transId,
7396                 params : params,
7397                 arg : arg,
7398                 url : url,
7399                 callback : callback,
7400                 scope : scope,
7401                 reader : reader
7402             };
7403             var conn = this;
7404
7405             window[trans.cb] = function(o){
7406                 conn.handleResponse(o, trans);
7407             };
7408
7409             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7410
7411             if(this.autoAbort !== false){
7412                 this.abort();
7413             }
7414
7415             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7416
7417             var script = document.createElement("script");
7418             script.setAttribute("src", url);
7419             script.setAttribute("type", "text/javascript");
7420             script.setAttribute("id", trans.scriptId);
7421             this.head.appendChild(script);
7422
7423             this.trans = trans;
7424         }else{
7425             callback.call(scope||this, null, arg, false);
7426         }
7427     },
7428
7429     // private
7430     isLoading : function(){
7431         return this.trans ? true : false;
7432     },
7433
7434     /**
7435      * Abort the current server request.
7436      */
7437     abort : function(){
7438         if(this.isLoading()){
7439             this.destroyTrans(this.trans);
7440         }
7441     },
7442
7443     // private
7444     destroyTrans : function(trans, isLoaded){
7445         this.head.removeChild(document.getElementById(trans.scriptId));
7446         clearTimeout(trans.timeoutId);
7447         if(isLoaded){
7448             window[trans.cb] = undefined;
7449             try{
7450                 delete window[trans.cb];
7451             }catch(e){}
7452         }else{
7453             // if hasn't been loaded, wait for load to remove it to prevent script error
7454             window[trans.cb] = function(){
7455                 window[trans.cb] = undefined;
7456                 try{
7457                     delete window[trans.cb];
7458                 }catch(e){}
7459             };
7460         }
7461     },
7462
7463     // private
7464     handleResponse : function(o, trans){
7465         this.trans = false;
7466         this.destroyTrans(trans, true);
7467         var result;
7468         try {
7469             result = trans.reader.readRecords(o);
7470         }catch(e){
7471             this.fireEvent("loadexception", this, o, trans.arg, e);
7472             trans.callback.call(trans.scope||window, null, trans.arg, false);
7473             return;
7474         }
7475         this.fireEvent("load", this, o, trans.arg);
7476         trans.callback.call(trans.scope||window, result, trans.arg, true);
7477     },
7478
7479     // private
7480     handleFailure : function(trans){
7481         this.trans = false;
7482         this.destroyTrans(trans, false);
7483         this.fireEvent("loadexception", this, null, trans.arg);
7484         trans.callback.call(trans.scope||window, null, trans.arg, false);
7485     }
7486 });/*
7487  * Based on:
7488  * Ext JS Library 1.1.1
7489  * Copyright(c) 2006-2007, Ext JS, LLC.
7490  *
7491  * Originally Released Under LGPL - original licence link has changed is not relivant.
7492  *
7493  * Fork - LGPL
7494  * <script type="text/javascript">
7495  */
7496
7497 /**
7498  * @class Roo.data.JsonReader
7499  * @extends Roo.data.DataReader
7500  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7501  * based on mappings in a provided Roo.data.Record constructor.
7502  * 
7503  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7504  * in the reply previously. 
7505  * 
7506  * <p>
7507  * Example code:
7508  * <pre><code>
7509 var RecordDef = Roo.data.Record.create([
7510     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
7511     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
7512 ]);
7513 var myReader = new Roo.data.JsonReader({
7514     totalProperty: "results",    // The property which contains the total dataset size (optional)
7515     root: "rows",                // The property which contains an Array of row objects
7516     id: "id"                     // The property within each row object that provides an ID for the record (optional)
7517 }, RecordDef);
7518 </code></pre>
7519  * <p>
7520  * This would consume a JSON file like this:
7521  * <pre><code>
7522 { 'results': 2, 'rows': [
7523     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7524     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7525 }
7526 </code></pre>
7527  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7528  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7529  * paged from the remote server.
7530  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7531  * @cfg {String} root name of the property which contains the Array of row objects.
7532  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7533  * @constructor
7534  * Create a new JsonReader
7535  * @param {Object} meta Metadata configuration options
7536  * @param {Object} recordType Either an Array of field definition objects,
7537  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7538  */
7539 Roo.data.JsonReader = function(meta, recordType){
7540     
7541     meta = meta || {};
7542     // set some defaults:
7543     Roo.applyIf(meta, {
7544         totalProperty: 'total',
7545         successProperty : 'success',
7546         root : 'data',
7547         id : 'id'
7548     });
7549     
7550     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7551 };
7552 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7553     
7554     /**
7555      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
7556      * Used by Store query builder to append _requestMeta to params.
7557      * 
7558      */
7559     metaFromRemote : false,
7560     /**
7561      * This method is only used by a DataProxy which has retrieved data from a remote server.
7562      * @param {Object} response The XHR object which contains the JSON data in its responseText.
7563      * @return {Object} data A data block which is used by an Roo.data.Store object as
7564      * a cache of Roo.data.Records.
7565      */
7566     read : function(response){
7567         var json = response.responseText;
7568        
7569         var o = /* eval:var:o */ eval("("+json+")");
7570         if(!o) {
7571             throw {message: "JsonReader.read: Json object not found"};
7572         }
7573         
7574         if(o.metaData){
7575             
7576             delete this.ef;
7577             this.metaFromRemote = true;
7578             this.meta = o.metaData;
7579             this.recordType = Roo.data.Record.create(o.metaData.fields);
7580             this.onMetaChange(this.meta, this.recordType, o);
7581         }
7582         return this.readRecords(o);
7583     },
7584
7585     // private function a store will implement
7586     onMetaChange : function(meta, recordType, o){
7587
7588     },
7589
7590     /**
7591          * @ignore
7592          */
7593     simpleAccess: function(obj, subsc) {
7594         return obj[subsc];
7595     },
7596
7597         /**
7598          * @ignore
7599          */
7600     getJsonAccessor: function(){
7601         var re = /[\[\.]/;
7602         return function(expr) {
7603             try {
7604                 return(re.test(expr))
7605                     ? new Function("obj", "return obj." + expr)
7606                     : function(obj){
7607                         return obj[expr];
7608                     };
7609             } catch(e){}
7610             return Roo.emptyFn;
7611         };
7612     }(),
7613
7614     /**
7615      * Create a data block containing Roo.data.Records from an XML document.
7616      * @param {Object} o An object which contains an Array of row objects in the property specified
7617      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7618      * which contains the total size of the dataset.
7619      * @return {Object} data A data block which is used by an Roo.data.Store object as
7620      * a cache of Roo.data.Records.
7621      */
7622     readRecords : function(o){
7623         /**
7624          * After any data loads, the raw JSON data is available for further custom processing.
7625          * @type Object
7626          */
7627         this.o = o;
7628         var s = this.meta, Record = this.recordType,
7629             f = Record.prototype.fields, fi = f.items, fl = f.length;
7630
7631 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
7632         if (!this.ef) {
7633             if(s.totalProperty) {
7634                     this.getTotal = this.getJsonAccessor(s.totalProperty);
7635                 }
7636                 if(s.successProperty) {
7637                     this.getSuccess = this.getJsonAccessor(s.successProperty);
7638                 }
7639                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7640                 if (s.id) {
7641                         var g = this.getJsonAccessor(s.id);
7642                         this.getId = function(rec) {
7643                                 var r = g(rec);
7644                                 return (r === undefined || r === "") ? null : r;
7645                         };
7646                 } else {
7647                         this.getId = function(){return null;};
7648                 }
7649             this.ef = [];
7650             for(var jj = 0; jj < fl; jj++){
7651                 f = fi[jj];
7652                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7653                 this.ef[jj] = this.getJsonAccessor(map);
7654             }
7655         }
7656
7657         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7658         if(s.totalProperty){
7659             var vt = parseInt(this.getTotal(o), 10);
7660             if(!isNaN(vt)){
7661                 totalRecords = vt;
7662             }
7663         }
7664         if(s.successProperty){
7665             var vs = this.getSuccess(o);
7666             if(vs === false || vs === 'false'){
7667                 success = false;
7668             }
7669         }
7670         var records = [];
7671             for(var i = 0; i < c; i++){
7672                     var n = root[i];
7673                 var values = {};
7674                 var id = this.getId(n);
7675                 for(var j = 0; j < fl; j++){
7676                     f = fi[j];
7677                 var v = this.ef[j](n);
7678                 if (!f.convert) {
7679                     Roo.log('missing convert for ' + f.name);
7680                     Roo.log(f);
7681                     continue;
7682                 }
7683                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7684                 }
7685                 var record = new Record(values, id);
7686                 record.json = n;
7687                 records[i] = record;
7688             }
7689             return {
7690             raw : o,
7691                 success : success,
7692                 records : records,
7693                 totalRecords : totalRecords
7694             };
7695     }
7696 });/*
7697  * Based on:
7698  * Ext JS Library 1.1.1
7699  * Copyright(c) 2006-2007, Ext JS, LLC.
7700  *
7701  * Originally Released Under LGPL - original licence link has changed is not relivant.
7702  *
7703  * Fork - LGPL
7704  * <script type="text/javascript">
7705  */
7706
7707 /**
7708  * @class Roo.data.ArrayReader
7709  * @extends Roo.data.DataReader
7710  * Data reader class to create an Array of Roo.data.Record objects from an Array.
7711  * Each element of that Array represents a row of data fields. The
7712  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7713  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7714  * <p>
7715  * Example code:.
7716  * <pre><code>
7717 var RecordDef = Roo.data.Record.create([
7718     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
7719     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
7720 ]);
7721 var myReader = new Roo.data.ArrayReader({
7722     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
7723 }, RecordDef);
7724 </code></pre>
7725  * <p>
7726  * This would consume an Array like this:
7727  * <pre><code>
7728 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7729   </code></pre>
7730  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7731  * @constructor
7732  * Create a new JsonReader
7733  * @param {Object} meta Metadata configuration options.
7734  * @param {Object} recordType Either an Array of field definition objects
7735  * as specified to {@link Roo.data.Record#create},
7736  * or an {@link Roo.data.Record} object
7737  * created using {@link Roo.data.Record#create}.
7738  */
7739 Roo.data.ArrayReader = function(meta, recordType){
7740     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7741 };
7742
7743 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7744     /**
7745      * Create a data block containing Roo.data.Records from an XML document.
7746      * @param {Object} o An Array of row objects which represents the dataset.
7747      * @return {Object} data A data block which is used by an Roo.data.Store object as
7748      * a cache of Roo.data.Records.
7749      */
7750     readRecords : function(o){
7751         var sid = this.meta ? this.meta.id : null;
7752         var recordType = this.recordType, fields = recordType.prototype.fields;
7753         var records = [];
7754         var root = o;
7755             for(var i = 0; i < root.length; i++){
7756                     var n = root[i];
7757                 var values = {};
7758                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7759                 for(var j = 0, jlen = fields.length; j < jlen; j++){
7760                 var f = fields.items[j];
7761                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7762                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7763                 v = f.convert(v);
7764                 values[f.name] = v;
7765             }
7766                 var record = new recordType(values, id);
7767                 record.json = n;
7768                 records[records.length] = record;
7769             }
7770             return {
7771                 records : records,
7772                 totalRecords : records.length
7773             };
7774     }
7775 });/*
7776  * - LGPL
7777  * * 
7778  */
7779
7780 /**
7781  * @class Roo.bootstrap.ComboBox
7782  * @extends Roo.bootstrap.TriggerField
7783  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7784  * @cfg {Boolean} append (true|false) default false
7785  * @constructor
7786  * Create a new ComboBox.
7787  * @param {Object} config Configuration options
7788  */
7789 Roo.bootstrap.ComboBox = function(config){
7790     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7791     this.addEvents({
7792         /**
7793          * @event expand
7794          * Fires when the dropdown list is expanded
7795              * @param {Roo.bootstrap.ComboBox} combo This combo box
7796              */
7797         'expand' : true,
7798         /**
7799          * @event collapse
7800          * Fires when the dropdown list is collapsed
7801              * @param {Roo.bootstrap.ComboBox} combo This combo box
7802              */
7803         'collapse' : true,
7804         /**
7805          * @event beforeselect
7806          * Fires before a list item is selected. Return false to cancel the selection.
7807              * @param {Roo.bootstrap.ComboBox} combo This combo box
7808              * @param {Roo.data.Record} record The data record returned from the underlying store
7809              * @param {Number} index The index of the selected item in the dropdown list
7810              */
7811         'beforeselect' : true,
7812         /**
7813          * @event select
7814          * Fires when a list item is selected
7815              * @param {Roo.bootstrap.ComboBox} combo This combo box
7816              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7817              * @param {Number} index The index of the selected item in the dropdown list
7818              */
7819         'select' : true,
7820         /**
7821          * @event beforequery
7822          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7823          * The event object passed has these properties:
7824              * @param {Roo.bootstrap.ComboBox} combo This combo box
7825              * @param {String} query The query
7826              * @param {Boolean} forceAll true to force "all" query
7827              * @param {Boolean} cancel true to cancel the query
7828              * @param {Object} e The query event object
7829              */
7830         'beforequery': true,
7831          /**
7832          * @event add
7833          * Fires when the 'add' icon is pressed (add a listener to enable add button)
7834              * @param {Roo.bootstrap.ComboBox} combo This combo box
7835              */
7836         'add' : true,
7837         /**
7838          * @event edit
7839          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7840              * @param {Roo.bootstrap.ComboBox} combo This combo box
7841              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7842              */
7843         'edit' : true,
7844         /**
7845          * @event remove
7846          * Fires when the remove value from the combobox array
7847              * @param {Roo.bootstrap.ComboBox} combo This combo box
7848              */
7849         'remove' : true
7850         
7851     });
7852     
7853     
7854     this.selectedIndex = -1;
7855     if(this.mode == 'local'){
7856         if(config.queryDelay === undefined){
7857             this.queryDelay = 10;
7858         }
7859         if(config.minChars === undefined){
7860             this.minChars = 0;
7861         }
7862     }
7863 };
7864
7865 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7866      
7867     /**
7868      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7869      * rendering into an Roo.Editor, defaults to false)
7870      */
7871     /**
7872      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7873      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7874      */
7875     /**
7876      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7877      */
7878     /**
7879      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7880      * the dropdown list (defaults to undefined, with no header element)
7881      */
7882
7883      /**
7884      * @cfg {String/Roo.Template} tpl The template to use to render the output
7885      */
7886      
7887      /**
7888      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7889      */
7890     listWidth: undefined,
7891     /**
7892      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7893      * mode = 'remote' or 'text' if mode = 'local')
7894      */
7895     displayField: undefined,
7896     /**
7897      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7898      * mode = 'remote' or 'value' if mode = 'local'). 
7899      * Note: use of a valueField requires the user make a selection
7900      * in order for a value to be mapped.
7901      */
7902     valueField: undefined,
7903     
7904     
7905     /**
7906      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7907      * field's data value (defaults to the underlying DOM element's name)
7908      */
7909     hiddenName: undefined,
7910     /**
7911      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7912      */
7913     listClass: '',
7914     /**
7915      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7916      */
7917     selectedClass: 'active',
7918     
7919     /**
7920      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7921      */
7922     shadow:'sides',
7923     /**
7924      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7925      * anchor positions (defaults to 'tl-bl')
7926      */
7927     listAlign: 'tl-bl?',
7928     /**
7929      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7930      */
7931     maxHeight: 300,
7932     /**
7933      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
7934      * query specified by the allQuery config option (defaults to 'query')
7935      */
7936     triggerAction: 'query',
7937     /**
7938      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7939      * (defaults to 4, does not apply if editable = false)
7940      */
7941     minChars : 4,
7942     /**
7943      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7944      * delay (typeAheadDelay) if it matches a known value (defaults to false)
7945      */
7946     typeAhead: false,
7947     /**
7948      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7949      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7950      */
7951     queryDelay: 500,
7952     /**
7953      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7954      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
7955      */
7956     pageSize: 0,
7957     /**
7958      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
7959      * when editable = true (defaults to false)
7960      */
7961     selectOnFocus:false,
7962     /**
7963      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7964      */
7965     queryParam: 'query',
7966     /**
7967      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
7968      * when mode = 'remote' (defaults to 'Loading...')
7969      */
7970     loadingText: 'Loading...',
7971     /**
7972      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7973      */
7974     resizable: false,
7975     /**
7976      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7977      */
7978     handleHeight : 8,
7979     /**
7980      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7981      * traditional select (defaults to true)
7982      */
7983     editable: true,
7984     /**
7985      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7986      */
7987     allQuery: '',
7988     /**
7989      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7990      */
7991     mode: 'remote',
7992     /**
7993      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7994      * listWidth has a higher value)
7995      */
7996     minListWidth : 70,
7997     /**
7998      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7999      * allow the user to set arbitrary text into the field (defaults to false)
8000      */
8001     forceSelection:false,
8002     /**
8003      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
8004      * if typeAhead = true (defaults to 250)
8005      */
8006     typeAheadDelay : 250,
8007     /**
8008      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
8009      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
8010      */
8011     valueNotFoundText : undefined,
8012     /**
8013      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
8014      */
8015     blockFocus : false,
8016     
8017     /**
8018      * @cfg {Boolean} disableClear Disable showing of clear button.
8019      */
8020     disableClear : false,
8021     /**
8022      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
8023      */
8024     alwaysQuery : false,
8025     
8026     /**
8027      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
8028      */
8029     multiple : false,
8030     
8031     //private
8032     addicon : false,
8033     editicon: false,
8034     
8035     page: 0,
8036     hasQuery: false,
8037     append: false,
8038     loadNext: false,
8039     item: [],
8040     
8041     // element that contains real text value.. (when hidden is used..)
8042      
8043     // private
8044     initEvents: function(){
8045         
8046         if (!this.store) {
8047             throw "can not find store for combo";
8048         }
8049         this.store = Roo.factory(this.store, Roo.data);
8050         
8051         
8052         
8053         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8054         
8055         
8056         if(this.hiddenName){
8057             
8058             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8059             
8060             this.hiddenField.dom.value =
8061                 this.hiddenValue !== undefined ? this.hiddenValue :
8062                 this.value !== undefined ? this.value : '';
8063
8064             // prevent input submission
8065             this.el.dom.removeAttribute('name');
8066             this.hiddenField.dom.setAttribute('name', this.hiddenName);
8067              
8068              
8069         }
8070         //if(Roo.isGecko){
8071         //    this.el.dom.setAttribute('autocomplete', 'off');
8072         //}
8073
8074         var cls = 'x-combo-list';
8075         this.list = this.el.select('ul.dropdown-menu',true).first();
8076
8077         //this.list = new Roo.Layer({
8078         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8079         //});
8080         
8081         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8082         this.list.setWidth(lw);
8083         
8084         this.list.on('mouseover', this.onViewOver, this);
8085         this.list.on('mousemove', this.onViewMove, this);
8086         
8087         this.list.on('scroll', this.onViewScroll, this);
8088         
8089         /*
8090         this.list.swallowEvent('mousewheel');
8091         this.assetHeight = 0;
8092
8093         if(this.title){
8094             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8095             this.assetHeight += this.header.getHeight();
8096         }
8097
8098         this.innerList = this.list.createChild({cls:cls+'-inner'});
8099         this.innerList.on('mouseover', this.onViewOver, this);
8100         this.innerList.on('mousemove', this.onViewMove, this);
8101         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8102         
8103         if(this.allowBlank && !this.pageSize && !this.disableClear){
8104             this.footer = this.list.createChild({cls:cls+'-ft'});
8105             this.pageTb = new Roo.Toolbar(this.footer);
8106            
8107         }
8108         if(this.pageSize){
8109             this.footer = this.list.createChild({cls:cls+'-ft'});
8110             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8111                     {pageSize: this.pageSize});
8112             
8113         }
8114         
8115         if (this.pageTb && this.allowBlank && !this.disableClear) {
8116             var _this = this;
8117             this.pageTb.add(new Roo.Toolbar.Fill(), {
8118                 cls: 'x-btn-icon x-btn-clear',
8119                 text: '&#160;',
8120                 handler: function()
8121                 {
8122                     _this.collapse();
8123                     _this.clearValue();
8124                     _this.onSelect(false, -1);
8125                 }
8126             });
8127         }
8128         if (this.footer) {
8129             this.assetHeight += this.footer.getHeight();
8130         }
8131         */
8132             
8133         if(!this.tpl){
8134             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8135         }
8136
8137         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8138             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8139         });
8140         //this.view.wrapEl.setDisplayed(false);
8141         this.view.on('click', this.onViewClick, this);
8142         
8143         
8144         
8145         this.store.on('beforeload', this.onBeforeLoad, this);
8146         this.store.on('load', this.onLoad, this);
8147         this.store.on('loadexception', this.onLoadException, this);
8148         /*
8149         if(this.resizable){
8150             this.resizer = new Roo.Resizable(this.list,  {
8151                pinned:true, handles:'se'
8152             });
8153             this.resizer.on('resize', function(r, w, h){
8154                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8155                 this.listWidth = w;
8156                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8157                 this.restrictHeight();
8158             }, this);
8159             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8160         }
8161         */
8162         if(!this.editable){
8163             this.editable = true;
8164             this.setEditable(false);
8165         }
8166         
8167         /*
8168         
8169         if (typeof(this.events.add.listeners) != 'undefined') {
8170             
8171             this.addicon = this.wrap.createChild(
8172                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8173        
8174             this.addicon.on('click', function(e) {
8175                 this.fireEvent('add', this);
8176             }, this);
8177         }
8178         if (typeof(this.events.edit.listeners) != 'undefined') {
8179             
8180             this.editicon = this.wrap.createChild(
8181                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8182             if (this.addicon) {
8183                 this.editicon.setStyle('margin-left', '40px');
8184             }
8185             this.editicon.on('click', function(e) {
8186                 
8187                 // we fire even  if inothing is selected..
8188                 this.fireEvent('edit', this, this.lastData );
8189                 
8190             }, this);
8191         }
8192         */
8193         
8194         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8195             "up" : function(e){
8196                 this.inKeyMode = true;
8197                 this.selectPrev();
8198             },
8199
8200             "down" : function(e){
8201                 if(!this.isExpanded()){
8202                     this.onTriggerClick();
8203                 }else{
8204                     this.inKeyMode = true;
8205                     this.selectNext();
8206                 }
8207             },
8208
8209             "enter" : function(e){
8210                 this.onViewClick();
8211                 //return true;
8212             },
8213
8214             "esc" : function(e){
8215                 this.collapse();
8216             },
8217
8218             "tab" : function(e){
8219                 this.collapse();
8220                 
8221                 if(this.fireEvent("specialkey", this, e)){
8222                     this.onViewClick(false);
8223                 }
8224                 
8225                 return true;
8226             },
8227
8228             scope : this,
8229
8230             doRelay : function(foo, bar, hname){
8231                 if(hname == 'down' || this.scope.isExpanded()){
8232                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8233                 }
8234                 return true;
8235             },
8236
8237             forceKeyDown: true
8238         });
8239         
8240         
8241         this.queryDelay = Math.max(this.queryDelay || 10,
8242                 this.mode == 'local' ? 10 : 250);
8243         
8244         
8245         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8246         
8247         if(this.typeAhead){
8248             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8249         }
8250         if(this.editable !== false){
8251             this.inputEl().on("keyup", this.onKeyUp, this);
8252         }
8253         if(this.forceSelection){
8254             this.on('blur', this.doForce, this);
8255         }
8256         
8257         if(this.multiple){
8258             this.choices = this.el.select('ul.select2-choices', true).first();
8259             this.searchField = this.el.select('ul li.select2-search-field', true).first();
8260         }
8261     },
8262
8263     onDestroy : function(){
8264         if(this.view){
8265             this.view.setStore(null);
8266             this.view.el.removeAllListeners();
8267             this.view.el.remove();
8268             this.view.purgeListeners();
8269         }
8270         if(this.list){
8271             this.list.dom.innerHTML  = '';
8272         }
8273         if(this.store){
8274             this.store.un('beforeload', this.onBeforeLoad, this);
8275             this.store.un('load', this.onLoad, this);
8276             this.store.un('loadexception', this.onLoadException, this);
8277         }
8278         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8279     },
8280
8281     // private
8282     fireKey : function(e){
8283         if(e.isNavKeyPress() && !this.list.isVisible()){
8284             this.fireEvent("specialkey", this, e);
8285         }
8286     },
8287
8288     // private
8289     onResize: function(w, h){
8290 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8291 //        
8292 //        if(typeof w != 'number'){
8293 //            // we do not handle it!?!?
8294 //            return;
8295 //        }
8296 //        var tw = this.trigger.getWidth();
8297 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
8298 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
8299 //        var x = w - tw;
8300 //        this.inputEl().setWidth( this.adjustWidth('input', x));
8301 //            
8302 //        //this.trigger.setStyle('left', x+'px');
8303 //        
8304 //        if(this.list && this.listWidth === undefined){
8305 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8306 //            this.list.setWidth(lw);
8307 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8308 //        }
8309         
8310     
8311         
8312     },
8313
8314     /**
8315      * Allow or prevent the user from directly editing the field text.  If false is passed,
8316      * the user will only be able to select from the items defined in the dropdown list.  This method
8317      * is the runtime equivalent of setting the 'editable' config option at config time.
8318      * @param {Boolean} value True to allow the user to directly edit the field text
8319      */
8320     setEditable : function(value){
8321         if(value == this.editable){
8322             return;
8323         }
8324         this.editable = value;
8325         if(!value){
8326             this.inputEl().dom.setAttribute('readOnly', true);
8327             this.inputEl().on('mousedown', this.onTriggerClick,  this);
8328             this.inputEl().addClass('x-combo-noedit');
8329         }else{
8330             this.inputEl().dom.setAttribute('readOnly', false);
8331             this.inputEl().un('mousedown', this.onTriggerClick,  this);
8332             this.inputEl().removeClass('x-combo-noedit');
8333         }
8334     },
8335
8336     // private
8337     
8338     onBeforeLoad : function(combo,opts){
8339         if(!this.hasFocus){
8340             return;
8341         }
8342          if (!opts.add) {
8343             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8344          }
8345         this.restrictHeight();
8346         this.selectedIndex = -1;
8347     },
8348
8349     // private
8350     onLoad : function(){
8351         
8352         this.hasQuery = false;
8353         
8354         if(!this.hasFocus){
8355             return;
8356         }
8357         
8358         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8359             this.loading.hide();
8360         }
8361         
8362         if(this.store.getCount() > 0){
8363             this.expand();
8364             this.restrictHeight();
8365             if(this.lastQuery == this.allQuery){
8366                 if(this.editable){
8367                     this.inputEl().dom.select();
8368                 }
8369                 if(!this.selectByValue(this.value, true)){
8370                     this.select(0, true);
8371                 }
8372             }else{
8373                 this.selectNext();
8374                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8375                     this.taTask.delay(this.typeAheadDelay);
8376                 }
8377             }
8378         }else{
8379             this.onEmptyResults();
8380         }
8381         
8382         //this.el.focus();
8383     },
8384     // private
8385     onLoadException : function()
8386     {
8387         this.hasQuery = false;
8388         
8389         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8390             this.loading.hide();
8391         }
8392         
8393         this.collapse();
8394         Roo.log(this.store.reader.jsonData);
8395         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8396             // fixme
8397             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8398         }
8399         
8400         
8401     },
8402     // private
8403     onTypeAhead : function(){
8404         if(this.store.getCount() > 0){
8405             var r = this.store.getAt(0);
8406             var newValue = r.data[this.displayField];
8407             var len = newValue.length;
8408             var selStart = this.getRawValue().length;
8409             
8410             if(selStart != len){
8411                 this.setRawValue(newValue);
8412                 this.selectText(selStart, newValue.length);
8413             }
8414         }
8415     },
8416
8417     // private
8418     onSelect : function(record, index){
8419         
8420         if(this.fireEvent('beforeselect', this, record, index) !== false){
8421         
8422             this.setFromData(index > -1 ? record.data : false);
8423             
8424             this.collapse();
8425             this.fireEvent('select', this, record, index);
8426         }
8427     },
8428
8429     /**
8430      * Returns the currently selected field value or empty string if no value is set.
8431      * @return {String} value The selected value
8432      */
8433     getValue : function(){
8434         
8435         if(this.multiple){
8436             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8437         }
8438         
8439         if(this.valueField){
8440             return typeof this.value != 'undefined' ? this.value : '';
8441         }else{
8442             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8443         }
8444     },
8445
8446     /**
8447      * Clears any text/value currently set in the field
8448      */
8449     clearValue : function(){
8450         if(this.hiddenField){
8451             this.hiddenField.dom.value = '';
8452         }
8453         this.value = '';
8454         this.setRawValue('');
8455         this.lastSelectionText = '';
8456         
8457     },
8458
8459     /**
8460      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
8461      * will be displayed in the field.  If the value does not match the data value of an existing item,
8462      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8463      * Otherwise the field will be blank (although the value will still be set).
8464      * @param {String} value The value to match
8465      */
8466     setValue : function(v){
8467         if(this.multiple){
8468             this.syncValue();
8469             return;
8470         }
8471         
8472         var text = v;
8473         if(this.valueField){
8474             var r = this.findRecord(this.valueField, v);
8475             if(r){
8476                 text = r.data[this.displayField];
8477             }else if(this.valueNotFoundText !== undefined){
8478                 text = this.valueNotFoundText;
8479             }
8480         }
8481         this.lastSelectionText = text;
8482         if(this.hiddenField){
8483             this.hiddenField.dom.value = v;
8484         }
8485         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8486         this.value = v;
8487     },
8488     /**
8489      * @property {Object} the last set data for the element
8490      */
8491     
8492     lastData : false,
8493     /**
8494      * Sets the value of the field based on a object which is related to the record format for the store.
8495      * @param {Object} value the value to set as. or false on reset?
8496      */
8497     setFromData : function(o){
8498         
8499         if(this.multiple){
8500             this.addItem(o);
8501             return;
8502         }
8503             
8504         var dv = ''; // display value
8505         var vv = ''; // value value..
8506         this.lastData = o;
8507         if (this.displayField) {
8508             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8509         } else {
8510             // this is an error condition!!!
8511             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8512         }
8513         
8514         if(this.valueField){
8515             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8516         }
8517         
8518         if(this.hiddenField){
8519             this.hiddenField.dom.value = vv;
8520             
8521             this.lastSelectionText = dv;
8522             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8523             this.value = vv;
8524             return;
8525         }
8526         // no hidden field.. - we store the value in 'value', but still display
8527         // display field!!!!
8528         this.lastSelectionText = dv;
8529         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8530         this.value = vv;
8531         
8532         
8533     },
8534     // private
8535     reset : function(){
8536         // overridden so that last data is reset..
8537         this.setValue(this.originalValue);
8538         this.clearInvalid();
8539         this.lastData = false;
8540         if (this.view) {
8541             this.view.clearSelections();
8542         }
8543     },
8544     // private
8545     findRecord : function(prop, value){
8546         var record;
8547         if(this.store.getCount() > 0){
8548             this.store.each(function(r){
8549                 if(r.data[prop] == value){
8550                     record = r;
8551                     return false;
8552                 }
8553                 return true;
8554             });
8555         }
8556         return record;
8557     },
8558     
8559     getName: function()
8560     {
8561         // returns hidden if it's set..
8562         if (!this.rendered) {return ''};
8563         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
8564         
8565     },
8566     // private
8567     onViewMove : function(e, t){
8568         this.inKeyMode = false;
8569     },
8570
8571     // private
8572     onViewOver : function(e, t){
8573         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8574             return;
8575         }
8576         var item = this.view.findItemFromChild(t);
8577         if(item){
8578             var index = this.view.indexOf(item);
8579             this.select(index, false);
8580         }
8581     },
8582
8583     // private
8584     onViewClick : function(doFocus)
8585     {
8586         var index = this.view.getSelectedIndexes()[0];
8587         var r = this.store.getAt(index);
8588         if(r){
8589             this.onSelect(r, index);
8590         }
8591         if(doFocus !== false && !this.blockFocus){
8592             this.inputEl().focus();
8593         }
8594     },
8595
8596     // private
8597     restrictHeight : function(){
8598         //this.innerList.dom.style.height = '';
8599         //var inner = this.innerList.dom;
8600         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8601         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8602         //this.list.beginUpdate();
8603         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8604         this.list.alignTo(this.inputEl(), this.listAlign);
8605         //this.list.endUpdate();
8606     },
8607
8608     // private
8609     onEmptyResults : function(){
8610         this.collapse();
8611     },
8612
8613     /**
8614      * Returns true if the dropdown list is expanded, else false.
8615      */
8616     isExpanded : function(){
8617         return this.list.isVisible();
8618     },
8619
8620     /**
8621      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8622      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8623      * @param {String} value The data value of the item to select
8624      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8625      * selected item if it is not currently in view (defaults to true)
8626      * @return {Boolean} True if the value matched an item in the list, else false
8627      */
8628     selectByValue : function(v, scrollIntoView){
8629         if(v !== undefined && v !== null){
8630             var r = this.findRecord(this.valueField || this.displayField, v);
8631             if(r){
8632                 this.select(this.store.indexOf(r), scrollIntoView);
8633                 return true;
8634             }
8635         }
8636         return false;
8637     },
8638
8639     /**
8640      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8641      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8642      * @param {Number} index The zero-based index of the list item to select
8643      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8644      * selected item if it is not currently in view (defaults to true)
8645      */
8646     select : function(index, scrollIntoView){
8647         this.selectedIndex = index;
8648         this.view.select(index);
8649         if(scrollIntoView !== false){
8650             var el = this.view.getNode(index);
8651             if(el){
8652                 //this.innerList.scrollChildIntoView(el, false);
8653                 
8654             }
8655         }
8656     },
8657
8658     // private
8659     selectNext : function(){
8660         var ct = this.store.getCount();
8661         if(ct > 0){
8662             if(this.selectedIndex == -1){
8663                 this.select(0);
8664             }else if(this.selectedIndex < ct-1){
8665                 this.select(this.selectedIndex+1);
8666             }
8667         }
8668     },
8669
8670     // private
8671     selectPrev : function(){
8672         var ct = this.store.getCount();
8673         if(ct > 0){
8674             if(this.selectedIndex == -1){
8675                 this.select(0);
8676             }else if(this.selectedIndex != 0){
8677                 this.select(this.selectedIndex-1);
8678             }
8679         }
8680     },
8681
8682     // private
8683     onKeyUp : function(e){
8684         if(this.editable !== false && !e.isSpecialKey()){
8685             this.lastKey = e.getKey();
8686             this.dqTask.delay(this.queryDelay);
8687         }
8688     },
8689
8690     // private
8691     validateBlur : function(){
8692         return !this.list || !this.list.isVisible();   
8693     },
8694
8695     // private
8696     initQuery : function(){
8697         this.doQuery(this.getRawValue());
8698     },
8699
8700     // private
8701     doForce : function(){
8702         if(this.el.dom.value.length > 0){
8703             this.el.dom.value =
8704                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8705              
8706         }
8707     },
8708
8709     /**
8710      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
8711      * query allowing the query action to be canceled if needed.
8712      * @param {String} query The SQL query to execute
8713      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8714      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
8715      * saved in the current store (defaults to false)
8716      */
8717     doQuery : function(q, forceAll){
8718         
8719         if(q === undefined || q === null){
8720             q = '';
8721         }
8722         var qe = {
8723             query: q,
8724             forceAll: forceAll,
8725             combo: this,
8726             cancel:false
8727         };
8728         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8729             return false;
8730         }
8731         q = qe.query;
8732         
8733         forceAll = qe.forceAll;
8734         if(forceAll === true || (q.length >= this.minChars)){
8735             
8736             this.hasQuery = true;
8737             
8738             if(this.lastQuery != q || this.alwaysQuery){
8739                 this.lastQuery = q;
8740                 if(this.mode == 'local'){
8741                     this.selectedIndex = -1;
8742                     if(forceAll){
8743                         this.store.clearFilter();
8744                     }else{
8745                         this.store.filter(this.displayField, q);
8746                     }
8747                     this.onLoad();
8748                 }else{
8749                     this.store.baseParams[this.queryParam] = q;
8750                     
8751                     var options = {params : this.getParams(q)};
8752                     
8753                     if(this.loadNext){
8754                         options.add = true;
8755                         options.params.start = this.page * this.pageSize;
8756                     }
8757                     
8758                     this.store.load(options);
8759                     this.expand();
8760                 }
8761             }else{
8762                 this.selectedIndex = -1;
8763                 this.onLoad();   
8764             }
8765         }
8766         
8767         this.loadNext = false;
8768     },
8769
8770     // private
8771     getParams : function(q){
8772         var p = {};
8773         //p[this.queryParam] = q;
8774         
8775         if(this.pageSize){
8776             p.start = 0;
8777             p.limit = this.pageSize;
8778         }
8779         return p;
8780     },
8781
8782     /**
8783      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8784      */
8785     collapse : function(){
8786         if(!this.isExpanded()){
8787             return;
8788         }
8789         
8790         this.list.hide();
8791         Roo.get(document).un('mousedown', this.collapseIf, this);
8792         Roo.get(document).un('mousewheel', this.collapseIf, this);
8793         if (!this.editable) {
8794             Roo.get(document).un('keydown', this.listKeyPress, this);
8795         }
8796         this.fireEvent('collapse', this);
8797     },
8798
8799     // private
8800     collapseIf : function(e){
8801         var in_combo  = e.within(this.el);
8802         var in_list =  e.within(this.list);
8803         
8804         if (in_combo || in_list) {
8805             //e.stopPropagation();
8806             return;
8807         }
8808
8809         this.collapse();
8810         
8811     },
8812
8813     /**
8814      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8815      */
8816     expand : function(){
8817        
8818         if(this.isExpanded() || !this.hasFocus){
8819             return;
8820         }
8821          Roo.log('expand');
8822         this.list.alignTo(this.inputEl(), this.listAlign);
8823         this.list.show();
8824         Roo.get(document).on('mousedown', this.collapseIf, this);
8825         Roo.get(document).on('mousewheel', this.collapseIf, this);
8826         if (!this.editable) {
8827             Roo.get(document).on('keydown', this.listKeyPress, this);
8828         }
8829         
8830         this.fireEvent('expand', this);
8831     },
8832
8833     // private
8834     // Implements the default empty TriggerField.onTriggerClick function
8835     onTriggerClick : function()
8836     {
8837         Roo.log('trigger click');
8838         
8839         if(this.disabled){
8840             return;
8841         }
8842         
8843         this.page = 0;
8844         this.loadNext = false;
8845         
8846         if(this.isExpanded()){
8847             this.collapse();
8848             if (!this.blockFocus) {
8849                 this.inputEl().focus();
8850             }
8851             
8852         }else {
8853             this.hasFocus = true;
8854             if(this.triggerAction == 'all') {
8855                 this.doQuery(this.allQuery, true);
8856             } else {
8857                 this.doQuery(this.getRawValue());
8858             }
8859             if (!this.blockFocus) {
8860                 this.inputEl().focus();
8861             }
8862         }
8863     },
8864     listKeyPress : function(e)
8865     {
8866         //Roo.log('listkeypress');
8867         // scroll to first matching element based on key pres..
8868         if (e.isSpecialKey()) {
8869             return false;
8870         }
8871         var k = String.fromCharCode(e.getKey()).toUpperCase();
8872         //Roo.log(k);
8873         var match  = false;
8874         var csel = this.view.getSelectedNodes();
8875         var cselitem = false;
8876         if (csel.length) {
8877             var ix = this.view.indexOf(csel[0]);
8878             cselitem  = this.store.getAt(ix);
8879             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8880                 cselitem = false;
8881             }
8882             
8883         }
8884         
8885         this.store.each(function(v) { 
8886             if (cselitem) {
8887                 // start at existing selection.
8888                 if (cselitem.id == v.id) {
8889                     cselitem = false;
8890                 }
8891                 return true;
8892             }
8893                 
8894             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8895                 match = this.store.indexOf(v);
8896                 return false;
8897             }
8898             return true;
8899         }, this);
8900         
8901         if (match === false) {
8902             return true; // no more action?
8903         }
8904         // scroll to?
8905         this.view.select(match);
8906         var sn = Roo.get(this.view.getSelectedNodes()[0])
8907         //sn.scrollIntoView(sn.dom.parentNode, false);
8908     },
8909     
8910     onViewScroll : function(e, t){
8911         
8912         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8913             return;
8914         }
8915         
8916         this.hasQuery = true;
8917         
8918         this.loading = this.list.select('.loading', true).first();
8919         
8920         if(this.loading === null){
8921             this.list.createChild({
8922                 tag: 'div',
8923                 cls: 'loading select2-more-results select2-active',
8924                 html: 'Loading more results...'
8925             })
8926             
8927             this.loading = this.list.select('.loading', true).first();
8928             
8929             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8930             
8931             this.loading.hide();
8932         }
8933         
8934         this.loading.show();
8935         
8936         var _combo = this;
8937         
8938         this.page++;
8939         this.loadNext = true;
8940         
8941         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8942         
8943         return;
8944     },
8945     
8946     addItem : function(o)
8947     {   
8948         var dv = ''; // display value
8949         
8950         if (this.displayField) {
8951             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8952         } else {
8953             // this is an error condition!!!
8954             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8955         }
8956         
8957         if(!dv.length){
8958             return;
8959         }
8960         
8961         var choice = this.choices.createChild({
8962             tag: 'li',
8963             cls: 'select2-search-choice',
8964             cn: [
8965                 {
8966                     tag: 'div',
8967                     html: dv
8968                 },
8969                 {
8970                     tag: 'a',
8971                     href: '#',
8972                     cls: 'select2-search-choice-close',
8973                     tabindex: '-1'
8974                 }
8975             ]
8976             
8977         }, this.searchField);
8978         
8979         var close = choice.select('a.select2-search-choice-close', true).first()
8980         
8981         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8982         
8983         this.item.push(o);
8984         this.lastData = o;
8985         
8986         this.syncValue();
8987         
8988         this.inputEl().dom.value = '';
8989         
8990     },
8991     
8992     onRemoveItem : function(e, _self, o)
8993     {
8994         Roo.log('remove item');
8995         var index = this.item.indexOf(o.data) * 1;
8996         
8997         if( index < 0){
8998             Roo.log('not this item?!');
8999             return;
9000         }
9001         
9002         this.item.splice(index, 1);
9003         o.item.remove();
9004         
9005         this.syncValue();
9006         
9007         this.fireEvent('remove', this);
9008         
9009     },
9010     
9011     syncValue : function()
9012     {
9013         if(!this.item.length){
9014             this.clearValue();
9015             return;
9016         }
9017             
9018         var value = [];
9019         var _this = this;
9020         Roo.each(this.item, function(i){
9021             if(_this.valueField){
9022                 value.push(i[_this.valueField]);
9023                 return;
9024             }
9025
9026             value.push(i);
9027         });
9028
9029         this.value = value.join(',');
9030
9031         if(this.hiddenField){
9032             this.hiddenField.dom.value = this.value;
9033         }
9034     },
9035     
9036     clearItem : function()
9037     {
9038         if(!this.multiple){
9039             return;
9040         }
9041         
9042         this.item = [];
9043         
9044         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9045            c.remove();
9046         });
9047         
9048         this.syncValue();
9049     }
9050     
9051     
9052
9053     /** 
9054     * @cfg {Boolean} grow 
9055     * @hide 
9056     */
9057     /** 
9058     * @cfg {Number} growMin 
9059     * @hide 
9060     */
9061     /** 
9062     * @cfg {Number} growMax 
9063     * @hide 
9064     */
9065     /**
9066      * @hide
9067      * @method autoSize
9068      */
9069 });
9070 /*
9071  * Based on:
9072  * Ext JS Library 1.1.1
9073  * Copyright(c) 2006-2007, Ext JS, LLC.
9074  *
9075  * Originally Released Under LGPL - original licence link has changed is not relivant.
9076  *
9077  * Fork - LGPL
9078  * <script type="text/javascript">
9079  */
9080
9081 /**
9082  * @class Roo.View
9083  * @extends Roo.util.Observable
9084  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9085  * This class also supports single and multi selection modes. <br>
9086  * Create a data model bound view:
9087  <pre><code>
9088  var store = new Roo.data.Store(...);
9089
9090  var view = new Roo.View({
9091     el : "my-element",
9092     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9093  
9094     singleSelect: true,
9095     selectedClass: "ydataview-selected",
9096     store: store
9097  });
9098
9099  // listen for node click?
9100  view.on("click", function(vw, index, node, e){
9101  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9102  });
9103
9104  // load XML data
9105  dataModel.load("foobar.xml");
9106  </code></pre>
9107  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9108  * <br><br>
9109  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9110  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9111  * 
9112  * Note: old style constructor is still suported (container, template, config)
9113  * 
9114  * @constructor
9115  * Create a new View
9116  * @param {Object} config The config object
9117  * 
9118  */
9119 Roo.View = function(config, depreciated_tpl, depreciated_config){
9120     
9121     if (typeof(depreciated_tpl) == 'undefined') {
9122         // new way.. - universal constructor.
9123         Roo.apply(this, config);
9124         this.el  = Roo.get(this.el);
9125     } else {
9126         // old format..
9127         this.el  = Roo.get(config);
9128         this.tpl = depreciated_tpl;
9129         Roo.apply(this, depreciated_config);
9130     }
9131     this.wrapEl  = this.el.wrap().wrap();
9132     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9133     
9134     
9135     if(typeof(this.tpl) == "string"){
9136         this.tpl = new Roo.Template(this.tpl);
9137     } else {
9138         // support xtype ctors..
9139         this.tpl = new Roo.factory(this.tpl, Roo);
9140     }
9141     
9142     
9143     this.tpl.compile();
9144    
9145   
9146     
9147      
9148     /** @private */
9149     this.addEvents({
9150         /**
9151          * @event beforeclick
9152          * Fires before a click is processed. Returns false to cancel the default action.
9153          * @param {Roo.View} this
9154          * @param {Number} index The index of the target node
9155          * @param {HTMLElement} node The target node
9156          * @param {Roo.EventObject} e The raw event object
9157          */
9158             "beforeclick" : true,
9159         /**
9160          * @event click
9161          * Fires when a template node is clicked.
9162          * @param {Roo.View} this
9163          * @param {Number} index The index of the target node
9164          * @param {HTMLElement} node The target node
9165          * @param {Roo.EventObject} e The raw event object
9166          */
9167             "click" : true,
9168         /**
9169          * @event dblclick
9170          * Fires when a template node is double clicked.
9171          * @param {Roo.View} this
9172          * @param {Number} index The index of the target node
9173          * @param {HTMLElement} node The target node
9174          * @param {Roo.EventObject} e The raw event object
9175          */
9176             "dblclick" : true,
9177         /**
9178          * @event contextmenu
9179          * Fires when a template node is right clicked.
9180          * @param {Roo.View} this
9181          * @param {Number} index The index of the target node
9182          * @param {HTMLElement} node The target node
9183          * @param {Roo.EventObject} e The raw event object
9184          */
9185             "contextmenu" : true,
9186         /**
9187          * @event selectionchange
9188          * Fires when the selected nodes change.
9189          * @param {Roo.View} this
9190          * @param {Array} selections Array of the selected nodes
9191          */
9192             "selectionchange" : true,
9193     
9194         /**
9195          * @event beforeselect
9196          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9197          * @param {Roo.View} this
9198          * @param {HTMLElement} node The node to be selected
9199          * @param {Array} selections Array of currently selected nodes
9200          */
9201             "beforeselect" : true,
9202         /**
9203          * @event preparedata
9204          * Fires on every row to render, to allow you to change the data.
9205          * @param {Roo.View} this
9206          * @param {Object} data to be rendered (change this)
9207          */
9208           "preparedata" : true
9209           
9210           
9211         });
9212
9213
9214
9215     this.el.on({
9216         "click": this.onClick,
9217         "dblclick": this.onDblClick,
9218         "contextmenu": this.onContextMenu,
9219         scope:this
9220     });
9221
9222     this.selections = [];
9223     this.nodes = [];
9224     this.cmp = new Roo.CompositeElementLite([]);
9225     if(this.store){
9226         this.store = Roo.factory(this.store, Roo.data);
9227         this.setStore(this.store, true);
9228     }
9229     
9230     if ( this.footer && this.footer.xtype) {
9231            
9232          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9233         
9234         this.footer.dataSource = this.store
9235         this.footer.container = fctr;
9236         this.footer = Roo.factory(this.footer, Roo);
9237         fctr.insertFirst(this.el);
9238         
9239         // this is a bit insane - as the paging toolbar seems to detach the el..
9240 //        dom.parentNode.parentNode.parentNode
9241          // they get detached?
9242     }
9243     
9244     
9245     Roo.View.superclass.constructor.call(this);
9246     
9247     
9248 };
9249
9250 Roo.extend(Roo.View, Roo.util.Observable, {
9251     
9252      /**
9253      * @cfg {Roo.data.Store} store Data store to load data from.
9254      */
9255     store : false,
9256     
9257     /**
9258      * @cfg {String|Roo.Element} el The container element.
9259      */
9260     el : '',
9261     
9262     /**
9263      * @cfg {String|Roo.Template} tpl The template used by this View 
9264      */
9265     tpl : false,
9266     /**
9267      * @cfg {String} dataName the named area of the template to use as the data area
9268      *                          Works with domtemplates roo-name="name"
9269      */
9270     dataName: false,
9271     /**
9272      * @cfg {String} selectedClass The css class to add to selected nodes
9273      */
9274     selectedClass : "x-view-selected",
9275      /**
9276      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9277      */
9278     emptyText : "",
9279     
9280     /**
9281      * @cfg {String} text to display on mask (default Loading)
9282      */
9283     mask : false,
9284     /**
9285      * @cfg {Boolean} multiSelect Allow multiple selection
9286      */
9287     multiSelect : false,
9288     /**
9289      * @cfg {Boolean} singleSelect Allow single selection
9290      */
9291     singleSelect:  false,
9292     
9293     /**
9294      * @cfg {Boolean} toggleSelect - selecting 
9295      */
9296     toggleSelect : false,
9297     
9298     /**
9299      * Returns the element this view is bound to.
9300      * @return {Roo.Element}
9301      */
9302     getEl : function(){
9303         return this.wrapEl;
9304     },
9305     
9306     
9307
9308     /**
9309      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9310      */
9311     refresh : function(){
9312         Roo.log('refresh');
9313         var t = this.tpl;
9314         
9315         // if we are using something like 'domtemplate', then
9316         // the what gets used is:
9317         // t.applySubtemplate(NAME, data, wrapping data..)
9318         // the outer template then get' applied with
9319         //     the store 'extra data'
9320         // and the body get's added to the
9321         //      roo-name="data" node?
9322         //      <span class='roo-tpl-{name}'></span> ?????
9323         
9324         
9325         
9326         this.clearSelections();
9327         this.el.update("");
9328         var html = [];
9329         var records = this.store.getRange();
9330         if(records.length < 1) {
9331             
9332             // is this valid??  = should it render a template??
9333             
9334             this.el.update(this.emptyText);
9335             return;
9336         }
9337         var el = this.el;
9338         if (this.dataName) {
9339             this.el.update(t.apply(this.store.meta)); //????
9340             el = this.el.child('.roo-tpl-' + this.dataName);
9341         }
9342         
9343         for(var i = 0, len = records.length; i < len; i++){
9344             var data = this.prepareData(records[i].data, i, records[i]);
9345             this.fireEvent("preparedata", this, data, i, records[i]);
9346             html[html.length] = Roo.util.Format.trim(
9347                 this.dataName ?
9348                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9349                     t.apply(data)
9350             );
9351         }
9352         
9353         
9354         
9355         el.update(html.join(""));
9356         this.nodes = el.dom.childNodes;
9357         this.updateIndexes(0);
9358     },
9359     
9360
9361     /**
9362      * Function to override to reformat the data that is sent to
9363      * the template for each node.
9364      * DEPRICATED - use the preparedata event handler.
9365      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9366      * a JSON object for an UpdateManager bound view).
9367      */
9368     prepareData : function(data, index, record)
9369     {
9370         this.fireEvent("preparedata", this, data, index, record);
9371         return data;
9372     },
9373
9374     onUpdate : function(ds, record){
9375          Roo.log('on update');   
9376         this.clearSelections();
9377         var index = this.store.indexOf(record);
9378         var n = this.nodes[index];
9379         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9380         n.parentNode.removeChild(n);
9381         this.updateIndexes(index, index);
9382     },
9383
9384     
9385     
9386 // --------- FIXME     
9387     onAdd : function(ds, records, index)
9388     {
9389         Roo.log(['on Add', ds, records, index] );        
9390         this.clearSelections();
9391         if(this.nodes.length == 0){
9392             this.refresh();
9393             return;
9394         }
9395         var n = this.nodes[index];
9396         for(var i = 0, len = records.length; i < len; i++){
9397             var d = this.prepareData(records[i].data, i, records[i]);
9398             if(n){
9399                 this.tpl.insertBefore(n, d);
9400             }else{
9401                 
9402                 this.tpl.append(this.el, d);
9403             }
9404         }
9405         this.updateIndexes(index);
9406     },
9407
9408     onRemove : function(ds, record, index){
9409         Roo.log('onRemove');
9410         this.clearSelections();
9411         var el = this.dataName  ?
9412             this.el.child('.roo-tpl-' + this.dataName) :
9413             this.el; 
9414         
9415         el.dom.removeChild(this.nodes[index]);
9416         this.updateIndexes(index);
9417     },
9418
9419     /**
9420      * Refresh an individual node.
9421      * @param {Number} index
9422      */
9423     refreshNode : function(index){
9424         this.onUpdate(this.store, this.store.getAt(index));
9425     },
9426
9427     updateIndexes : function(startIndex, endIndex){
9428         var ns = this.nodes;
9429         startIndex = startIndex || 0;
9430         endIndex = endIndex || ns.length - 1;
9431         for(var i = startIndex; i <= endIndex; i++){
9432             ns[i].nodeIndex = i;
9433         }
9434     },
9435
9436     /**
9437      * Changes the data store this view uses and refresh the view.
9438      * @param {Store} store
9439      */
9440     setStore : function(store, initial){
9441         if(!initial && this.store){
9442             this.store.un("datachanged", this.refresh);
9443             this.store.un("add", this.onAdd);
9444             this.store.un("remove", this.onRemove);
9445             this.store.un("update", this.onUpdate);
9446             this.store.un("clear", this.refresh);
9447             this.store.un("beforeload", this.onBeforeLoad);
9448             this.store.un("load", this.onLoad);
9449             this.store.un("loadexception", this.onLoad);
9450         }
9451         if(store){
9452           
9453             store.on("datachanged", this.refresh, this);
9454             store.on("add", this.onAdd, this);
9455             store.on("remove", this.onRemove, this);
9456             store.on("update", this.onUpdate, this);
9457             store.on("clear", this.refresh, this);
9458             store.on("beforeload", this.onBeforeLoad, this);
9459             store.on("load", this.onLoad, this);
9460             store.on("loadexception", this.onLoad, this);
9461         }
9462         
9463         if(store){
9464             this.refresh();
9465         }
9466     },
9467     /**
9468      * onbeforeLoad - masks the loading area.
9469      *
9470      */
9471     onBeforeLoad : function(store,opts)
9472     {
9473          Roo.log('onBeforeLoad');   
9474         if (!opts.add) {
9475             this.el.update("");
9476         }
9477         this.el.mask(this.mask ? this.mask : "Loading" ); 
9478     },
9479     onLoad : function ()
9480     {
9481         this.el.unmask();
9482     },
9483     
9484
9485     /**
9486      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9487      * @param {HTMLElement} node
9488      * @return {HTMLElement} The template node
9489      */
9490     findItemFromChild : function(node){
9491         var el = this.dataName  ?
9492             this.el.child('.roo-tpl-' + this.dataName,true) :
9493             this.el.dom; 
9494         
9495         if(!node || node.parentNode == el){
9496                     return node;
9497             }
9498             var p = node.parentNode;
9499             while(p && p != el){
9500             if(p.parentNode == el){
9501                 return p;
9502             }
9503             p = p.parentNode;
9504         }
9505             return null;
9506     },
9507
9508     /** @ignore */
9509     onClick : function(e){
9510         var item = this.findItemFromChild(e.getTarget());
9511         if(item){
9512             var index = this.indexOf(item);
9513             if(this.onItemClick(item, index, e) !== false){
9514                 this.fireEvent("click", this, index, item, e);
9515             }
9516         }else{
9517             this.clearSelections();
9518         }
9519     },
9520
9521     /** @ignore */
9522     onContextMenu : function(e){
9523         var item = this.findItemFromChild(e.getTarget());
9524         if(item){
9525             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9526         }
9527     },
9528
9529     /** @ignore */
9530     onDblClick : function(e){
9531         var item = this.findItemFromChild(e.getTarget());
9532         if(item){
9533             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9534         }
9535     },
9536
9537     onItemClick : function(item, index, e)
9538     {
9539         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9540             return false;
9541         }
9542         if (this.toggleSelect) {
9543             var m = this.isSelected(item) ? 'unselect' : 'select';
9544             Roo.log(m);
9545             var _t = this;
9546             _t[m](item, true, false);
9547             return true;
9548         }
9549         if(this.multiSelect || this.singleSelect){
9550             if(this.multiSelect && e.shiftKey && this.lastSelection){
9551                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9552             }else{
9553                 this.select(item, this.multiSelect && e.ctrlKey);
9554                 this.lastSelection = item;
9555             }
9556             e.preventDefault();
9557         }
9558         return true;
9559     },
9560
9561     /**
9562      * Get the number of selected nodes.
9563      * @return {Number}
9564      */
9565     getSelectionCount : function(){
9566         return this.selections.length;
9567     },
9568
9569     /**
9570      * Get the currently selected nodes.
9571      * @return {Array} An array of HTMLElements
9572      */
9573     getSelectedNodes : function(){
9574         return this.selections;
9575     },
9576
9577     /**
9578      * Get the indexes of the selected nodes.
9579      * @return {Array}
9580      */
9581     getSelectedIndexes : function(){
9582         var indexes = [], s = this.selections;
9583         for(var i = 0, len = s.length; i < len; i++){
9584             indexes.push(s[i].nodeIndex);
9585         }
9586         return indexes;
9587     },
9588
9589     /**
9590      * Clear all selections
9591      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9592      */
9593     clearSelections : function(suppressEvent){
9594         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9595             this.cmp.elements = this.selections;
9596             this.cmp.removeClass(this.selectedClass);
9597             this.selections = [];
9598             if(!suppressEvent){
9599                 this.fireEvent("selectionchange", this, this.selections);
9600             }
9601         }
9602     },
9603
9604     /**
9605      * Returns true if the passed node is selected
9606      * @param {HTMLElement/Number} node The node or node index
9607      * @return {Boolean}
9608      */
9609     isSelected : function(node){
9610         var s = this.selections;
9611         if(s.length < 1){
9612             return false;
9613         }
9614         node = this.getNode(node);
9615         return s.indexOf(node) !== -1;
9616     },
9617
9618     /**
9619      * Selects nodes.
9620      * @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
9621      * @param {Boolean} keepExisting (optional) true to keep existing selections
9622      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9623      */
9624     select : function(nodeInfo, keepExisting, suppressEvent){
9625         if(nodeInfo instanceof Array){
9626             if(!keepExisting){
9627                 this.clearSelections(true);
9628             }
9629             for(var i = 0, len = nodeInfo.length; i < len; i++){
9630                 this.select(nodeInfo[i], true, true);
9631             }
9632             return;
9633         } 
9634         var node = this.getNode(nodeInfo);
9635         if(!node || this.isSelected(node)){
9636             return; // already selected.
9637         }
9638         if(!keepExisting){
9639             this.clearSelections(true);
9640         }
9641         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9642             Roo.fly(node).addClass(this.selectedClass);
9643             this.selections.push(node);
9644             if(!suppressEvent){
9645                 this.fireEvent("selectionchange", this, this.selections);
9646             }
9647         }
9648         
9649         
9650     },
9651       /**
9652      * Unselects nodes.
9653      * @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
9654      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9655      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9656      */
9657     unselect : function(nodeInfo, keepExisting, suppressEvent)
9658     {
9659         if(nodeInfo instanceof Array){
9660             Roo.each(this.selections, function(s) {
9661                 this.unselect(s, nodeInfo);
9662             }, this);
9663             return;
9664         }
9665         var node = this.getNode(nodeInfo);
9666         if(!node || !this.isSelected(node)){
9667             Roo.log("not selected");
9668             return; // not selected.
9669         }
9670         // fireevent???
9671         var ns = [];
9672         Roo.each(this.selections, function(s) {
9673             if (s == node ) {
9674                 Roo.fly(node).removeClass(this.selectedClass);
9675
9676                 return;
9677             }
9678             ns.push(s);
9679         },this);
9680         
9681         this.selections= ns;
9682         this.fireEvent("selectionchange", this, this.selections);
9683     },
9684
9685     /**
9686      * Gets a template node.
9687      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9688      * @return {HTMLElement} The node or null if it wasn't found
9689      */
9690     getNode : function(nodeInfo){
9691         if(typeof nodeInfo == "string"){
9692             return document.getElementById(nodeInfo);
9693         }else if(typeof nodeInfo == "number"){
9694             return this.nodes[nodeInfo];
9695         }
9696         return nodeInfo;
9697     },
9698
9699     /**
9700      * Gets a range template nodes.
9701      * @param {Number} startIndex
9702      * @param {Number} endIndex
9703      * @return {Array} An array of nodes
9704      */
9705     getNodes : function(start, end){
9706         var ns = this.nodes;
9707         start = start || 0;
9708         end = typeof end == "undefined" ? ns.length - 1 : end;
9709         var nodes = [];
9710         if(start <= end){
9711             for(var i = start; i <= end; i++){
9712                 nodes.push(ns[i]);
9713             }
9714         } else{
9715             for(var i = start; i >= end; i--){
9716                 nodes.push(ns[i]);
9717             }
9718         }
9719         return nodes;
9720     },
9721
9722     /**
9723      * Finds the index of the passed node
9724      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9725      * @return {Number} The index of the node or -1
9726      */
9727     indexOf : function(node){
9728         node = this.getNode(node);
9729         if(typeof node.nodeIndex == "number"){
9730             return node.nodeIndex;
9731         }
9732         var ns = this.nodes;
9733         for(var i = 0, len = ns.length; i < len; i++){
9734             if(ns[i] == node){
9735                 return i;
9736             }
9737         }
9738         return -1;
9739     }
9740 });
9741 /*
9742  * - LGPL
9743  *
9744  * based on jquery fullcalendar
9745  * 
9746  */
9747
9748 Roo.bootstrap = Roo.bootstrap || {};
9749 /**
9750  * @class Roo.bootstrap.Calendar
9751  * @extends Roo.bootstrap.Component
9752  * Bootstrap Calendar class
9753  * @cfg {Boolean} loadMask (true|false) default false
9754     
9755  * @constructor
9756  * Create a new Container
9757  * @param {Object} config The config object
9758  */
9759
9760
9761
9762 Roo.bootstrap.Calendar = function(config){
9763     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9764      this.addEvents({
9765         /**
9766              * @event select
9767              * Fires when a date is selected
9768              * @param {DatePicker} this
9769              * @param {Date} date The selected date
9770              */
9771         'select': true,
9772         /**
9773              * @event monthchange
9774              * Fires when the displayed month changes 
9775              * @param {DatePicker} this
9776              * @param {Date} date The selected month
9777              */
9778         'monthchange': true,
9779         /**
9780              * @event evententer
9781              * Fires when mouse over an event
9782              * @param {Calendar} this
9783              * @param {event} Event
9784              */
9785         'evententer': true,
9786         /**
9787              * @event eventleave
9788              * Fires when the mouse leaves an
9789              * @param {Calendar} this
9790              * @param {event}
9791              */
9792         'eventleave': true,
9793         /**
9794              * @event eventclick
9795              * Fires when the mouse click an
9796              * @param {Calendar} this
9797              * @param {event}
9798              */
9799         'eventclick': true
9800         
9801     });
9802
9803 };
9804
9805 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
9806     
9807      /**
9808      * @cfg {Number} startDay
9809      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9810      */
9811     startDay : 0,
9812     
9813     loadMask : false,
9814       
9815     getAutoCreate : function(){
9816         
9817         
9818         var fc_button = function(name, corner, style, content ) {
9819             return Roo.apply({},{
9820                 tag : 'span',
9821                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
9822                          (corner.length ?
9823                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9824                             ''
9825                         ),
9826                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9827                 unselectable: 'on'
9828             });
9829         };
9830         
9831         var header = {
9832             tag : 'table',
9833             cls : 'fc-header',
9834             style : 'width:100%',
9835             cn : [
9836                 {
9837                     tag: 'tr',
9838                     cn : [
9839                         {
9840                             tag : 'td',
9841                             cls : 'fc-header-left',
9842                             cn : [
9843                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
9844                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
9845                                 { tag: 'span', cls: 'fc-header-space' },
9846                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
9847                                 
9848                                 
9849                             ]
9850                         },
9851                         
9852                         {
9853                             tag : 'td',
9854                             cls : 'fc-header-center',
9855                             cn : [
9856                                 {
9857                                     tag: 'span',
9858                                     cls: 'fc-header-title',
9859                                     cn : {
9860                                         tag: 'H2',
9861                                         html : 'month / year'
9862                                     }
9863                                 }
9864                                 
9865                             ]
9866                         },
9867                         {
9868                             tag : 'td',
9869                             cls : 'fc-header-right',
9870                             cn : [
9871                           /*      fc_button('month', 'left', '', 'month' ),
9872                                 fc_button('week', '', '', 'week' ),
9873                                 fc_button('day', 'right', '', 'day' )
9874                             */    
9875                                 
9876                             ]
9877                         }
9878                         
9879                     ]
9880                 }
9881             ]
9882         };
9883         
9884        
9885         var cal_heads = function() {
9886             var ret = [];
9887             // fixme - handle this.
9888             
9889             for (var i =0; i < Date.dayNames.length; i++) {
9890                 var d = Date.dayNames[i];
9891                 ret.push({
9892                     tag: 'th',
9893                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9894                     html : d.substring(0,3)
9895                 });
9896                 
9897             }
9898             ret[0].cls += ' fc-first';
9899             ret[6].cls += ' fc-last';
9900             return ret;
9901         };
9902         var cal_cell = function(n) {
9903             return  {
9904                 tag: 'td',
9905                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9906                 cn : [
9907                     {
9908                         cn : [
9909                             {
9910                                 cls: 'fc-day-number',
9911                                 html: 'D'
9912                             },
9913                             {
9914                                 cls: 'fc-day-content',
9915                              
9916                                 cn : [
9917                                      {
9918                                         style: 'position: relative;' // height: 17px;
9919                                     }
9920                                 ]
9921                             }
9922                             
9923                             
9924                         ]
9925                     }
9926                 ]
9927                 
9928             }
9929         };
9930         var cal_rows = function() {
9931             
9932             var ret = []
9933             for (var r = 0; r < 6; r++) {
9934                 var row= {
9935                     tag : 'tr',
9936                     cls : 'fc-week',
9937                     cn : []
9938                 };
9939                 
9940                 for (var i =0; i < Date.dayNames.length; i++) {
9941                     var d = Date.dayNames[i];
9942                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9943
9944                 }
9945                 row.cn[0].cls+=' fc-first';
9946                 row.cn[0].cn[0].style = 'min-height:90px';
9947                 row.cn[6].cls+=' fc-last';
9948                 ret.push(row);
9949                 
9950             }
9951             ret[0].cls += ' fc-first';
9952             ret[4].cls += ' fc-prev-last';
9953             ret[5].cls += ' fc-last';
9954             return ret;
9955             
9956         };
9957         
9958         var cal_table = {
9959             tag: 'table',
9960             cls: 'fc-border-separate',
9961             style : 'width:100%',
9962             cellspacing  : 0,
9963             cn : [
9964                 { 
9965                     tag: 'thead',
9966                     cn : [
9967                         { 
9968                             tag: 'tr',
9969                             cls : 'fc-first fc-last',
9970                             cn : cal_heads()
9971                         }
9972                     ]
9973                 },
9974                 { 
9975                     tag: 'tbody',
9976                     cn : cal_rows()
9977                 }
9978                   
9979             ]
9980         };
9981          
9982          var cfg = {
9983             cls : 'fc fc-ltr',
9984             cn : [
9985                 header,
9986                 {
9987                     cls : 'fc-content',
9988                     style : "position: relative;",
9989                     cn : [
9990                         {
9991                             cls : 'fc-view fc-view-month fc-grid',
9992                             style : 'position: relative',
9993                             unselectable : 'on',
9994                             cn : [
9995                                 {
9996                                     cls : 'fc-event-container',
9997                                     style : 'position:absolute;z-index:8;top:0;left:0;'
9998                                 },
9999                                 cal_table
10000                             ]
10001                         }
10002                     ]
10003     
10004                 }
10005            ] 
10006             
10007         };
10008         
10009          
10010         
10011         return cfg;
10012     },
10013     
10014     
10015     initEvents : function()
10016     {
10017         if(!this.store){
10018             throw "can not find store for calendar";
10019         }
10020         
10021         var mark = {
10022             tag: "div",
10023             cls:"x-dlg-mask",
10024             style: "text-align:center",
10025             cn: [
10026                 {
10027                     tag: "div",
10028                     style: "background-color:white;width:50%;margin:250 auto",
10029                     cn: [
10030                         {
10031                             tag: "img",
10032                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10033                         },
10034                         {
10035                             tag: "span",
10036                             html: "Loading"
10037                         }
10038                         
10039                     ]
10040                 }
10041             ]
10042         }
10043         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10044         
10045         var size = this.el.select('.fc-content', true).first().getSize();
10046         this.maskEl.setSize(size.width, size.height);
10047         this.maskEl.enableDisplayMode("block");
10048         if(!this.loadMask){
10049             this.maskEl.hide();
10050         }
10051         
10052         this.store = Roo.factory(this.store, Roo.data);
10053         this.store.on('load', this.onLoad, this);
10054         this.store.on('beforeload', this.onBeforeLoad, this);
10055         
10056         this.resize();
10057         
10058         this.cells = this.el.select('.fc-day',true);
10059         //Roo.log(this.cells);
10060         this.textNodes = this.el.query('.fc-day-number');
10061         this.cells.addClassOnOver('fc-state-hover');
10062         
10063         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10064         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10065         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10066         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10067         
10068         this.on('monthchange', this.onMonthChange, this);
10069         
10070         this.update(new Date().clearTime());
10071     },
10072     
10073     resize : function() {
10074         var sz  = this.el.getSize();
10075         
10076         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10077         this.el.select('.fc-day-content div',true).setHeight(34);
10078     },
10079     
10080     
10081     // private
10082     showPrevMonth : function(e){
10083         this.update(this.activeDate.add("mo", -1));
10084     },
10085     showToday : function(e){
10086         this.update(new Date().clearTime());
10087     },
10088     // private
10089     showNextMonth : function(e){
10090         this.update(this.activeDate.add("mo", 1));
10091     },
10092
10093     // private
10094     showPrevYear : function(){
10095         this.update(this.activeDate.add("y", -1));
10096     },
10097
10098     // private
10099     showNextYear : function(){
10100         this.update(this.activeDate.add("y", 1));
10101     },
10102
10103     
10104    // private
10105     update : function(date)
10106     {
10107         var vd = this.activeDate;
10108         this.activeDate = date;
10109 //        if(vd && this.el){
10110 //            var t = date.getTime();
10111 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10112 //                Roo.log('using add remove');
10113 //                
10114 //                this.fireEvent('monthchange', this, date);
10115 //                
10116 //                this.cells.removeClass("fc-state-highlight");
10117 //                this.cells.each(function(c){
10118 //                   if(c.dateValue == t){
10119 //                       c.addClass("fc-state-highlight");
10120 //                       setTimeout(function(){
10121 //                            try{c.dom.firstChild.focus();}catch(e){}
10122 //                       }, 50);
10123 //                       return false;
10124 //                   }
10125 //                   return true;
10126 //                });
10127 //                return;
10128 //            }
10129 //        }
10130         
10131         var days = date.getDaysInMonth();
10132         
10133         var firstOfMonth = date.getFirstDateOfMonth();
10134         var startingPos = firstOfMonth.getDay()-this.startDay;
10135         
10136         if(startingPos < this.startDay){
10137             startingPos += 7;
10138         }
10139         
10140         var pm = date.add(Date.MONTH, -1);
10141         var prevStart = pm.getDaysInMonth()-startingPos;
10142 //        
10143         this.cells = this.el.select('.fc-day',true);
10144         this.textNodes = this.el.query('.fc-day-number');
10145         this.cells.addClassOnOver('fc-state-hover');
10146         
10147         var cells = this.cells.elements;
10148         var textEls = this.textNodes;
10149         
10150         Roo.each(cells, function(cell){
10151             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10152         });
10153         
10154         days += startingPos;
10155
10156         // convert everything to numbers so it's fast
10157         var day = 86400000;
10158         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10159         //Roo.log(d);
10160         //Roo.log(pm);
10161         //Roo.log(prevStart);
10162         
10163         var today = new Date().clearTime().getTime();
10164         var sel = date.clearTime().getTime();
10165         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10166         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10167         var ddMatch = this.disabledDatesRE;
10168         var ddText = this.disabledDatesText;
10169         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10170         var ddaysText = this.disabledDaysText;
10171         var format = this.format;
10172         
10173         var setCellClass = function(cal, cell){
10174             
10175             //Roo.log('set Cell Class');
10176             cell.title = "";
10177             var t = d.getTime();
10178             
10179             //Roo.log(d);
10180             
10181             cell.dateValue = t;
10182             if(t == today){
10183                 cell.className += " fc-today";
10184                 cell.className += " fc-state-highlight";
10185                 cell.title = cal.todayText;
10186             }
10187             if(t == sel){
10188                 // disable highlight in other month..
10189                 //cell.className += " fc-state-highlight";
10190                 
10191             }
10192             // disabling
10193             if(t < min) {
10194                 cell.className = " fc-state-disabled";
10195                 cell.title = cal.minText;
10196                 return;
10197             }
10198             if(t > max) {
10199                 cell.className = " fc-state-disabled";
10200                 cell.title = cal.maxText;
10201                 return;
10202             }
10203             if(ddays){
10204                 if(ddays.indexOf(d.getDay()) != -1){
10205                     cell.title = ddaysText;
10206                     cell.className = " fc-state-disabled";
10207                 }
10208             }
10209             if(ddMatch && format){
10210                 var fvalue = d.dateFormat(format);
10211                 if(ddMatch.test(fvalue)){
10212                     cell.title = ddText.replace("%0", fvalue);
10213                     cell.className = " fc-state-disabled";
10214                 }
10215             }
10216             
10217             if (!cell.initialClassName) {
10218                 cell.initialClassName = cell.dom.className;
10219             }
10220             
10221             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10222         };
10223
10224         var i = 0;
10225         
10226         for(; i < startingPos; i++) {
10227             textEls[i].innerHTML = (++prevStart);
10228             d.setDate(d.getDate()+1);
10229             
10230             cells[i].className = "fc-past fc-other-month";
10231             setCellClass(this, cells[i]);
10232         }
10233         
10234         var intDay = 0;
10235         
10236         for(; i < days; i++){
10237             intDay = i - startingPos + 1;
10238             textEls[i].innerHTML = (intDay);
10239             d.setDate(d.getDate()+1);
10240             
10241             cells[i].className = ''; // "x-date-active";
10242             setCellClass(this, cells[i]);
10243         }
10244         var extraDays = 0;
10245         
10246         for(; i < 42; i++) {
10247             textEls[i].innerHTML = (++extraDays);
10248             d.setDate(d.getDate()+1);
10249             
10250             cells[i].className = "fc-future fc-other-month";
10251             setCellClass(this, cells[i]);
10252         }
10253         
10254         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10255         
10256         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10257         
10258         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10259         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10260         
10261         if(totalRows != 6){
10262             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10263             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10264         }
10265         
10266         this.fireEvent('monthchange', this, date);
10267         
10268         
10269         /*
10270         if(!this.internalRender){
10271             var main = this.el.dom.firstChild;
10272             var w = main.offsetWidth;
10273             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10274             Roo.fly(main).setWidth(w);
10275             this.internalRender = true;
10276             // opera does not respect the auto grow header center column
10277             // then, after it gets a width opera refuses to recalculate
10278             // without a second pass
10279             if(Roo.isOpera && !this.secondPass){
10280                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10281                 this.secondPass = true;
10282                 this.update.defer(10, this, [date]);
10283             }
10284         }
10285         */
10286         
10287     },
10288     
10289     findCell : function(dt) {
10290         dt = dt.clearTime().getTime();
10291         var ret = false;
10292         this.cells.each(function(c){
10293             //Roo.log("check " +c.dateValue + '?=' + dt);
10294             if(c.dateValue == dt){
10295                 ret = c;
10296                 return false;
10297             }
10298             return true;
10299         });
10300         
10301         return ret;
10302     },
10303     
10304     findCells : function(ev) {
10305         var s = ev.start.clone().clearTime().getTime();
10306        // Roo.log(s);
10307         var e= ev.end.clone().clearTime().getTime();
10308        // Roo.log(e);
10309         var ret = [];
10310         this.cells.each(function(c){
10311              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10312             
10313             if(c.dateValue > e){
10314                 return ;
10315             }
10316             if(c.dateValue < s){
10317                 return ;
10318             }
10319             ret.push(c);
10320         });
10321         
10322         return ret;    
10323     },
10324     
10325     findBestRow: function(cells)
10326     {
10327         var ret = 0;
10328         
10329         for (var i =0 ; i < cells.length;i++) {
10330             ret  = Math.max(cells[i].rows || 0,ret);
10331         }
10332         return ret;
10333         
10334     },
10335     
10336     
10337     addItem : function(ev)
10338     {
10339         // look for vertical location slot in
10340         var cells = this.findCells(ev);
10341         
10342         ev.row = this.findBestRow(cells);
10343         
10344         // work out the location.
10345         
10346         var crow = false;
10347         var rows = [];
10348         for(var i =0; i < cells.length; i++) {
10349             if (!crow) {
10350                 crow = {
10351                     start : cells[i],
10352                     end :  cells[i]
10353                 };
10354                 continue;
10355             }
10356             if (crow.start.getY() == cells[i].getY()) {
10357                 // on same row.
10358                 crow.end = cells[i];
10359                 continue;
10360             }
10361             // different row.
10362             rows.push(crow);
10363             crow = {
10364                 start: cells[i],
10365                 end : cells[i]
10366             };
10367             
10368         }
10369         
10370         rows.push(crow);
10371         ev.els = [];
10372         ev.rows = rows;
10373         ev.cells = cells;
10374         for (var i = 0; i < cells.length;i++) {
10375             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10376             
10377         }
10378         
10379         this.calevents.push(ev);
10380     },
10381     
10382     clearEvents: function() {
10383         
10384         if(!this.calevents){
10385             return;
10386         }
10387         
10388         Roo.each(this.cells.elements, function(c){
10389             c.rows = 0;
10390         });
10391         
10392         Roo.each(this.calevents, function(e) {
10393             Roo.each(e.els, function(el) {
10394                 el.un('mouseenter' ,this.onEventEnter, this);
10395                 el.un('mouseleave' ,this.onEventLeave, this);
10396                 el.remove();
10397             },this);
10398         },this);
10399         
10400     },
10401     
10402     renderEvents: function()
10403     {   
10404         // first make sure there is enough space..
10405         
10406         this.cells.each(function(c) {
10407         
10408             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10409         });
10410         
10411         for (var e = 0; e < this.calevents.length; e++) {
10412             var ev = this.calevents[e];
10413             var cells = ev.cells;
10414             var rows = ev.rows;
10415             
10416             for(var i =0; i < rows.length; i++) {
10417                 
10418                  
10419                 // how many rows should it span..
10420                 
10421                 var  cfg = {
10422                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10423                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10424                     
10425                     unselectable : "on",
10426                     cn : [
10427                         {
10428                             cls: 'fc-event-inner',
10429                             cn : [
10430 //                                {
10431 //                                  tag:'span',
10432 //                                  cls: 'fc-event-time',
10433 //                                  html : cells.length > 1 ? '' : ev.time
10434 //                                },
10435                                 {
10436                                   tag:'span',
10437                                   cls: 'fc-event-title',
10438                                   html : String.format('{0}', ev.title)
10439                                 }
10440                                 
10441                                 
10442                             ]
10443                         },
10444                         {
10445                             cls: 'ui-resizable-handle ui-resizable-e',
10446                             html : '&nbsp;&nbsp;&nbsp'
10447                         }
10448                         
10449                     ]
10450                 };
10451                 if (i == 0) {
10452                     cfg.cls += ' fc-event-start';
10453                 }
10454                 if ((i+1) == rows.length) {
10455                     cfg.cls += ' fc-event-end';
10456                 }
10457                 
10458                 var ctr = this.el.select('.fc-event-container',true).first();
10459                 var cg = ctr.createChild(cfg);
10460                 
10461                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10462                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10463                 cg.on('click', this.onEventClick, this, ev);
10464                 
10465                 ev.els.push(cg);
10466                 
10467                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10468                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10469                 //Roo.log(cg);
10470                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
10471                 cg.setWidth(ebox.right - sbox.x -2);
10472             }
10473             
10474             
10475         }
10476         
10477     },
10478     
10479     onEventEnter: function (e, el,event,d) {
10480         this.fireEvent('evententer', this, el, event);
10481     },
10482     
10483     onEventLeave: function (e, el,event,d) {
10484         this.fireEvent('eventleave', this, el, event);
10485     },
10486     
10487     onEventClick: function (e, el,event,d) {
10488         this.fireEvent('eventclick', this, el, event);
10489     },
10490     
10491     onMonthChange: function () {
10492         this.store.load();
10493     },
10494     
10495     onLoad: function () 
10496     {   
10497         this.calevents = [];
10498         var cal = this;
10499         
10500         if(this.store.getCount() > 0){
10501             this.store.data.each(function(d){
10502                cal.addItem({
10503                     id : d.data.id,
10504                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10505                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10506                     time : d.data.start_time,
10507                     title : d.data.title,
10508                     description : d.data.description,
10509                     venue : d.data.venue
10510                 });
10511             });
10512         }
10513         
10514         this.renderEvents();
10515         
10516         if(this.loadMask){
10517             this.maskEl.hide();
10518         }
10519     },
10520     
10521     onBeforeLoad: function()
10522     {
10523         this.clearEvents();
10524         
10525         if(this.loadMask){
10526             this.maskEl.show();
10527         }
10528     }
10529 });
10530
10531  
10532  /*
10533  * - LGPL
10534  *
10535  * element
10536  * 
10537  */
10538
10539 /**
10540  * @class Roo.bootstrap.Popover
10541  * @extends Roo.bootstrap.Component
10542  * Bootstrap Popover class
10543  * @cfg {String} html contents of the popover   (or false to use children..)
10544  * @cfg {String} title of popover (or false to hide)
10545  * @cfg {String} placement how it is placed
10546  * @cfg {String} trigger click || hover (or false to trigger manually)
10547  * @cfg {String} over what (parent or false to trigger manually.)
10548  * 
10549  * @constructor
10550  * Create a new Popover
10551  * @param {Object} config The config object
10552  */
10553
10554 Roo.bootstrap.Popover = function(config){
10555     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10556 };
10557
10558 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
10559     
10560     title: 'Fill in a title',
10561     html: false,
10562     
10563     placement : 'right',
10564     trigger : 'hover', // hover
10565     
10566     over: 'parent',
10567     
10568     can_build_overlaid : false,
10569     
10570     getChildContainer : function()
10571     {
10572         return this.el.select('.popover-content',true).first();
10573     },
10574     
10575     getAutoCreate : function(){
10576          Roo.log('make popover?');
10577         var cfg = {
10578            cls : 'popover roo-dynamic',
10579            style: 'display:block',
10580            cn : [
10581                 {
10582                     cls : 'arrow'
10583                 },
10584                 {
10585                     cls : 'popover-inner',
10586                     cn : [
10587                         {
10588                             tag: 'h3',
10589                             cls: 'popover-title',
10590                             html : this.title
10591                         },
10592                         {
10593                             cls : 'popover-content',
10594                             html : this.html
10595                         }
10596                     ]
10597                     
10598                 }
10599            ]
10600         };
10601         
10602         return cfg;
10603     },
10604     setTitle: function(str)
10605     {
10606         this.el.select('.popover-title',true).first().dom.innerHTML = str;
10607     },
10608     setContent: function(str)
10609     {
10610         this.el.select('.popover-content',true).first().dom.innerHTML = str;
10611     },
10612     // as it get's added to the bottom of the page.
10613     onRender : function(ct, position)
10614     {
10615         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10616         if(!this.el){
10617             var cfg = Roo.apply({},  this.getAutoCreate());
10618             cfg.id = Roo.id();
10619             
10620             if (this.cls) {
10621                 cfg.cls += ' ' + this.cls;
10622             }
10623             if (this.style) {
10624                 cfg.style = this.style;
10625             }
10626             Roo.log("adding to ")
10627             this.el = Roo.get(document.body).createChild(cfg, position);
10628             Roo.log(this.el);
10629         }
10630         this.initEvents();
10631     },
10632     
10633     initEvents : function()
10634     {
10635         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10636         this.el.enableDisplayMode('block');
10637         this.el.hide();
10638         if (this.over === false) {
10639             return; 
10640         }
10641         if (this.triggers === false) {
10642             return;
10643         }
10644         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10645         var triggers = this.trigger ? this.trigger.split(' ') : [];
10646         Roo.each(triggers, function(trigger) {
10647         
10648             if (trigger == 'click') {
10649                 on_el.on('click', this.toggle, this);
10650             } else if (trigger != 'manual') {
10651                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
10652                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10653       
10654                 on_el.on(eventIn  ,this.enter, this);
10655                 on_el.on(eventOut, this.leave, this);
10656             }
10657         }, this);
10658         
10659     },
10660     
10661     
10662     // private
10663     timeout : null,
10664     hoverState : null,
10665     
10666     toggle : function () {
10667         this.hoverState == 'in' ? this.leave() : this.enter();
10668     },
10669     
10670     enter : function () {
10671        
10672     
10673         clearTimeout(this.timeout);
10674     
10675         this.hoverState = 'in'
10676     
10677         if (!this.delay || !this.delay.show) {
10678             this.show();
10679             return 
10680         }
10681         var _t = this;
10682         this.timeout = setTimeout(function () {
10683             if (_t.hoverState == 'in') {
10684                 _t.show();
10685             }
10686         }, this.delay.show)
10687     },
10688     leave : function() {
10689         clearTimeout(this.timeout);
10690     
10691         this.hoverState = 'out'
10692     
10693         if (!this.delay || !this.delay.hide) {
10694             this.hide();
10695             return 
10696         }
10697         var _t = this;
10698         this.timeout = setTimeout(function () {
10699             if (_t.hoverState == 'out') {
10700                 _t.hide();
10701             }
10702         }, this.delay.hide)
10703     },
10704     
10705     show : function (on_el)
10706     {
10707         if (!on_el) {
10708             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10709         }
10710         // set content.
10711         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10712         if (this.html !== false) {
10713             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10714         }
10715         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10716         if (!this.title.length) {
10717             this.el.select('.popover-title',true).hide();
10718         }
10719         
10720         var placement = typeof this.placement == 'function' ?
10721             this.placement.call(this, this.el, on_el) :
10722             this.placement;
10723             
10724         var autoToken = /\s?auto?\s?/i;
10725         var autoPlace = autoToken.test(placement);
10726         if (autoPlace) {
10727             placement = placement.replace(autoToken, '') || 'top';
10728         }
10729         
10730         //this.el.detach()
10731         //this.el.setXY([0,0]);
10732         this.el.show();
10733         this.el.dom.style.display='block';
10734         this.el.addClass(placement);
10735         
10736         //this.el.appendTo(on_el);
10737         
10738         var p = this.getPosition();
10739         var box = this.el.getBox();
10740         
10741         if (autoPlace) {
10742             // fixme..
10743         }
10744         var align = Roo.bootstrap.Popover.alignment[placement]
10745         this.el.alignTo(on_el, align[0],align[1]);
10746         //var arrow = this.el.select('.arrow',true).first();
10747         //arrow.set(align[2], 
10748         
10749         this.el.addClass('in');
10750         this.hoverState = null;
10751         
10752         if (this.el.hasClass('fade')) {
10753             // fade it?
10754         }
10755         
10756     },
10757     hide : function()
10758     {
10759         this.el.setXY([0,0]);
10760         this.el.removeClass('in');
10761         this.el.hide();
10762         
10763     }
10764     
10765 });
10766
10767 Roo.bootstrap.Popover.alignment = {
10768     'left' : ['r-l', [-10,0], 'right'],
10769     'right' : ['l-r', [10,0], 'left'],
10770     'bottom' : ['t-b', [0,10], 'top'],
10771     'top' : [ 'b-t', [0,-10], 'bottom']
10772 };
10773
10774  /*
10775  * - LGPL
10776  *
10777  * Progress
10778  * 
10779  */
10780
10781 /**
10782  * @class Roo.bootstrap.Progress
10783  * @extends Roo.bootstrap.Component
10784  * Bootstrap Progress class
10785  * @cfg {Boolean} striped striped of the progress bar
10786  * @cfg {Boolean} active animated of the progress bar
10787  * 
10788  * 
10789  * @constructor
10790  * Create a new Progress
10791  * @param {Object} config The config object
10792  */
10793
10794 Roo.bootstrap.Progress = function(config){
10795     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10796 };
10797
10798 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
10799     
10800     striped : false,
10801     active: false,
10802     
10803     getAutoCreate : function(){
10804         var cfg = {
10805             tag: 'div',
10806             cls: 'progress'
10807         };
10808         
10809         
10810         if(this.striped){
10811             cfg.cls += ' progress-striped';
10812         }
10813       
10814         if(this.active){
10815             cfg.cls += ' active';
10816         }
10817         
10818         
10819         return cfg;
10820     }
10821    
10822 });
10823
10824  
10825
10826  /*
10827  * - LGPL
10828  *
10829  * ProgressBar
10830  * 
10831  */
10832
10833 /**
10834  * @class Roo.bootstrap.ProgressBar
10835  * @extends Roo.bootstrap.Component
10836  * Bootstrap ProgressBar class
10837  * @cfg {Number} aria_valuenow aria-value now
10838  * @cfg {Number} aria_valuemin aria-value min
10839  * @cfg {Number} aria_valuemax aria-value max
10840  * @cfg {String} label label for the progress bar
10841  * @cfg {String} panel (success | info | warning | danger )
10842  * @cfg {String} role role of the progress bar
10843  * @cfg {String} sr_only text
10844  * 
10845  * 
10846  * @constructor
10847  * Create a new ProgressBar
10848  * @param {Object} config The config object
10849  */
10850
10851 Roo.bootstrap.ProgressBar = function(config){
10852     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10853 };
10854
10855 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
10856     
10857     aria_valuenow : 0,
10858     aria_valuemin : 0,
10859     aria_valuemax : 100,
10860     label : false,
10861     panel : false,
10862     role : false,
10863     sr_only: false,
10864     
10865     getAutoCreate : function()
10866     {
10867         
10868         var cfg = {
10869             tag: 'div',
10870             cls: 'progress-bar',
10871             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10872         };
10873         
10874         if(this.sr_only){
10875             cfg.cn = {
10876                 tag: 'span',
10877                 cls: 'sr-only',
10878                 html: this.sr_only
10879             }
10880         }
10881         
10882         if(this.role){
10883             cfg.role = this.role;
10884         }
10885         
10886         if(this.aria_valuenow){
10887             cfg['aria-valuenow'] = this.aria_valuenow;
10888         }
10889         
10890         if(this.aria_valuemin){
10891             cfg['aria-valuemin'] = this.aria_valuemin;
10892         }
10893         
10894         if(this.aria_valuemax){
10895             cfg['aria-valuemax'] = this.aria_valuemax;
10896         }
10897         
10898         if(this.label && !this.sr_only){
10899             cfg.html = this.label;
10900         }
10901         
10902         if(this.panel){
10903             cfg.cls += ' progress-bar-' + this.panel;
10904         }
10905         
10906         return cfg;
10907     },
10908     
10909     update : function(aria_valuenow)
10910     {
10911         this.aria_valuenow = aria_valuenow;
10912         
10913         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10914     }
10915    
10916 });
10917
10918  
10919
10920  /*
10921  * - LGPL
10922  *
10923  * TabPanel
10924  * 
10925  */
10926
10927 /**
10928  * @class Roo.bootstrap.TabPanel
10929  * @extends Roo.bootstrap.Component
10930  * Bootstrap TabPanel class
10931  * @cfg {Boolean} active panel active
10932  * @cfg {String} html panel content
10933  * @cfg {String} tabId tab relate id
10934  * 
10935  * 
10936  * @constructor
10937  * Create a new TabPanel
10938  * @param {Object} config The config object
10939  */
10940
10941 Roo.bootstrap.TabPanel = function(config){
10942     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10943 };
10944
10945 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
10946     
10947     active: false,
10948     html: false,
10949     tabId: false,
10950     
10951     getAutoCreate : function(){
10952         var cfg = {
10953             tag: 'div',
10954             cls: 'tab-pane',
10955             html: this.html || ''
10956         };
10957         
10958         if(this.active){
10959             cfg.cls += ' active';
10960         }
10961         
10962         if(this.tabId){
10963             cfg.tabId = this.tabId;
10964         }
10965         
10966         return cfg;
10967     }
10968    
10969 });
10970
10971  
10972
10973  /*
10974  * - LGPL
10975  *
10976  * DateField
10977  * 
10978  */
10979
10980 /**
10981  * @class Roo.bootstrap.DateField
10982  * @extends Roo.bootstrap.Input
10983  * Bootstrap DateField class
10984  * @cfg {Number} weekStart default 0
10985  * @cfg {Number} weekStart default 0
10986  * @cfg {Number} viewMode default empty, (months|years)
10987  * @cfg {Number} minViewMode default empty, (months|years)
10988  * @cfg {Number} startDate default -Infinity
10989  * @cfg {Number} endDate default Infinity
10990  * @cfg {Boolean} todayHighlight default false
10991  * @cfg {Boolean} todayBtn default false
10992  * @cfg {Boolean} calendarWeeks default false
10993  * @cfg {Object} daysOfWeekDisabled default empty
10994  * 
10995  * @cfg {Boolean} keyboardNavigation default true
10996  * @cfg {String} language default en
10997  * 
10998  * @constructor
10999  * Create a new DateField
11000  * @param {Object} config The config object
11001  */
11002
11003 Roo.bootstrap.DateField = function(config){
11004     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
11005      this.addEvents({
11006             /**
11007              * @event show
11008              * Fires when this field show.
11009              * @param {Roo.bootstrap.DateField} this
11010              * @param {Mixed} date The date value
11011              */
11012             show : true,
11013             /**
11014              * @event show
11015              * Fires when this field hide.
11016              * @param {Roo.bootstrap.DateField} this
11017              * @param {Mixed} date The date value
11018              */
11019             hide : true,
11020             /**
11021              * @event select
11022              * Fires when select a date.
11023              * @param {Roo.bootstrap.DateField} this
11024              * @param {Mixed} date The date value
11025              */
11026             select : true
11027         });
11028 };
11029
11030 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
11031     
11032     /**
11033      * @cfg {String} format
11034      * The default date format string which can be overriden for localization support.  The format must be
11035      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11036      */
11037     format : "m/d/y",
11038     /**
11039      * @cfg {String} altFormats
11040      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11041      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11042      */
11043     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11044     
11045     weekStart : 0,
11046     
11047     viewMode : '',
11048     
11049     minViewMode : '',
11050     
11051     todayHighlight : false,
11052     
11053     todayBtn: false,
11054     
11055     language: 'en',
11056     
11057     keyboardNavigation: true,
11058     
11059     calendarWeeks: false,
11060     
11061     startDate: -Infinity,
11062     
11063     endDate: Infinity,
11064     
11065     daysOfWeekDisabled: [],
11066     
11067     _events: [],
11068     
11069     UTCDate: function()
11070     {
11071         return new Date(Date.UTC.apply(Date, arguments));
11072     },
11073     
11074     UTCToday: function()
11075     {
11076         var today = new Date();
11077         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11078     },
11079     
11080     getDate: function() {
11081             var d = this.getUTCDate();
11082             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11083     },
11084     
11085     getUTCDate: function() {
11086             return this.date;
11087     },
11088     
11089     setDate: function(d) {
11090             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11091     },
11092     
11093     setUTCDate: function(d) {
11094             this.date = d;
11095             this.setValue(this.formatDate(this.date));
11096     },
11097         
11098     onRender: function(ct, position)
11099     {
11100         
11101         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11102         
11103         this.language = this.language || 'en';
11104         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11105         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11106         
11107         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11108         this.format = this.format || 'm/d/y';
11109         this.isInline = false;
11110         this.isInput = true;
11111         this.component = this.el.select('.add-on', true).first() || false;
11112         this.component = (this.component && this.component.length === 0) ? false : this.component;
11113         this.hasInput = this.component && this.inputEL().length;
11114         
11115         if (typeof(this.minViewMode === 'string')) {
11116             switch (this.minViewMode) {
11117                 case 'months':
11118                     this.minViewMode = 1;
11119                     break;
11120                 case 'years':
11121                     this.minViewMode = 2;
11122                     break;
11123                 default:
11124                     this.minViewMode = 0;
11125                     break;
11126             }
11127         }
11128         
11129         if (typeof(this.viewMode === 'string')) {
11130             switch (this.viewMode) {
11131                 case 'months':
11132                     this.viewMode = 1;
11133                     break;
11134                 case 'years':
11135                     this.viewMode = 2;
11136                     break;
11137                 default:
11138                     this.viewMode = 0;
11139                     break;
11140             }
11141         }
11142                 
11143         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11144         
11145         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11146         
11147         this.picker().on('mousedown', this.onMousedown, this);
11148         this.picker().on('click', this.onClick, this);
11149         
11150         this.picker().addClass('datepicker-dropdown');
11151         
11152         this.startViewMode = this.viewMode;
11153         
11154         
11155         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11156             if(!this.calendarWeeks){
11157                 v.remove();
11158                 return;
11159             };
11160             
11161             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11162             v.attr('colspan', function(i, val){
11163                 return parseInt(val) + 1;
11164             });
11165         })
11166                         
11167         
11168         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11169         
11170         this.setStartDate(this.startDate);
11171         this.setEndDate(this.endDate);
11172         
11173         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11174         
11175         this.fillDow();
11176         this.fillMonths();
11177         this.update();
11178         this.showMode();
11179         
11180         if(this.isInline) {
11181             this.show();
11182         }
11183     },
11184     
11185     picker : function()
11186     {
11187         return this.el.select('.datepicker', true).first();
11188     },
11189     
11190     fillDow: function()
11191     {
11192         var dowCnt = this.weekStart;
11193         
11194         var dow = {
11195             tag: 'tr',
11196             cn: [
11197                 
11198             ]
11199         };
11200         
11201         if(this.calendarWeeks){
11202             dow.cn.push({
11203                 tag: 'th',
11204                 cls: 'cw',
11205                 html: '&nbsp;'
11206             })
11207         }
11208         
11209         while (dowCnt < this.weekStart + 7) {
11210             dow.cn.push({
11211                 tag: 'th',
11212                 cls: 'dow',
11213                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11214             });
11215         }
11216         
11217         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11218     },
11219     
11220     fillMonths: function()
11221     {    
11222         var i = 0
11223         var months = this.picker().select('>.datepicker-months td', true).first();
11224         
11225         months.dom.innerHTML = '';
11226         
11227         while (i < 12) {
11228             var month = {
11229                 tag: 'span',
11230                 cls: 'month',
11231                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11232             }
11233             
11234             months.createChild(month);
11235         }
11236         
11237     },
11238     
11239     update: function(){
11240         
11241         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11242         
11243         if (this.date < this.startDate) {
11244             this.viewDate = new Date(this.startDate);
11245         } else if (this.date > this.endDate) {
11246             this.viewDate = new Date(this.endDate);
11247         } else {
11248             this.viewDate = new Date(this.date);
11249         }
11250         
11251         this.fill();
11252     },
11253     
11254     fill: function() {
11255         var d = new Date(this.viewDate),
11256                 year = d.getUTCFullYear(),
11257                 month = d.getUTCMonth(),
11258                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11259                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11260                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11261                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11262                 currentDate = this.date && this.date.valueOf(),
11263                 today = this.UTCToday();
11264         
11265         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11266         
11267 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11268         
11269 //        this.picker.select('>tfoot th.today').
11270 //                                              .text(dates[this.language].today)
11271 //                                              .toggle(this.todayBtn !== false);
11272     
11273         this.updateNavArrows();
11274         this.fillMonths();
11275                                                 
11276         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11277         
11278         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11279          
11280         prevMonth.setUTCDate(day);
11281         
11282         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11283         
11284         var nextMonth = new Date(prevMonth);
11285         
11286         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11287         
11288         nextMonth = nextMonth.valueOf();
11289         
11290         var fillMonths = false;
11291         
11292         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11293         
11294         while(prevMonth.valueOf() < nextMonth) {
11295             var clsName = '';
11296             
11297             if (prevMonth.getUTCDay() === this.weekStart) {
11298                 if(fillMonths){
11299                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11300                 }
11301                     
11302                 fillMonths = {
11303                     tag: 'tr',
11304                     cn: []
11305                 };
11306                 
11307                 if(this.calendarWeeks){
11308                     // ISO 8601: First week contains first thursday.
11309                     // ISO also states week starts on Monday, but we can be more abstract here.
11310                     var
11311                     // Start of current week: based on weekstart/current date
11312                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11313                     // Thursday of this week
11314                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11315                     // First Thursday of year, year from thursday
11316                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11317                     // Calendar week: ms between thursdays, div ms per day, div 7 days
11318                     calWeek =  (th - yth) / 864e5 / 7 + 1;
11319                     
11320                     fillMonths.cn.push({
11321                         tag: 'td',
11322                         cls: 'cw',
11323                         html: calWeek
11324                     });
11325                 }
11326             }
11327             
11328             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11329                 clsName += ' old';
11330             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11331                 clsName += ' new';
11332             }
11333             if (this.todayHighlight &&
11334                 prevMonth.getUTCFullYear() == today.getFullYear() &&
11335                 prevMonth.getUTCMonth() == today.getMonth() &&
11336                 prevMonth.getUTCDate() == today.getDate()) {
11337                 clsName += ' today';
11338             }
11339             
11340             if (currentDate && prevMonth.valueOf() === currentDate) {
11341                 clsName += ' active';
11342             }
11343             
11344             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11345                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11346                     clsName += ' disabled';
11347             }
11348             
11349             fillMonths.cn.push({
11350                 tag: 'td',
11351                 cls: 'day ' + clsName,
11352                 html: prevMonth.getDate()
11353             })
11354             
11355             prevMonth.setDate(prevMonth.getDate()+1);
11356         }
11357           
11358         var currentYear = this.date && this.date.getUTCFullYear();
11359         var currentMonth = this.date && this.date.getUTCMonth();
11360         
11361         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11362         
11363         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11364             v.removeClass('active');
11365             
11366             if(currentYear === year && k === currentMonth){
11367                 v.addClass('active');
11368             }
11369             
11370             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11371                 v.addClass('disabled');
11372             }
11373             
11374         });
11375         
11376         
11377         year = parseInt(year/10, 10) * 10;
11378         
11379         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11380         
11381         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11382         
11383         year -= 1;
11384         for (var i = -1; i < 11; i++) {
11385             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11386                 tag: 'span',
11387                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11388                 html: year
11389             })
11390             
11391             year += 1;
11392         }
11393     },
11394     
11395     showMode: function(dir) {
11396         if (dir) {
11397             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11398         }
11399         Roo.each(this.picker().select('>div',true).elements, function(v){
11400             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11401             v.hide();
11402         });
11403         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11404     },
11405     
11406     place: function()
11407     {
11408         if(this.isInline) return;
11409         
11410         this.picker().removeClass(['bottom', 'top']);
11411         
11412         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11413             /*
11414              * place to the top of element!
11415              *
11416              */
11417             
11418             this.picker().addClass('top');
11419             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11420             
11421             return;
11422         }
11423         
11424         this.picker().addClass('bottom');
11425         
11426         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11427     },
11428     
11429     parseDate : function(value){
11430         if(!value || value instanceof Date){
11431             return value;
11432         }
11433         var v = Date.parseDate(value, this.format);
11434         if (!v && this.useIso) {
11435             v = Date.parseDate(value, 'Y-m-d');
11436         }
11437         if(!v && this.altFormats){
11438             if(!this.altFormatsArray){
11439                 this.altFormatsArray = this.altFormats.split("|");
11440             }
11441             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11442                 v = Date.parseDate(value, this.altFormatsArray[i]);
11443             }
11444         }
11445         return v;
11446     },
11447     
11448     formatDate : function(date, fmt){
11449         return (!date || !(date instanceof Date)) ?
11450         date : date.dateFormat(fmt || this.format);
11451     },
11452     
11453     onFocus : function()
11454     {
11455         Roo.bootstrap.DateField.superclass.onFocus.call(this);
11456         this.show();
11457     },
11458     
11459     onBlur : function()
11460     {
11461         Roo.bootstrap.DateField.superclass.onBlur.call(this);
11462         this.hide();
11463     },
11464     
11465     show : function()
11466     {
11467         this.picker().show();
11468         this.update();
11469         this.place();
11470         
11471         this.fireEvent('show', this, this.date);
11472     },
11473     
11474     hide : function()
11475     {
11476         if(this.isInline) return;
11477         this.picker().hide();
11478         this.viewMode = this.startViewMode;
11479         this.showMode();
11480         
11481         this.fireEvent('hide', this, this.date);
11482         
11483     },
11484     
11485     onMousedown: function(e){
11486         e.stopPropagation();
11487         e.preventDefault();
11488     },
11489     
11490     keyup: function(e){
11491         Roo.bootstrap.DateField.superclass.keyup.call(this);
11492         this.update();
11493         
11494     },
11495
11496     setValue: function(v){
11497         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11498         
11499         this.fireEvent('select', this, this.date);
11500         
11501     },
11502     
11503     fireKey: function(e){
11504         if (!this.picker().isVisible()){
11505             if (e.keyCode == 27) // allow escape to hide and re-show picker
11506                 this.show();
11507             return;
11508         }
11509         var dateChanged = false,
11510         dir, day, month,
11511         newDate, newViewDate;
11512         switch(e.keyCode){
11513             case 27: // escape
11514                 this.hide();
11515                 e.preventDefault();
11516                 break;
11517             case 37: // left
11518             case 39: // right
11519                 if (!this.keyboardNavigation) break;
11520                 dir = e.keyCode == 37 ? -1 : 1;
11521                 
11522                 if (e.ctrlKey){
11523                     newDate = this.moveYear(this.date, dir);
11524                     newViewDate = this.moveYear(this.viewDate, dir);
11525                 } else if (e.shiftKey){
11526                     newDate = this.moveMonth(this.date, dir);
11527                     newViewDate = this.moveMonth(this.viewDate, dir);
11528                 } else {
11529                     newDate = new Date(this.date);
11530                     newDate.setUTCDate(this.date.getUTCDate() + dir);
11531                     newViewDate = new Date(this.viewDate);
11532                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11533                 }
11534                 if (this.dateWithinRange(newDate)){
11535                     this.date = newDate;
11536                     this.viewDate = newViewDate;
11537                     this.setValue(this.formatDate(this.date));
11538                     this.update();
11539                     e.preventDefault();
11540                     dateChanged = true;
11541                 }
11542                 break;
11543             case 38: // up
11544             case 40: // down
11545                 if (!this.keyboardNavigation) break;
11546                 dir = e.keyCode == 38 ? -1 : 1;
11547                 if (e.ctrlKey){
11548                     newDate = this.moveYear(this.date, dir);
11549                     newViewDate = this.moveYear(this.viewDate, dir);
11550                 } else if (e.shiftKey){
11551                     newDate = this.moveMonth(this.date, dir);
11552                     newViewDate = this.moveMonth(this.viewDate, dir);
11553                 } else {
11554                     newDate = new Date(this.date);
11555                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11556                     newViewDate = new Date(this.viewDate);
11557                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11558                 }
11559                 if (this.dateWithinRange(newDate)){
11560                     this.date = newDate;
11561                     this.viewDate = newViewDate;
11562                     this.setValue(this.formatDate(this.date));
11563                     this.update();
11564                     e.preventDefault();
11565                     dateChanged = true;
11566                 }
11567                 break;
11568             case 13: // enter
11569                 this.setValue(this.formatDate(this.date));
11570                 this.hide();
11571                 e.preventDefault();
11572                 break;
11573             case 9: // tab
11574                 this.setValue(this.formatDate(this.date));
11575                 this.hide();
11576                 break;
11577         }
11578     },
11579     
11580     
11581     onClick: function(e) {
11582         e.stopPropagation();
11583         e.preventDefault();
11584         
11585         var target = e.getTarget();
11586         
11587         if(target.nodeName.toLowerCase() === 'i'){
11588             target = Roo.get(target).dom.parentNode;
11589         }
11590         
11591         var nodeName = target.nodeName;
11592         var className = target.className;
11593         var html = target.innerHTML;
11594         
11595         switch(nodeName.toLowerCase()) {
11596             case 'th':
11597                 switch(className) {
11598                     case 'switch':
11599                         this.showMode(1);
11600                         break;
11601                     case 'prev':
11602                     case 'next':
11603                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11604                         switch(this.viewMode){
11605                                 case 0:
11606                                         this.viewDate = this.moveMonth(this.viewDate, dir);
11607                                         break;
11608                                 case 1:
11609                                 case 2:
11610                                         this.viewDate = this.moveYear(this.viewDate, dir);
11611                                         break;
11612                         }
11613                         this.fill();
11614                         break;
11615                     case 'today':
11616                         var date = new Date();
11617                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11618                         this.fill()
11619                         this.setValue(this.formatDate(this.date));
11620                         this.hide();
11621                         break;
11622                 }
11623                 break;
11624             case 'span':
11625                 if (className.indexOf('disabled') === -1) {
11626                     this.viewDate.setUTCDate(1);
11627                     if (className.indexOf('month') !== -1) {
11628                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11629                     } else {
11630                         var year = parseInt(html, 10) || 0;
11631                         this.viewDate.setUTCFullYear(year);
11632                         
11633                     }
11634                     this.showMode(-1);
11635                     this.fill();
11636                 }
11637                 break;
11638                 
11639             case 'td':
11640                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11641                     var day = parseInt(html, 10) || 1;
11642                     var year = this.viewDate.getUTCFullYear(),
11643                         month = this.viewDate.getUTCMonth();
11644
11645                     if (className.indexOf('old') !== -1) {
11646                         if(month === 0 ){
11647                             month = 11;
11648                             year -= 1;
11649                         }else{
11650                             month -= 1;
11651                         }
11652                     } else if (className.indexOf('new') !== -1) {
11653                         if (month == 11) {
11654                             month = 0;
11655                             year += 1;
11656                         } else {
11657                             month += 1;
11658                         }
11659                     }
11660                     this.date = this.UTCDate(year, month, day,0,0,0,0);
11661                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11662                     this.fill();
11663                     this.setValue(this.formatDate(this.date));
11664                     this.hide();
11665                 }
11666                 break;
11667         }
11668     },
11669     
11670     setStartDate: function(startDate){
11671         this.startDate = startDate || -Infinity;
11672         if (this.startDate !== -Infinity) {
11673             this.startDate = this.parseDate(this.startDate);
11674         }
11675         this.update();
11676         this.updateNavArrows();
11677     },
11678
11679     setEndDate: function(endDate){
11680         this.endDate = endDate || Infinity;
11681         if (this.endDate !== Infinity) {
11682             this.endDate = this.parseDate(this.endDate);
11683         }
11684         this.update();
11685         this.updateNavArrows();
11686     },
11687     
11688     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11689         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11690         if (typeof(this.daysOfWeekDisabled) !== 'object') {
11691             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11692         }
11693         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11694             return parseInt(d, 10);
11695         });
11696         this.update();
11697         this.updateNavArrows();
11698     },
11699     
11700     updateNavArrows: function() {
11701         var d = new Date(this.viewDate),
11702         year = d.getUTCFullYear(),
11703         month = d.getUTCMonth();
11704         
11705         Roo.each(this.picker().select('.prev', true).elements, function(v){
11706             v.show();
11707             switch (this.viewMode) {
11708                 case 0:
11709
11710                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11711                         v.hide();
11712                     }
11713                     break;
11714                 case 1:
11715                 case 2:
11716                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11717                         v.hide();
11718                     }
11719                     break;
11720             }
11721         });
11722         
11723         Roo.each(this.picker().select('.next', true).elements, function(v){
11724             v.show();
11725             switch (this.viewMode) {
11726                 case 0:
11727
11728                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11729                         v.hide();
11730                     }
11731                     break;
11732                 case 1:
11733                 case 2:
11734                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11735                         v.hide();
11736                     }
11737                     break;
11738             }
11739         })
11740     },
11741     
11742     moveMonth: function(date, dir){
11743         if (!dir) return date;
11744         var new_date = new Date(date.valueOf()),
11745         day = new_date.getUTCDate(),
11746         month = new_date.getUTCMonth(),
11747         mag = Math.abs(dir),
11748         new_month, test;
11749         dir = dir > 0 ? 1 : -1;
11750         if (mag == 1){
11751             test = dir == -1
11752             // If going back one month, make sure month is not current month
11753             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11754             ? function(){
11755                 return new_date.getUTCMonth() == month;
11756             }
11757             // If going forward one month, make sure month is as expected
11758             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11759             : function(){
11760                 return new_date.getUTCMonth() != new_month;
11761             };
11762             new_month = month + dir;
11763             new_date.setUTCMonth(new_month);
11764             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11765             if (new_month < 0 || new_month > 11)
11766                 new_month = (new_month + 12) % 12;
11767         } else {
11768             // For magnitudes >1, move one month at a time...
11769             for (var i=0; i<mag; i++)
11770                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11771                 new_date = this.moveMonth(new_date, dir);
11772             // ...then reset the day, keeping it in the new month
11773             new_month = new_date.getUTCMonth();
11774             new_date.setUTCDate(day);
11775             test = function(){
11776                 return new_month != new_date.getUTCMonth();
11777             };
11778         }
11779         // Common date-resetting loop -- if date is beyond end of month, make it
11780         // end of month
11781         while (test()){
11782             new_date.setUTCDate(--day);
11783             new_date.setUTCMonth(new_month);
11784         }
11785         return new_date;
11786     },
11787
11788     moveYear: function(date, dir){
11789         return this.moveMonth(date, dir*12);
11790     },
11791
11792     dateWithinRange: function(date){
11793         return date >= this.startDate && date <= this.endDate;
11794     },
11795
11796     
11797     remove: function() {
11798         this.picker().remove();
11799     }
11800    
11801 });
11802
11803 Roo.apply(Roo.bootstrap.DateField,  {
11804     
11805     head : {
11806         tag: 'thead',
11807         cn: [
11808         {
11809             tag: 'tr',
11810             cn: [
11811             {
11812                 tag: 'th',
11813                 cls: 'prev',
11814                 html: '<i class="icon-arrow-left"/>'
11815             },
11816             {
11817                 tag: 'th',
11818                 cls: 'switch',
11819                 colspan: '5'
11820             },
11821             {
11822                 tag: 'th',
11823                 cls: 'next',
11824                 html: '<i class="icon-arrow-right"/>'
11825             }
11826
11827             ]
11828         }
11829         ]
11830     },
11831     
11832     content : {
11833         tag: 'tbody',
11834         cn: [
11835         {
11836             tag: 'tr',
11837             cn: [
11838             {
11839                 tag: 'td',
11840                 colspan: '7'
11841             }
11842             ]
11843         }
11844         ]
11845     },
11846     
11847     footer : {
11848         tag: 'tfoot',
11849         cn: [
11850         {
11851             tag: 'tr',
11852             cn: [
11853             {
11854                 tag: 'th',
11855                 colspan: '7',
11856                 cls: 'today'
11857             }
11858                     
11859             ]
11860         }
11861         ]
11862     },
11863     
11864     dates:{
11865         en: {
11866             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11867             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11868             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11869             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11870             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11871             today: "Today"
11872         }
11873     },
11874     
11875     modes: [
11876     {
11877         clsName: 'days',
11878         navFnc: 'Month',
11879         navStep: 1
11880     },
11881     {
11882         clsName: 'months',
11883         navFnc: 'FullYear',
11884         navStep: 1
11885     },
11886     {
11887         clsName: 'years',
11888         navFnc: 'FullYear',
11889         navStep: 10
11890     }]
11891 });
11892
11893 Roo.apply(Roo.bootstrap.DateField,  {
11894   
11895     template : {
11896         tag: 'div',
11897         cls: 'datepicker dropdown-menu',
11898         cn: [
11899         {
11900             tag: 'div',
11901             cls: 'datepicker-days',
11902             cn: [
11903             {
11904                 tag: 'table',
11905                 cls: 'table-condensed',
11906                 cn:[
11907                 Roo.bootstrap.DateField.head,
11908                 {
11909                     tag: 'tbody'
11910                 },
11911                 Roo.bootstrap.DateField.footer
11912                 ]
11913             }
11914             ]
11915         },
11916         {
11917             tag: 'div',
11918             cls: 'datepicker-months',
11919             cn: [
11920             {
11921                 tag: 'table',
11922                 cls: 'table-condensed',
11923                 cn:[
11924                 Roo.bootstrap.DateField.head,
11925                 Roo.bootstrap.DateField.content,
11926                 Roo.bootstrap.DateField.footer
11927                 ]
11928             }
11929             ]
11930         },
11931         {
11932             tag: 'div',
11933             cls: 'datepicker-years',
11934             cn: [
11935             {
11936                 tag: 'table',
11937                 cls: 'table-condensed',
11938                 cn:[
11939                 Roo.bootstrap.DateField.head,
11940                 Roo.bootstrap.DateField.content,
11941                 Roo.bootstrap.DateField.footer
11942                 ]
11943             }
11944             ]
11945         }
11946         ]
11947     }
11948 });
11949
11950  
11951
11952  /*
11953  * - LGPL
11954  *
11955  * TimeField
11956  * 
11957  */
11958
11959 /**
11960  * @class Roo.bootstrap.TimeField
11961  * @extends Roo.bootstrap.Input
11962  * Bootstrap DateField class
11963  * 
11964  * 
11965  * @constructor
11966  * Create a new TimeField
11967  * @param {Object} config The config object
11968  */
11969
11970 Roo.bootstrap.TimeField = function(config){
11971     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11972     this.addEvents({
11973             /**
11974              * @event show
11975              * Fires when this field show.
11976              * @param {Roo.bootstrap.DateField} this
11977              * @param {Mixed} date The date value
11978              */
11979             show : true,
11980             /**
11981              * @event show
11982              * Fires when this field hide.
11983              * @param {Roo.bootstrap.DateField} this
11984              * @param {Mixed} date The date value
11985              */
11986             hide : true,
11987             /**
11988              * @event select
11989              * Fires when select a date.
11990              * @param {Roo.bootstrap.DateField} this
11991              * @param {Mixed} date The date value
11992              */
11993             select : true
11994         });
11995 };
11996
11997 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
11998     
11999     /**
12000      * @cfg {String} format
12001      * The default time format string which can be overriden for localization support.  The format must be
12002      * valid according to {@link Date#parseDate} (defaults to 'H:i').
12003      */
12004     format : "H:i",
12005        
12006     onRender: function(ct, position)
12007     {
12008         
12009         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
12010                 
12011         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
12012         
12013         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12014         
12015         this.pop = this.picker().select('>.datepicker-time',true).first();
12016         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
12017         
12018         this.picker().on('mousedown', this.onMousedown, this);
12019         this.picker().on('click', this.onClick, this);
12020         
12021         this.picker().addClass('datepicker-dropdown');
12022     
12023         this.fillTime();
12024         this.update();
12025             
12026         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12027         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12028         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12029         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12030         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12031         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12032
12033     },
12034     
12035     fireKey: function(e){
12036         if (!this.picker().isVisible()){
12037             if (e.keyCode == 27) // allow escape to hide and re-show picker
12038                 this.show();
12039             return;
12040         }
12041
12042         e.preventDefault();
12043         
12044         switch(e.keyCode){
12045             case 27: // escape
12046                 this.hide();
12047                 break;
12048             case 37: // left
12049             case 39: // right
12050                 this.onTogglePeriod();
12051                 break;
12052             case 38: // up
12053                 this.onIncrementMinutes();
12054                 break;
12055             case 40: // down
12056                 this.onDecrementMinutes();
12057                 break;
12058             case 13: // enter
12059             case 9: // tab
12060                 this.setTime();
12061                 break;
12062         }
12063     },
12064     
12065     onClick: function(e) {
12066         e.stopPropagation();
12067         e.preventDefault();
12068     },
12069     
12070     picker : function()
12071     {
12072         return this.el.select('.datepicker', true).first();
12073     },
12074     
12075     fillTime: function()
12076     {    
12077         var time = this.pop.select('tbody', true).first();
12078         
12079         time.dom.innerHTML = '';
12080         
12081         time.createChild({
12082             tag: 'tr',
12083             cn: [
12084                 {
12085                     tag: 'td',
12086                     cn: [
12087                         {
12088                             tag: 'a',
12089                             href: '#',
12090                             cls: 'btn',
12091                             cn: [
12092                                 {
12093                                     tag: 'span',
12094                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
12095                                 }
12096                             ]
12097                         } 
12098                     ]
12099                 },
12100                 {
12101                     tag: 'td',
12102                     cls: 'separator'
12103                 },
12104                 {
12105                     tag: 'td',
12106                     cn: [
12107                         {
12108                             tag: 'a',
12109                             href: '#',
12110                             cls: 'btn',
12111                             cn: [
12112                                 {
12113                                     tag: 'span',
12114                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12115                                 }
12116                             ]
12117                         }
12118                     ]
12119                 },
12120                 {
12121                     tag: 'td',
12122                     cls: 'separator'
12123                 }
12124             ]
12125         });
12126         
12127         time.createChild({
12128             tag: 'tr',
12129             cn: [
12130                 {
12131                     tag: 'td',
12132                     cn: [
12133                         {
12134                             tag: 'span',
12135                             cls: 'timepicker-hour',
12136                             html: '00'
12137                         }  
12138                     ]
12139                 },
12140                 {
12141                     tag: 'td',
12142                     cls: 'separator',
12143                     html: ':'
12144                 },
12145                 {
12146                     tag: 'td',
12147                     cn: [
12148                         {
12149                             tag: 'span',
12150                             cls: 'timepicker-minute',
12151                             html: '00'
12152                         }  
12153                     ]
12154                 },
12155                 {
12156                     tag: 'td',
12157                     cls: 'separator'
12158                 },
12159                 {
12160                     tag: 'td',
12161                     cn: [
12162                         {
12163                             tag: 'button',
12164                             type: 'button',
12165                             cls: 'btn btn-primary period',
12166                             html: 'AM'
12167                             
12168                         }
12169                     ]
12170                 }
12171             ]
12172         });
12173         
12174         time.createChild({
12175             tag: 'tr',
12176             cn: [
12177                 {
12178                     tag: 'td',
12179                     cn: [
12180                         {
12181                             tag: 'a',
12182                             href: '#',
12183                             cls: 'btn',
12184                             cn: [
12185                                 {
12186                                     tag: 'span',
12187                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12188                                 }
12189                             ]
12190                         }
12191                     ]
12192                 },
12193                 {
12194                     tag: 'td',
12195                     cls: 'separator'
12196                 },
12197                 {
12198                     tag: 'td',
12199                     cn: [
12200                         {
12201                             tag: 'a',
12202                             href: '#',
12203                             cls: 'btn',
12204                             cn: [
12205                                 {
12206                                     tag: 'span',
12207                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
12208                                 }
12209                             ]
12210                         }
12211                     ]
12212                 },
12213                 {
12214                     tag: 'td',
12215                     cls: 'separator'
12216                 }
12217             ]
12218         });
12219         
12220     },
12221     
12222     update: function()
12223     {
12224         
12225         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12226         
12227         this.fill();
12228     },
12229     
12230     fill: function() 
12231     {
12232         var hours = this.time.getHours();
12233         var minutes = this.time.getMinutes();
12234         var period = 'AM';
12235         
12236         if(hours > 11){
12237             period = 'PM';
12238         }
12239         
12240         if(hours == 0){
12241             hours = 12;
12242         }
12243         
12244         
12245         if(hours > 12){
12246             hours = hours - 12;
12247         }
12248         
12249         if(hours < 10){
12250             hours = '0' + hours;
12251         }
12252         
12253         if(minutes < 10){
12254             minutes = '0' + minutes;
12255         }
12256         
12257         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12258         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12259         this.pop.select('button', true).first().dom.innerHTML = period;
12260         
12261     },
12262     
12263     place: function()
12264     {   
12265         this.picker().removeClass(['bottom', 'top']);
12266         
12267         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12268             /*
12269              * place to the top of element!
12270              *
12271              */
12272             
12273             this.picker().addClass('top');
12274             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12275             
12276             return;
12277         }
12278         
12279         this.picker().addClass('bottom');
12280         
12281         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12282     },
12283   
12284     onFocus : function()
12285     {
12286         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12287         this.show();
12288     },
12289     
12290     onBlur : function()
12291     {
12292         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12293         this.hide();
12294     },
12295     
12296     show : function()
12297     {
12298         this.picker().show();
12299         this.pop.show();
12300         this.update();
12301         this.place();
12302         
12303         this.fireEvent('show', this, this.date);
12304     },
12305     
12306     hide : function()
12307     {
12308         this.picker().hide();
12309         this.pop.hide();
12310         
12311         this.fireEvent('hide', this, this.date);
12312     },
12313     
12314     setTime : function()
12315     {
12316         this.hide();
12317         this.setValue(this.time.format(this.format));
12318         
12319         this.fireEvent('select', this, this.date);
12320         
12321         
12322     },
12323     
12324     onMousedown: function(e){
12325         e.stopPropagation();
12326         e.preventDefault();
12327     },
12328     
12329     onIncrementHours: function()
12330     {
12331         Roo.log('onIncrementHours');
12332         this.time = this.time.add(Date.HOUR, 1);
12333         this.update();
12334         
12335     },
12336     
12337     onDecrementHours: function()
12338     {
12339         Roo.log('onDecrementHours');
12340         this.time = this.time.add(Date.HOUR, -1);
12341         this.update();
12342     },
12343     
12344     onIncrementMinutes: function()
12345     {
12346         Roo.log('onIncrementMinutes');
12347         this.time = this.time.add(Date.MINUTE, 1);
12348         this.update();
12349     },
12350     
12351     onDecrementMinutes: function()
12352     {
12353         Roo.log('onDecrementMinutes');
12354         this.time = this.time.add(Date.MINUTE, -1);
12355         this.update();
12356     },
12357     
12358     onTogglePeriod: function()
12359     {
12360         Roo.log('onTogglePeriod');
12361         this.time = this.time.add(Date.HOUR, 12);
12362         this.update();
12363     }
12364     
12365    
12366 });
12367
12368 Roo.apply(Roo.bootstrap.TimeField,  {
12369     
12370     content : {
12371         tag: 'tbody',
12372         cn: [
12373             {
12374                 tag: 'tr',
12375                 cn: [
12376                 {
12377                     tag: 'td',
12378                     colspan: '7'
12379                 }
12380                 ]
12381             }
12382         ]
12383     },
12384     
12385     footer : {
12386         tag: 'tfoot',
12387         cn: [
12388             {
12389                 tag: 'tr',
12390                 cn: [
12391                 {
12392                     tag: 'th',
12393                     colspan: '7',
12394                     cls: '',
12395                     cn: [
12396                         {
12397                             tag: 'button',
12398                             cls: 'btn btn-info ok',
12399                             html: 'OK'
12400                         }
12401                     ]
12402                 }
12403
12404                 ]
12405             }
12406         ]
12407     }
12408 });
12409
12410 Roo.apply(Roo.bootstrap.TimeField,  {
12411   
12412     template : {
12413         tag: 'div',
12414         cls: 'datepicker dropdown-menu',
12415         cn: [
12416             {
12417                 tag: 'div',
12418                 cls: 'datepicker-time',
12419                 cn: [
12420                 {
12421                     tag: 'table',
12422                     cls: 'table-condensed',
12423                     cn:[
12424                     Roo.bootstrap.TimeField.content,
12425                     Roo.bootstrap.TimeField.footer
12426                     ]
12427                 }
12428                 ]
12429             }
12430         ]
12431     }
12432 });
12433
12434  
12435
12436  /*
12437  * - LGPL
12438  *
12439  * CheckBox
12440  * 
12441  */
12442
12443 /**
12444  * @class Roo.bootstrap.CheckBox
12445  * @extends Roo.bootstrap.Input
12446  * Bootstrap CheckBox class
12447  * 
12448  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12449  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12450  * @cfg {String} boxLabel The text that appears beside the checkbox
12451  * @cfg {Boolean} checked initnal the element
12452  * 
12453  * @constructor
12454  * Create a new CheckBox
12455  * @param {Object} config The config object
12456  */
12457
12458 Roo.bootstrap.CheckBox = function(config){
12459     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12460    
12461         this.addEvents({
12462             /**
12463             * @event check
12464             * Fires when the element is checked or unchecked.
12465             * @param {Roo.bootstrap.CheckBox} this This input
12466             * @param {Boolean} checked The new checked value
12467             */
12468            check : true
12469         });
12470 };
12471
12472 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
12473     
12474     inputType: 'checkbox',
12475     inputValue: 1,
12476     valueOff: 0,
12477     boxLabel: false,
12478     checked: false,
12479     
12480     getAutoCreate : function()
12481     {
12482         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12483         
12484         var id = Roo.id();
12485         
12486         var cfg = {};
12487         
12488         cfg.cls = 'form-group' //input-group
12489         
12490         var input =  {
12491             tag: 'input',
12492             id : id,
12493             type : this.inputType,
12494             value : (!this.checked) ? this.valueOff : this.inputValue,
12495             cls : 'form-box',
12496             placeholder : this.placeholder || ''
12497             
12498         };
12499         
12500         if (this.disabled) {
12501             input.disabled=true;
12502         }
12503         
12504         if(this.checked){
12505             input.checked = this.checked;
12506         }
12507         
12508         if (this.name) {
12509             input.name = this.name;
12510         }
12511         
12512         if (this.size) {
12513             input.cls += ' input-' + this.size;
12514         }
12515         
12516         var settings=this;
12517         ['xs','sm','md','lg'].map(function(size){
12518             if (settings[size]) {
12519                 cfg.cls += ' col-' + size + '-' + settings[size];
12520             }
12521         });
12522         
12523         var inputblock = input;
12524         
12525         if (this.before || this.after) {
12526             
12527             inputblock = {
12528                 cls : 'input-group',
12529                 cn :  [] 
12530             };
12531             if (this.before) {
12532                 inputblock.cn.push({
12533                     tag :'span',
12534                     cls : 'input-group-addon',
12535                     html : this.before
12536                 });
12537             }
12538             inputblock.cn.push(input);
12539             if (this.after) {
12540                 inputblock.cn.push({
12541                     tag :'span',
12542                     cls : 'input-group-addon',
12543                     html : this.after
12544                 });
12545             }
12546             
12547         };
12548         
12549         if (align ==='left' && this.fieldLabel.length) {
12550                 Roo.log("left and has label");
12551                 cfg.cn = [
12552                     
12553                     {
12554                         tag: 'label',
12555                         'for' :  id,
12556                         cls : 'control-label col-md-' + this.labelWidth,
12557                         html : this.fieldLabel
12558                         
12559                     },
12560                     {
12561                         cls : "col-md-" + (12 - this.labelWidth), 
12562                         cn: [
12563                             inputblock
12564                         ]
12565                     }
12566                     
12567                 ];
12568         } else if ( this.fieldLabel.length) {
12569                 Roo.log(" label");
12570                 cfg.cn = [
12571                    
12572                     {
12573                         tag: this.boxLabel ? 'span' : 'label',
12574                         'for': id,
12575                         cls: 'control-label box-input-label',
12576                         //cls : 'input-group-addon',
12577                         html : this.fieldLabel
12578                         
12579                     },
12580                     
12581                     inputblock
12582                     
12583                 ];
12584
12585         } else {
12586             
12587                    Roo.log(" no label && no align");
12588                 cfg.cn = [
12589                     
12590                         inputblock
12591                     
12592                 ];
12593                 
12594                 
12595         };
12596         
12597         if(this.boxLabel){
12598             cfg.cn.push({
12599                 tag: 'label',
12600                 'for': id,
12601                 cls: 'box-label',
12602                 html: this.boxLabel
12603             })
12604         }
12605         
12606         return cfg;
12607         
12608     },
12609     
12610     /**
12611      * return the real input element.
12612      */
12613     inputEl: function ()
12614     {
12615         return this.el.select('input.form-box',true).first();
12616     },
12617     
12618     initEvents : function()
12619     {
12620 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12621         
12622         this.inputEl().on('click', this.onClick,  this);
12623         
12624     },
12625     
12626     onClick : function()
12627     {   
12628         this.setChecked(!this.checked);
12629     },
12630     
12631     setChecked : function(state,suppressEvent)
12632     {
12633         this.checked = state;
12634         
12635         this.inputEl().dom.checked = state;
12636         
12637         if(suppressEvent !== true){
12638             this.fireEvent('check', this, state);
12639         }
12640         
12641         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12642         
12643     },
12644     
12645     setValue : function(v,suppressEvent)
12646     {
12647         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12648     }
12649     
12650 });
12651
12652  
12653 /*
12654  * - LGPL
12655  *
12656  * Radio
12657  * 
12658  */
12659
12660 /**
12661  * @class Roo.bootstrap.Radio
12662  * @extends Roo.bootstrap.CheckBox
12663  * Bootstrap Radio class
12664
12665  * @constructor
12666  * Create a new Radio
12667  * @param {Object} config The config object
12668  */
12669
12670 Roo.bootstrap.Radio = function(config){
12671     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12672    
12673 };
12674
12675 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
12676     
12677     inputType: 'radio',
12678     inputValue: '',
12679     valueOff: '',
12680     
12681     getAutoCreate : function()
12682     {
12683         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12684         
12685         var id = Roo.id();
12686         
12687         var cfg = {};
12688         
12689         cfg.cls = 'form-group' //input-group
12690         
12691         var input =  {
12692             tag: 'input',
12693             id : id,
12694             type : this.inputType,
12695             value : (!this.checked) ? this.valueOff : this.inputValue,
12696             cls : 'form-box',
12697             placeholder : this.placeholder || ''
12698             
12699         };
12700         
12701         if (this.disabled) {
12702             input.disabled=true;
12703         }
12704         
12705         if(this.checked){
12706             input.checked = this.checked;
12707         }
12708         
12709         if (this.name) {
12710             input.name = this.name;
12711         }
12712         
12713         if (this.size) {
12714             input.cls += ' input-' + this.size;
12715         }
12716         
12717         var settings=this;
12718         ['xs','sm','md','lg'].map(function(size){
12719             if (settings[size]) {
12720                 cfg.cls += ' col-' + size + '-' + settings[size];
12721             }
12722         });
12723         
12724         var inputblock = input;
12725         
12726         if (this.before || this.after) {
12727             
12728             inputblock = {
12729                 cls : 'input-group',
12730                 cn :  [] 
12731             };
12732             if (this.before) {
12733                 inputblock.cn.push({
12734                     tag :'span',
12735                     cls : 'input-group-addon',
12736                     html : this.before
12737                 });
12738             }
12739             inputblock.cn.push(input);
12740             if (this.after) {
12741                 inputblock.cn.push({
12742                     tag :'span',
12743                     cls : 'input-group-addon',
12744                     html : this.after
12745                 });
12746             }
12747             
12748         };
12749         
12750         if (align ==='left' && this.fieldLabel.length) {
12751                 Roo.log("left and has label");
12752                 cfg.cn = [
12753                     
12754                     {
12755                         tag: 'label',
12756                         'for' :  id,
12757                         cls : 'control-label col-md-' + this.labelWidth,
12758                         html : this.fieldLabel
12759                         
12760                     },
12761                     {
12762                         cls : "col-md-" + (12 - this.labelWidth), 
12763                         cn: [
12764                             inputblock
12765                         ]
12766                     }
12767                     
12768                 ];
12769         } else if ( this.fieldLabel.length) {
12770                 Roo.log(" label");
12771                  cfg.cn = [
12772                    
12773                     {
12774                         tag: 'label',
12775                         'for': id,
12776                         cls: 'control-label box-input-label',
12777                         //cls : 'input-group-addon',
12778                         html : this.fieldLabel
12779                         
12780                     },
12781                     
12782                     inputblock
12783                     
12784                 ];
12785
12786         } else {
12787             
12788                    Roo.log(" no label && no align");
12789                 cfg.cn = [
12790                     
12791                         inputblock
12792                     
12793                 ];
12794                 
12795                 
12796         };
12797         
12798         if(this.boxLabel){
12799             cfg.cn.push({
12800                 tag: 'label',
12801                 'for': id,
12802                 cls: 'box-label',
12803                 html: this.boxLabel
12804             })
12805         }
12806         
12807         return cfg;
12808         
12809     },
12810    
12811     onClick : function()
12812     {   
12813         this.setChecked(true);
12814     },
12815     
12816     setChecked : function(state,suppressEvent)
12817     {
12818         if(state){
12819             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12820                 v.dom.checked = false;
12821             });
12822         }
12823         
12824         this.checked = state;
12825         this.inputEl().dom.checked = state;
12826         
12827         if(suppressEvent !== true){
12828             this.fireEvent('check', this, state);
12829         }
12830         
12831         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12832         
12833     },
12834     
12835     getGroupValue : function()
12836     {
12837         var value = ''
12838         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12839             if(v.dom.checked == true){
12840                 value = v.dom.value;
12841             }
12842         });
12843         
12844         return value;
12845     },
12846     
12847     /**
12848      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12849      * @return {Mixed} value The field value
12850      */
12851     getValue : function(){
12852         return this.getGroupValue();
12853     }
12854     
12855 });
12856
12857  
12858 //<script type="text/javascript">
12859
12860 /*
12861  * Based  Ext JS Library 1.1.1
12862  * Copyright(c) 2006-2007, Ext JS, LLC.
12863  * LGPL
12864  *
12865  */
12866  
12867 /**
12868  * @class Roo.HtmlEditorCore
12869  * @extends Roo.Component
12870  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12871  *
12872  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12873  */
12874
12875 Roo.HtmlEditorCore = function(config){
12876     
12877     
12878     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12879     this.addEvents({
12880         /**
12881          * @event initialize
12882          * Fires when the editor is fully initialized (including the iframe)
12883          * @param {Roo.HtmlEditorCore} this
12884          */
12885         initialize: true,
12886         /**
12887          * @event activate
12888          * Fires when the editor is first receives the focus. Any insertion must wait
12889          * until after this event.
12890          * @param {Roo.HtmlEditorCore} this
12891          */
12892         activate: true,
12893          /**
12894          * @event beforesync
12895          * Fires before the textarea is updated with content from the editor iframe. Return false
12896          * to cancel the sync.
12897          * @param {Roo.HtmlEditorCore} this
12898          * @param {String} html
12899          */
12900         beforesync: true,
12901          /**
12902          * @event beforepush
12903          * Fires before the iframe editor is updated with content from the textarea. Return false
12904          * to cancel the push.
12905          * @param {Roo.HtmlEditorCore} this
12906          * @param {String} html
12907          */
12908         beforepush: true,
12909          /**
12910          * @event sync
12911          * Fires when the textarea is updated with content from the editor iframe.
12912          * @param {Roo.HtmlEditorCore} this
12913          * @param {String} html
12914          */
12915         sync: true,
12916          /**
12917          * @event push
12918          * Fires when the iframe editor is updated with content from the textarea.
12919          * @param {Roo.HtmlEditorCore} this
12920          * @param {String} html
12921          */
12922         push: true,
12923         
12924         /**
12925          * @event editorevent
12926          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12927          * @param {Roo.HtmlEditorCore} this
12928          */
12929         editorevent: true
12930     });
12931      
12932 };
12933
12934
12935 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
12936
12937
12938      /**
12939      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
12940      */
12941     
12942     owner : false,
12943     
12944      /**
12945      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
12946      *                        Roo.resizable.
12947      */
12948     resizable : false,
12949      /**
12950      * @cfg {Number} height (in pixels)
12951      */   
12952     height: 300,
12953    /**
12954      * @cfg {Number} width (in pixels)
12955      */   
12956     width: 500,
12957     
12958     /**
12959      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12960      * 
12961      */
12962     stylesheets: false,
12963     
12964     // id of frame..
12965     frameId: false,
12966     
12967     // private properties
12968     validationEvent : false,
12969     deferHeight: true,
12970     initialized : false,
12971     activated : false,
12972     sourceEditMode : false,
12973     onFocus : Roo.emptyFn,
12974     iframePad:3,
12975     hideMode:'offsets',
12976     
12977     clearUp: true,
12978     
12979      
12980     
12981
12982     /**
12983      * Protected method that will not generally be called directly. It
12984      * is called when the editor initializes the iframe with HTML contents. Override this method if you
12985      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12986      */
12987     getDocMarkup : function(){
12988         // body styles..
12989         var st = '';
12990         Roo.log(this.stylesheets);
12991         
12992         // inherit styels from page...?? 
12993         if (this.stylesheets === false) {
12994             
12995             Roo.get(document.head).select('style').each(function(node) {
12996                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12997             });
12998             
12999             Roo.get(document.head).select('link').each(function(node) { 
13000                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13001             });
13002             
13003         } else if (!this.stylesheets.length) {
13004                 // simple..
13005                 st = '<style type="text/css">' +
13006                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13007                    '</style>';
13008         } else {
13009             Roo.each(this.stylesheets, function(s) {
13010                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
13011             });
13012             
13013         }
13014         
13015         st +=  '<style type="text/css">' +
13016             'IMG { cursor: pointer } ' +
13017         '</style>';
13018
13019         
13020         return '<html><head>' + st  +
13021             //<style type="text/css">' +
13022             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13023             //'</style>' +
13024             ' </head><body class="roo-htmleditor-body"></body></html>';
13025     },
13026
13027     // private
13028     onRender : function(ct, position)
13029     {
13030         var _t = this;
13031         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13032         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13033         
13034         
13035         this.el.dom.style.border = '0 none';
13036         this.el.dom.setAttribute('tabIndex', -1);
13037         this.el.addClass('x-hidden hide');
13038         
13039         
13040         
13041         if(Roo.isIE){ // fix IE 1px bogus margin
13042             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13043         }
13044        
13045         
13046         this.frameId = Roo.id();
13047         
13048          
13049         
13050         var iframe = this.owner.wrap.createChild({
13051             tag: 'iframe',
13052             cls: 'form-control', // bootstrap..
13053             id: this.frameId,
13054             name: this.frameId,
13055             frameBorder : 'no',
13056             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
13057         }, this.el
13058         );
13059         
13060         
13061         this.iframe = iframe.dom;
13062
13063          this.assignDocWin();
13064         
13065         this.doc.designMode = 'on';
13066        
13067         this.doc.open();
13068         this.doc.write(this.getDocMarkup());
13069         this.doc.close();
13070
13071         
13072         var task = { // must defer to wait for browser to be ready
13073             run : function(){
13074                 //console.log("run task?" + this.doc.readyState);
13075                 this.assignDocWin();
13076                 if(this.doc.body || this.doc.readyState == 'complete'){
13077                     try {
13078                         this.doc.designMode="on";
13079                     } catch (e) {
13080                         return;
13081                     }
13082                     Roo.TaskMgr.stop(task);
13083                     this.initEditor.defer(10, this);
13084                 }
13085             },
13086             interval : 10,
13087             duration: 10000,
13088             scope: this
13089         };
13090         Roo.TaskMgr.start(task);
13091
13092         
13093          
13094     },
13095
13096     // private
13097     onResize : function(w, h)
13098     {
13099          Roo.log('resize: ' +w + ',' + h );
13100         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13101         if(!this.iframe){
13102             return;
13103         }
13104         if(typeof w == 'number'){
13105             
13106             this.iframe.style.width = w + 'px';
13107         }
13108         if(typeof h == 'number'){
13109             
13110             this.iframe.style.height = h + 'px';
13111             if(this.doc){
13112                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13113             }
13114         }
13115         
13116     },
13117
13118     /**
13119      * Toggles the editor between standard and source edit mode.
13120      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13121      */
13122     toggleSourceEdit : function(sourceEditMode){
13123         
13124         this.sourceEditMode = sourceEditMode === true;
13125         
13126         if(this.sourceEditMode){
13127  
13128             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13129             
13130         }else{
13131             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13132             //this.iframe.className = '';
13133             this.deferFocus();
13134         }
13135         //this.setSize(this.owner.wrap.getSize());
13136         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13137     },
13138
13139     
13140   
13141
13142     /**
13143      * Protected method that will not generally be called directly. If you need/want
13144      * custom HTML cleanup, this is the method you should override.
13145      * @param {String} html The HTML to be cleaned
13146      * return {String} The cleaned HTML
13147      */
13148     cleanHtml : function(html){
13149         html = String(html);
13150         if(html.length > 5){
13151             if(Roo.isSafari){ // strip safari nonsense
13152                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13153             }
13154         }
13155         if(html == '&nbsp;'){
13156             html = '';
13157         }
13158         return html;
13159     },
13160
13161     /**
13162      * HTML Editor -> Textarea
13163      * Protected method that will not generally be called directly. Syncs the contents
13164      * of the editor iframe with the textarea.
13165      */
13166     syncValue : function(){
13167         if(this.initialized){
13168             var bd = (this.doc.body || this.doc.documentElement);
13169             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13170             var html = bd.innerHTML;
13171             if(Roo.isSafari){
13172                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13173                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13174                 if(m && m[1]){
13175                     html = '<div style="'+m[0]+'">' + html + '</div>';
13176                 }
13177             }
13178             html = this.cleanHtml(html);
13179             // fix up the special chars.. normaly like back quotes in word...
13180             // however we do not want to do this with chinese..
13181             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13182                 var cc = b.charCodeAt();
13183                 if (
13184                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13185                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13186                     (cc >= 0xf900 && cc < 0xfb00 )
13187                 ) {
13188                         return b;
13189                 }
13190                 return "&#"+cc+";" 
13191             });
13192             if(this.owner.fireEvent('beforesync', this, html) !== false){
13193                 this.el.dom.value = html;
13194                 this.owner.fireEvent('sync', this, html);
13195             }
13196         }
13197     },
13198
13199     /**
13200      * Protected method that will not generally be called directly. Pushes the value of the textarea
13201      * into the iframe editor.
13202      */
13203     pushValue : function(){
13204         if(this.initialized){
13205             var v = this.el.dom.value.trim();
13206             
13207 //            if(v.length < 1){
13208 //                v = '&#160;';
13209 //            }
13210             
13211             if(this.owner.fireEvent('beforepush', this, v) !== false){
13212                 var d = (this.doc.body || this.doc.documentElement);
13213                 d.innerHTML = v;
13214                 this.cleanUpPaste();
13215                 this.el.dom.value = d.innerHTML;
13216                 this.owner.fireEvent('push', this, v);
13217             }
13218         }
13219     },
13220
13221     // private
13222     deferFocus : function(){
13223         this.focus.defer(10, this);
13224     },
13225
13226     // doc'ed in Field
13227     focus : function(){
13228         if(this.win && !this.sourceEditMode){
13229             this.win.focus();
13230         }else{
13231             this.el.focus();
13232         }
13233     },
13234     
13235     assignDocWin: function()
13236     {
13237         var iframe = this.iframe;
13238         
13239          if(Roo.isIE){
13240             this.doc = iframe.contentWindow.document;
13241             this.win = iframe.contentWindow;
13242         } else {
13243             if (!Roo.get(this.frameId)) {
13244                 return;
13245             }
13246             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13247             this.win = Roo.get(this.frameId).dom.contentWindow;
13248         }
13249     },
13250     
13251     // private
13252     initEditor : function(){
13253         //console.log("INIT EDITOR");
13254         this.assignDocWin();
13255         
13256         
13257         
13258         this.doc.designMode="on";
13259         this.doc.open();
13260         this.doc.write(this.getDocMarkup());
13261         this.doc.close();
13262         
13263         var dbody = (this.doc.body || this.doc.documentElement);
13264         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13265         // this copies styles from the containing element into thsi one..
13266         // not sure why we need all of this..
13267         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13268         ss['background-attachment'] = 'fixed'; // w3c
13269         dbody.bgProperties = 'fixed'; // ie
13270         Roo.DomHelper.applyStyles(dbody, ss);
13271         Roo.EventManager.on(this.doc, {
13272             //'mousedown': this.onEditorEvent,
13273             'mouseup': this.onEditorEvent,
13274             'dblclick': this.onEditorEvent,
13275             'click': this.onEditorEvent,
13276             'keyup': this.onEditorEvent,
13277             buffer:100,
13278             scope: this
13279         });
13280         if(Roo.isGecko){
13281             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13282         }
13283         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13284             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13285         }
13286         this.initialized = true;
13287
13288         this.owner.fireEvent('initialize', this);
13289         this.pushValue();
13290     },
13291
13292     // private
13293     onDestroy : function(){
13294         
13295         
13296         
13297         if(this.rendered){
13298             
13299             //for (var i =0; i < this.toolbars.length;i++) {
13300             //    // fixme - ask toolbars for heights?
13301             //    this.toolbars[i].onDestroy();
13302            // }
13303             
13304             //this.wrap.dom.innerHTML = '';
13305             //this.wrap.remove();
13306         }
13307     },
13308
13309     // private
13310     onFirstFocus : function(){
13311         
13312         this.assignDocWin();
13313         
13314         
13315         this.activated = true;
13316          
13317     
13318         if(Roo.isGecko){ // prevent silly gecko errors
13319             this.win.focus();
13320             var s = this.win.getSelection();
13321             if(!s.focusNode || s.focusNode.nodeType != 3){
13322                 var r = s.getRangeAt(0);
13323                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13324                 r.collapse(true);
13325                 this.deferFocus();
13326             }
13327             try{
13328                 this.execCmd('useCSS', true);
13329                 this.execCmd('styleWithCSS', false);
13330             }catch(e){}
13331         }
13332         this.owner.fireEvent('activate', this);
13333     },
13334
13335     // private
13336     adjustFont: function(btn){
13337         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13338         //if(Roo.isSafari){ // safari
13339         //    adjust *= 2;
13340        // }
13341         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13342         if(Roo.isSafari){ // safari
13343             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13344             v =  (v < 10) ? 10 : v;
13345             v =  (v > 48) ? 48 : v;
13346             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13347             
13348         }
13349         
13350         
13351         v = Math.max(1, v+adjust);
13352         
13353         this.execCmd('FontSize', v  );
13354     },
13355
13356     onEditorEvent : function(e){
13357         this.owner.fireEvent('editorevent', this, e);
13358       //  this.updateToolbar();
13359         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13360     },
13361
13362     insertTag : function(tg)
13363     {
13364         // could be a bit smarter... -> wrap the current selected tRoo..
13365         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13366             
13367             range = this.createRange(this.getSelection());
13368             var wrappingNode = this.doc.createElement(tg.toLowerCase());
13369             wrappingNode.appendChild(range.extractContents());
13370             range.insertNode(wrappingNode);
13371
13372             return;
13373             
13374             
13375             
13376         }
13377         this.execCmd("formatblock",   tg);
13378         
13379     },
13380     
13381     insertText : function(txt)
13382     {
13383         
13384         
13385         var range = this.createRange();
13386         range.deleteContents();
13387                //alert(Sender.getAttribute('label'));
13388                
13389         range.insertNode(this.doc.createTextNode(txt));
13390     } ,
13391     
13392      
13393
13394     /**
13395      * Executes a Midas editor command on the editor document and performs necessary focus and
13396      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13397      * @param {String} cmd The Midas command
13398      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13399      */
13400     relayCmd : function(cmd, value){
13401         this.win.focus();
13402         this.execCmd(cmd, value);
13403         this.owner.fireEvent('editorevent', this);
13404         //this.updateToolbar();
13405         this.owner.deferFocus();
13406     },
13407
13408     /**
13409      * Executes a Midas editor command directly on the editor document.
13410      * For visual commands, you should use {@link #relayCmd} instead.
13411      * <b>This should only be called after the editor is initialized.</b>
13412      * @param {String} cmd The Midas command
13413      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13414      */
13415     execCmd : function(cmd, value){
13416         this.doc.execCommand(cmd, false, value === undefined ? null : value);
13417         this.syncValue();
13418     },
13419  
13420  
13421    
13422     /**
13423      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13424      * to insert tRoo.
13425      * @param {String} text | dom node.. 
13426      */
13427     insertAtCursor : function(text)
13428     {
13429         
13430         
13431         
13432         if(!this.activated){
13433             return;
13434         }
13435         /*
13436         if(Roo.isIE){
13437             this.win.focus();
13438             var r = this.doc.selection.createRange();
13439             if(r){
13440                 r.collapse(true);
13441                 r.pasteHTML(text);
13442                 this.syncValue();
13443                 this.deferFocus();
13444             
13445             }
13446             return;
13447         }
13448         */
13449         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13450             this.win.focus();
13451             
13452             
13453             // from jquery ui (MIT licenced)
13454             var range, node;
13455             var win = this.win;
13456             
13457             if (win.getSelection && win.getSelection().getRangeAt) {
13458                 range = win.getSelection().getRangeAt(0);
13459                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13460                 range.insertNode(node);
13461             } else if (win.document.selection && win.document.selection.createRange) {
13462                 // no firefox support
13463                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13464                 win.document.selection.createRange().pasteHTML(txt);
13465             } else {
13466                 // no firefox support
13467                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13468                 this.execCmd('InsertHTML', txt);
13469             } 
13470             
13471             this.syncValue();
13472             
13473             this.deferFocus();
13474         }
13475     },
13476  // private
13477     mozKeyPress : function(e){
13478         if(e.ctrlKey){
13479             var c = e.getCharCode(), cmd;
13480           
13481             if(c > 0){
13482                 c = String.fromCharCode(c).toLowerCase();
13483                 switch(c){
13484                     case 'b':
13485                         cmd = 'bold';
13486                         break;
13487                     case 'i':
13488                         cmd = 'italic';
13489                         break;
13490                     
13491                     case 'u':
13492                         cmd = 'underline';
13493                         break;
13494                     
13495                     case 'v':
13496                         this.cleanUpPaste.defer(100, this);
13497                         return;
13498                         
13499                 }
13500                 if(cmd){
13501                     this.win.focus();
13502                     this.execCmd(cmd);
13503                     this.deferFocus();
13504                     e.preventDefault();
13505                 }
13506                 
13507             }
13508         }
13509     },
13510
13511     // private
13512     fixKeys : function(){ // load time branching for fastest keydown performance
13513         if(Roo.isIE){
13514             return function(e){
13515                 var k = e.getKey(), r;
13516                 if(k == e.TAB){
13517                     e.stopEvent();
13518                     r = this.doc.selection.createRange();
13519                     if(r){
13520                         r.collapse(true);
13521                         r.pasteHTML('&#160;&#160;&#160;&#160;');
13522                         this.deferFocus();
13523                     }
13524                     return;
13525                 }
13526                 
13527                 if(k == e.ENTER){
13528                     r = this.doc.selection.createRange();
13529                     if(r){
13530                         var target = r.parentElement();
13531                         if(!target || target.tagName.toLowerCase() != 'li'){
13532                             e.stopEvent();
13533                             r.pasteHTML('<br />');
13534                             r.collapse(false);
13535                             r.select();
13536                         }
13537                     }
13538                 }
13539                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13540                     this.cleanUpPaste.defer(100, this);
13541                     return;
13542                 }
13543                 
13544                 
13545             };
13546         }else if(Roo.isOpera){
13547             return function(e){
13548                 var k = e.getKey();
13549                 if(k == e.TAB){
13550                     e.stopEvent();
13551                     this.win.focus();
13552                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
13553                     this.deferFocus();
13554                 }
13555                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13556                     this.cleanUpPaste.defer(100, this);
13557                     return;
13558                 }
13559                 
13560             };
13561         }else if(Roo.isSafari){
13562             return function(e){
13563                 var k = e.getKey();
13564                 
13565                 if(k == e.TAB){
13566                     e.stopEvent();
13567                     this.execCmd('InsertText','\t');
13568                     this.deferFocus();
13569                     return;
13570                 }
13571                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13572                     this.cleanUpPaste.defer(100, this);
13573                     return;
13574                 }
13575                 
13576              };
13577         }
13578     }(),
13579     
13580     getAllAncestors: function()
13581     {
13582         var p = this.getSelectedNode();
13583         var a = [];
13584         if (!p) {
13585             a.push(p); // push blank onto stack..
13586             p = this.getParentElement();
13587         }
13588         
13589         
13590         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13591             a.push(p);
13592             p = p.parentNode;
13593         }
13594         a.push(this.doc.body);
13595         return a;
13596     },
13597     lastSel : false,
13598     lastSelNode : false,
13599     
13600     
13601     getSelection : function() 
13602     {
13603         this.assignDocWin();
13604         return Roo.isIE ? this.doc.selection : this.win.getSelection();
13605     },
13606     
13607     getSelectedNode: function() 
13608     {
13609         // this may only work on Gecko!!!
13610         
13611         // should we cache this!!!!
13612         
13613         
13614         
13615          
13616         var range = this.createRange(this.getSelection()).cloneRange();
13617         
13618         if (Roo.isIE) {
13619             var parent = range.parentElement();
13620             while (true) {
13621                 var testRange = range.duplicate();
13622                 testRange.moveToElementText(parent);
13623                 if (testRange.inRange(range)) {
13624                     break;
13625                 }
13626                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13627                     break;
13628                 }
13629                 parent = parent.parentElement;
13630             }
13631             return parent;
13632         }
13633         
13634         // is ancestor a text element.
13635         var ac =  range.commonAncestorContainer;
13636         if (ac.nodeType == 3) {
13637             ac = ac.parentNode;
13638         }
13639         
13640         var ar = ac.childNodes;
13641          
13642         var nodes = [];
13643         var other_nodes = [];
13644         var has_other_nodes = false;
13645         for (var i=0;i<ar.length;i++) {
13646             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
13647                 continue;
13648             }
13649             // fullly contained node.
13650             
13651             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13652                 nodes.push(ar[i]);
13653                 continue;
13654             }
13655             
13656             // probably selected..
13657             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13658                 other_nodes.push(ar[i]);
13659                 continue;
13660             }
13661             // outer..
13662             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
13663                 continue;
13664             }
13665             
13666             
13667             has_other_nodes = true;
13668         }
13669         if (!nodes.length && other_nodes.length) {
13670             nodes= other_nodes;
13671         }
13672         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13673             return false;
13674         }
13675         
13676         return nodes[0];
13677     },
13678     createRange: function(sel)
13679     {
13680         // this has strange effects when using with 
13681         // top toolbar - not sure if it's a great idea.
13682         //this.editor.contentWindow.focus();
13683         if (typeof sel != "undefined") {
13684             try {
13685                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13686             } catch(e) {
13687                 return this.doc.createRange();
13688             }
13689         } else {
13690             return this.doc.createRange();
13691         }
13692     },
13693     getParentElement: function()
13694     {
13695         
13696         this.assignDocWin();
13697         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13698         
13699         var range = this.createRange(sel);
13700          
13701         try {
13702             var p = range.commonAncestorContainer;
13703             while (p.nodeType == 3) { // text node
13704                 p = p.parentNode;
13705             }
13706             return p;
13707         } catch (e) {
13708             return null;
13709         }
13710     
13711     },
13712     /***
13713      *
13714      * Range intersection.. the hard stuff...
13715      *  '-1' = before
13716      *  '0' = hits..
13717      *  '1' = after.
13718      *         [ -- selected range --- ]
13719      *   [fail]                        [fail]
13720      *
13721      *    basically..
13722      *      if end is before start or  hits it. fail.
13723      *      if start is after end or hits it fail.
13724      *
13725      *   if either hits (but other is outside. - then it's not 
13726      *   
13727      *    
13728      **/
13729     
13730     
13731     // @see http://www.thismuchiknow.co.uk/?p=64.
13732     rangeIntersectsNode : function(range, node)
13733     {
13734         var nodeRange = node.ownerDocument.createRange();
13735         try {
13736             nodeRange.selectNode(node);
13737         } catch (e) {
13738             nodeRange.selectNodeContents(node);
13739         }
13740     
13741         var rangeStartRange = range.cloneRange();
13742         rangeStartRange.collapse(true);
13743     
13744         var rangeEndRange = range.cloneRange();
13745         rangeEndRange.collapse(false);
13746     
13747         var nodeStartRange = nodeRange.cloneRange();
13748         nodeStartRange.collapse(true);
13749     
13750         var nodeEndRange = nodeRange.cloneRange();
13751         nodeEndRange.collapse(false);
13752     
13753         return rangeStartRange.compareBoundaryPoints(
13754                  Range.START_TO_START, nodeEndRange) == -1 &&
13755                rangeEndRange.compareBoundaryPoints(
13756                  Range.START_TO_START, nodeStartRange) == 1;
13757         
13758          
13759     },
13760     rangeCompareNode : function(range, node)
13761     {
13762         var nodeRange = node.ownerDocument.createRange();
13763         try {
13764             nodeRange.selectNode(node);
13765         } catch (e) {
13766             nodeRange.selectNodeContents(node);
13767         }
13768         
13769         
13770         range.collapse(true);
13771     
13772         nodeRange.collapse(true);
13773      
13774         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13775         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
13776          
13777         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13778         
13779         var nodeIsBefore   =  ss == 1;
13780         var nodeIsAfter    = ee == -1;
13781         
13782         if (nodeIsBefore && nodeIsAfter)
13783             return 0; // outer
13784         if (!nodeIsBefore && nodeIsAfter)
13785             return 1; //right trailed.
13786         
13787         if (nodeIsBefore && !nodeIsAfter)
13788             return 2;  // left trailed.
13789         // fully contined.
13790         return 3;
13791     },
13792
13793     // private? - in a new class?
13794     cleanUpPaste :  function()
13795     {
13796         // cleans up the whole document..
13797         Roo.log('cleanuppaste');
13798         
13799         this.cleanUpChildren(this.doc.body);
13800         var clean = this.cleanWordChars(this.doc.body.innerHTML);
13801         if (clean != this.doc.body.innerHTML) {
13802             this.doc.body.innerHTML = clean;
13803         }
13804         
13805     },
13806     
13807     cleanWordChars : function(input) {// change the chars to hex code
13808         var he = Roo.HtmlEditorCore;
13809         
13810         var output = input;
13811         Roo.each(he.swapCodes, function(sw) { 
13812             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13813             
13814             output = output.replace(swapper, sw[1]);
13815         });
13816         
13817         return output;
13818     },
13819     
13820     
13821     cleanUpChildren : function (n)
13822     {
13823         if (!n.childNodes.length) {
13824             return;
13825         }
13826         for (var i = n.childNodes.length-1; i > -1 ; i--) {
13827            this.cleanUpChild(n.childNodes[i]);
13828         }
13829     },
13830     
13831     
13832         
13833     
13834     cleanUpChild : function (node)
13835     {
13836         var ed = this;
13837         //console.log(node);
13838         if (node.nodeName == "#text") {
13839             // clean up silly Windows -- stuff?
13840             return; 
13841         }
13842         if (node.nodeName == "#comment") {
13843             node.parentNode.removeChild(node);
13844             // clean up silly Windows -- stuff?
13845             return; 
13846         }
13847         
13848         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13849             // remove node.
13850             node.parentNode.removeChild(node);
13851             return;
13852             
13853         }
13854         
13855         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13856         
13857         // remove <a name=....> as rendering on yahoo mailer is borked with this.
13858         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13859         
13860         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13861         //    remove_keep_children = true;
13862         //}
13863         
13864         if (remove_keep_children) {
13865             this.cleanUpChildren(node);
13866             // inserts everything just before this node...
13867             while (node.childNodes.length) {
13868                 var cn = node.childNodes[0];
13869                 node.removeChild(cn);
13870                 node.parentNode.insertBefore(cn, node);
13871             }
13872             node.parentNode.removeChild(node);
13873             return;
13874         }
13875         
13876         if (!node.attributes || !node.attributes.length) {
13877             this.cleanUpChildren(node);
13878             return;
13879         }
13880         
13881         function cleanAttr(n,v)
13882         {
13883             
13884             if (v.match(/^\./) || v.match(/^\//)) {
13885                 return;
13886             }
13887             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13888                 return;
13889             }
13890             if (v.match(/^#/)) {
13891                 return;
13892             }
13893 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13894             node.removeAttribute(n);
13895             
13896         }
13897         
13898         function cleanStyle(n,v)
13899         {
13900             if (v.match(/expression/)) { //XSS?? should we even bother..
13901                 node.removeAttribute(n);
13902                 return;
13903             }
13904             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13905             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13906             
13907             
13908             var parts = v.split(/;/);
13909             var clean = [];
13910             
13911             Roo.each(parts, function(p) {
13912                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13913                 if (!p.length) {
13914                     return true;
13915                 }
13916                 var l = p.split(':').shift().replace(/\s+/g,'');
13917                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13918                 
13919                 if ( cblack.indexOf(l) > -1) {
13920 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13921                     //node.removeAttribute(n);
13922                     return true;
13923                 }
13924                 //Roo.log()
13925                 // only allow 'c whitelisted system attributes'
13926                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
13927 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13928                     //node.removeAttribute(n);
13929                     return true;
13930                 }
13931                 
13932                 
13933                  
13934                 
13935                 clean.push(p);
13936                 return true;
13937             });
13938             if (clean.length) { 
13939                 node.setAttribute(n, clean.join(';'));
13940             } else {
13941                 node.removeAttribute(n);
13942             }
13943             
13944         }
13945         
13946         
13947         for (var i = node.attributes.length-1; i > -1 ; i--) {
13948             var a = node.attributes[i];
13949             //console.log(a);
13950             
13951             if (a.name.toLowerCase().substr(0,2)=='on')  {
13952                 node.removeAttribute(a.name);
13953                 continue;
13954             }
13955             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13956                 node.removeAttribute(a.name);
13957                 continue;
13958             }
13959             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13960                 cleanAttr(a.name,a.value); // fixme..
13961                 continue;
13962             }
13963             if (a.name == 'style') {
13964                 cleanStyle(a.name,a.value);
13965                 continue;
13966             }
13967             /// clean up MS crap..
13968             // tecnically this should be a list of valid class'es..
13969             
13970             
13971             if (a.name == 'class') {
13972                 if (a.value.match(/^Mso/)) {
13973                     node.className = '';
13974                 }
13975                 
13976                 if (a.value.match(/body/)) {
13977                     node.className = '';
13978                 }
13979                 continue;
13980             }
13981             
13982             // style cleanup!?
13983             // class cleanup?
13984             
13985         }
13986         
13987         
13988         this.cleanUpChildren(node);
13989         
13990         
13991     }
13992     
13993     
13994     // hide stuff that is not compatible
13995     /**
13996      * @event blur
13997      * @hide
13998      */
13999     /**
14000      * @event change
14001      * @hide
14002      */
14003     /**
14004      * @event focus
14005      * @hide
14006      */
14007     /**
14008      * @event specialkey
14009      * @hide
14010      */
14011     /**
14012      * @cfg {String} fieldClass @hide
14013      */
14014     /**
14015      * @cfg {String} focusClass @hide
14016      */
14017     /**
14018      * @cfg {String} autoCreate @hide
14019      */
14020     /**
14021      * @cfg {String} inputType @hide
14022      */
14023     /**
14024      * @cfg {String} invalidClass @hide
14025      */
14026     /**
14027      * @cfg {String} invalidText @hide
14028      */
14029     /**
14030      * @cfg {String} msgFx @hide
14031      */
14032     /**
14033      * @cfg {String} validateOnBlur @hide
14034      */
14035 });
14036
14037 Roo.HtmlEditorCore.white = [
14038         'area', 'br', 'img', 'input', 'hr', 'wbr',
14039         
14040        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
14041        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
14042        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
14043        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
14044        'table',   'ul',         'xmp', 
14045        
14046        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
14047       'thead',   'tr', 
14048      
14049       'dir', 'menu', 'ol', 'ul', 'dl',
14050        
14051       'embed',  'object'
14052 ];
14053
14054
14055 Roo.HtmlEditorCore.black = [
14056     //    'embed',  'object', // enable - backend responsiblity to clean thiese
14057         'applet', // 
14058         'base',   'basefont', 'bgsound', 'blink',  'body', 
14059         'frame',  'frameset', 'head',    'html',   'ilayer', 
14060         'iframe', 'layer',  'link',     'meta',    'object',   
14061         'script', 'style' ,'title',  'xml' // clean later..
14062 ];
14063 Roo.HtmlEditorCore.clean = [
14064     'script', 'style', 'title', 'xml'
14065 ];
14066 Roo.HtmlEditorCore.remove = [
14067     'font'
14068 ];
14069 // attributes..
14070
14071 Roo.HtmlEditorCore.ablack = [
14072     'on'
14073 ];
14074     
14075 Roo.HtmlEditorCore.aclean = [ 
14076     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
14077 ];
14078
14079 // protocols..
14080 Roo.HtmlEditorCore.pwhite= [
14081         'http',  'https',  'mailto'
14082 ];
14083
14084 // white listed style attributes.
14085 Roo.HtmlEditorCore.cwhite= [
14086       //  'text-align', /// default is to allow most things..
14087       
14088          
14089 //        'font-size'//??
14090 ];
14091
14092 // black listed style attributes.
14093 Roo.HtmlEditorCore.cblack= [
14094       //  'font-size' -- this can be set by the project 
14095 ];
14096
14097
14098 Roo.HtmlEditorCore.swapCodes   =[ 
14099     [    8211, "--" ], 
14100     [    8212, "--" ], 
14101     [    8216,  "'" ],  
14102     [    8217, "'" ],  
14103     [    8220, '"' ],  
14104     [    8221, '"' ],  
14105     [    8226, "*" ],  
14106     [    8230, "..." ]
14107 ]; 
14108
14109     /*
14110  * - LGPL
14111  *
14112  * HtmlEditor
14113  * 
14114  */
14115
14116 /**
14117  * @class Roo.bootstrap.HtmlEditor
14118  * @extends Roo.bootstrap.TextArea
14119  * Bootstrap HtmlEditor class
14120
14121  * @constructor
14122  * Create a new HtmlEditor
14123  * @param {Object} config The config object
14124  */
14125
14126 Roo.bootstrap.HtmlEditor = function(config){
14127     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14128     if (!this.toolbars) {
14129         this.toolbars = [];
14130     }
14131     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14132     this.addEvents({
14133             /**
14134              * @event initialize
14135              * Fires when the editor is fully initialized (including the iframe)
14136              * @param {HtmlEditor} this
14137              */
14138             initialize: true,
14139             /**
14140              * @event activate
14141              * Fires when the editor is first receives the focus. Any insertion must wait
14142              * until after this event.
14143              * @param {HtmlEditor} this
14144              */
14145             activate: true,
14146              /**
14147              * @event beforesync
14148              * Fires before the textarea is updated with content from the editor iframe. Return false
14149              * to cancel the sync.
14150              * @param {HtmlEditor} this
14151              * @param {String} html
14152              */
14153             beforesync: true,
14154              /**
14155              * @event beforepush
14156              * Fires before the iframe editor is updated with content from the textarea. Return false
14157              * to cancel the push.
14158              * @param {HtmlEditor} this
14159              * @param {String} html
14160              */
14161             beforepush: true,
14162              /**
14163              * @event sync
14164              * Fires when the textarea is updated with content from the editor iframe.
14165              * @param {HtmlEditor} this
14166              * @param {String} html
14167              */
14168             sync: true,
14169              /**
14170              * @event push
14171              * Fires when the iframe editor is updated with content from the textarea.
14172              * @param {HtmlEditor} this
14173              * @param {String} html
14174              */
14175             push: true,
14176              /**
14177              * @event editmodechange
14178              * Fires when the editor switches edit modes
14179              * @param {HtmlEditor} this
14180              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14181              */
14182             editmodechange: true,
14183             /**
14184              * @event editorevent
14185              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14186              * @param {HtmlEditor} this
14187              */
14188             editorevent: true,
14189             /**
14190              * @event firstfocus
14191              * Fires when on first focus - needed by toolbars..
14192              * @param {HtmlEditor} this
14193              */
14194             firstfocus: true,
14195             /**
14196              * @event autosave
14197              * Auto save the htmlEditor value as a file into Events
14198              * @param {HtmlEditor} this
14199              */
14200             autosave: true,
14201             /**
14202              * @event savedpreview
14203              * preview the saved version of htmlEditor
14204              * @param {HtmlEditor} this
14205              */
14206             savedpreview: true
14207         });
14208 };
14209
14210
14211 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
14212     
14213     
14214       /**
14215      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14216      */
14217     toolbars : false,
14218    
14219      /**
14220      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14221      *                        Roo.resizable.
14222      */
14223     resizable : false,
14224      /**
14225      * @cfg {Number} height (in pixels)
14226      */   
14227     height: 300,
14228    /**
14229      * @cfg {Number} width (in pixels)
14230      */   
14231     width: false,
14232     
14233     /**
14234      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14235      * 
14236      */
14237     stylesheets: false,
14238     
14239     // id of frame..
14240     frameId: false,
14241     
14242     // private properties
14243     validationEvent : false,
14244     deferHeight: true,
14245     initialized : false,
14246     activated : false,
14247     
14248     onFocus : Roo.emptyFn,
14249     iframePad:3,
14250     hideMode:'offsets',
14251     
14252     
14253     tbContainer : false,
14254     
14255     toolbarContainer :function() {
14256         return this.wrap.select('.x-html-editor-tb',true).first();
14257     },
14258
14259     /**
14260      * Protected method that will not generally be called directly. It
14261      * is called when the editor creates its toolbar. Override this method if you need to
14262      * add custom toolbar buttons.
14263      * @param {HtmlEditor} editor
14264      */
14265     createToolbar : function(){
14266         
14267         Roo.log("create toolbars");
14268         
14269         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14270         this.toolbars[0].render(this.toolbarContainer());
14271         
14272         return;
14273         
14274 //        if (!editor.toolbars || !editor.toolbars.length) {
14275 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14276 //        }
14277 //        
14278 //        for (var i =0 ; i < editor.toolbars.length;i++) {
14279 //            editor.toolbars[i] = Roo.factory(
14280 //                    typeof(editor.toolbars[i]) == 'string' ?
14281 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
14282 //                Roo.bootstrap.HtmlEditor);
14283 //            editor.toolbars[i].init(editor);
14284 //        }
14285     },
14286
14287      
14288     // private
14289     onRender : function(ct, position)
14290     {
14291        // Roo.log("Call onRender: " + this.xtype);
14292         var _t = this;
14293         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14294       
14295         this.wrap = this.inputEl().wrap({
14296             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14297         });
14298         
14299         this.editorcore.onRender(ct, position);
14300          
14301         if (this.resizable) {
14302             this.resizeEl = new Roo.Resizable(this.wrap, {
14303                 pinned : true,
14304                 wrap: true,
14305                 dynamic : true,
14306                 minHeight : this.height,
14307                 height: this.height,
14308                 handles : this.resizable,
14309                 width: this.width,
14310                 listeners : {
14311                     resize : function(r, w, h) {
14312                         _t.onResize(w,h); // -something
14313                     }
14314                 }
14315             });
14316             
14317         }
14318         this.createToolbar(this);
14319        
14320         
14321         if(!this.width && this.resizable){
14322             this.setSize(this.wrap.getSize());
14323         }
14324         if (this.resizeEl) {
14325             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14326             // should trigger onReize..
14327         }
14328         
14329     },
14330
14331     // private
14332     onResize : function(w, h)
14333     {
14334         Roo.log('resize: ' +w + ',' + h );
14335         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14336         var ew = false;
14337         var eh = false;
14338         
14339         if(this.inputEl() ){
14340             if(typeof w == 'number'){
14341                 var aw = w - this.wrap.getFrameWidth('lr');
14342                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14343                 ew = aw;
14344             }
14345             if(typeof h == 'number'){
14346                  var tbh = -11;  // fixme it needs to tool bar size!
14347                 for (var i =0; i < this.toolbars.length;i++) {
14348                     // fixme - ask toolbars for heights?
14349                     tbh += this.toolbars[i].el.getHeight();
14350                     //if (this.toolbars[i].footer) {
14351                     //    tbh += this.toolbars[i].footer.el.getHeight();
14352                     //}
14353                 }
14354               
14355                 
14356                 
14357                 
14358                 
14359                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14360                 ah -= 5; // knock a few pixes off for look..
14361                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14362                 var eh = ah;
14363             }
14364         }
14365         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14366         this.editorcore.onResize(ew,eh);
14367         
14368     },
14369
14370     /**
14371      * Toggles the editor between standard and source edit mode.
14372      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14373      */
14374     toggleSourceEdit : function(sourceEditMode)
14375     {
14376         this.editorcore.toggleSourceEdit(sourceEditMode);
14377         
14378         if(this.editorcore.sourceEditMode){
14379             Roo.log('editor - showing textarea');
14380             
14381 //            Roo.log('in');
14382 //            Roo.log(this.syncValue());
14383             this.syncValue();
14384             this.inputEl().removeClass('hide');
14385             this.inputEl().dom.removeAttribute('tabIndex');
14386             this.inputEl().focus();
14387         }else{
14388             Roo.log('editor - hiding textarea');
14389 //            Roo.log('out')
14390 //            Roo.log(this.pushValue()); 
14391             this.pushValue();
14392             
14393             this.inputEl().addClass('hide');
14394             this.inputEl().dom.setAttribute('tabIndex', -1);
14395             //this.deferFocus();
14396         }
14397          
14398         if(this.resizable){
14399             this.setSize(this.wrap.getSize());
14400         }
14401         
14402         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14403     },
14404  
14405     // private (for BoxComponent)
14406     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14407
14408     // private (for BoxComponent)
14409     getResizeEl : function(){
14410         return this.wrap;
14411     },
14412
14413     // private (for BoxComponent)
14414     getPositionEl : function(){
14415         return this.wrap;
14416     },
14417
14418     // private
14419     initEvents : function(){
14420         this.originalValue = this.getValue();
14421     },
14422
14423 //    /**
14424 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14425 //     * @method
14426 //     */
14427 //    markInvalid : Roo.emptyFn,
14428 //    /**
14429 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14430 //     * @method
14431 //     */
14432 //    clearInvalid : Roo.emptyFn,
14433
14434     setValue : function(v){
14435         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14436         this.editorcore.pushValue();
14437     },
14438
14439      
14440     // private
14441     deferFocus : function(){
14442         this.focus.defer(10, this);
14443     },
14444
14445     // doc'ed in Field
14446     focus : function(){
14447         this.editorcore.focus();
14448         
14449     },
14450       
14451
14452     // private
14453     onDestroy : function(){
14454         
14455         
14456         
14457         if(this.rendered){
14458             
14459             for (var i =0; i < this.toolbars.length;i++) {
14460                 // fixme - ask toolbars for heights?
14461                 this.toolbars[i].onDestroy();
14462             }
14463             
14464             this.wrap.dom.innerHTML = '';
14465             this.wrap.remove();
14466         }
14467     },
14468
14469     // private
14470     onFirstFocus : function(){
14471         //Roo.log("onFirstFocus");
14472         this.editorcore.onFirstFocus();
14473          for (var i =0; i < this.toolbars.length;i++) {
14474             this.toolbars[i].onFirstFocus();
14475         }
14476         
14477     },
14478     
14479     // private
14480     syncValue : function()
14481     {   
14482         this.editorcore.syncValue();
14483     },
14484     
14485     pushValue : function()
14486     {   
14487         this.editorcore.pushValue();
14488     }
14489      
14490     
14491     // hide stuff that is not compatible
14492     /**
14493      * @event blur
14494      * @hide
14495      */
14496     /**
14497      * @event change
14498      * @hide
14499      */
14500     /**
14501      * @event focus
14502      * @hide
14503      */
14504     /**
14505      * @event specialkey
14506      * @hide
14507      */
14508     /**
14509      * @cfg {String} fieldClass @hide
14510      */
14511     /**
14512      * @cfg {String} focusClass @hide
14513      */
14514     /**
14515      * @cfg {String} autoCreate @hide
14516      */
14517     /**
14518      * @cfg {String} inputType @hide
14519      */
14520     /**
14521      * @cfg {String} invalidClass @hide
14522      */
14523     /**
14524      * @cfg {String} invalidText @hide
14525      */
14526     /**
14527      * @cfg {String} msgFx @hide
14528      */
14529     /**
14530      * @cfg {String} validateOnBlur @hide
14531      */
14532 });
14533  
14534     
14535    
14536    
14537    
14538       
14539
14540 /**
14541  * @class Roo.bootstrap.HtmlEditorToolbar1
14542  * Basic Toolbar
14543  * 
14544  * Usage:
14545  *
14546  new Roo.bootstrap.HtmlEditor({
14547     ....
14548     toolbars : [
14549         new Roo.bootstrap.HtmlEditorToolbar1({
14550             disable : { fonts: 1 , format: 1, ..., ... , ...],
14551             btns : [ .... ]
14552         })
14553     }
14554      
14555  * 
14556  * @cfg {Object} disable List of elements to disable..
14557  * @cfg {Array} btns List of additional buttons.
14558  * 
14559  * 
14560  * NEEDS Extra CSS? 
14561  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14562  */
14563  
14564 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14565 {
14566     
14567     Roo.apply(this, config);
14568     
14569     // default disabled, based on 'good practice'..
14570     this.disable = this.disable || {};
14571     Roo.applyIf(this.disable, {
14572         fontSize : true,
14573         colors : true,
14574         specialElements : true
14575     });
14576     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14577     
14578     this.editor = config.editor;
14579     this.editorcore = config.editor.editorcore;
14580     
14581     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14582     
14583     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14584     // dont call parent... till later.
14585 }
14586 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
14587     
14588     
14589     bar : true,
14590     
14591     editor : false,
14592     editorcore : false,
14593     
14594     
14595     formats : [
14596         "p" ,  
14597         "h1","h2","h3","h4","h5","h6", 
14598         "pre", "code", 
14599         "abbr", "acronym", "address", "cite", "samp", "var",
14600         'div','span'
14601     ],
14602     
14603     onRender : function(ct, position)
14604     {
14605        // Roo.log("Call onRender: " + this.xtype);
14606         
14607        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14608        Roo.log(this.el);
14609        this.el.dom.style.marginBottom = '0';
14610        var _this = this;
14611        var editorcore = this.editorcore;
14612        var editor= this.editor;
14613        
14614        var children = [];
14615        var btn = function(id,cmd , toggle, handler){
14616        
14617             var  event = toggle ? 'toggle' : 'click';
14618        
14619             var a = {
14620                 size : 'sm',
14621                 xtype: 'Button',
14622                 xns: Roo.bootstrap,
14623                 glyphicon : id,
14624                 cmd : id || cmd,
14625                 enableToggle:toggle !== false,
14626                 //html : 'submit'
14627                 pressed : toggle ? false : null,
14628                 listeners : {}
14629             }
14630             a.listeners[toggle ? 'toggle' : 'click'] = function() {
14631                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
14632             }
14633             children.push(a);
14634             return a;
14635        }
14636         
14637         var style = {
14638                 xtype: 'Button',
14639                 size : 'sm',
14640                 xns: Roo.bootstrap,
14641                 glyphicon : 'font',
14642                 //html : 'submit'
14643                 menu : {
14644                     xtype: 'Menu',
14645                     xns: Roo.bootstrap,
14646                     items:  []
14647                 }
14648         };
14649         Roo.each(this.formats, function(f) {
14650             style.menu.items.push({
14651                 xtype :'MenuItem',
14652                 xns: Roo.bootstrap,
14653                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14654                 tagname : f,
14655                 listeners : {
14656                     click : function()
14657                     {
14658                         editorcore.insertTag(this.tagname);
14659                         editor.focus();
14660                     }
14661                 }
14662                 
14663             });
14664         });
14665          children.push(style);   
14666             
14667             
14668         btn('bold',false,true);
14669         btn('italic',false,true);
14670         btn('align-left', 'justifyleft',true);
14671         btn('align-center', 'justifycenter',true);
14672         btn('align-right' , 'justifyright',true);
14673         btn('link', false, false, function(btn) {
14674             //Roo.log("create link?");
14675             var url = prompt(this.createLinkText, this.defaultLinkValue);
14676             if(url && url != 'http:/'+'/'){
14677                 this.editorcore.relayCmd('createlink', url);
14678             }
14679         }),
14680         btn('list','insertunorderedlist',true);
14681         btn('pencil', false,true, function(btn){
14682                 Roo.log(this);
14683                 
14684                 this.toggleSourceEdit(btn.pressed);
14685         });
14686         /*
14687         var cog = {
14688                 xtype: 'Button',
14689                 size : 'sm',
14690                 xns: Roo.bootstrap,
14691                 glyphicon : 'cog',
14692                 //html : 'submit'
14693                 menu : {
14694                     xtype: 'Menu',
14695                     xns: Roo.bootstrap,
14696                     items:  []
14697                 }
14698         };
14699         
14700         cog.menu.items.push({
14701             xtype :'MenuItem',
14702             xns: Roo.bootstrap,
14703             html : Clean styles,
14704             tagname : f,
14705             listeners : {
14706                 click : function()
14707                 {
14708                     editorcore.insertTag(this.tagname);
14709                     editor.focus();
14710                 }
14711             }
14712             
14713         });
14714        */
14715         
14716          
14717        this.xtype = 'Navbar';
14718         
14719         for(var i=0;i< children.length;i++) {
14720             
14721             this.buttons.add(this.addxtypeChild(children[i]));
14722             
14723         }
14724         
14725         editor.on('editorevent', this.updateToolbar, this);
14726     },
14727     onBtnClick : function(id)
14728     {
14729        this.editorcore.relayCmd(id);
14730        this.editorcore.focus();
14731     },
14732     
14733     /**
14734      * Protected method that will not generally be called directly. It triggers
14735      * a toolbar update by reading the markup state of the current selection in the editor.
14736      */
14737     updateToolbar: function(){
14738
14739         if(!this.editorcore.activated){
14740             this.editor.onFirstFocus(); // is this neeed?
14741             return;
14742         }
14743
14744         var btns = this.buttons; 
14745         var doc = this.editorcore.doc;
14746         btns.get('bold').setActive(doc.queryCommandState('bold'));
14747         btns.get('italic').setActive(doc.queryCommandState('italic'));
14748         //btns.get('underline').setActive(doc.queryCommandState('underline'));
14749         
14750         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14751         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14752         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14753         
14754         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14755         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14756          /*
14757         
14758         var ans = this.editorcore.getAllAncestors();
14759         if (this.formatCombo) {
14760             
14761             
14762             var store = this.formatCombo.store;
14763             this.formatCombo.setValue("");
14764             for (var i =0; i < ans.length;i++) {
14765                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14766                     // select it..
14767                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14768                     break;
14769                 }
14770             }
14771         }
14772         
14773         
14774         
14775         // hides menus... - so this cant be on a menu...
14776         Roo.bootstrap.MenuMgr.hideAll();
14777         */
14778         Roo.bootstrap.MenuMgr.hideAll();
14779         //this.editorsyncValue();
14780     },
14781     onFirstFocus: function() {
14782         this.buttons.each(function(item){
14783            item.enable();
14784         });
14785     },
14786     toggleSourceEdit : function(sourceEditMode){
14787         
14788           
14789         if(sourceEditMode){
14790             Roo.log("disabling buttons");
14791            this.buttons.each( function(item){
14792                 if(item.cmd != 'pencil'){
14793                     item.disable();
14794                 }
14795             });
14796           
14797         }else{
14798             Roo.log("enabling buttons");
14799             if(this.editorcore.initialized){
14800                 this.buttons.each( function(item){
14801                     item.enable();
14802                 });
14803             }
14804             
14805         }
14806         Roo.log("calling toggole on editor");
14807         // tell the editor that it's been pressed..
14808         this.editor.toggleSourceEdit(sourceEditMode);
14809        
14810     }
14811 });
14812
14813
14814
14815
14816
14817 /**
14818  * @class Roo.bootstrap.Table.AbstractSelectionModel
14819  * @extends Roo.util.Observable
14820  * Abstract base class for grid SelectionModels.  It provides the interface that should be
14821  * implemented by descendant classes.  This class should not be directly instantiated.
14822  * @constructor
14823  */
14824 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14825     this.locked = false;
14826     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14827 };
14828
14829
14830 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
14831     /** @ignore Called by the grid automatically. Do not call directly. */
14832     init : function(grid){
14833         this.grid = grid;
14834         this.initEvents();
14835     },
14836
14837     /**
14838      * Locks the selections.
14839      */
14840     lock : function(){
14841         this.locked = true;
14842     },
14843
14844     /**
14845      * Unlocks the selections.
14846      */
14847     unlock : function(){
14848         this.locked = false;
14849     },
14850
14851     /**
14852      * Returns true if the selections are locked.
14853      * @return {Boolean}
14854      */
14855     isLocked : function(){
14856         return this.locked;
14857     }
14858 });
14859 /**
14860  * @class Roo.bootstrap.Table.ColumnModel
14861  * @extends Roo.util.Observable
14862  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14863  * the columns in the table.
14864  
14865  * @constructor
14866  * @param {Object} config An Array of column config objects. See this class's
14867  * config objects for details.
14868 */
14869 Roo.bootstrap.Table.ColumnModel = function(config){
14870         /**
14871      * The config passed into the constructor
14872      */
14873     this.config = config;
14874     this.lookup = {};
14875
14876     // if no id, create one
14877     // if the column does not have a dataIndex mapping,
14878     // map it to the order it is in the config
14879     for(var i = 0, len = config.length; i < len; i++){
14880         var c = config[i];
14881         if(typeof c.dataIndex == "undefined"){
14882             c.dataIndex = i;
14883         }
14884         if(typeof c.renderer == "string"){
14885             c.renderer = Roo.util.Format[c.renderer];
14886         }
14887         if(typeof c.id == "undefined"){
14888             c.id = Roo.id();
14889         }
14890 //        if(c.editor && c.editor.xtype){
14891 //            c.editor  = Roo.factory(c.editor, Roo.grid);
14892 //        }
14893 //        if(c.editor && c.editor.isFormField){
14894 //            c.editor = new Roo.grid.GridEditor(c.editor);
14895 //        }
14896
14897         this.lookup[c.id] = c;
14898     }
14899
14900     /**
14901      * The width of columns which have no width specified (defaults to 100)
14902      * @type Number
14903      */
14904     this.defaultWidth = 100;
14905
14906     /**
14907      * Default sortable of columns which have no sortable specified (defaults to false)
14908      * @type Boolean
14909      */
14910     this.defaultSortable = false;
14911
14912     this.addEvents({
14913         /**
14914              * @event widthchange
14915              * Fires when the width of a column changes.
14916              * @param {ColumnModel} this
14917              * @param {Number} columnIndex The column index
14918              * @param {Number} newWidth The new width
14919              */
14920             "widthchange": true,
14921         /**
14922              * @event headerchange
14923              * Fires when the text of a header changes.
14924              * @param {ColumnModel} this
14925              * @param {Number} columnIndex The column index
14926              * @param {Number} newText The new header text
14927              */
14928             "headerchange": true,
14929         /**
14930              * @event hiddenchange
14931              * Fires when a column is hidden or "unhidden".
14932              * @param {ColumnModel} this
14933              * @param {Number} columnIndex The column index
14934              * @param {Boolean} hidden true if hidden, false otherwise
14935              */
14936             "hiddenchange": true,
14937             /**
14938          * @event columnmoved
14939          * Fires when a column is moved.
14940          * @param {ColumnModel} this
14941          * @param {Number} oldIndex
14942          * @param {Number} newIndex
14943          */
14944         "columnmoved" : true,
14945         /**
14946          * @event columlockchange
14947          * Fires when a column's locked state is changed
14948          * @param {ColumnModel} this
14949          * @param {Number} colIndex
14950          * @param {Boolean} locked true if locked
14951          */
14952         "columnlockchange" : true
14953     });
14954     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14955 };
14956 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14957     /**
14958      * @cfg {String} header The header text to display in the Grid view.
14959      */
14960     /**
14961      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14962      * {@link Roo.data.Record} definition from which to draw the column's value. If not
14963      * specified, the column's index is used as an index into the Record's data Array.
14964      */
14965     /**
14966      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14967      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14968      */
14969     /**
14970      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14971      * Defaults to the value of the {@link #defaultSortable} property.
14972      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14973      */
14974     /**
14975      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
14976      */
14977     /**
14978      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
14979      */
14980     /**
14981      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14982      */
14983     /**
14984      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14985      */
14986     /**
14987      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14988      * given the cell's data value. See {@link #setRenderer}. If not specified, the
14989      * default renderer uses the raw data value.
14990      */
14991     /**
14992      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
14993      */
14994
14995     /**
14996      * Returns the id of the column at the specified index.
14997      * @param {Number} index The column index
14998      * @return {String} the id
14999      */
15000     getColumnId : function(index){
15001         return this.config[index].id;
15002     },
15003
15004     /**
15005      * Returns the column for a specified id.
15006      * @param {String} id The column id
15007      * @return {Object} the column
15008      */
15009     getColumnById : function(id){
15010         return this.lookup[id];
15011     },
15012
15013     
15014     /**
15015      * Returns the column for a specified dataIndex.
15016      * @param {String} dataIndex The column dataIndex
15017      * @return {Object|Boolean} the column or false if not found
15018      */
15019     getColumnByDataIndex: function(dataIndex){
15020         var index = this.findColumnIndex(dataIndex);
15021         return index > -1 ? this.config[index] : false;
15022     },
15023     
15024     /**
15025      * Returns the index for a specified column id.
15026      * @param {String} id The column id
15027      * @return {Number} the index, or -1 if not found
15028      */
15029     getIndexById : function(id){
15030         for(var i = 0, len = this.config.length; i < len; i++){
15031             if(this.config[i].id == id){
15032                 return i;
15033             }
15034         }
15035         return -1;
15036     },
15037     
15038     /**
15039      * Returns the index for a specified column dataIndex.
15040      * @param {String} dataIndex The column dataIndex
15041      * @return {Number} the index, or -1 if not found
15042      */
15043     
15044     findColumnIndex : function(dataIndex){
15045         for(var i = 0, len = this.config.length; i < len; i++){
15046             if(this.config[i].dataIndex == dataIndex){
15047                 return i;
15048             }
15049         }
15050         return -1;
15051     },
15052     
15053     
15054     moveColumn : function(oldIndex, newIndex){
15055         var c = this.config[oldIndex];
15056         this.config.splice(oldIndex, 1);
15057         this.config.splice(newIndex, 0, c);
15058         this.dataMap = null;
15059         this.fireEvent("columnmoved", this, oldIndex, newIndex);
15060     },
15061
15062     isLocked : function(colIndex){
15063         return this.config[colIndex].locked === true;
15064     },
15065
15066     setLocked : function(colIndex, value, suppressEvent){
15067         if(this.isLocked(colIndex) == value){
15068             return;
15069         }
15070         this.config[colIndex].locked = value;
15071         if(!suppressEvent){
15072             this.fireEvent("columnlockchange", this, colIndex, value);
15073         }
15074     },
15075
15076     getTotalLockedWidth : function(){
15077         var totalWidth = 0;
15078         for(var i = 0; i < this.config.length; i++){
15079             if(this.isLocked(i) && !this.isHidden(i)){
15080                 this.totalWidth += this.getColumnWidth(i);
15081             }
15082         }
15083         return totalWidth;
15084     },
15085
15086     getLockedCount : function(){
15087         for(var i = 0, len = this.config.length; i < len; i++){
15088             if(!this.isLocked(i)){
15089                 return i;
15090             }
15091         }
15092     },
15093
15094     /**
15095      * Returns the number of columns.
15096      * @return {Number}
15097      */
15098     getColumnCount : function(visibleOnly){
15099         if(visibleOnly === true){
15100             var c = 0;
15101             for(var i = 0, len = this.config.length; i < len; i++){
15102                 if(!this.isHidden(i)){
15103                     c++;
15104                 }
15105             }
15106             return c;
15107         }
15108         return this.config.length;
15109     },
15110
15111     /**
15112      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15113      * @param {Function} fn
15114      * @param {Object} scope (optional)
15115      * @return {Array} result
15116      */
15117     getColumnsBy : function(fn, scope){
15118         var r = [];
15119         for(var i = 0, len = this.config.length; i < len; i++){
15120             var c = this.config[i];
15121             if(fn.call(scope||this, c, i) === true){
15122                 r[r.length] = c;
15123             }
15124         }
15125         return r;
15126     },
15127
15128     /**
15129      * Returns true if the specified column is sortable.
15130      * @param {Number} col The column index
15131      * @return {Boolean}
15132      */
15133     isSortable : function(col){
15134         if(typeof this.config[col].sortable == "undefined"){
15135             return this.defaultSortable;
15136         }
15137         return this.config[col].sortable;
15138     },
15139
15140     /**
15141      * Returns the rendering (formatting) function defined for the column.
15142      * @param {Number} col The column index.
15143      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15144      */
15145     getRenderer : function(col){
15146         if(!this.config[col].renderer){
15147             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15148         }
15149         return this.config[col].renderer;
15150     },
15151
15152     /**
15153      * Sets the rendering (formatting) function for a column.
15154      * @param {Number} col The column index
15155      * @param {Function} fn The function to use to process the cell's raw data
15156      * to return HTML markup for the grid view. The render function is called with
15157      * the following parameters:<ul>
15158      * <li>Data value.</li>
15159      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15160      * <li>css A CSS style string to apply to the table cell.</li>
15161      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15162      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15163      * <li>Row index</li>
15164      * <li>Column index</li>
15165      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15166      */
15167     setRenderer : function(col, fn){
15168         this.config[col].renderer = fn;
15169     },
15170
15171     /**
15172      * Returns the width for the specified column.
15173      * @param {Number} col The column index
15174      * @return {Number}
15175      */
15176     getColumnWidth : function(col){
15177         return this.config[col].width * 1 || this.defaultWidth;
15178     },
15179
15180     /**
15181      * Sets the width for a column.
15182      * @param {Number} col The column index
15183      * @param {Number} width The new width
15184      */
15185     setColumnWidth : function(col, width, suppressEvent){
15186         this.config[col].width = width;
15187         this.totalWidth = null;
15188         if(!suppressEvent){
15189              this.fireEvent("widthchange", this, col, width);
15190         }
15191     },
15192
15193     /**
15194      * Returns the total width of all columns.
15195      * @param {Boolean} includeHidden True to include hidden column widths
15196      * @return {Number}
15197      */
15198     getTotalWidth : function(includeHidden){
15199         if(!this.totalWidth){
15200             this.totalWidth = 0;
15201             for(var i = 0, len = this.config.length; i < len; i++){
15202                 if(includeHidden || !this.isHidden(i)){
15203                     this.totalWidth += this.getColumnWidth(i);
15204                 }
15205             }
15206         }
15207         return this.totalWidth;
15208     },
15209
15210     /**
15211      * Returns the header for the specified column.
15212      * @param {Number} col The column index
15213      * @return {String}
15214      */
15215     getColumnHeader : function(col){
15216         return this.config[col].header;
15217     },
15218
15219     /**
15220      * Sets the header for a column.
15221      * @param {Number} col The column index
15222      * @param {String} header The new header
15223      */
15224     setColumnHeader : function(col, header){
15225         this.config[col].header = header;
15226         this.fireEvent("headerchange", this, col, header);
15227     },
15228
15229     /**
15230      * Returns the tooltip for the specified column.
15231      * @param {Number} col The column index
15232      * @return {String}
15233      */
15234     getColumnTooltip : function(col){
15235             return this.config[col].tooltip;
15236     },
15237     /**
15238      * Sets the tooltip for a column.
15239      * @param {Number} col The column index
15240      * @param {String} tooltip The new tooltip
15241      */
15242     setColumnTooltip : function(col, tooltip){
15243             this.config[col].tooltip = tooltip;
15244     },
15245
15246     /**
15247      * Returns the dataIndex for the specified column.
15248      * @param {Number} col The column index
15249      * @return {Number}
15250      */
15251     getDataIndex : function(col){
15252         return this.config[col].dataIndex;
15253     },
15254
15255     /**
15256      * Sets the dataIndex for a column.
15257      * @param {Number} col The column index
15258      * @param {Number} dataIndex The new dataIndex
15259      */
15260     setDataIndex : function(col, dataIndex){
15261         this.config[col].dataIndex = dataIndex;
15262     },
15263
15264     
15265     
15266     /**
15267      * Returns true if the cell is editable.
15268      * @param {Number} colIndex The column index
15269      * @param {Number} rowIndex The row index
15270      * @return {Boolean}
15271      */
15272     isCellEditable : function(colIndex, rowIndex){
15273         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15274     },
15275
15276     /**
15277      * Returns the editor defined for the cell/column.
15278      * return false or null to disable editing.
15279      * @param {Number} colIndex The column index
15280      * @param {Number} rowIndex The row index
15281      * @return {Object}
15282      */
15283     getCellEditor : function(colIndex, rowIndex){
15284         return this.config[colIndex].editor;
15285     },
15286
15287     /**
15288      * Sets if a column is editable.
15289      * @param {Number} col The column index
15290      * @param {Boolean} editable True if the column is editable
15291      */
15292     setEditable : function(col, editable){
15293         this.config[col].editable = editable;
15294     },
15295
15296
15297     /**
15298      * Returns true if the column is hidden.
15299      * @param {Number} colIndex The column index
15300      * @return {Boolean}
15301      */
15302     isHidden : function(colIndex){
15303         return this.config[colIndex].hidden;
15304     },
15305
15306
15307     /**
15308      * Returns true if the column width cannot be changed
15309      */
15310     isFixed : function(colIndex){
15311         return this.config[colIndex].fixed;
15312     },
15313
15314     /**
15315      * Returns true if the column can be resized
15316      * @return {Boolean}
15317      */
15318     isResizable : function(colIndex){
15319         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15320     },
15321     /**
15322      * Sets if a column is hidden.
15323      * @param {Number} colIndex The column index
15324      * @param {Boolean} hidden True if the column is hidden
15325      */
15326     setHidden : function(colIndex, hidden){
15327         this.config[colIndex].hidden = hidden;
15328         this.totalWidth = null;
15329         this.fireEvent("hiddenchange", this, colIndex, hidden);
15330     },
15331
15332     /**
15333      * Sets the editor for a column.
15334      * @param {Number} col The column index
15335      * @param {Object} editor The editor object
15336      */
15337     setEditor : function(col, editor){
15338         this.config[col].editor = editor;
15339     }
15340 });
15341
15342 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15343         if(typeof value == "string" && value.length < 1){
15344             return "&#160;";
15345         }
15346         return value;
15347 };
15348
15349 // Alias for backwards compatibility
15350 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15351
15352 /**
15353  * @extends Roo.bootstrap.Table.AbstractSelectionModel
15354  * @class Roo.bootstrap.Table.RowSelectionModel
15355  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15356  * It supports multiple selections and keyboard selection/navigation. 
15357  * @constructor
15358  * @param {Object} config
15359  */
15360
15361 Roo.bootstrap.Table.RowSelectionModel = function(config){
15362     Roo.apply(this, config);
15363     this.selections = new Roo.util.MixedCollection(false, function(o){
15364         return o.id;
15365     });
15366
15367     this.last = false;
15368     this.lastActive = false;
15369
15370     this.addEvents({
15371         /**
15372              * @event selectionchange
15373              * Fires when the selection changes
15374              * @param {SelectionModel} this
15375              */
15376             "selectionchange" : true,
15377         /**
15378              * @event afterselectionchange
15379              * Fires after the selection changes (eg. by key press or clicking)
15380              * @param {SelectionModel} this
15381              */
15382             "afterselectionchange" : true,
15383         /**
15384              * @event beforerowselect
15385              * Fires when a row is selected being selected, return false to cancel.
15386              * @param {SelectionModel} this
15387              * @param {Number} rowIndex The selected index
15388              * @param {Boolean} keepExisting False if other selections will be cleared
15389              */
15390             "beforerowselect" : true,
15391         /**
15392              * @event rowselect
15393              * Fires when a row is selected.
15394              * @param {SelectionModel} this
15395              * @param {Number} rowIndex The selected index
15396              * @param {Roo.data.Record} r The record
15397              */
15398             "rowselect" : true,
15399         /**
15400              * @event rowdeselect
15401              * Fires when a row is deselected.
15402              * @param {SelectionModel} this
15403              * @param {Number} rowIndex The selected index
15404              */
15405         "rowdeselect" : true
15406     });
15407     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15408     this.locked = false;
15409 };
15410
15411 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
15412     /**
15413      * @cfg {Boolean} singleSelect
15414      * True to allow selection of only one row at a time (defaults to false)
15415      */
15416     singleSelect : false,
15417
15418     // private
15419     initEvents : function(){
15420
15421         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15422             this.grid.on("mousedown", this.handleMouseDown, this);
15423         }else{ // allow click to work like normal
15424             this.grid.on("rowclick", this.handleDragableRowClick, this);
15425         }
15426
15427         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15428             "up" : function(e){
15429                 if(!e.shiftKey){
15430                     this.selectPrevious(e.shiftKey);
15431                 }else if(this.last !== false && this.lastActive !== false){
15432                     var last = this.last;
15433                     this.selectRange(this.last,  this.lastActive-1);
15434                     this.grid.getView().focusRow(this.lastActive);
15435                     if(last !== false){
15436                         this.last = last;
15437                     }
15438                 }else{
15439                     this.selectFirstRow();
15440                 }
15441                 this.fireEvent("afterselectionchange", this);
15442             },
15443             "down" : function(e){
15444                 if(!e.shiftKey){
15445                     this.selectNext(e.shiftKey);
15446                 }else if(this.last !== false && this.lastActive !== false){
15447                     var last = this.last;
15448                     this.selectRange(this.last,  this.lastActive+1);
15449                     this.grid.getView().focusRow(this.lastActive);
15450                     if(last !== false){
15451                         this.last = last;
15452                     }
15453                 }else{
15454                     this.selectFirstRow();
15455                 }
15456                 this.fireEvent("afterselectionchange", this);
15457             },
15458             scope: this
15459         });
15460
15461         var view = this.grid.view;
15462         view.on("refresh", this.onRefresh, this);
15463         view.on("rowupdated", this.onRowUpdated, this);
15464         view.on("rowremoved", this.onRemove, this);
15465     },
15466
15467     // private
15468     onRefresh : function(){
15469         var ds = this.grid.dataSource, i, v = this.grid.view;
15470         var s = this.selections;
15471         s.each(function(r){
15472             if((i = ds.indexOfId(r.id)) != -1){
15473                 v.onRowSelect(i);
15474             }else{
15475                 s.remove(r);
15476             }
15477         });
15478     },
15479
15480     // private
15481     onRemove : function(v, index, r){
15482         this.selections.remove(r);
15483     },
15484
15485     // private
15486     onRowUpdated : function(v, index, r){
15487         if(this.isSelected(r)){
15488             v.onRowSelect(index);
15489         }
15490     },
15491
15492     /**
15493      * Select records.
15494      * @param {Array} records The records to select
15495      * @param {Boolean} keepExisting (optional) True to keep existing selections
15496      */
15497     selectRecords : function(records, keepExisting){
15498         if(!keepExisting){
15499             this.clearSelections();
15500         }
15501         var ds = this.grid.dataSource;
15502         for(var i = 0, len = records.length; i < len; i++){
15503             this.selectRow(ds.indexOf(records[i]), true);
15504         }
15505     },
15506
15507     /**
15508      * Gets the number of selected rows.
15509      * @return {Number}
15510      */
15511     getCount : function(){
15512         return this.selections.length;
15513     },
15514
15515     /**
15516      * Selects the first row in the grid.
15517      */
15518     selectFirstRow : function(){
15519         this.selectRow(0);
15520     },
15521
15522     /**
15523      * Select the last row.
15524      * @param {Boolean} keepExisting (optional) True to keep existing selections
15525      */
15526     selectLastRow : function(keepExisting){
15527         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15528     },
15529
15530     /**
15531      * Selects the row immediately following the last selected row.
15532      * @param {Boolean} keepExisting (optional) True to keep existing selections
15533      */
15534     selectNext : function(keepExisting){
15535         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15536             this.selectRow(this.last+1, keepExisting);
15537             this.grid.getView().focusRow(this.last);
15538         }
15539     },
15540
15541     /**
15542      * Selects the row that precedes the last selected row.
15543      * @param {Boolean} keepExisting (optional) True to keep existing selections
15544      */
15545     selectPrevious : function(keepExisting){
15546         if(this.last){
15547             this.selectRow(this.last-1, keepExisting);
15548             this.grid.getView().focusRow(this.last);
15549         }
15550     },
15551
15552     /**
15553      * Returns the selected records
15554      * @return {Array} Array of selected records
15555      */
15556     getSelections : function(){
15557         return [].concat(this.selections.items);
15558     },
15559
15560     /**
15561      * Returns the first selected record.
15562      * @return {Record}
15563      */
15564     getSelected : function(){
15565         return this.selections.itemAt(0);
15566     },
15567
15568
15569     /**
15570      * Clears all selections.
15571      */
15572     clearSelections : function(fast){
15573         if(this.locked) return;
15574         if(fast !== true){
15575             var ds = this.grid.dataSource;
15576             var s = this.selections;
15577             s.each(function(r){
15578                 this.deselectRow(ds.indexOfId(r.id));
15579             }, this);
15580             s.clear();
15581         }else{
15582             this.selections.clear();
15583         }
15584         this.last = false;
15585     },
15586
15587
15588     /**
15589      * Selects all rows.
15590      */
15591     selectAll : function(){
15592         if(this.locked) return;
15593         this.selections.clear();
15594         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15595             this.selectRow(i, true);
15596         }
15597     },
15598
15599     /**
15600      * Returns True if there is a selection.
15601      * @return {Boolean}
15602      */
15603     hasSelection : function(){
15604         return this.selections.length > 0;
15605     },
15606
15607     /**
15608      * Returns True if the specified row is selected.
15609      * @param {Number/Record} record The record or index of the record to check
15610      * @return {Boolean}
15611      */
15612     isSelected : function(index){
15613         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15614         return (r && this.selections.key(r.id) ? true : false);
15615     },
15616
15617     /**
15618      * Returns True if the specified record id is selected.
15619      * @param {String} id The id of record to check
15620      * @return {Boolean}
15621      */
15622     isIdSelected : function(id){
15623         return (this.selections.key(id) ? true : false);
15624     },
15625
15626     // private
15627     handleMouseDown : function(e, t){
15628         var view = this.grid.getView(), rowIndex;
15629         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15630             return;
15631         };
15632         if(e.shiftKey && this.last !== false){
15633             var last = this.last;
15634             this.selectRange(last, rowIndex, e.ctrlKey);
15635             this.last = last; // reset the last
15636             view.focusRow(rowIndex);
15637         }else{
15638             var isSelected = this.isSelected(rowIndex);
15639             if(e.button !== 0 && isSelected){
15640                 view.focusRow(rowIndex);
15641             }else if(e.ctrlKey && isSelected){
15642                 this.deselectRow(rowIndex);
15643             }else if(!isSelected){
15644                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15645                 view.focusRow(rowIndex);
15646             }
15647         }
15648         this.fireEvent("afterselectionchange", this);
15649     },
15650     // private
15651     handleDragableRowClick :  function(grid, rowIndex, e) 
15652     {
15653         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15654             this.selectRow(rowIndex, false);
15655             grid.view.focusRow(rowIndex);
15656              this.fireEvent("afterselectionchange", this);
15657         }
15658     },
15659     
15660     /**
15661      * Selects multiple rows.
15662      * @param {Array} rows Array of the indexes of the row to select
15663      * @param {Boolean} keepExisting (optional) True to keep existing selections
15664      */
15665     selectRows : function(rows, keepExisting){
15666         if(!keepExisting){
15667             this.clearSelections();
15668         }
15669         for(var i = 0, len = rows.length; i < len; i++){
15670             this.selectRow(rows[i], true);
15671         }
15672     },
15673
15674     /**
15675      * Selects a range of rows. All rows in between startRow and endRow are also selected.
15676      * @param {Number} startRow The index of the first row in the range
15677      * @param {Number} endRow The index of the last row in the range
15678      * @param {Boolean} keepExisting (optional) True to retain existing selections
15679      */
15680     selectRange : function(startRow, endRow, keepExisting){
15681         if(this.locked) return;
15682         if(!keepExisting){
15683             this.clearSelections();
15684         }
15685         if(startRow <= endRow){
15686             for(var i = startRow; i <= endRow; i++){
15687                 this.selectRow(i, true);
15688             }
15689         }else{
15690             for(var i = startRow; i >= endRow; i--){
15691                 this.selectRow(i, true);
15692             }
15693         }
15694     },
15695
15696     /**
15697      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15698      * @param {Number} startRow The index of the first row in the range
15699      * @param {Number} endRow The index of the last row in the range
15700      */
15701     deselectRange : function(startRow, endRow, preventViewNotify){
15702         if(this.locked) return;
15703         for(var i = startRow; i <= endRow; i++){
15704             this.deselectRow(i, preventViewNotify);
15705         }
15706     },
15707
15708     /**
15709      * Selects a row.
15710      * @param {Number} row The index of the row to select
15711      * @param {Boolean} keepExisting (optional) True to keep existing selections
15712      */
15713     selectRow : function(index, keepExisting, preventViewNotify){
15714         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15715         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15716             if(!keepExisting || this.singleSelect){
15717                 this.clearSelections();
15718             }
15719             var r = this.grid.dataSource.getAt(index);
15720             this.selections.add(r);
15721             this.last = this.lastActive = index;
15722             if(!preventViewNotify){
15723                 this.grid.getView().onRowSelect(index);
15724             }
15725             this.fireEvent("rowselect", this, index, r);
15726             this.fireEvent("selectionchange", this);
15727         }
15728     },
15729
15730     /**
15731      * Deselects a row.
15732      * @param {Number} row The index of the row to deselect
15733      */
15734     deselectRow : function(index, preventViewNotify){
15735         if(this.locked) return;
15736         if(this.last == index){
15737             this.last = false;
15738         }
15739         if(this.lastActive == index){
15740             this.lastActive = false;
15741         }
15742         var r = this.grid.dataSource.getAt(index);
15743         this.selections.remove(r);
15744         if(!preventViewNotify){
15745             this.grid.getView().onRowDeselect(index);
15746         }
15747         this.fireEvent("rowdeselect", this, index);
15748         this.fireEvent("selectionchange", this);
15749     },
15750
15751     // private
15752     restoreLast : function(){
15753         if(this._last){
15754             this.last = this._last;
15755         }
15756     },
15757
15758     // private
15759     acceptsNav : function(row, col, cm){
15760         return !cm.isHidden(col) && cm.isCellEditable(col, row);
15761     },
15762
15763     // private
15764     onEditorKey : function(field, e){
15765         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15766         if(k == e.TAB){
15767             e.stopEvent();
15768             ed.completeEdit();
15769             if(e.shiftKey){
15770                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15771             }else{
15772                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15773             }
15774         }else if(k == e.ENTER && !e.ctrlKey){
15775             e.stopEvent();
15776             ed.completeEdit();
15777             if(e.shiftKey){
15778                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15779             }else{
15780                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15781             }
15782         }else if(k == e.ESC){
15783             ed.cancelEdit();
15784         }
15785         if(newCell){
15786             g.startEditing(newCell[0], newCell[1]);
15787         }
15788     }
15789 });/*
15790  * - LGPL
15791  *
15792  * element
15793  * 
15794  */
15795
15796 /**
15797  * @class Roo.bootstrap.MessageBar
15798  * @extends Roo.bootstrap.Component
15799  * Bootstrap MessageBar class
15800  * @cfg {String} html contents of the MessageBar
15801  * @cfg {String} weight (info | success | warning | danger) default info
15802  * @cfg {String} beforeClass insert the bar before the given class
15803  * @cfg {Boolean} closable (true | false) default false
15804  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15805  * 
15806  * @constructor
15807  * Create a new Element
15808  * @param {Object} config The config object
15809  */
15810
15811 Roo.bootstrap.MessageBar = function(config){
15812     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15813 };
15814
15815 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
15816     
15817     html: '',
15818     weight: 'info',
15819     closable: false,
15820     fixed: false,
15821     beforeClass: 'bootstrap-sticky-wrap',
15822     
15823     getAutoCreate : function(){
15824         
15825         var cfg = {
15826             tag: 'div',
15827             cls: 'alert alert-dismissable alert-' + this.weight,
15828             cn: [
15829                 {
15830                     tag: 'span',
15831                     cls: 'message',
15832                     html: this.html || ''
15833                 }
15834             ]
15835         }
15836         
15837         if(this.fixed){
15838             cfg.cls += ' alert-messages-fixed';
15839         }
15840         
15841         if(this.closable){
15842             cfg.cn.push({
15843                 tag: 'button',
15844                 cls: 'close',
15845                 html: 'x'
15846             });
15847         }
15848         
15849         return cfg;
15850     },
15851     
15852     onRender : function(ct, position)
15853     {
15854         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15855         
15856         if(!this.el){
15857             var cfg = Roo.apply({},  this.getAutoCreate());
15858             cfg.id = Roo.id();
15859             
15860             if (this.cls) {
15861                 cfg.cls += ' ' + this.cls;
15862             }
15863             if (this.style) {
15864                 cfg.style = this.style;
15865             }
15866             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15867             
15868             this.el.setVisibilityMode(Roo.Element.DISPLAY);
15869         }
15870         
15871         this.el.select('>button.close').on('click', this.hide, this);
15872         
15873     },
15874     
15875     show : function()
15876     {
15877         if (!this.rendered) {
15878             this.render();
15879         }
15880         
15881         this.el.show();
15882         
15883         this.fireEvent('show', this);
15884         
15885     },
15886     
15887     hide : function()
15888     {
15889         if (!this.rendered) {
15890             this.render();
15891         }
15892         
15893         this.el.hide();
15894         
15895         this.fireEvent('hide', this);
15896     },
15897     
15898     update : function()
15899     {
15900 //        var e = this.el.dom.firstChild;
15901 //        
15902 //        if(this.closable){
15903 //            e = e.nextSibling;
15904 //        }
15905 //        
15906 //        e.data = this.html || '';
15907
15908         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
15909     }
15910    
15911 });
15912
15913  
15914
15915